Skip to content

Add wasm32-unknown-emscripten target support#1969

Draft
guybedford wants to merge 1 commit into
tokio-rs:masterfrom
guybedford:emscripten
Draft

Add wasm32-unknown-emscripten target support#1969
guybedford wants to merge 1 commit into
tokio-rs:masterfrom
guybedford:emscripten

Conversation

@guybedford

@guybedford guybedford commented Jul 11, 2026

Copy link
Copy Markdown

Draft PR for #642.

Adds wasm32-unknown-emscripten as a target for mio, with a CI job running the suite under Node. Opening as a draft for now since it depends on unreleased patchsets for Emscripten and Rust's libc.

This lays the groundwork for Tokio support for Emscripten. Initially I did not plan to PR Mio, but in discussion with @Darksonn and @Noah-Kennedy it was suggested to use Mio if possible, and this has worked out well in my opinion.

Once emscripten-core/emscripten#27207 lands, Emscripten exposes a real epoll, so the existing Linux epoll selector can reused rather than adding a new backend.

Patch Sets

This PR will remain a draft while it relies on the following upstream patches:

In addition Rust nightly is needed for two additional patches only landed recently.

Test status

Tests run directly on the Emscripten Node.js build with NODERAWSOCKETS and NODERAWFS to provide a transparent runner without further harness configuration being necessary.

Tests use JSPI to be able to support the harness integration, but can work without JSPI when integrating with the callback model - a dedicated emscripten test is included for these.

Early feedback on the approach very much welcome.

Comment thread src/poll.rs Outdated
@guybedford

guybedford commented Jul 12, 2026 via email

Copy link
Copy Markdown
Author

@Darksonn

Copy link
Copy Markdown
Member

Why do you want the callback mechanism on the Tokio side rather than just storing them in this buffer until the next time Tokio calls poll? I'm not sure we can safely invoke wakers from the callback anyway.

@guybedford guybedford force-pushed the emscripten branch 3 times, most recently from e7f7165 to e3ecd61 Compare July 13, 2026 20:53
Adds `wasm32-unknown-emscripten` as a target for mio, plus a CI job that runs
the suite under Node. Resolves tokio-rs#642.

Emscripten exposes a real epoll backed by its runtime event loop, so the
existing Linux epoll selector is reused rather than adding a new backend. The
wasm `compile_error!` guard is relaxed to let emscripten through, and the
`epoll`/`eventfd`/pipe-waker cfg lists gain emscripten.

Because emscripten cannot block in `epoll_wait` without JSPI/ASYNCIFY, the
epoll fd is exposed via `AsRawFd` on `Poll`/`Registry` so the runtime driving
mio can arm emscripten's `emscripten_epoll_set_callback` on it directly,
signalling on a fresh host tick whenever the set has ready events. A
zero-timeout `Poll::poll` remains available as a synchronous readiness probe
where a blocking wait is impossible.

AF_UNIX support is stream-only: emscripten's node-backed sockets have no
datagram primitive, so `UnixDatagram` and the `socketpair`-based helpers are
not compiled there. Sockets set `O_NONBLOCK` via `fcntl` since emscripten's
`socket(2)` silently strips `SOCK_NONBLOCK`/`SOCK_CLOEXEC`.

The suite spawns OS threads (socket-peer test harnesses), so std is rebuilt
with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD.
Blocking harness socket ops (accept/recv) block their pthread through
emscripten's sync-proxy wait primitive (`_emscripten_fd_wait`) - no JSPI or
stack suspension is involved anywhere in the suite. NODERAWFS/NODERAWSOCKETS
back the filesystem and sockets with node's. This needs nightly + rust-src.
No custom target spec is required: nightly now emits the __main_argc_argv
entry point (rust-lang/rust#158937). Doctests are skipped on this target:
rustdoc does not apply the emcc link args, so the examples cannot be linked
with the socket/thread runtime.

Temporary, until the dependencies land upstream:

* Cargo.toml patches libc to guybedford/libc#emscripten for the emscripten
  epoll/pthread externs (rust-lang/libc#5270).
* the CI job builds against the guybedford/emscripten `cf` fork, which carries
  the epoll callback, `_emscripten_fd_wait` blocking-ops, AF_UNIX pathname
  stream socket, and multicast getsockopt patches this target depends on.

The two `reconnect_udp_socket_*` tests are ignored on emscripten: libuv does
not re-associate a connected UDP socket when it is reconnected to a new peer,
so no readiness is delivered for the new peer.

Suite result: 144 passed, 0 failed, 5 ignored under Node - green on CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants