dannymeijer opened issue #14288:
Summary
wasmtime-wasi'sp1feature cannot be enabled without also enablingwasmtime/async, which pulls inwasmtime-internal-fiberand therefore requires a working C toolchain. Embedders that use only the synchronous WASIp1 API pay that cost for a capability they never touch.The chain
In
wasmtime-wasi48.0.1:[features] default = ["p1", "p2"] p0 = ["p1"] p1 = ["dep:wiggle", "p2"] p2 = ["wasmtime/component-model", "wasmtime/async"]
p1requiresp2, andp2enableswasmtime/async. Inwasmtime48.0.1:async = [ "dep:wasmtime-fiber", "wasmtime-component-macro?/async", "runtime", ]And
wasmtime-internal-fiber48.0.1 shipssrc/windows.c, compiled throughccin its build script. So enablingp1transitively makes a pure-Rust build into one that needs a C compiler.There is no feature combination that avoids this:
default-features = false, features = ["p1"]still reacheswasmtime/asyncthroughp2.Why this is surprising
p1's own module documentation describes the relationship as incidental rather than semantic:Support for WASIp1 is built on top of support for WASIp2 available at the crate root, but that's just an internal implementation detail.
And
p1offersadd_to_linker_syncalongsideadd_to_linker_async, so a fully synchronous embedder is a first-class supported use case. Our embedding usesp1::add_to_linker_syncand contains nocall_asyncorasync_supportanywhere, yet still requires fibers.Concrete impact
We distribute a compiler toolchain that embeds Wasmtime to run WASIp1 guest modules. On Windows the fiber dependency turns "download our release and run it" into "download our release and also install Visual Studio Build Tools or MinGW-w64", solely to build a feature we never call. For a language toolchain trying to offer a self-contained install, that is a significant prerequisite to inherit from an unused code path.
This is not Windows-specific in principle — it is a C toolchain requirement for every sync-only embedder on every host — but Windows is where it is most visible, because a C compiler is not present by default.
Request
Could
p1be usable withoutwasmtime/async? Two shapes that would work, without preference between them:
- Split
p2sowasmtime/component-modelandwasmtime/asynccan be enabled independently, lettingp1depend only on the former.- Give
wasmtime-wasianasyncfeature, default-on for compatibility, thatdefault-features = falsecan switch off, withadd_to_linker_asyncgated behind it.If the
p1→p2→asyncpath is load-bearing rather than incidental, that would be useful to know too — we would then document the C toolchain as a permanent prerequisite rather than waiting on it.Happy to help
We have a native Windows x64 environment set up and can test a patch on both the sync path and the removal of the C-compiler requirement.
Versions
Observed on
wasmtime-wasi48.0.1 andwasmtime48.0.1; the same relationship holds in 46.0.3 and 47.0.4. In 36.x the coupling was even tighter —asyncwas hardcoded in[dependencies.wasmtime]rather than feature-gated — so this has already improved, and the remaining step is thep1→p2edge.
alexcrichton commented on issue #14288:
This is something that's unlikely to change in the design of
wasmtime-wasias-is today. The wasip1 implementation is built on wasip2, and the wasip2 implementation is foundationally built on async. Changing anything there (e.g. a standalone sync wasip1 implementation) would be a large duplication of what's already there and not something that would be too viable for us to maintain.Can you speak a bit more though to the impact of requiring a C toolchain? For example what target are you compiling for? It might be something we could improve in our build since C shouldn't be needed for all targets at least.
dannymeijer commented on issue #14288:
Thanks — that's a clear answer on the design side, and the reasoning makes sense.
The target is
x86_64-pc-windows-msvc.We're Incan, a language that compiles to Rust. Packages can ship compile-time source transformations, and those run as sandboxed
wasm32-wasip1guests inside the compiler — third-party code executing during a build, so the sandbox is the whole reason Wasmtime is there rather than a component we could swap out. Our use is entirely synchronous: one linker,preview1::add_to_linker_sync, nocall_asyncanywhere in the tree.The impact is that our Windows install instructions become "download the release, then install Visual Studio Build Tools or MinGW-w64". We ship a self-contained archive carrying its own pinned Rust toolchain specifically so users don't assemble an environment, and a C compiler is the one prerequisite we can't put in the archive. On Linux and macOS nobody notices, since a C compiler is usually already there.
Our side of this is tracked in encero-systems/incan#1344, which has the full dependency chain and our version pinning if that's useful — it's written for our own contributors, so it carries some of our terminology.
A few things we noticed while tracing where the C comes from. Observations rather than a proposal; you'll know what they're worth:
wasmtime-internal-fiber'sbuild.rsonly reaches forccon two targets,windowsands390x. Everything else returns early with the comment "assume that this is included via inline assembly in the crate itself". From outside that makes the requirement look narrower than "the build needs C" — more like Windows being one of the last two targets that hasn't moved.The Windows side is
src/windows.c: nine lines, one function,wasmtime_fiber_get_current, returningGetCurrentFiber(). We're assuming it's C becauseGetCurrentFiber()is awinnt.hmacro rather than a real export, so it isn't reachable throughwindows-systhe way an ordinary Win32 call would be.We were curious whether the value is reachable from Rust anyway. On x86_64 it's the TEB slot at
gs:[0x20], and an inline-asm read returned exactly the pointerConvertThreadToFiberhad handed back:
ConvertThreadToFiber -> 0x1e6b04be9f0 gs:[0x20] (asm read) -> 0x1e6b04be9f0Two limits, in case that reads as more than it is: we only tried x86_64, and aarch64-pc-windows-msvc uses a different TEB offset, so it isn't a whole-target answer. And the read shouldn't carry
pure/readonly, since the value legitimately changes across a fiber switch.None of that accounts for constraints we can't see — the versioned symbol export, sanitizer builds, the aarch64 side — so it isn't a patch proposal, just what we found working out where the requirement came from.
Happy to test anything on a native Windows x64 host; we have one set up and are working through Windows support at the moment.
alexcrichton commented on issue #14288:
Ah ok yeah so Windows is the sole platform that needs C code for the fiber support, and that's purely because we haven't been able to find public Microsoft-hosted documentation about the TEB/TIB. I've found documentation that it is indeed
gs:[0x20]and such, but I haven't been able to find any sources from Microsoft itself about the stability of these fields.If you're able to find large projects (e.g. Chrome or Firefox or something) which already do the inline-asm thing then I'd be more comfortable adding that here. In lieu of that though there's not a ton else we can do because this is just an API that Windows only allows stable access of through C/C++, not any other language.
dannymeijer commented on issue #14288:
Sure Alex — completly understand where you're coming from. I'll look into this and let you know.
Last updated: Sep 20 2026 at 18:08 UTC