emscripten: Add epoll support - #5427
Merged
Merged
Conversation
6 tasks
guybedford
force-pushed
the
emscripten-apis
branch
from
August 18, 2026 23:37
1d5c1a0 to
16584c2
Compare
tgross35
reviewed
Aug 19, 2026
This comment has been minimized.
This comment has been minimized.
guybedford
force-pushed
the
emscripten-apis
branch
from
August 19, 2026 16:24
16584c2 to
88bcf99
Compare
This comment has been minimized.
This comment has been minimized.
Emscripten restored the `sys/epoll.h` header in 6.0.2 and implements `epoll_create`, `epoll_create1`, `epoll_ctl`, `epoll_wait` and `epoll_pwait` in the JS filesystem as of 6.0.8. Version detection in libc-test now also tracks the tiny version component so that point releases can be distinguished, and parses the uppercase `__EMSCRIPTEN_MAJOR__` macro forms used since Emscripten 5.0.1, where the lowercase names became non-integer aliases. Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L17 Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L70-L74 Link: emscripten-core/emscripten#27206 Link: emscripten-core/emscripten#27207
guybedford
force-pushed
the
emscripten-apis
branch
from
August 19, 2026 21:32
88bcf99 to
0cf3493
Compare
Collaborator
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
tgross35
approved these changes
Aug 20, 2026
tgross35
pushed a commit
to tgross35/rust-libc
that referenced
this pull request
Sep 4, 2026
Emscripten restored the `sys/epoll.h` header in 6.0.2 and implements `epoll_create`, `epoll_create1`, `epoll_ctl`, `epoll_wait` and `epoll_pwait` in the JS filesystem as of 6.0.8. Version detection in libc-test now also tracks the tiny version component so that point releases can be distinguished, and parses the uppercase `__EMSCRIPTEN_MAJOR__` macro forms used since Emscripten 5.0.1, where the lowercase names became non-integer aliases. Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L17 Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L70-L74 Link: emscripten-core/emscripten#27206 Link: emscripten-core/emscripten#27207 (backport <rust-lang#5427>) (cherry picked from commit 04c162b)
Merged
tgross35
pushed a commit
to tgross35/rust-libc
that referenced
this pull request
Sep 4, 2026
Emscripten restored the `sys/epoll.h` header in 6.0.2 and implements `epoll_create`, `epoll_create1`, `epoll_ctl`, `epoll_wait` and `epoll_pwait` in the JS filesystem as of 6.0.8. Version detection in libc-test now also tracks the tiny version component so that point releases can be distinguished, and parses the uppercase `__EMSCRIPTEN_MAJOR__` macro forms used since Emscripten 5.0.1, where the lowercase names became non-integer aliases. Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L17 Link: https://github.com/emscripten-core/emscripten/blob/4483d70a78098ed5d860dff2dc21f3025b2da2ee/system/lib/libc/musl/include/sys/epoll.h#L70-L74 Link: emscripten-core/emscripten#27206 Link: emscripten-core/emscripten#27207 (backport <rust-lang#5427>) (cherry picked from commit 04c162b)
guybedford
added a commit
to guybedford/tokio
that referenced
this pull request
Sep 9, 2026
Follow-on to tokio-rs#8285, enabling `tokio::net` on Emscripten over mio's epoll selector and Node's raw sockets (`-sNODERAWSOCKETS`). The I/O driver is the native one. Under JSPI, Emscripten's `epoll_wait` is a blocking wait that suspends on the host event loop, resuming on readiness (or the mio waker pipe) or the deadline, so `park` and `park_timeout` need nothing target-specific. The one addition is for the scheduler's zero-duration maintenance park: a zero-timeout `epoll_wait` is a synchronous probe, and the host loop is the only producer of readiness, so the driver yields one host turn first, as the zero-duration `ParkThread` park already does. Without JSPI a real wait panics rather than spinning, matching the existing park semantics. Under pthreads with `-sPROXY_TO_PTHREAD` the wait blocks on the worker as on native, with no target-specific code at all. TcpStream/TcpListener/UdpSocket, stream `AF_UNIX` sockets, `lookup_host` and `AsyncFd` work as on native. Gated where Node lacks the primitive: datagram `AF_UNIX` (`UnixDatagram`, `UnixSocket::new_datagram`), `socketpair(2)` (`UnixStream::pair`), and `SO_PEERCRED` (`peer_cred` reports unsupported). Name resolution goes through Emscripten's synchronous `getaddrinfo`, which maps hostnames to synthetic addresses, so the `localhost` tests are ignored on this target. CI adds `net` to both emscripten test runs on the released emsdk: JSPI (815 passed, 0 failed, 19 ignored) and pthreads, which regains `-sPROXY_TO_PTHREAD` (818 passed, 0 failed, 19 ignored). The JSPI run uses Rust beta until 1.99 is stable, which is where `OwnedFd::try_clone` (mio's registry handle) gains Emscripten support. TEMPORARY: `mio` comes from tokio-rs/mio#1969 until released, which in turn takes `libc` from the `libc-0.2` branch for its emscripten epoll bindings (rust-lang/libc#5427).
guybedford
added a commit
to guybedford/tokio
that referenced
this pull request
Sep 11, 2026
Follow-on to tokio-rs#8285, enabling `tokio::net` on Emscripten over mio's epoll selector and Node's raw sockets (`-sNODERAWSOCKETS`). The I/O driver is the native one. Under JSPI, Emscripten's `epoll_wait` is a blocking wait that suspends on the host event loop, resuming on readiness (or the mio waker pipe) or the deadline, so `park` and `park_timeout` need nothing target-specific beyond `jspi::io_wait`, which makes the wait a park in the JSPI sense: it leaves the runtime around the `epoll_wait` as the timer park does, so sibling activations may drive their own runtime while it is suspended. It also handles the scheduler's zero-duration maintenance park: a zero-timeout `epoll_wait` is a synchronous probe, and the host loop is the only producer of readiness, so the driver yields one host turn first, as the zero-duration `ParkThread` park already does. Without JSPI a real wait panics rather than spinning, matching the existing park semantics. Under pthreads with `-sPROXY_TO_PTHREAD` the wait blocks on the worker as on native, with no target-specific code at all. A wait with no deadline is a real `epoll_wait` that a socket could wake, so it suspends rather than panicking as the reactor-less park does; the `rt_emscripten_block_on` test of that panic is gated off `net`. TcpStream/TcpListener/UdpSocket, stream `AF_UNIX` sockets, `lookup_host` and `AsyncFd` work as on native. Gated where Node lacks the primitive: datagram `AF_UNIX` (`UnixDatagram`, `UnixSocket::new_datagram`), `socketpair(2)` (`UnixStream::pair`), and `SO_PEERCRED` (`peer_cred` reports unsupported). Name resolution goes through Emscripten's synchronous `getaddrinfo`, which maps hostnames to synthetic addresses, so the `localhost` tests are ignored on this target. CI adds `net` to both emscripten test runs on the released emsdk: JSPI (846 passed, 0 failed, 19 ignored) and pthreads, which regains `-sPROXY_TO_PTHREAD` (818 passed, 0 failed, 19 ignored). The JSPI lane links a `--pre-js` that fails a test binary whose `main` never returns: a park that suspends with no wake source leaves Node's event loop to drain and the process to exit 0, which cargo would otherwise take as success. The JSPI run uses Rust beta until 1.99 is stable, which is where `OwnedFd::try_clone` (mio's registry handle) gains Emscripten support. TEMPORARY: `mio` comes from tokio-rs/mio#1969 until released, which in turn takes `libc` from the `libc-0.2` branch for its emscripten epoll bindings (rust-lang/libc#5427).
guybedford
added a commit
to guybedford/tokio
that referenced
this pull request
Sep 11, 2026
Follow-on to tokio-rs#8285, enabling `tokio::net` on Emscripten over mio's epoll selector and Node's raw sockets (`-sNODERAWSOCKETS`). The I/O driver is the native one. Under JSPI, Emscripten's `epoll_wait` is a blocking wait that suspends on the host event loop, resuming on readiness (or the mio waker pipe) or the deadline, so `park` and `park_timeout` need nothing target-specific beyond `jspi::io_wait`, which makes the wait a park in the JSPI sense: it leaves the runtime around the `epoll_wait` as the timer park does, so sibling activations may drive their own runtime while it is suspended. It also handles the scheduler's zero-duration maintenance park: a zero-timeout `epoll_wait` is a synchronous probe, and the host loop is the only producer of readiness, so the driver yields one host turn first, as the zero-duration `ParkThread` park already does. Without JSPI a real wait panics rather than spinning, matching the existing park semantics. Under pthreads with `-sPROXY_TO_PTHREAD` the wait blocks on the worker as on native, with no target-specific code at all. A wait with no deadline is a real `epoll_wait` that a socket could wake, so it suspends rather than panicking as the reactor-less park does; the `rt_emscripten_block_on` test of that panic is gated off `net`. TcpStream/TcpListener/UdpSocket, stream `AF_UNIX` sockets, `lookup_host` and `AsyncFd` work as on native. Gated where Node lacks the primitive: datagram `AF_UNIX` (`UnixDatagram`, `UnixSocket::new_datagram`), `socketpair(2)` (`UnixStream::pair`), and `SO_PEERCRED` (`peer_cred` reports unsupported). Name resolution goes through Emscripten's synchronous `getaddrinfo`, which maps hostnames to synthetic addresses, so the `localhost` tests are ignored on this target. CI adds `net` to both emscripten test runs on the released emsdk: JSPI (846 passed, 0 failed, 19 ignored) and pthreads, which regains `-sPROXY_TO_PTHREAD` (818 passed, 0 failed, 19 ignored). The JSPI lane links a `--pre-js` that fails a test binary whose `main` never returns: a park that suspends with no wake source leaves Node's event loop to drain and the process to exit 0, which cargo would otherwise take as success. The JSPI run uses Rust beta until 1.99 is stable, which is where `OwnedFd::try_clone` (mio's registry handle) gains Emscripten support. TEMPORARY: `mio` comes from tokio-rs/mio#1969 until released, which in turn takes `libc` from the `libc-0.2` branch for its emscripten epoll bindings (rust-lang/libc#5427).
guybedford
added a commit
to guybedford/tokio
that referenced
this pull request
Sep 14, 2026
Follow-on to tokio-rs#8285, enabling `tokio::net` on Emscripten over mio's epoll selector and Node's raw sockets (`-sNODERAWSOCKETS`). The I/O driver is the native one. Under JSPI, Emscripten's `epoll_wait` is a blocking wait that suspends on the host event loop, resuming on readiness (or the mio waker pipe) or the deadline, so `park` and `park_timeout` need nothing target-specific beyond `jspi::io_wait`, which handles the scheduler's zero-duration maintenance park: a zero-timeout `epoll_wait` is a synchronous probe, and the host loop is the only producer of readiness, so the driver yields one host turn first, as the zero-duration `ParkThread` park already does. Without JSPI a real wait panics rather than spinning, matching the existing park semantics. Under pthreads with `-sPROXY_TO_PTHREAD` the wait blocks on the worker as on native, with no target-specific code at all. A wait with no deadline is a real `epoll_wait` that a socket could wake, so it suspends rather than panicking as the reactor-less park does; the `rt_emscripten_block_on` test of that panic is gated off `net`. TcpStream/TcpListener/UdpSocket, stream `AF_UNIX` sockets, `lookup_host` and `AsyncFd` work as on native. Gated where Node lacks the primitive: datagram `AF_UNIX` (`UnixDatagram`, `UnixSocket::new_datagram`), `socketpair(2)` (`UnixStream::pair`), and `SO_PEERCRED` (`peer_cred` reports unsupported). Name resolution goes through Emscripten's synchronous `getaddrinfo`, which maps hostnames to synthetic addresses, so the `localhost` tests are ignored on this target. CI adds `net` to both emscripten test runs on the released emsdk: JSPI (846 passed, 0 failed, 19 ignored) and pthreads, which regains `-sPROXY_TO_PTHREAD` (818 passed, 0 failed, 19 ignored). The JSPI lane links a `--pre-js` that fails a test binary whose `main` never returns: a park that suspends with no wake source leaves Node's event loop to drain and the process to exit 0, which cargo would otherwise take as success. The JSPI run uses Rust beta until 1.99 is stable, which is where `OwnedFd::try_clone` (mio's registry handle) gains Emscripten support. TEMPORARY: `mio` comes from tokio-rs/mio#1969 until released, which in turn takes `libc` from the `libc-0.2` branch for its emscripten epoll bindings (rust-lang/libc#5427).
guybedford
added a commit
to guybedford/mio
that referenced
this pull request
Sep 15, 2026
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 cfg lists gain emscripten. 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 test peers are std sockets on helper threads, so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. Emscripten sockets never block - a call that would block returns EAGAIN - so the peers' blocking `accept`/`read` go through `util::accept`/`util::read`, which on emscripten wait for readiness with `poll(2)` (which does park a pthread) and retry; elsewhere they are the plain std calls. This runs on a released emsdk (6.0.9) with no emscripten patches. Nightly + rust-src are needed for -Zbuild-std; no custom target spec is required since nightly 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. Temporary, until released: Cargo.toml takes libc from the `libc-0.2` branch for the emscripten epoll bindings (rust-lang/libc#5427). Suite result: 147 passed, 0 failed, 3 ignored under Node. The one emscripten-specific ignore is `tcp_stream::raw_fd` (`getsockname` after a non-blocking connect can transiently report an unbound local address).
guybedford
added a commit
to guybedford/mio
that referenced
this pull request
Sep 15, 2026
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 cfg lists gain emscripten. 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 test peers are std sockets on helper threads, so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. Emscripten sockets never block - a call that would block returns EAGAIN - so the peers' blocking `accept`/`read` go through `util::accept`/`util::read`, which on emscripten wait for readiness with `poll(2)` (which does park a pthread) and retry; elsewhere they are the plain std calls. This runs on a released emsdk (6.0.9) with no emscripten patches. Nightly + rust-src are needed for -Zbuild-std; no custom target spec is required since nightly 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. Temporary, until released: Cargo.toml takes libc from the `libc-0.2` branch for the emscripten epoll bindings (rust-lang/libc#5427). Suite result: 147 passed, 0 failed, 3 ignored under Node. The one emscripten-specific ignore is `tcp_stream::raw_fd` (`getsockname` after a non-blocking connect can transiently report an unbound local address).
guybedford
added a commit
to guybedford/tokio
that referenced
this pull request
Sep 18, 2026
Follow-on to tokio-rs#8285, enabling `tokio::net` on Emscripten over mio's epoll selector and Node's raw sockets (`-sNODERAWSOCKETS`). The I/O driver is the native one. Under JSPI, Emscripten's `epoll_wait` is a blocking wait that suspends on the host event loop, resuming on readiness (or the mio waker pipe) or the deadline, so `park` and `park_timeout` need nothing target-specific beyond `jspi::io_wait`, which handles the scheduler's zero-duration maintenance park: a zero-timeout `epoll_wait` is a synchronous probe, and the host loop is the only producer of readiness, so the driver yields one host turn first, as the zero-duration `ParkThread` park already does. Without JSPI a real wait panics rather than spinning, matching the existing park semantics. Under pthreads with `-sPROXY_TO_PTHREAD` the wait blocks on the worker as on native, with no target-specific code at all. A wait with no deadline is a real `epoll_wait` that a socket could wake, so it suspends rather than panicking as the reactor-less park does; the `rt_emscripten_block_on` test of that panic is gated off `net`. TcpStream/TcpListener/UdpSocket, stream `AF_UNIX` sockets, `lookup_host` and `AsyncFd` work as on native. Gated where Node lacks the primitive: datagram `AF_UNIX` (`UnixDatagram`, `UnixSocket::new_datagram`), `socketpair(2)` (`UnixStream::pair`), and `SO_PEERCRED` (`peer_cred` reports unsupported). Name resolution goes through Emscripten's synchronous `getaddrinfo`, which maps hostnames to synthetic addresses, so the `localhost` tests are ignored on this target. CI adds `net` to both emscripten test runs on the released emsdk: JSPI (846 passed, 0 failed, 19 ignored) and pthreads, which regains `-sPROXY_TO_PTHREAD` (818 passed, 0 failed, 19 ignored). The JSPI lane links a `--pre-js` that fails a test binary whose `main` never returns: a park that suspends with no wake source leaves Node's event loop to drain and the process to exit 0, which cargo would otherwise take as success. The JSPI run uses Rust beta until 1.99 is stable, which is where `OwnedFd::try_clone` (mio's registry handle) gains Emscripten support. TEMPORARY: `mio` comes from tokio-rs/mio#1969 until released, which in turn takes `libc` from the `libc-0.2` branch for its emscripten epoll bindings (rust-lang/libc#5427).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds the epoll APIs for the
wasm32-unknown-emscriptentarget. Emscripten restored thesys/epoll.hheader in 6.0.2 (emscripten-core/emscripten#27206) and implements the epoll functions in the JS filesystem as of the upcoming 6.0.8 (emscripten-core/emscripten#27207).EPOLL_CLOEXECandepoll_create,epoll_create1,epoll_ctl,epoll_waitandepoll_pwaitfor Emscripten (theepoll_eventtype andEPOLL*constants were already defined for the target)test_emscripten, version-gated so that all currently released Emscripten versions (including the 3.1.68 CI pin) are unaffected__EMSCRIPTEN_major__macros are non-integer aliases of new uppercase forms, and the tiny version component is now tracked so that point releases can be distinguishedTested against Emscripten stable 6.0.7, where the header, type and constant tests run and pass, with the function link tests remaining version-gated until 6.0.8, and against the fully gated skip path.