Skip to content

feat: JSPI test suite for the wasm32-unknown-emscripten target - #8285

Open
guybedford wants to merge 6 commits into
tokio-rs:masterfrom
guybedford:emscripten-jspi
Open

feat: JSPI test suite for the wasm32-unknown-emscripten target#8285
guybedford wants to merge 6 commits into
tokio-rs:masterfrom
guybedford:emscripten-jspi

Conversation

@guybedford

@guybedford guybedford commented Jul 15, 2026

Copy link
Copy Markdown

Motivation

Support the wasm32-unknown-emscripten platform test suite and async timers using JSPI, so that async #[tokio::test] tests run end-to-end.

Solution

This extends #8281 (the single-threaded wasm32-unknown-emscripten target) with JavaScript Promise Integration (JSPI)-based suspension.

Under a single-threaded build Emscripten shares the host event loop with JavaScript, so a block_on that would have to block panics with a targeted error.

With -sJSPI, blocking can be supported since we can yield to the JS evet loop. To support real work happening in the Tokio test suite, tokio::test now uses -sJSPI to run real IO in the JS single-threaded mode. The soundness property here is that we ban reentrancy to ensure Rust code invariants are not broken. Even though the JS event loop is able to do real blocking work, the entire Wasm binary is suspended until that blocking work completes, and no new operations are performed while suspended. A debug-only internal guard asserts this non-reentrancy invariant and compiles out in release.

The runtime otherwise remains the same - the scheduler, task system, and time driver are unchanged. The Emscripten-specific piece is just the park leaf, handled by an internal runtime::jspi module (~100 lines) holding the one suspending __asyncjs__ timer import, with JSPI availability detected through emscripten_has_asyncify() == 2. The scheduler's existing event_interval maintenance park arrives as a zero-duration park and becomes a 0ms host turn, so greedy self-waking tasks cannot starve host timers.

Scope is rt, time, sync, macros, io-util, test-util. fs/io-std (inline blocking, as Emscripten's FS syscalls are synchronous) and net (epoll, over tokio-rs/mio#1969) are planned follow-ups on top of this base.

Tests run in Node.js in two CI lanes:

  1. JSPI lane: the full test suite for the supported features — 581 passed, 0 failed, 5 ignored. Real-timer tests are consolidated into the single rt_emscripten_jspi.rs file.
  2. Non-JSPI lane: rt_emscripten_block_on verifies fixed-point drives and the targeted would-block panics — 4 passed, 0 failed.

@github-actions github-actions Bot added R-loom-sync Run loom sync tests on this PR R-loom-time-driver Run loom time driver tests on this PR R-loom-current-thread Run loom current-thread tests on this PR R-loom-multi-thread Run loom multi-thread tests on this PR R-loom-blocking Run loom `runtime::spawn_blocking` tests on this PR labels Jul 15, 2026
@guybedford guybedford changed the title feat: support the wasm32-unknown-emscripten target feat: JSPI test suite for the wasm32-unknown-emscripten target Jul 15, 2026
Comment thread tokio/src/runtime/scheduler/current_thread/hosted_event_loop.rs Outdated
Comment thread tokio/src/process/mod.rs Outdated
Comment thread tokio/src/blocking.rs Outdated
Comment thread tokio/tests/emscripten_timer_rearm.rs Outdated
Comment thread tokio-test/src/lib.rs
@Darksonn Darksonn added A-tokio Area: The main tokio crate T-wasm Topic: Web Assembly labels Jul 16, 2026
@github-actions github-actions Bot removed R-loom-current-thread Run loom current-thread tests on this PR R-loom-multi-thread Run loom multi-thread tests on this PR R-loom-blocking Run loom `runtime::spawn_blocking` tests on this PR labels Jul 18, 2026
@guybedford
guybedford force-pushed the emscripten-jspi branch 2 times, most recently from 6121ff9 to 3258304 Compare July 18, 2026 03:34
guybedford and others added 6 commits July 27, 2026 18:51
Integrates the #[tokio::test] suite for wasm32-unknown-emscripten
using the canonical current_thread scheduler, drivers, and task system
on top of JSPI, including the native block_on drive loop. The only
platform-specific code is the JSPI park, where inside a JSPI promising
wrapper (entered by the #[tokio::test] expansion via the jspi crate's
enter_promising) a timed park suspends the whole stack on a host
timer while the host event loop runs, so that waits are real. This JSPI
suspension path is guarded by an activation wrapper which is only enabled
by the test suite currently. Outside an
activation, or for a wait nothing could ever wake (no reactor here,
and host timers are the only mid-park wake source), the leaf panics
with a targeted error instead of deadlocking the host loop.

spawn_blocking remains unsupported (no threads). CI runs the contract
tests in two lanes: -sJSPI (waits suspend) and without (waits panic).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate R-loom-sync Run loom sync tests on this PR R-loom-time-driver Run loom time driver tests on this PR T-wasm Topic: Web Assembly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants