feat: JSPI support for wasm32-unknown-emscripten - #1
Open
guybedford wants to merge 72 commits into
Open
guybedford wants to merge 72 commits into
guybedford wants to merge 72 commits into
Conversation
guybedford
force-pushed
the
emscripten-jspi
branch
4 times, most recently
from
July 18, 2026 03:34
6121ff9 to
3258304
Compare
guybedford
force-pushed
the
emscripten-target
branch
from
July 28, 2026 01:51
44ba412 to
f6527a5
Compare
guybedford
force-pushed
the
emscripten-jspi
branch
from
July 28, 2026 01:53
25e78d4 to
54a7119
Compare
guybedford
force-pushed
the
emscripten-jspi
branch
from
August 5, 2026 19:06
54a7119 to
ecfecd3
Compare
guybedford
force-pushed
the
emscripten-target
branch
from
August 25, 2026 16:49
7e34bc3 to
37399a1
Compare
guybedford
force-pushed
the
emscripten-jspi
branch
from
August 25, 2026 19:09
5d67f7c to
e5bcb09
Compare
guybedford
force-pushed
the
emscripten-target
branch
from
August 26, 2026 01:04
e03f205 to
1a62cae
Compare
guybedford
force-pushed
the
emscripten-jspi
branch
2 times, most recently
from
August 26, 2026 01:05
bb7a461 to
23060f5
Compare
guybedford
force-pushed
the
emscripten-target
branch
from
August 26, 2026 01:11
1a62cae to
fab0ff8
Compare
guybedford
force-pushed
the
emscripten-jspi
branch
3 times, most recently
from
August 26, 2026 02:03
c83349a to
64c8614
Compare
guybedford
force-pushed
the
emscripten-target
branch
from
August 26, 2026 02:03
6c62214 to
570a195
Compare
`github.event.pull_request.base.ref` is empty for push events, so the workspace semver check runs on `tokio-1.*.x` branches. Also check `github.ref_name` to preserve the intended release-branch exclusion while leaving pull request behavior unchanged. Fixes: tokio-rs#8389
Per bytecodealliance/wasmtime#13558, Wasmtime v46.0.1 was the last release to support `wasm32-wasip1-threads`, so we use that for the WASIp1 testing. For WASIp2, we should be able to use any recent version of Wasmtime, but we pin to a specific version anyway to avoid surprises. (cherry picked from commit 5760ccd)
guybedford
force-pushed
the
emscripten-target
branch
from
September 3, 2026 21:09
49c9f3f to
f6ec781
Compare
Co-authored-by: philphauler <webfastonline@gmail.com>
Co-authored-by: philphauler <webfastonline@gmail.com>
Co-authored-by: philphauler <webfastonline@gmail.com>
Co-authored-by: philphauler <webfastonline@gmail.com>
guybedford
force-pushed
the
emscripten-jspi
branch
from
September 6, 2026 20:45
64c8614 to
d2a8827
Compare
Implement futures_core::FusedStream for tokio_stream::Empty and tokio_stream::Once. - Empty is always terminated. - Once is terminated once its single value has been taken. Matches futures-util behavior.
guybedford
force-pushed
the
emscripten-jspi
branch
2 times, most recently
from
September 14, 2026 16:41
b5e76c3 to
995f5ea
Compare
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 7. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v4...v7) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [mymindstorm/setup-emsdk](https://github.com/mymindstorm/setup-emsdk) from 14 to 16. - [Release notes](https://github.com/mymindstorm/setup-emsdk/releases) - [Commits](emscripten-core/setup-emsdk@v14...v16) --- updated-dependencies: - dependency-name: mymindstorm/setup-emsdk dependency-version: '16' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alice Ryhl <aliceryhl@google.com>
The action moved from mymindstorm/setup-emsdk to the emscripten-core org.
guybedford
force-pushed
the
emscripten-jspi
branch
from
September 17, 2026 21:21
a5aa188 to
b7a99fb
Compare
Follow-on to tokio-rs#8281, supporting JSPI as an alternative approach to suspension. A `context::jspi` module provides the `__asyncjs__` suspension import timer, the only suspending import, as well as a feature check for JSPI being used. The park leaf suspends the whole Wasm stack on a host timer, so the host loop runs while Tokio waits and resumes it when the timer fires. The scheduler, task system and time driver are the canonical ones, tokio-macros is untouched, and no public API is added. The scheduler's `event_interval` maintenance park arrives as a zero-duration park and resumes through a host immediate, so a busy scheduler still gives the host loop a turn, the role the non-blocking I/O poll plays on native: the immediate it schedules next lands in the following event loop iteration, which begins by running expired timers. Without JSPI the zero-duration park is a no-op, as on native. `setTimeout(0)` also works but is clamped to a millisecond, and a microtask queue does not work at all, since those drain before the loop advances. The runtime stays entered while parked, as on native: a `block_on` from another promising activation on the same thread during the park panics as a nested runtime. Driving the runtime from several activations is a follow-on. Coverage expands from one run of the suite in which a timed wait blocked the thread, plus a single pthread test file, to three lanes: * non-JSPI: 4 passed, 0 failed * JSPI: 788 passed, 0 failed, 10 ignored * pthreads (`-pthread`): 762 passed, 0 failed, 10 ignored In the process, the pthreads test suite is ungated as well, and changed to share the blocking shim, since Emscripten has a sync-only FS. Real-timer tests for Emscripten are consolidated into the single `rt_emscripten_jspi.rs` file. * `spawn_blocking` remains unsupported in non-pthread builds * `net` (epoll, over tokio-rs/mio#1969) remains the follow-up * Without `-sJSPI`, a non-zero wait now panics rather than blocking the thread through `std::thread::sleep`. * A wait with no deadline panics in either mode. There is no reactor here, so host timers are the only mid-park wake source and nothing could ever deliver the wake. Refs: tokio-rs#8281
Moves the JSPI primitives to runtime::jspi so the thread parker behind blocking_recv and friends suspends in sync-only builds as well. The park and scheduler-context state now restore on unwind, and a stale JS park slot can no longer remove its successor's entry.
guybedford
force-pushed
the
emscripten-jspi
branch
from
September 18, 2026 02:08
b7a99fb to
c2a3138
Compare
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.
Motivation
Supporting the
wasm32-unknown-emscriptenplatform.Solution
This extends and is based to tokio-rs#8281, the single-threaded wasm32-unknown-emscripten target, to support JavaScript Promise Integration (JSPI)-based suspension.
Under a single-threaded build, Emscripten shares the host event loop with JavaScript, throwing on any blocking operations.
With JSPI, operations may suspend the Wasm execution on a JS promise allowing the runtime to be suspended but reentrant. For this reason, this is not like a wake, but instead multiple of these parked JSPI blocks can coexist and be woken in any order. When JSPI is enabled, and a
block_onfuture would have to suspend (a timer deadline, an external wake, or a deferred yield_now's host turn), the calling Wasm stack parks on the host loop via JSPI-specific parking and resumes on the wake.Runtime::block_on,Handle::block_on, and theblocking_*sync APIs all behave as on native. The scheduler, task system, and time driver are all core Tokio. The Emscripten-specific aspect is just the hosted event loop drive loop and the JSPI park primitive over Emscripten's promise API, along with timer/keepalive glue. The parked stacks live in an Emscripten member of the existingruntime::contextthread-local and no new public API is added.spawn_blockingremains unsupported. Sincetokio::fsand the stdio types do rely on it, the internal blocking shim completes their std calls inline, which is possible since Emscripten's filesystem syscalls are synchronous and there is nothing to offload.netis currently not included as it depends on unreleased Emscripten epoll work. When that is ready this PR can either be updated with that.This PR does not include
netfunctionality yet, as that depends on the Mio PR in tokio-rs/mio#1969. But extending this approach to full socket epolls is straightforward on top of this base as the next follow-on once the Mio PR lands.Tests run in Node.js with two modes -
The JSPI suite tests include features
rt,time,sync,macros,fs,io-util,io-std,test-utilas some emscripten-specific tests. There are only a couple of documented ignores (dup(), link() MEMFS limits, on_thread_park). - 744 passed, 0 failed, 12 ignoredThe non-JSPI tests remain limited pending deeper host event loop runtime work, a separate follow-on. - 4 passing, 0 failed.