There is a class of software problem best described as two well-meaning systems showing up to the same party each convinced it was the host. Cloudflare has just announced the resolution of one such standoff, and the result — an experimental preview, mind you — is that native Rust code, and eventually Rust’s mighty async engine Tokio, can run on Cloudflare Workers. The announced vehicle is a DeLorean of niche compiler knowledge: first-class support for the wasm32-unknown-emscripten Rust target on wasm-bindgen, the open-source toolchain that powers Rust-based WebAssembly applications on Cloudflare’s V8-based Workers runtime.

To appreciate what happened, you need to meet the two rivals. Emscripten is an open-source WebAssembly compiler toolchain, originally created by Mozilla and now maintained by Google engineers, that bridges native code onto the web platform by virtualizing things like timers, file systems and sockets. wasm-bindgen does something adjacent for Rust: it generates the glue between Rust code and JavaScript. Both jobs involve bossing around the same JavaScript output. According to Cloudflare’s blog post, “both tools assume they are in charge of loading and interacting with JavaScript and generating the final JS and Wasm output,” which meant Google’s internal users had to pick one and only one — and adding a second set of tooling would have doubled the support surface for Google’s Portable Toolchains team.

The person who tripped over the problem was Mitch Foley of Google’s Portable Toolchains team, who discovered it when another internal Google team wanted wasm-bindgen to interface their C++ with JavaScript. Foley and his colleague Yifan Yang devised the peace plan: Emscripten would continue to drive the build, load the Wasm module and provide the companion JavaScript, while wasm-bindgen would produce a smaller, portable version of its JavaScript bindings in a format that could slot directly into Emscripten’s library system. In the marriage, one drives, the other packs.

This was not purely a technical negotiation, the post notes — both Emscripten and the wasm-bindgen maintainers had to agree to support the plan and maintain integration tests depending on the other project. After feedback from Google’s Portable Toolchains team, Google’s Wasm Tools team and Cloudflare’s engineers, the changes landed in both projects. The toolchains now interoperate under a configuration flag with the satisfyingly frictionless name -sWASM_BINDGEN — Cloudflare says the effort was initiated by Google over a year ago.

Tokio, and why your Rust runtime objects to being turned off and on again

With the target supported, many Rust libraries worked out of the box. Some low-level libraries — libc, socket2 and Mio — needed small patches, mostly just telling them that target_os = “emscripten” was an acceptable place to live. Library maintainers, Cloudflare notes, were “very receptive” to reviewing the support. The real monster was Tokio, Rust’s premier async runtime.

The incompatibility is almost philosophical. Cloudflare Workers are single-threaded and hosted inside a JavaScript event loop; Tokio is designed around blocking operations handled through threaded parking — which is to say, it likes to put work to sleep and wake it later, behavior an event loop tolerates rather less well than a cat tolerates a bath. A blocking socket read cannot block the shared JavaScript loop. Cloudflare had two ways out: WebAssembly JavaScript Promise Integration (JSPI), or surgery on Tokio itself to add event-loop runtime integration. Its contributed patchsets support both, and the first Tokio patch for the Emscripten target has already landed upstream.

JSPI is the delightful one. It lets a blocking Wasm call suspend its stack and hand control back to the JavaScript event loop — exactly the parking behavior Tokio expects. But it goes further: a new Wasm call can arrive while an earlier one is suspended, entering a brand-new stack while arbitrary old stacks hang in limbo. The trouble is that Rust has no idea any of this is happening. Tokio tracks its runtime context in thread-local storage, and a stack switch is not a thread switch — so the suspended stack and the new stack appear to share one runtime context. Tokio, believing it is already running, then panics “because the runtime is already entered,” a tantrum I think about often, in most group projects.

The fix, in Cloudflare’s pre-release patchset, is to swap the thread-local context on every JSPI enter, exit, suspend and resume — “cooperative time-multiplexed threading,” with each suspended stack politely carrying its own context around. Finalizing that design and getting it upstream is ongoing with the Tokio and Emscripten teams. The alternative route — teaching Tokio a native event-loop runtime that works across Windows, macOS and JavaScript hosts — was not alien to Tokio’s maintainers (event loops are an old native-UI paradigm), but required working out a cross-platform design.

All of this is pre-release: Cloudflare has published the experimental patchsets with example applications covering building Emscripten Rust Workers, running the Tokio async runtime in a Worker, and TCP sockets on Workers via Emscripten and Tokio. Compatibility is reportedly “significantly improved,” and support for Tokio-based applications running natively on Workers is coming.

And the proof of concept, the thing Cloudflare chose to demonstrate that years of toolchain diplomacy had worked, is this: Pumpkin, an open-source Rust-native Minecraft server, running inside a Durable Object with TCP ingress, using real TCP sockets via Tokio. A Minecraft server, living in a Durable Object, because two compiler projects agreed to share the driving. Computer science is, at heart, the art of doing absurdly difficult things so that subsequent things can be slightly silly, and this is close to the ideal.