From 0df286017194562e457dc6c5652c78a97be7c1d8 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 14 Jul 2026 14:27:40 -0700 Subject: [PATCH 1/3] feat: enable the wasm32-unknown-emscripten target --- .github/workflows/ci.yml | 41 ++++++++ spellcheck.dic | 5 +- tokio-macros/src/entry.rs | 20 +++- tokio-test/src/lib.rs | 29 ++++++ tokio/Cargo.toml | 14 ++- tokio/src/lib.rs | 55 ++++++++++ tokio/src/macros/cfg.rs | 52 +++++++++- tokio/src/process/emscripten.rs | 127 ++++++++++++++++++++++++ tokio/src/process/mod.rs | 7 +- tokio/src/runtime/time/mod.rs | 12 ++- tokio/src/runtime/time/tests/mod.rs | 2 +- tokio/src/sync/tests/atomic_waker.rs | 2 +- tokio/src/sync/tests/notify.rs | 2 +- tokio/src/sync/tests/semaphore_batch.rs | 2 +- tokio/src/task/coop/mod.rs | 2 +- tokio/src/task/local.rs | 2 +- tokio/src/time/mod.rs | 14 ++- tokio/src/util/idle_notified_set.rs | 2 +- tokio/src/util/mod.rs | 18 ++-- tokio/src/util/trace.rs | 17 ++-- tokio/src/util/wake_list.rs | 5 + tokio/tests/macros_join.rs | 6 +- tokio/tests/macros_pin.rs | 4 +- tokio/tests/macros_select.rs | 4 +- tokio/tests/macros_try_join.rs | 4 +- tokio/tests/sync_barrier.rs | 2 +- tokio/tests/sync_broadcast.rs | 4 +- tokio/tests/sync_broadcast_weak.rs | 2 +- tokio/tests/sync_errors.rs | 2 +- tokio/tests/sync_mpsc.rs | 6 +- tokio/tests/sync_mpsc_weak.rs | 2 +- tokio/tests/sync_mutex.rs | 6 +- tokio/tests/sync_mutex_owned.rs | 6 +- tokio/tests/sync_notify.rs | 2 +- tokio/tests/sync_notify_owned.rs | 2 +- tokio/tests/sync_oneshot.rs | 6 +- tokio/tests/sync_rwlock.rs | 6 +- tokio/tests/sync_semaphore.rs | 2 +- tokio/tests/sync_semaphore_owned.rs | 2 +- tokio/tests/sync_watch.rs | 2 +- tokio/tests/time_wasm.rs | 6 +- 41 files changed, 432 insertions(+), 74 deletions(-) create mode 100644 tokio/src/process/emscripten.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5a023538f4..97af268d19f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1168,6 +1168,47 @@ jobs: RUSTFLAGS: --cfg tokio_unstable CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime run -Sinherit-network + wasm32-unknown-emscripten: + name: test tokio for wasm32-unknown-emscripten + needs: basics + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Install Rust ${{ env.rust_stable }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_stable }} + targets: wasm32-unknown-emscripten + + - name: Install Emscripten + uses: mymindstorm/setup-emsdk@v14 + with: + version: 'latest' + + - uses: actions/setup-node@v4 + with: + node-version: 26 + + - uses: Swatinem/rust-cache@v2 + + - name: Install cargo-hack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack + + - name: Check tokio feature matrix for emscripten + run: cargo hack check -p tokio --each-feature --exclude-features full,net,process,signal,rt-multi-thread,io-uring,taskdump,schedule-latency --target wasm32-unknown-emscripten + working-directory: tokio + env: + RUSTFLAGS: "" + + - name: Test tokio for emscripten + run: cargo test -p tokio --target wasm32-unknown-emscripten --features "rt,time,sync,macros,io-util,test-util" --tests + working-directory: tokio + env: + CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER: node + RUSTFLAGS: "-C link-args=-sALLOW_MEMORY_GROWTH=1 -C link-args=-sEXIT_RUNTIME=1 -C link-args=-sSTACK_SIZE=1048576" + check-external-types: name: check-external-types (${{ matrix.os }}) needs: basics diff --git a/spellcheck.dic b/spellcheck.dic index aeb1e9db419..ddd75d6e1fa 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -1,4 +1,4 @@ -324 +327 & + < @@ -106,6 +106,9 @@ dns DNS DoS dwOpenMode +emscripten +Emscripten +emscripten's endian enqueue enqueued diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 290888387bb..9c4f7f9c3b2 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -506,7 +506,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt let body_ident = quote! { body }; // This explicit `return` is intentional. See tokio-rs/tokio#4636 - let last_block = quote_spanned! {last_stmt_end_span=> + let native_last_block = quote_spanned! {last_stmt_end_span=> #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return, clippy::unwrap_in_result)] { @@ -521,6 +521,24 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt }; + // Emscripten runs the native expansion; only the `multi_thread` flavor + // diverges — it has no native threads there, so it's rejected with a + // targeted error rather than the opaque failure of the native + // multi-thread `block_on`. + let last_block = match config.flavor { + RuntimeFlavor::Threaded => quote! { + #[cfg(not(target_os = "emscripten"))] + #native_last_block + #[cfg(target_os = "emscripten")] + ::core::compile_error!( + "the `multi_thread` runtime flavor is not available on \ + wasm32-unknown-emscripten (no native threads); use \ + `flavor = \"current_thread\"`" + ); + }, + _ => native_last_block, + }; + let body = input.body(); // For test functions pin the body to the stack and use `Pin<&mut dyn diff --git a/tokio-test/src/lib.rs b/tokio-test/src/lib.rs index 87e63861210..321392bf9ff 100644 --- a/tokio-test/src/lib.rs +++ b/tokio-test/src/lib.rs @@ -24,6 +24,7 @@ pub mod task; /// [`tokio::runtime::Runtime::block_on`][runtime-block-on]. /// /// [runtime-block-on]: https://docs.rs/tokio/1.3.0/tokio/runtime/struct.Runtime.html#method.block_on +#[cfg(not(target_os = "emscripten"))] pub fn block_on(future: F) -> F::Output { use tokio::runtime; @@ -34,3 +35,31 @@ pub fn block_on(future: F) -> F::Output { rt.block_on(future) } + +/// Emscripten variant: polls once with a no-op waker. Ready futures (mocks, +/// pure computation) complete; ones that must yield (timers, I/O) panic — +/// use `#[tokio::test]` for those. +#[cfg(target_os = "emscripten")] +pub fn block_on(future: F) -> F::Output { + use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| RawWaker::new(std::ptr::null(), &VTABLE), + |_| {}, + |_| {}, + |_| {}, + ); + // SAFETY: vtable entries are valid no-ops. + let waker = unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &VTABLE)) }; + let mut cx = Context::from_waker(&waker); + + let mut future = Box::pin(future); + match future.as_mut().poll(&mut cx) { + Poll::Ready(output) => output, + Poll::Pending => panic!( + "tokio_test::block_on: future returned Pending on emscripten. \ + The main thread cannot block on JS event-loop wakeups; use #[tokio::test] \ + for futures that need timers/network I/O." + ), + } +} diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index e260bbc5bd9..0cd6fa059ff 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -101,7 +101,7 @@ bytes = { version = "1.2.1", optional = true } mio = { version = "1.2.0", optional = true, default-features = false } parking_lot = { version = "0.12.0", optional = true } -[target.'cfg(any(not(target_family = "wasm"), all(target_os = "wasi", not(target_env = "p1"))))'.dependencies] +[target.'cfg(any(not(target_family = "wasm"), target_os = "emscripten", all(target_os = "wasi", not(target_env = "p1"))))'.dependencies] socket2 = { version = "0.6.3", optional = true, features = ["all"] } # Currently unstable. The API exposed by these features may be broken at any time. @@ -130,6 +130,8 @@ libc = { version = "0.2.168", optional = true } [target.'cfg(unix)'.dev-dependencies] libc = { version = "0.2.168" } + +[target.'cfg(all(unix, not(target_os = "emscripten")))'.dev-dependencies] nix = { version = "0.31.0", default-features = false, features = ["aio", "fs", "socket"] } [target.'cfg(windows)'.dependencies.windows-sys] @@ -146,22 +148,26 @@ features = [ [dev-dependencies] tokio-test = "0.4.0" tokio-stream = "0.1" -tokio-util = { version = "0.7", features = ["rt"] } futures = { version = "0.3.0", features = ["async-await"] } futures-test = "0.3.31" mockall = "0.13.0" async-stream = "0.3" futures-concurrency = "7.6.3" +[target.'cfg(not(target_os = "emscripten"))'.dev-dependencies] +tokio-util = { version = "0.7", features = ["rt"] } + [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] socket2 = "0.6.0" -tempfile = "3.1.0" proptest = "1" +[target.'cfg(any(not(target_family = "wasm"), target_os = "emscripten"))'.dev-dependencies] +tempfile = "3.1.0" + [target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dev-dependencies] rand = "0.9" -[target.'cfg(all(target_family = "wasm", not(target_os = "wasi")))'.dev-dependencies] +[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test = "0.3.0" [target.'cfg(target_os = "freebsd")'.dev-dependencies] diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index efa527606af..bbe71de86b1 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -444,6 +444,53 @@ //! immediately instead of blocking forever. On platforms that don't support //! time, this means that the runtime can never be idle in any way. //! +//! ### Emscripten support +//! +//! The `wasm32-unknown-emscripten` target is supported at parity with the +//! other wasm targets. A host-event-loop execution model with a parking +//! `block_on` is a planned follow-up; until it lands, a `block_on` whose +//! future cannot resolve synchronously behaves as on other single-threaded +//! wasm targets. +//! +//! Supported features: `rt`, `time`, `sync`, `macros`, `fs`, `io-util`, +//! `io-std`, and `test-util`. `fs` and the stdio types run their `std::*` +//! calls inline, since emscripten's filesystem syscalls complete +//! synchronously. The `net` reactor (epoll-backed, over emscripten's socket +//! support) is planned as a follow-up; until then the `net` feature fails to +//! build for this target (rejected by `mio`). +//! +//! The `process`, `signal`, and `rt-multi-thread` features are rejected at +//! compile time: `process`/`signal` have no underlying primitives (`fork`/`exec`, +//! kernel signal delivery) and `rt-multi-thread` has no native threads. +//! +//! `spawn_blocking` has no threadpool to dispatch to, so the closure runs as a +//! canonical spawned task on the single thread and returns the usual +//! `JoinHandle`. `tokio::fs::*` and `tokio::io::{stdin, stdout, stderr}` +//! likewise run their `std::*` calls inline, because emscripten's libc +//! syscalls complete synchronously and don't block the cooperative scheduler. +//! +//! Panics behave as on native: `wasm32-unknown-emscripten` defaults to +//! `panic = "unwind"`, so panic recovery works, a panicking task yields +//! `Err(JoinError)`, and `JoinError::is_panic` / `JoinError::into_panic` +//! report the payload. +//! +//! `#[tokio::test]` / `#[tokio::main]` use the native macro expansion; the +//! `multi_thread` flavor is rejected (no native threads) — use +//! `flavor = "current_thread"`. +//! +//! +//! #### Linking and running on emscripten +//! +//! No js-library or other custom file is required, and plain `node` runs the +//! test binaries directly: +//! +//! ```text +//! CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER="node" +//! RUSTFLAGS="-C link-args=-sALLOW_MEMORY_GROWTH=1 \ +//! -C link-args=-sEXIT_RUNTIME=1 \ +//! -C link-args=-sSTACK_SIZE=1048576" +//! ``` +//! //! ## Unstable `WASM` support //! //! Tokio also has unstable support for some additional `WASM` features. This @@ -467,6 +514,7 @@ compile_error! { #[cfg(all( not(tokio_unstable), target_family = "wasm", + not(target_os = "emscripten"), any( feature = "fs", feature = "io-std", @@ -478,6 +526,13 @@ compile_error! { ))] compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm."); +// On emscripten, `process`, `signal`, and `rt-multi-thread` compile but are +// inert, so `full` (and any dependency that enables these features) still +// builds. `process` and `signal` have no `fork`/`exec` or kernel signal +// delivery, so their modules are compiled out (see `cfg_process!` / +// `cfg_signal!`). The multi-threaded runtime compiles but only runs under a +// `PROXY_TO_PTHREAD` build; `#[tokio::main]` steers to `current_thread`. + #[cfg(all(not(tokio_unstable), feature = "io-uring"))] compile_error!("The `io-uring` feature requires `--cfg tokio_unstable`."); diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index 0a011eb630c..1db46e3505a 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -398,6 +398,11 @@ macro_rules! cfg_process { #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[cfg(not(loom))] #[cfg(not(target_os = "wasi"))] + // emscripten has no `fork`/`exec`, so the `process` module is a + // throwing stub there (see `process/emscripten.rs`); it still + // compiles so dependents that name the types build. The orphan + // reaper / signal driver it would otherwise need stays off via + // `cfg_process_driver!`. $item )* } @@ -407,6 +412,9 @@ macro_rules! cfg_process_driver { ($($item:item)*) => { #[cfg(unix)] #[cfg(not(loom))] + // The driver (orphan reaper backed by the signal handler) doesn't exist + // on emscripten; the process module there is a throwing stub. + #[cfg(not(target_os = "emscripten"))] cfg_process! { $($item)* } } } @@ -414,7 +422,10 @@ macro_rules! cfg_process_driver { macro_rules! cfg_not_process_driver { ($($item:item)*) => { $( - #[cfg(not(all(unix, not(loom), feature = "process")))] + #[cfg(any( + not(all(unix, not(loom), feature = "process")), + target_os = "emscripten", + ))] $item )* } @@ -427,6 +438,8 @@ macro_rules! cfg_signal { #[cfg_attr(docsrs, doc(cfg(feature = "signal")))] #[cfg(not(loom))] #[cfg(not(target_os = "wasi"))] + // No kernel signal delivery on emscripten; inert there. + #[cfg(not(target_os = "emscripten"))] $item )* } @@ -437,6 +450,7 @@ macro_rules! cfg_signal_internal { $( #[cfg(any(feature = "signal", all(unix, feature = "process")))] #[cfg(not(loom))] + #[cfg(not(target_os = "emscripten"))] $item )* } @@ -452,7 +466,7 @@ macro_rules! cfg_signal_internal_and_unix { macro_rules! cfg_not_signal_internal { ($($item:item)*) => { $( - #[cfg(any(loom, not(unix), not(any(feature = "signal", all(unix, feature = "process")))))] + #[cfg(any(loom, not(unix), target_os = "emscripten", not(any(feature = "signal", all(unix, feature = "process")))))] $item )* } @@ -715,7 +729,7 @@ macro_rules! cfg_not_wasip1 { macro_rules! cfg_is_wasm_not_wasi { ($($item:item)*) => { $( - #[cfg(all(target_family = "wasm", not(target_os = "wasi")))] + #[cfg(all(target_family = "wasm", target_os = "unknown"))] $item )* } @@ -768,3 +782,35 @@ macro_rules! cfg_not_schedule_latency { )* } } + +/// Enables emscripten-specific code. +macro_rules! cfg_emscripten { + ($($item:item)*) => { + $( + #[cfg(target_os = "emscripten")] + #[cfg_attr(docsrs, doc(cfg(target_os = "emscripten")))] + $item + )* + } +} + +/// Enables code for non-emscripten targets. +macro_rules! cfg_not_emscripten { + ($($item:item)*) => { + $( + #[cfg(not(target_os = "emscripten"))] + $item + )* + } +} + +/// Enables code requiring both the `rt` feature and the emscripten target. +macro_rules! cfg_rt_emscripten { + ($($item:item)*) => { + $( + #[cfg(all(feature = "rt", target_os = "emscripten"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "rt", target_os = "emscripten"))))] + $item + )* + } +} diff --git a/tokio/src/process/emscripten.rs b/tokio/src/process/emscripten.rs new file mode 100644 index 00000000000..093b3eb8aa1 --- /dev/null +++ b/tokio/src/process/emscripten.rs @@ -0,0 +1,127 @@ +//! Throwing process stub for `wasm32-unknown-emscripten`. +//! +//! emscripten has no `fork`/`exec`, so there is nothing to spawn or reap: the +//! orphan reaper / signal driver the unix backend relies on is compiled out +//! (see `cfg_process_driver!`). We still provide the `imp` surface that +//! `process::mod` is generic over so dependents that merely name these types +//! keep compiling. Spawning fails before it reaches here (`std`'s own +//! `Command::spawn` returns `Unsupported` on emscripten), and the `Child` / +//! `ChildStdio` types are uninhabited — every method is unreachable. + +use crate::io::{AsyncRead, AsyncWrite, ReadBuf}; +use crate::process::kill::Kill; +use crate::process::SpawnedChild; + +use std::fmt; +use std::future::Future; +use std::io; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}; +use std::pin::Pin; +use std::process::{Child as StdChild, ExitStatus, Stdio}; +use std::task::{Context, Poll}; + +fn unsupported() -> io::Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "spawning processes is not supported on wasm32-unknown-emscripten", + )) +} + +pub(crate) enum Child {} + +impl Child { + pub(crate) fn id(&self) -> u32 { + match *self {} + } + + pub(crate) fn try_wait(&mut self) -> io::Result> { + match *self {} + } +} + +impl fmt::Debug for Child { + fn fmt(&self, _fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +impl Kill for Child { + fn kill(&mut self) -> io::Result<()> { + match *self {} + } +} + +impl Future for Child { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + match *self.get_mut() {} + } +} + +pub(crate) fn build_child(_child: StdChild) -> io::Result { + unsupported() +} + +pub(crate) enum ChildStdio {} + +impl ChildStdio { + pub(crate) fn into_owned_fd(self) -> io::Result { + match self {} + } +} + +impl fmt::Debug for ChildStdio { + fn fmt(&self, _fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +impl AsRawFd for ChildStdio { + fn as_raw_fd(&self) -> RawFd { + match *self {} + } +} + +impl AsFd for ChildStdio { + fn as_fd(&self) -> BorrowedFd<'_> { + match *self {} + } +} + +impl AsyncWrite for ChildStdio { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + match *self.get_mut() {} + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + match *self.get_mut() {} + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + match *self.get_mut() {} + } +} + +impl AsyncRead for ChildStdio { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &mut ReadBuf<'_>, + ) -> Poll> { + match *self.get_mut() {} + } +} + +pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result { + match io {} +} + +pub(crate) fn stdio(io: T) -> io::Result { + let _ = io.into_raw_fd(); + unsupported() +} diff --git a/tokio/src/process/mod.rs b/tokio/src/process/mod.rs index fb233cb4eb2..37914aa6465 100644 --- a/tokio/src/process/mod.rs +++ b/tokio/src/process/mod.rs @@ -227,7 +227,12 @@ //! [`Child`]: crate::process::Child #[path = "unix/mod.rs"] -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "emscripten")))] +mod imp; + +// emscripten has no `fork`/`exec`: a throwing stub keeps the API present. +#[path = "emscripten.rs"] +#[cfg(target_os = "emscripten")] mod imp; #[cfg(unix)] diff --git a/tokio/src/runtime/time/mod.rs b/tokio/src/runtime/time/mod.rs index bbdb664c1d3..631049b7a67 100644 --- a/tokio/src/runtime/time/mod.rs +++ b/tokio/src/runtime/time/mod.rs @@ -287,12 +287,22 @@ impl Driver { } impl Handle { - pub(self) fn process(&self, clock: &Clock) { + #[cfg_attr(not(target_os = "emscripten"), allow(dead_code))] + pub(crate) fn process(&self, clock: &Clock) { let now = self.time_source().now(clock); self.process_at_time(now); } + /// Returns the absolute deadline in ticks for the soonest unexpired + /// timer, or `None` if no timers are registered. Used by the emscripten + /// schedule loop to compute its `setTimeout` delay; ticks here are the + /// time wheel's monotonic tick basis (`time_source.now()`). + #[cfg(target_os = "emscripten")] + pub(crate) fn next_expiration_tick(&self) -> Option { + self.inner.lock().wheel.next_expiration_time() + } + pub(self) fn process_at_time(&self, mut now: u64) { let mut waker_list = WakeList::new(); diff --git a/tokio/src/runtime/time/tests/mod.rs b/tokio/src/runtime/time/tests/mod.rs index 84c765af69e..0773f91c167 100644 --- a/tokio/src/runtime/time/tests/mod.rs +++ b/tokio/src/runtime/time/tests/mod.rs @@ -1,4 +1,4 @@ -#![cfg(not(target_os = "wasi"))] +#![cfg(all(not(target_os = "wasi"), not(target_os = "emscripten")))] use std::{task::Context, time::Duration}; diff --git a/tokio/src/sync/tests/atomic_waker.rs b/tokio/src/sync/tests/atomic_waker.rs index b182574f325..8c5e1a113d0 100644 --- a/tokio/src/sync/tests/atomic_waker.rs +++ b/tokio/src/sync/tests/atomic_waker.rs @@ -15,7 +15,7 @@ impl AssertSync for AtomicWaker {} impl AssertSend for Waker {} impl AssertSync for Waker {} -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; #[test] diff --git a/tokio/src/sync/tests/notify.rs b/tokio/src/sync/tests/notify.rs index 540aa22444a..bfbc6f486c8 100644 --- a/tokio/src/sync/tests/notify.rs +++ b/tokio/src/sync/tests/notify.rs @@ -3,7 +3,7 @@ use std::future::Future; use std::sync::Arc; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; #[test] diff --git a/tokio/src/sync/tests/semaphore_batch.rs b/tokio/src/sync/tests/semaphore_batch.rs index fb5e8fdd6f7..84f062d8f23 100644 --- a/tokio/src/sync/tests/semaphore_batch.rs +++ b/tokio/src/sync/tests/semaphore_batch.rs @@ -3,7 +3,7 @@ use tokio_test::*; const MAX_PERMITS: usize = crate::sync::Semaphore::MAX_PERMITS; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; #[test] diff --git a/tokio/src/task/coop/mod.rs b/tokio/src/task/coop/mod.rs index 4a520e6f527..5b9ebbd9e50 100644 --- a/tokio/src/task/coop/mod.rs +++ b/tokio/src/task/coop/mod.rs @@ -496,7 +496,7 @@ cfg_coop! { mod test { use super::*; - #[cfg(all(target_family = "wasm", not(target_os = "wasi")))] + #[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; fn get() -> Budget { diff --git a/tokio/src/task/local.rs b/tokio/src/task/local.rs index 4ad9c832921..5876319cfb1 100644 --- a/tokio/src/task/local.rs +++ b/tokio/src/task/local.rs @@ -1278,7 +1278,7 @@ impl LocalState { // ensure they are on the same thread that owns the `LocalSet`. unsafe impl Send for LocalState {} -#[cfg(all(test, not(loom)))] +#[cfg(all(test, not(loom), not(target_os = "emscripten")))] mod tests { use super::*; diff --git a/tokio/src/time/mod.rs b/tokio/src/time/mod.rs index 8627a7838aa..e2621e5af9b 100644 --- a/tokio/src/time/mod.rs +++ b/tokio/src/time/mod.rs @@ -19,6 +19,9 @@ //! //! These types must be used from within the context of the [`Runtime`](crate::runtime::Runtime). //! +//! On `wasm32-unknown-emscripten` these timers are driven by JavaScript's +//! event loop via `setTimeout`. +//! //! # Examples //! //! Wait 100ms and print "100 ms have elapsed" @@ -86,11 +89,6 @@ mod clock; pub(crate) use self::clock::Clock; -cfg_test_util! { - pub use clock::{advance, pause, resume}; -} - -pub mod error; mod instant; pub use self::instant::Instant; @@ -105,6 +103,12 @@ mod timeout; #[doc(inline)] pub use timeout::{timeout, timeout_at, Timeout}; +cfg_test_util! { + pub use clock::{advance, pause, resume}; +} + +pub mod error; + // Re-export for convenience #[doc(no_inline)] pub use std::time::Duration; diff --git a/tokio/src/util/idle_notified_set.rs b/tokio/src/util/idle_notified_set.rs index 4c5177c5630..3d9e7dd2b8c 100644 --- a/tokio/src/util/idle_notified_set.rs +++ b/tokio/src/util/idle_notified_set.rs @@ -492,7 +492,7 @@ unsafe impl linked_list::Link for ListEntry { } } -#[cfg(all(test, not(loom)))] +#[cfg(all(test, not(loom), not(target_os = "emscripten")))] mod tests { use crate::runtime::Builder; use crate::task::JoinSet; diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index c671fd6a1da..d715c3ea942 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -5,14 +5,15 @@ cfg_io_driver! { #[cfg(feature = "fs")] pub(crate) mod as_ref; -#[cfg(feature = "rt")] -pub(crate) mod atomic_cell; +cfg_rt! { + pub(crate) mod atomic_cell; +} -#[cfg(feature = "net")] -mod blocking_check; -#[cfg(feature = "net")] -#[allow(unused_imports)] -pub(crate) use blocking_check::check_socket_for_blocking; +cfg_net! { + mod blocking_check; + #[allow(unused_imports)] + pub(crate) use blocking_check::check_socket_for_blocking; +} pub(crate) mod metric_atomics; @@ -40,6 +41,9 @@ mod wake_list; feature = "signal", feature = "time", ))] +// Some WakeList consumers are cfg'd out on emscripten, so the re-export may be +// unused there. +#[cfg_attr(target_os = "emscripten", allow(unused_imports))] pub(crate) use wake_list::WakeList; #[cfg(any( diff --git a/tokio/src/util/trace.rs b/tokio/src/util/trace.rs index b22c2aeb593..9de1ee0ebfa 100644 --- a/tokio/src/util/trace.rs +++ b/tokio/src/util/trace.rs @@ -1,3 +1,4 @@ +// SpawnMeta uses native runtime task infrastructure cfg_rt! { use std::marker::PhantomData; @@ -180,12 +181,12 @@ cfg_rt! { } } -cfg_time! { - #[track_caller] - pub(crate) fn caller_location() -> Option<&'static std::panic::Location<'static>> { - #[cfg(all(tokio_unstable, feature = "tracing"))] - return Some(std::panic::Location::caller()); - #[cfg(not(all(tokio_unstable, feature = "tracing")))] - None - } +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +#[track_caller] +pub(crate) fn caller_location() -> Option<&'static std::panic::Location<'static>> { + #[cfg(all(tokio_unstable, feature = "tracing"))] + return Some(std::panic::Location::caller()); + #[cfg(not(all(tokio_unstable, feature = "tracing")))] + None } diff --git a/tokio/src/util/wake_list.rs b/tokio/src/util/wake_list.rs index 23a559d02be..84e69fabc83 100644 --- a/tokio/src/util/wake_list.rs +++ b/tokio/src/util/wake_list.rs @@ -1,3 +1,8 @@ +// On emscripten, `WakeList` is reachable via the feature gates that pull +// it into `util::mod`, but the actual consumers (time::clock, sync::notify, +// etc.) may be cfg'd out — leaving the type used only through a re-export. +#![cfg_attr(target_os = "emscripten", allow(dead_code))] + use core::mem::MaybeUninit; use core::ptr; use std::task::Waker; diff --git a/tokio/tests/macros_join.rs b/tokio/tests/macros_join.rs index 4c6db26d8ae..8b304be8da4 100644 --- a/tokio/tests/macros_join.rs +++ b/tokio/tests/macros_join.rs @@ -2,13 +2,13 @@ #![allow(clippy::disallowed_names)] use std::sync::Arc; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] #[cfg(target_pointer_width = "64")] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use tokio::sync::{oneshot, Semaphore}; diff --git a/tokio/tests/macros_pin.rs b/tokio/tests/macros_pin.rs index 2de68ad031f..19c29d56fdb 100644 --- a/tokio/tests/macros_pin.rs +++ b/tokio/tests/macros_pin.rs @@ -1,9 +1,9 @@ #![cfg(feature = "macros")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; async fn one() {} diff --git a/tokio/tests/macros_select.rs b/tokio/tests/macros_select.rs index 3b403e4ce27..662645c0740 100644 --- a/tokio/tests/macros_select.rs +++ b/tokio/tests/macros_select.rs @@ -1,10 +1,10 @@ #![cfg(feature = "macros")] #![allow(clippy::disallowed_names)] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use tokio::sync::oneshot; diff --git a/tokio/tests/macros_try_join.rs b/tokio/tests/macros_try_join.rs index 03172ca2a2d..25f811c8e22 100644 --- a/tokio/tests/macros_try_join.rs +++ b/tokio/tests/macros_try_join.rs @@ -6,10 +6,10 @@ use std::{convert::Infallible, sync::Arc}; use tokio::sync::{oneshot, Semaphore}; use tokio_test::{assert_pending, assert_ready, task}; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; #[maybe_tokio_test] diff --git a/tokio/tests/sync_barrier.rs b/tokio/tests/sync_barrier.rs index ac5977f24d8..a8f8e9790bc 100644 --- a/tokio/tests/sync_barrier.rs +++ b/tokio/tests/sync_barrier.rs @@ -2,7 +2,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::Barrier; diff --git a/tokio/tests/sync_broadcast.rs b/tokio/tests/sync_broadcast.rs index 2bfe235e511..94bfde6bbc2 100644 --- a/tokio/tests/sync_broadcast.rs +++ b/tokio/tests/sync_broadcast.rs @@ -2,7 +2,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::broadcast; @@ -563,7 +563,7 @@ fn sender_len() { } #[test] -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] fn sender_len_random() { let (tx, mut rx1) = broadcast::channel(16); let mut rx2 = tx.subscribe(); diff --git a/tokio/tests/sync_broadcast_weak.rs b/tokio/tests/sync_broadcast_weak.rs index 1e7fd6f2d67..ccca130e52e 100644 --- a/tokio/tests/sync_broadcast_weak.rs +++ b/tokio/tests/sync_broadcast_weak.rs @@ -2,7 +2,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::broadcast::{self, channel}; diff --git a/tokio/tests/sync_errors.rs b/tokio/tests/sync_errors.rs index 4e43c8f311e..2bc9b878111 100644 --- a/tokio/tests/sync_errors.rs +++ b/tokio/tests/sync_errors.rs @@ -1,7 +1,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; fn is_error() {} diff --git a/tokio/tests/sync_mpsc.rs b/tokio/tests/sync_mpsc.rs index 398ab633927..207bef40d7f 100644 --- a/tokio/tests/sync_mpsc.rs +++ b/tokio/tests/sync_mpsc.rs @@ -2,12 +2,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use std::fmt; diff --git a/tokio/tests/sync_mpsc_weak.rs b/tokio/tests/sync_mpsc_weak.rs index fba0fe4e33a..f09e79dab9d 100644 --- a/tokio/tests/sync_mpsc_weak.rs +++ b/tokio/tests/sync_mpsc_weak.rs @@ -2,7 +2,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::atomic::AtomicUsize; diff --git a/tokio/tests/sync_mutex.rs b/tokio/tests/sync_mutex.rs index 8d74addad75..37ecf7a2e20 100644 --- a/tokio/tests/sync_mutex.rs +++ b/tokio/tests/sync_mutex.rs @@ -1,12 +1,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use tokio::sync::Mutex; diff --git a/tokio/tests/sync_mutex_owned.rs b/tokio/tests/sync_mutex_owned.rs index 28b2afbf32b..95f7ba82e4b 100644 --- a/tokio/tests/sync_mutex_owned.rs +++ b/tokio/tests/sync_mutex_owned.rs @@ -1,12 +1,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use tokio::sync::Mutex; diff --git a/tokio/tests/sync_notify.rs b/tokio/tests/sync_notify.rs index ee7a9ecf0ad..3eb92716080 100644 --- a/tokio/tests/sync_notify.rs +++ b/tokio/tests/sync_notify.rs @@ -1,7 +1,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::Notify; diff --git a/tokio/tests/sync_notify_owned.rs b/tokio/tests/sync_notify_owned.rs index 06a0f6ade57..cef574a68aa 100644 --- a/tokio/tests/sync_notify_owned.rs +++ b/tokio/tests/sync_notify_owned.rs @@ -1,7 +1,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; diff --git a/tokio/tests/sync_oneshot.rs b/tokio/tests/sync_oneshot.rs index 08206ea1d33..8aaddbdb39a 100644 --- a/tokio/tests/sync_oneshot.rs +++ b/tokio/tests/sync_oneshot.rs @@ -1,12 +1,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use tokio::sync::oneshot; diff --git a/tokio/tests/sync_rwlock.rs b/tokio/tests/sync_rwlock.rs index 2dc7b0a62ac..c51f9616cc6 100644 --- a/tokio/tests/sync_rwlock.rs +++ b/tokio/tests/sync_rwlock.rs @@ -1,12 +1,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))] +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] use tokio::test as maybe_tokio_test; use std::task::Poll; diff --git a/tokio/tests/sync_semaphore.rs b/tokio/tests/sync_semaphore.rs index d976f1e6822..758c581fc8f 100644 --- a/tokio/tests/sync_semaphore.rs +++ b/tokio/tests/sync_semaphore.rs @@ -1,6 +1,6 @@ #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; diff --git a/tokio/tests/sync_semaphore_owned.rs b/tokio/tests/sync_semaphore_owned.rs index f9eeee0cfab..d0e3eb4cdf1 100644 --- a/tokio/tests/sync_semaphore_owned.rs +++ b/tokio/tests/sync_semaphore_owned.rs @@ -1,6 +1,6 @@ #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; diff --git a/tokio/tests/sync_watch.rs b/tokio/tests/sync_watch.rs index 48e4106841f..df366f4a8cb 100644 --- a/tokio/tests/sync_watch.rs +++ b/tokio/tests/sync_watch.rs @@ -2,7 +2,7 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] -#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +#[cfg(all(target_family = "wasm", target_os = "unknown"))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::watch; diff --git a/tokio/tests/time_wasm.rs b/tokio/tests/time_wasm.rs index 8e0483f2041..073df353de7 100644 --- a/tokio/tests/time_wasm.rs +++ b/tokio/tests/time_wasm.rs @@ -1,5 +1,9 @@ #![warn(rust_2018_idioms)] -#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] +#![cfg(all( + target_arch = "wasm32", + not(target_os = "wasi"), + not(target_os = "emscripten") +))] use wasm_bindgen_test::wasm_bindgen_test; From fbe86575cfe5eb7a8bab088b9f6677a3bbf6f270 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 14 Jul 2026 16:07:16 -0700 Subject: [PATCH 2/3] spelling --- spellcheck.dic | 4 ++-- tokio/src/lib.rs | 22 ++++++++++------------ tokio/src/macros/cfg.rs | 12 ++++++------ tokio/src/process/mod.rs | 2 +- tokio/src/runtime/time/mod.rs | 2 +- tokio/src/util/mod.rs | 2 +- tokio/src/util/wake_list.rs | 2 +- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/spellcheck.dic b/spellcheck.dic index ddd75d6e1fa..2e01dad8402 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -106,9 +106,8 @@ dns DNS DoS dwOpenMode -emscripten Emscripten -emscripten's +Emscripten's endian enqueue enqueued @@ -323,6 +322,7 @@ Wakers wakeup wakeups WASI +Wasm watchOS workstealing ZST diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index bbe71de86b1..d98898cb6c9 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -447,15 +447,14 @@ //! ### Emscripten support //! //! The `wasm32-unknown-emscripten` target is supported at parity with the -//! other wasm targets. A host-event-loop execution model with a parking +//! other Wasm targets. A host-event-loop execution model with a parking //! `block_on` is a planned follow-up; until it lands, a `block_on` whose //! future cannot resolve synchronously behaves as on other single-threaded -//! wasm targets. +//! Wasm targets. //! //! Supported features: `rt`, `time`, `sync`, `macros`, `fs`, `io-util`, -//! `io-std`, and `test-util`. `fs` and the stdio types run their `std::*` -//! calls inline, since emscripten's filesystem syscalls complete -//! synchronously. The `net` reactor (epoll-backed, over emscripten's socket +//! `io-std`, and `test-util` — the same surface as the other single-threaded +//! Wasm targets. The `net` reactor (epoll-backed, over Emscripten's socket //! support) is planned as a follow-up; until then the `net` feature fails to //! build for this target (rejected by `mio`). //! @@ -463,11 +462,10 @@ //! compile time: `process`/`signal` have no underlying primitives (`fork`/`exec`, //! kernel signal delivery) and `rt-multi-thread` has no native threads. //! -//! `spawn_blocking` has no threadpool to dispatch to, so the closure runs as a -//! canonical spawned task on the single thread and returns the usual -//! `JoinHandle`. `tokio::fs::*` and `tokio::io::{stdin, stdout, stderr}` -//! likewise run their `std::*` calls inline, because emscripten's libc -//! syscalls complete synchronously and don't block the cooperative scheduler. +//! `spawn_blocking` dispatches to the blocking thread pool and so behaves as on +//! the other single-threaded Wasm targets. Running `spawn_blocking` closures +//! and `fs`/stdio `std::*` calls inline over Emscripten's synchronous syscalls +//! is part of the planned host-event-loop follow-up. //! //! Panics behave as on native: `wasm32-unknown-emscripten` defaults to //! `panic = "unwind"`, so panic recovery works, a panicking task yields @@ -479,7 +477,7 @@ //! `flavor = "current_thread"`. //! //! -//! #### Linking and running on emscripten +//! #### Linking and running on Emscripten //! //! No js-library or other custom file is required, and plain `node` runs the //! test binaries directly: @@ -526,7 +524,7 @@ compile_error! { ))] compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm."); -// On emscripten, `process`, `signal`, and `rt-multi-thread` compile but are +// On Emscripten, `process`, `signal`, and `rt-multi-thread` compile but are // inert, so `full` (and any dependency that enables these features) still // builds. `process` and `signal` have no `fork`/`exec` or kernel signal // delivery, so their modules are compiled out (see `cfg_process!` / diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index 1db46e3505a..e9a9962ac8b 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -398,7 +398,7 @@ macro_rules! cfg_process { #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[cfg(not(loom))] #[cfg(not(target_os = "wasi"))] - // emscripten has no `fork`/`exec`, so the `process` module is a + // Emscripten has no `fork`/`exec`, so the `process` module is a // throwing stub there (see `process/emscripten.rs`); it still // compiles so dependents that name the types build. The orphan // reaper / signal driver it would otherwise need stays off via @@ -413,7 +413,7 @@ macro_rules! cfg_process_driver { #[cfg(unix)] #[cfg(not(loom))] // The driver (orphan reaper backed by the signal handler) doesn't exist - // on emscripten; the process module there is a throwing stub. + // on Emscripten; the process module there is a throwing stub. #[cfg(not(target_os = "emscripten"))] cfg_process! { $($item)* } } @@ -438,7 +438,7 @@ macro_rules! cfg_signal { #[cfg_attr(docsrs, doc(cfg(feature = "signal")))] #[cfg(not(loom))] #[cfg(not(target_os = "wasi"))] - // No kernel signal delivery on emscripten; inert there. + // No kernel signal delivery on Emscripten; inert there. #[cfg(not(target_os = "emscripten"))] $item )* @@ -783,7 +783,7 @@ macro_rules! cfg_not_schedule_latency { } } -/// Enables emscripten-specific code. +/// Enables Emscripten-specific code. macro_rules! cfg_emscripten { ($($item:item)*) => { $( @@ -794,7 +794,7 @@ macro_rules! cfg_emscripten { } } -/// Enables code for non-emscripten targets. +/// Enables code for non-Emscripten targets. macro_rules! cfg_not_emscripten { ($($item:item)*) => { $( @@ -804,7 +804,7 @@ macro_rules! cfg_not_emscripten { } } -/// Enables code requiring both the `rt` feature and the emscripten target. +/// Enables code requiring both the `rt` feature and the Emscripten target. macro_rules! cfg_rt_emscripten { ($($item:item)*) => { $( diff --git a/tokio/src/process/mod.rs b/tokio/src/process/mod.rs index 37914aa6465..6ca2e2b8f82 100644 --- a/tokio/src/process/mod.rs +++ b/tokio/src/process/mod.rs @@ -230,7 +230,7 @@ #[cfg(all(unix, not(target_os = "emscripten")))] mod imp; -// emscripten has no `fork`/`exec`: a throwing stub keeps the API present. +// Emscripten has no `fork`/`exec`: a throwing stub keeps the API present. #[path = "emscripten.rs"] #[cfg(target_os = "emscripten")] mod imp; diff --git a/tokio/src/runtime/time/mod.rs b/tokio/src/runtime/time/mod.rs index 631049b7a67..331906f5b77 100644 --- a/tokio/src/runtime/time/mod.rs +++ b/tokio/src/runtime/time/mod.rs @@ -295,7 +295,7 @@ impl Handle { } /// Returns the absolute deadline in ticks for the soonest unexpired - /// timer, or `None` if no timers are registered. Used by the emscripten + /// timer, or `None` if no timers are registered. Used by the Emscripten /// schedule loop to compute its `setTimeout` delay; ticks here are the /// time wheel's monotonic tick basis (`time_source.now()`). #[cfg(target_os = "emscripten")] diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index d715c3ea942..d4390c9b73e 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -41,7 +41,7 @@ mod wake_list; feature = "signal", feature = "time", ))] -// Some WakeList consumers are cfg'd out on emscripten, so the re-export may be +// Some WakeList consumers are cfg'd out on Emscripten, so the re-export may be // unused there. #[cfg_attr(target_os = "emscripten", allow(unused_imports))] pub(crate) use wake_list::WakeList; diff --git a/tokio/src/util/wake_list.rs b/tokio/src/util/wake_list.rs index 84e69fabc83..db077ebee17 100644 --- a/tokio/src/util/wake_list.rs +++ b/tokio/src/util/wake_list.rs @@ -1,4 +1,4 @@ -// On emscripten, `WakeList` is reachable via the feature gates that pull +// On Emscripten, `WakeList` is reachable via the feature gates that pull // it into `util::mod`, but the actual consumers (time::clock, sync::notify, // etc.) may be cfg'd out — leaving the type used only through a re-export. #![cfg_attr(target_os = "emscripten", allow(dead_code))] From f6527a570880c58cd6b630fb702329c480133759 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 15 Jul 2026 11:46:35 -0700 Subject: [PATCH 3/3] review: remove stale setTimeout doc and unused follow-up scaffolding --- tokio/src/macros/cfg.rs | 32 -------------------------------- tokio/src/runtime/time/mod.rs | 12 +----------- tokio/src/time/mod.rs | 14 +++++--------- tokio/src/util/mod.rs | 18 +++++++----------- tokio/src/util/trace.rs | 17 ++++++++--------- tokio/src/util/wake_list.rs | 5 ----- 6 files changed, 21 insertions(+), 77 deletions(-) diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index e9a9962ac8b..86c1f199386 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -782,35 +782,3 @@ macro_rules! cfg_not_schedule_latency { )* } } - -/// Enables Emscripten-specific code. -macro_rules! cfg_emscripten { - ($($item:item)*) => { - $( - #[cfg(target_os = "emscripten")] - #[cfg_attr(docsrs, doc(cfg(target_os = "emscripten")))] - $item - )* - } -} - -/// Enables code for non-Emscripten targets. -macro_rules! cfg_not_emscripten { - ($($item:item)*) => { - $( - #[cfg(not(target_os = "emscripten"))] - $item - )* - } -} - -/// Enables code requiring both the `rt` feature and the Emscripten target. -macro_rules! cfg_rt_emscripten { - ($($item:item)*) => { - $( - #[cfg(all(feature = "rt", target_os = "emscripten"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "rt", target_os = "emscripten"))))] - $item - )* - } -} diff --git a/tokio/src/runtime/time/mod.rs b/tokio/src/runtime/time/mod.rs index 331906f5b77..bbdb664c1d3 100644 --- a/tokio/src/runtime/time/mod.rs +++ b/tokio/src/runtime/time/mod.rs @@ -287,22 +287,12 @@ impl Driver { } impl Handle { - #[cfg_attr(not(target_os = "emscripten"), allow(dead_code))] - pub(crate) fn process(&self, clock: &Clock) { + pub(self) fn process(&self, clock: &Clock) { let now = self.time_source().now(clock); self.process_at_time(now); } - /// Returns the absolute deadline in ticks for the soonest unexpired - /// timer, or `None` if no timers are registered. Used by the Emscripten - /// schedule loop to compute its `setTimeout` delay; ticks here are the - /// time wheel's monotonic tick basis (`time_source.now()`). - #[cfg(target_os = "emscripten")] - pub(crate) fn next_expiration_tick(&self) -> Option { - self.inner.lock().wheel.next_expiration_time() - } - pub(self) fn process_at_time(&self, mut now: u64) { let mut waker_list = WakeList::new(); diff --git a/tokio/src/time/mod.rs b/tokio/src/time/mod.rs index e2621e5af9b..8627a7838aa 100644 --- a/tokio/src/time/mod.rs +++ b/tokio/src/time/mod.rs @@ -19,9 +19,6 @@ //! //! These types must be used from within the context of the [`Runtime`](crate::runtime::Runtime). //! -//! On `wasm32-unknown-emscripten` these timers are driven by JavaScript's -//! event loop via `setTimeout`. -//! //! # Examples //! //! Wait 100ms and print "100 ms have elapsed" @@ -89,6 +86,11 @@ mod clock; pub(crate) use self::clock::Clock; +cfg_test_util! { + pub use clock::{advance, pause, resume}; +} + +pub mod error; mod instant; pub use self::instant::Instant; @@ -103,12 +105,6 @@ mod timeout; #[doc(inline)] pub use timeout::{timeout, timeout_at, Timeout}; -cfg_test_util! { - pub use clock::{advance, pause, resume}; -} - -pub mod error; - // Re-export for convenience #[doc(no_inline)] pub use std::time::Duration; diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index d4390c9b73e..c671fd6a1da 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -5,15 +5,14 @@ cfg_io_driver! { #[cfg(feature = "fs")] pub(crate) mod as_ref; -cfg_rt! { - pub(crate) mod atomic_cell; -} +#[cfg(feature = "rt")] +pub(crate) mod atomic_cell; -cfg_net! { - mod blocking_check; - #[allow(unused_imports)] - pub(crate) use blocking_check::check_socket_for_blocking; -} +#[cfg(feature = "net")] +mod blocking_check; +#[cfg(feature = "net")] +#[allow(unused_imports)] +pub(crate) use blocking_check::check_socket_for_blocking; pub(crate) mod metric_atomics; @@ -41,9 +40,6 @@ mod wake_list; feature = "signal", feature = "time", ))] -// Some WakeList consumers are cfg'd out on Emscripten, so the re-export may be -// unused there. -#[cfg_attr(target_os = "emscripten", allow(unused_imports))] pub(crate) use wake_list::WakeList; #[cfg(any( diff --git a/tokio/src/util/trace.rs b/tokio/src/util/trace.rs index 9de1ee0ebfa..b22c2aeb593 100644 --- a/tokio/src/util/trace.rs +++ b/tokio/src/util/trace.rs @@ -1,4 +1,3 @@ -// SpawnMeta uses native runtime task infrastructure cfg_rt! { use std::marker::PhantomData; @@ -181,12 +180,12 @@ cfg_rt! { } } -#[cfg(feature = "time")] -#[cfg_attr(docsrs, doc(cfg(feature = "time")))] -#[track_caller] -pub(crate) fn caller_location() -> Option<&'static std::panic::Location<'static>> { - #[cfg(all(tokio_unstable, feature = "tracing"))] - return Some(std::panic::Location::caller()); - #[cfg(not(all(tokio_unstable, feature = "tracing")))] - None +cfg_time! { + #[track_caller] + pub(crate) fn caller_location() -> Option<&'static std::panic::Location<'static>> { + #[cfg(all(tokio_unstable, feature = "tracing"))] + return Some(std::panic::Location::caller()); + #[cfg(not(all(tokio_unstable, feature = "tracing")))] + None + } } diff --git a/tokio/src/util/wake_list.rs b/tokio/src/util/wake_list.rs index db077ebee17..23a559d02be 100644 --- a/tokio/src/util/wake_list.rs +++ b/tokio/src/util/wake_list.rs @@ -1,8 +1,3 @@ -// On Emscripten, `WakeList` is reachable via the feature gates that pull -// it into `util::mod`, but the actual consumers (time::clock, sync::notify, -// etc.) may be cfg'd out — leaving the type used only through a re-export. -#![cfg_attr(target_os = "emscripten", allow(dead_code))] - use core::mem::MaybeUninit; use core::ptr; use std::task::Waker;