Add wasm32-unknown-emscripten target support - #1969
Conversation
|
Yeah that could work - let me see if I can come up with a way for the
callback mechanism to only be on the Tokio side instead.
…On Sun, Jul 12, 2026 at 14:34 Alice Ryhl ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/poll.rs
<#1969 (comment)>:
> @@ -444,6 +444,40 @@ impl Poll {
}
}
+#[cfg(all(target_os = "emscripten", feature = "os-poll"))]
+impl Poll {
+ /// Create a `Poll` whose epoll set carries a persistent, non-blocking
+ /// readiness callback for the lifetime of the `Poll`. Instead of blocking in
+ /// [`Poll::poll`], the emscripten runtime delivers up to `capacity` ready
+ /// events to `callback` on a fresh host tick whenever the set makes progress
+ /// (needs no JSPI/ASYNCIFY). The callback is unregistered when the `Poll` is
+ /// dropped.
+ ///
+ /// The `callback` may freely re-enter this `Poll`/`Registry` (e.g. register
+ /// or deregister sources): it holds no internal borrow while running.
+ pub fn new_with_callback<F>(capacity: usize, callback: F) -> io::Result<Poll>
The code already copies the events into the Events buffer, so instead of
passing them to a callback, I don't see why you can't just keep them around
in said buffer until the next call to poll and return them to the caller
then. That wouldn't need any user-facing API change.
—
Reply to this email directly, view it on GitHub
<#1969?email_source=notifications&email_token=AAESFSWFKSGVAOT73HVO2FT5EP75RA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTINRYGA4DEMJSGI4KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#discussion_r3567166992>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAESFSVF7QLBRUCJ7LS35NL5EP75RAVCNFSNUABEKJSXA33TNF2G64TZHMZDGMJTHA4TQNB3JFZXG5LFHM2DQNRQGAZDGMJUHGQXMAQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
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 |
e3ecd61 to
4c43fd5
Compare
Darksonn
left a comment
There was a problem hiding this comment.
It looks like you were able to reduce this down to merely adjusting cfgs, which is great, thanks!
Thomasdezeeuw
left a comment
There was a problem hiding this comment.
I'm not sure what to do about the missing public api (UnixStream::pair and UnixDatagram). Removing them, as this API does, is one option, we can also include them, but always return an unsupported error.
What do you think @Darksonn?
|
|
||
| #[cfg_attr( | ||
| target_os = "emscripten", | ||
| ignore = "libuv does not re-associate a connected UDP socket on reconnect" |
There was a problem hiding this comment.
How is this related to libuv?
There was a problem hiding this comment.
The test runner here is Node.js. See libuv/libuv#5193 for the upstream PR (which it seems will likely not be approved).
|
My suggestion would be to remove the unsupported APIs. IMO it's no different from the fact that |
I've gone ahead and implemented this. |
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, delivering ready events on a fresh host tick whenever the set makes progress. 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 so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). 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 AF_UNIX pathname stream sockets 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.
… raw_fd ignore reason
Draft PR for #642.
Adds
wasm32-unknown-emscriptenas 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
NODERAWSOCKETSandNODERAWFSto 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-based hosted event loop model, which will also be supported on the Tokio side. Initially the callback mode was also tested here, but this was removed based on initial PR feedback.
tcp_stream::raw_fd(pending net: support sync connect for BoundSocket nodejs/node#64375), plus twoPOLLRDHUP/close-event cases shared with other targets, and two rebinding ephemeral port errors.Early feedback on the approach very much welcome.