diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78bdfbc7e..ed52a9208 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,16 +34,18 @@ jobs: runs-on: ubuntu-latest needs: - test - - test-unstable - test-parking_lot + - valgrind + - test-unstable - miri + - asan - cross - features - minrust + - minimal-versions - fmt - clippy - docs - - valgrind - loom-compile - check-readme - test-hyper @@ -195,7 +197,7 @@ jobs: - name: Install Rust ${{ env.rust_nightly }} uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2022-07-10 + toolchain: nightly components: miri override: true - uses: Swatinem/rust-cache@v1 @@ -489,9 +491,23 @@ jobs: - name: Install cargo-wasi run: cargo install cargo-wasi - # TODO: Expand this when full WASI support lands. - # Currently, this is a bare bones regression test - # for features that work today with wasi. + - name: WASI test tokio full + run: cargo test -p tokio --target wasm32-wasi --features full + env: + CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --" + RUSTFLAGS: --cfg tokio_unstable -Dwarnings + + - name: WASI test tokio-util full + run: cargo test -p tokio-util --target wasm32-wasi --features full + env: + CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --" + RUSTFLAGS: --cfg tokio_unstable -Dwarnings + + - name: WASI test tokio-stream + run: cargo test -p tokio-stream --target wasm32-wasi --features time,net,io-util,sync + env: + CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --" + RUSTFLAGS: --cfg tokio_unstable -Dwarnings - name: test tests-integration --features wasi-rt # TODO: this should become: `cargo hack wasi test --each-feature` diff --git a/.github/workflows/stress-test.yml b/.github/workflows/stress-test.yml index bcbede18e..e34e0d549 100644 --- a/.github/workflows/stress-test.yml +++ b/.github/workflows/stress-test.yml @@ -12,7 +12,7 @@ env: rust_stable: stable jobs: - stess-test: + stress-test: name: Stress Test runs-on: ubuntu-latest strategy: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7e758574..e25a51cde 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -562,7 +562,7 @@ Tokio ≥1.0.0 comes with LTS guarantees: The goal of these guarantees is to provide stability to the ecosystem. -## Mininum Supported Rust Version (MSRV) +## Minimum Supported Rust Version (MSRV) * All Tokio ≥1.0.0 releases will support at least a 6-month old Rust compiler release. diff --git a/SECURITY.md b/SECURITY.md index bf155ff9e..75c2c4d3c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,13 +1,13 @@ ## Report a security issue -The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [security@tokio.rs](mailto:security@tokio.rs). Security issues should not be reported via the public Github Issue tracker. +The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [security@tokio.rs](mailto:security@tokio.rs). Security issues should not be reported via the public GitHub Issue tracker. ## Vulnerability coordination -Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [Github Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable. +Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [GitHub Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable. -Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, Github username(s) and any other salient information to [security@tokio.rs](mailto:security@tokio.rs). Participation in security issue coordination processes is at the discretion of the Tokio team. +Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, GitHub username(s) and any other salient information to [security@tokio.rs](mailto:security@tokio.rs). Participation in security issue coordination processes is at the discretion of the Tokio team. ## Security advisories -The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project Github Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`). +The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project GitHub Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`). diff --git a/tests-integration/Cargo.toml b/tests-integration/Cargo.toml index d2dd5c9c3..82cdd4b24 100644 --- a/tests-integration/Cargo.toml +++ b/tests-integration/Cargo.toml @@ -7,7 +7,6 @@ publish = false [[bin]] name = "test-cat" -required-features = ["rt-process-io-util"] [[bin]] name = "test-mem" @@ -31,7 +30,6 @@ name = "rt_yield" required-features = ["rt", "macros", "sync"] [features] -rt-process-io-util = ["tokio/rt", "tokio/macros", "tokio/process", "tokio/io-util", "tokio/io-std"] # For mem check rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"] # For test-process-signal diff --git a/tests-integration/src/bin/test-cat.rs b/tests-integration/src/bin/test-cat.rs index 1e40c9d9d..3b30bf18a 100644 --- a/tests-integration/src/bin/test-cat.rs +++ b/tests-integration/src/bin/test-cat.rs @@ -1,14 +1,20 @@ //! A cat-like utility that can be used as a subprocess to test I/O //! stream communication. -use tokio::io::AsyncWriteExt; +use std::io; +use std::io::Write; -#[tokio::main(flavor = "current_thread")] -async fn main() { - let mut stdin = tokio::io::stdin(); - let mut stdout = tokio::io::stdout(); - - tokio::io::copy(&mut stdin, &mut stdout).await.unwrap(); - - stdout.flush().await.unwrap(); +fn main() { + let stdin = io::stdin(); + let mut stdout = io::stdout(); + let mut line = String::new(); + loop { + line.clear(); + stdin.read_line(&mut line).unwrap(); + if line.is_empty() { + break; + } + stdout.write_all(line.as_bytes()).unwrap(); + } + stdout.flush().unwrap(); } diff --git a/tests-integration/tests/macros_main.rs b/tests-integration/tests/macros_main.rs index 314428051..e34387e5e 100644 --- a/tests-integration/tests/macros_main.rs +++ b/tests-integration/tests/macros_main.rs @@ -1,4 +1,8 @@ -#![cfg(all(feature = "macros", feature = "rt-multi-thread"))] +#![cfg(all( + feature = "macros", + feature = "rt-multi-thread", + not(target_os = "wasi") +))] #[tokio::main] async fn basic_main() -> usize { diff --git a/tests-integration/tests/macros_select.rs b/tests-integration/tests/macros_select.rs index 4c4fef7ce..a1a242c0f 100644 --- a/tests-integration/tests/macros_select.rs +++ b/tests-integration/tests/macros_select.rs @@ -4,6 +4,7 @@ use futures::channel::oneshot; use futures::executor::block_on; use std::thread; +#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")] #[test] fn join_with_select() { block_on(async { diff --git a/tests-integration/tests/process_stdio.rs b/tests-integration/tests/process_stdio.rs index 94861635b..3f8ebfbfa 100644 --- a/tests-integration/tests/process_stdio.rs +++ b/tests-integration/tests/process_stdio.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::join; @@ -8,22 +8,12 @@ use tokio_test::assert_ok; use futures::future::{self, FutureExt}; use std::convert::TryInto; +use std::env; use std::io; use std::process::{ExitStatus, Stdio}; -// so, we need to change this back as a test, but for now this doesn't work because of: -// https://github.com/rust-lang/rust/pull/95469 -// -// undo when this is closed: https://github.com/tokio-rs/tokio/issues/4802 - -// fn cat() -> Command { -// let mut cmd = Command::new(std::env!("CARGO_BIN_EXE_test-cat")); -// cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); -// cmd -// } - fn cat() -> Command { - let mut cmd = Command::new("cat"); + let mut cmd = Command::new(env!("CARGO_BIN_EXE_test-cat")); cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); cmd } diff --git a/tokio-stream/Cargo.toml b/tokio-stream/Cargo.toml index a84c924bf..2d693f860 100644 --- a/tokio-stream/Cargo.toml +++ b/tokio-stream/Cargo.toml @@ -38,6 +38,7 @@ parking_lot = "0.12.0" tokio-test = { path = "../tokio-test" } futures = { version = "0.3", default-features = false } +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] proptest = "1" [package.metadata.docs.rs] diff --git a/tokio-stream/tests/stream_panic.rs b/tokio-stream/tests/stream_panic.rs index c0a3a45a4..22c1c2080 100644 --- a/tokio-stream/tests/stream_panic.rs +++ b/tokio-stream/tests/stream_panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "time")] +#![cfg(all(feature = "time", not(target_os = "wasi")))] // Wasi does not support panic recovery use parking_lot::{const_mutex, Mutex}; use std::error::Error; diff --git a/tokio-stream/tests/stream_stream_map.rs b/tokio-stream/tests/stream_stream_map.rs index 53f3d86c7..ffc489b32 100644 --- a/tokio-stream/tests/stream_stream_map.rs +++ b/tokio-stream/tests/stream_stream_map.rs @@ -325,6 +325,7 @@ fn one_ready_many_none() { } } +#[cfg(not(target_os = "wasi"))] proptest::proptest! { #[test] fn fuzz_pending_complete_mix(kinds: Vec) { diff --git a/tokio-test/src/macros.rs b/tokio-test/src/macros.rs index 7ca73451f..4c029c9e7 100644 --- a/tokio-test/src/macros.rs +++ b/tokio-test/src/macros.rs @@ -260,7 +260,7 @@ macro_rules! assert_err { }}; } -/// Asserts that an exact duration has elapsed since since the start instant ±1ms. +/// Asserts that an exact duration has elapsed since the start instant ±1ms. /// /// ```rust /// use tokio::time::{self, Instant}; diff --git a/tokio-util/src/lib.rs b/tokio-util/src/lib.rs index e4876a58a..524fc4705 100644 --- a/tokio-util/src/lib.rs +++ b/tokio-util/src/lib.rs @@ -29,6 +29,7 @@ cfg_codec! { } cfg_net! { + #[cfg(not(target_arch = "wasm32"))] pub mod udp; pub mod net; } diff --git a/tokio-util/src/sync/cancellation_token/tree_node.rs b/tokio-util/src/sync/cancellation_token/tree_node.rs index b6cd698e2..8f97dee8d 100644 --- a/tokio-util/src/sync/cancellation_token/tree_node.rs +++ b/tokio-util/src/sync/cancellation_token/tree_node.rs @@ -200,7 +200,7 @@ where /// `parent` MUST have been a parent of the node when they both got locked, /// otherwise there is a potential for a deadlock as invariant #2 would be violated. /// -/// To aquire the locks for node and parent, use [with_locked_node_and_parent]. +/// To acquire the locks for node and parent, use [with_locked_node_and_parent]. fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) { // Pre-allocate in the parent, for performance parent.children.reserve(node.children.len()); @@ -218,7 +218,7 @@ fn move_children_to_parent(node: &mut Inner, parent: &mut Inner) { /// Removes a child from the parent. /// /// `parent` MUST be the parent of `node`. -/// To aquire the locks for node and parent, use [with_locked_node_and_parent]. +/// To acquire the locks for node and parent, use [with_locked_node_and_parent]. fn remove_child(parent: &mut Inner, mut node: MutexGuard<'_, Inner>) { // Query the position from where to remove a node let pos = node.parent_idx; diff --git a/tokio-util/src/sync/tests/loom_cancellation_token.rs b/tokio-util/src/sync/tests/loom_cancellation_token.rs index e9c9f3dd9..4007b5be8 100644 --- a/tokio-util/src/sync/tests/loom_cancellation_token.rs +++ b/tokio-util/src/sync/tests/loom_cancellation_token.rs @@ -80,7 +80,7 @@ fn drop_token_no_child() { } #[test] -fn drop_token_with_childs() { +fn drop_token_with_children() { loom::model(|| { let token1 = CancellationToken::new(); let child_token1 = token1.child_token(); diff --git a/tokio-util/src/task/mod.rs b/tokio-util/src/task/mod.rs index 7ba8ad9a2..de41dd5db 100644 --- a/tokio-util/src/task/mod.rs +++ b/tokio-util/src/task/mod.rs @@ -2,7 +2,9 @@ #[cfg(tokio_unstable)] mod join_map; +#[cfg(not(target_os = "wasi"))] mod spawn_pinned; +#[cfg(not(target_os = "wasi"))] pub use spawn_pinned::LocalPoolHandle; #[cfg(tokio_unstable)] diff --git a/tokio-util/src/time/delay_queue.rs b/tokio-util/src/time/delay_queue.rs index 07082b977..f6a3c63a5 100644 --- a/tokio-util/src/time/delay_queue.rs +++ b/tokio-util/src/time/delay_queue.rs @@ -190,7 +190,7 @@ impl SlabStorage { let key_contained = self.key_map.contains_key(&key.into()); if key_contained { - // It's possible that a `compact` call creates capacitiy in `self.inner` in + // It's possible that a `compact` call creates capacity in `self.inner` in // such a way that a `self.inner.insert` call creates a `key` which was // previously given out during an `insert` call prior to the `compact` call. // If `key` is contained in `self.key_map`, we have encountered this exact situation, diff --git a/tokio-util/tests/context.rs b/tokio-util/tests/context.rs index 7510f36fd..c2c05d25c 100644 --- a/tokio-util/tests/context.rs +++ b/tokio-util/tests/context.rs @@ -1,4 +1,5 @@ #![cfg(feature = "rt")] +#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #![warn(rust_2018_idioms)] use tokio::runtime::Builder; diff --git a/tokio-util/tests/framed_write.rs b/tokio-util/tests/framed_write.rs index 259d9b0c9..01d71b2b8 100644 --- a/tokio-util/tests/framed_write.rs +++ b/tokio-util/tests/framed_write.rs @@ -130,7 +130,7 @@ fn write_hits_backpressure() { _ => unreachable!(), } - // Push a new new chunk + // Push a new chunk mock.calls.push_back(Ok(b[..].to_vec())); } // 1 'wouldblock', 4 * 2KB buffers, 1 b-byte buffer diff --git a/tokio-util/tests/io_sync_bridge.rs b/tokio-util/tests/io_sync_bridge.rs index 0d420857b..e22631e48 100644 --- a/tokio-util/tests/io_sync_bridge.rs +++ b/tokio-util/tests/io_sync_bridge.rs @@ -1,4 +1,5 @@ #![cfg(feature = "io-util")] +#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads use std::error::Error; use std::io::{Cursor, Read, Result as IoResult}; diff --git a/tokio-util/tests/panic.rs b/tokio-util/tests/panic.rs index fbaab5f22..e4fcb47ef 100644 --- a/tokio-util/tests/panic.rs +++ b/tokio-util/tests/panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery use parking_lot::{const_mutex, Mutex}; use std::error::Error; diff --git a/tokio-util/tests/spawn_pinned.rs b/tokio-util/tests/spawn_pinned.rs index ac68b7f34..b620cce04 100644 --- a/tokio-util/tests/spawn_pinned.rs +++ b/tokio-util/tests/spawn_pinned.rs @@ -1,4 +1,5 @@ #![warn(rust_2018_idioms)] +#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads use std::rc::Rc; use std::sync::Arc; diff --git a/tokio-util/tests/sync_cancellation_token.rs b/tokio-util/tests/sync_cancellation_token.rs index 28ba284b6..de8ecc777 100644 --- a/tokio-util/tests/sync_cancellation_token.rs +++ b/tokio-util/tests/sync_cancellation_token.rs @@ -258,7 +258,7 @@ fn cancel_only_all_descendants() { let child2_token = token.child_token(); let grandchild_token = child1_token.child_token(); let grandchild2_token = child1_token.child_token(); - let grandgrandchild_token = grandchild_token.child_token(); + let great_grandchild_token = grandchild_token.child_token(); assert!(!parent_token.is_cancelled()); assert!(!token.is_cancelled()); @@ -267,7 +267,7 @@ fn cancel_only_all_descendants() { assert!(!child2_token.is_cancelled()); assert!(!grandchild_token.is_cancelled()); assert!(!grandchild2_token.is_cancelled()); - assert!(!grandgrandchild_token.is_cancelled()); + assert!(!great_grandchild_token.is_cancelled()); let parent_fut = parent_token.cancelled(); let fut = token.cancelled(); @@ -276,7 +276,7 @@ fn cancel_only_all_descendants() { let child2_fut = child2_token.cancelled(); let grandchild_fut = grandchild_token.cancelled(); let grandchild2_fut = grandchild2_token.cancelled(); - let grandgrandchild_fut = grandgrandchild_token.cancelled(); + let great_grandchild_fut = great_grandchild_token.cancelled(); pin!(parent_fut); pin!(fut); @@ -285,7 +285,7 @@ fn cancel_only_all_descendants() { pin!(child2_fut); pin!(grandchild_fut); pin!(grandchild2_fut); - pin!(grandgrandchild_fut); + pin!(great_grandchild_fut); assert_eq!( Poll::Pending, @@ -321,7 +321,7 @@ fn cancel_only_all_descendants() { ); assert_eq!( Poll::Pending, - grandgrandchild_fut + great_grandchild_fut .as_mut() .poll(&mut Context::from_waker(&waker)) ); @@ -339,7 +339,7 @@ fn cancel_only_all_descendants() { assert!(child2_token.is_cancelled()); assert!(grandchild_token.is_cancelled()); assert!(grandchild2_token.is_cancelled()); - assert!(grandgrandchild_token.is_cancelled()); + assert!(great_grandchild_token.is_cancelled()); assert_eq!( Poll::Ready(()), @@ -367,7 +367,7 @@ fn cancel_only_all_descendants() { ); assert_eq!( Poll::Ready(()), - grandgrandchild_fut + great_grandchild_fut .as_mut() .poll(&mut Context::from_waker(&waker)) ); diff --git a/tokio-util/tests/time_delay_queue.rs b/tokio-util/tests/time_delay_queue.rs index cb163adf3..0fcdbf4a0 100644 --- a/tokio-util/tests/time_delay_queue.rs +++ b/tokio-util/tests/time_delay_queue.rs @@ -778,6 +778,7 @@ async fn compact_change_deadline() { assert!(entry.is_none()); } +#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")] #[tokio::test(start_paused = true)] async fn remove_after_compact() { let now = Instant::now(); @@ -794,6 +795,7 @@ async fn remove_after_compact() { assert!(panic.is_err()); } +#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")] #[tokio::test(start_paused = true)] async fn remove_after_compact_poll() { let now = Instant::now(); diff --git a/tokio-util/tests/udp.rs b/tokio-util/tests/udp.rs index b9436a30a..1b9980636 100644 --- a/tokio-util/tests/udp.rs +++ b/tokio-util/tests/udp.rs @@ -1,4 +1,5 @@ #![warn(rust_2018_idioms)] +#![cfg(not(target_os = "wasi"))] // Wasi doesn't support UDP use tokio::net::UdpSocket; use tokio_stream::StreamExt; diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 661962494..ff5b1360a 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -51,13 +51,14 @@ net = [ "mio/os-poll", "mio/os-ext", "mio/net", - "socket2", "winapi/fileapi", "winapi/handleapi", "winapi/namedpipeapi", "winapi/winbase", "winapi/winnt", "winapi/minwindef", + "winapi/accctrl", + "winapi/aclapi" ] process = [ "bytes", @@ -68,11 +69,11 @@ process = [ "mio/net", "signal-hook-registry", "winapi/handleapi", + "winapi/minwindef", "winapi/processthreadsapi", "winapi/threadpoollegacyapiset", "winapi/winbase", "winapi/winnt", - "winapi/minwindef", ] # Includes basic task execution capabilities rt = ["once_cell"] @@ -112,11 +113,13 @@ pin-project-lite = "0.2.0" bytes = { version = "1.0.0", optional = true } once_cell = { version = "1.5.2", optional = true } memchr = { version = "2.2", optional = true } -mio = { version = "0.8.1", optional = true } -socket2 = { version = "0.4.4", optional = true, features = [ "all" ] } +mio = { version = "0.8.4", optional = true } num_cpus = { version = "1.8.0", optional = true } parking_lot = { version = "0.12.0", optional = true } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +socket2 = { version = "0.4.4", features = [ "all" ] } + # Currently unstable. The API exposed by these features may be broken at any time. # Requires `--cfg tokio_unstable` to enable. [target.'cfg(tokio_unstable)'.dependencies] @@ -149,10 +152,12 @@ async-stream = "0.3" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] proptest = "1" -rand = "0.8.0" socket2 = "0.4" -[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] +rand = "0.8.0" + +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies] wasm-bindgen-test = "0.3.0" [target.'cfg(target_os = "freebsd")'.dev-dependencies] diff --git a/tokio/build.rs b/tokio/build.rs index 6da64cdea..315d0b0d6 100644 --- a/tokio/build.rs +++ b/tokio/build.rs @@ -34,6 +34,10 @@ fn main() { enable_const_thread_local = true; } } + + if !ac.probe_rustc_version(1, 51) { + autocfg::emit("tokio_no_addr_of") + } } Err(e) => { diff --git a/tokio/docs/reactor-refactor.md b/tokio/docs/reactor-refactor.md index 1c9ace15a..3005afc01 100644 --- a/tokio/docs/reactor-refactor.md +++ b/tokio/docs/reactor-refactor.md @@ -188,9 +188,9 @@ readiness, the driver's tick is packed into the atomic `usize`. The `ScheduledIo` readiness `AtomicUsize` is structured as: ``` -| reserved | generation | driver tick | readinesss | -|----------+------------+--------------+------------| -| 1 bit | 7 bits + 8 bits + 16 bits | +| reserved | generation | driver tick | readiness | +|----------+------------+--------------+-----------| +| 1 bit | 7 bits + 8 bits + 16 bits | ``` The `reserved` and `generation` components exist today. diff --git a/tokio/src/coop.rs b/tokio/src/coop.rs index bde1888a3..1ed1d17aa 100644 --- a/tokio/src/coop.rs +++ b/tokio/src/coop.rs @@ -207,7 +207,7 @@ cfg_coop! { mod test { use super::*; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; fn get() -> Budget { @@ -215,7 +215,7 @@ mod test { } #[test] - fn bugeting() { + fn budgeting() { use futures::future::poll_fn; use tokio_test::*; diff --git a/tokio/src/io/blocking.rs b/tokio/src/io/blocking.rs index 1d79ee7a2..f6db4500a 100644 --- a/tokio/src/io/blocking.rs +++ b/tokio/src/io/blocking.rs @@ -34,8 +34,9 @@ enum State { Busy(sys::Blocking<(io::Result, Buf, T)>), } -cfg_io_std! { +cfg_io_blocking! { impl Blocking { + #[cfg_attr(feature = "fs", allow(dead_code))] pub(crate) fn new(inner: T) -> Blocking { Blocking { inner: Some(inner), diff --git a/tokio/src/io/driver/mod.rs b/tokio/src/io/driver/mod.rs index 4b420e1c8..5d9e114a6 100644 --- a/tokio/src/io/driver/mod.rs +++ b/tokio/src/io/driver/mod.rs @@ -72,6 +72,8 @@ pub(super) struct Inner { io_dispatch: RwLock, /// Used to wake up the reactor from a call to `turn`. + /// Not supported on Wasi due to lack of threading support. + #[cfg(not(target_os = "wasi"))] waker: mio::Waker, metrics: IoDriverMetrics, @@ -115,6 +117,7 @@ impl Driver { /// creation. pub(crate) fn new() -> io::Result { let poll = mio::Poll::new()?; + #[cfg(not(target_os = "wasi"))] let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?; let registry = poll.registry().try_clone()?; @@ -129,6 +132,7 @@ impl Driver { inner: Arc::new(Inner { registry, io_dispatch: RwLock::new(IoDispatcher::new(allocator)), + #[cfg(not(target_os = "wasi"))] waker, metrics: IoDriverMetrics::default(), }), @@ -164,6 +168,11 @@ impl Driver { match self.poll.poll(&mut events, max_wait) { Ok(_) => {} Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + #[cfg(target_os = "wasi")] + Err(e) if e.kind() == io::ErrorKind::InvalidInput => { + // In case of wasm32_wasi this error happens, when trying to poll without subscriptions + // just return from the park, as there would be nothing, which wakes us up. + } Err(e) => return Err(e), } @@ -273,6 +282,7 @@ cfg_not_rt! { /// /// This function panics if there is no current reactor set, or if the `rt` /// feature flag is not enabled. + #[track_caller] pub(super) fn current() -> Self { panic!("{}", crate::util::error::CONTEXT_MISSING_ERROR) } @@ -300,6 +310,7 @@ impl Handle { /// blocked in `turn`, then the next call to `turn` will not block and /// return immediately. fn wakeup(&self) { + #[cfg(not(target_os = "wasi"))] self.inner.waker.wake().expect("failed to wake I/O driver"); } } diff --git a/tokio/src/io/driver/scheduled_io.rs b/tokio/src/io/driver/scheduled_io.rs index 76f93431b..229fe7b1c 100644 --- a/tokio/src/io/driver/scheduled_io.rs +++ b/tokio/src/io/driver/scheduled_io.rs @@ -66,6 +66,14 @@ cfg_io_readiness! { _p: PhantomPinned, } + generate_addr_of_methods! { + impl<> Waiter { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { + &self.pointers + } + } + } + /// Future returned by `readiness()`. struct Readiness<'a> { scheduled_io: &'a ScheduledIo, @@ -399,8 +407,8 @@ cfg_io_readiness! { ptr } - unsafe fn pointers(mut target: NonNull) -> NonNull> { - NonNull::from(&mut target.as_mut().pointers) + unsafe fn pointers(target: NonNull) -> NonNull> { + Waiter::addr_of_pointers(target) } } diff --git a/tokio/src/io/mod.rs b/tokio/src/io/mod.rs index cfdda61f6..7113af0c3 100644 --- a/tokio/src/io/mod.rs +++ b/tokio/src/io/mod.rs @@ -211,9 +211,11 @@ cfg_io_driver_impl! { pub use driver::{Interest, Ready}; } + #[cfg_attr(target_os = "wasi", allow(unused_imports))] mod poll_evented; #[cfg(not(loom))] + #[cfg_attr(target_os = "wasi", allow(unused_imports))] pub(crate) use poll_evented::PollEvented; } diff --git a/tokio/src/io/poll_evented.rs b/tokio/src/io/poll_evented.rs index ce4c1426a..25cece627 100644 --- a/tokio/src/io/poll_evented.rs +++ b/tokio/src/io/poll_evented.rs @@ -77,6 +77,7 @@ impl PollEvented { /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] #[cfg_attr(feature = "signal", allow(unused))] pub(crate) fn new(io: E) -> io::Result { PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE) @@ -97,6 +98,7 @@ impl PollEvented { /// a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) /// function. + #[track_caller] #[cfg_attr(feature = "signal", allow(unused))] pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result { Self::new_with_interest_and_handle(io, interest, Handle::current()) @@ -134,7 +136,7 @@ impl PollEvented { } feature! { - #![any(feature = "net", feature = "process")] + #![any(feature = "net", all(unix, feature = "process"))] use crate::io::ReadBuf; use std::task::{Context, Poll}; diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index c0d7e6252..e8ddf16b3 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -393,6 +393,20 @@ compile_error! { "Tokio requires the platform pointer width to be 32, 64, or 128 bits" } +#[cfg(all( + not(tokio_unstable), + target_arch = "wasm32", + any( + feature = "fs", + feature = "io-std", + feature = "net", + feature = "process", + feature = "rt-multi-thread", + feature = "signal" + ) +))] +compile_error!("Only features sync,macros,io-util,rt are supported on wasm."); + // Includes re-exports used by macros. // // This module is not intended to be part of the public API. In general, any @@ -417,7 +431,12 @@ cfg_process! { pub mod process; } -#[cfg(any(feature = "net", feature = "fs", feature = "io-std"))] +#[cfg(any( + feature = "fs", + feature = "io-std", + feature = "net", + all(windows, feature = "process"), +))] mod blocking; cfg_rt! { diff --git a/tokio/src/macros/addr_of.rs b/tokio/src/macros/addr_of.rs new file mode 100644 index 000000000..51d488972 --- /dev/null +++ b/tokio/src/macros/addr_of.rs @@ -0,0 +1,53 @@ +//! This module defines a macro that lets you go from a raw pointer to a struct +//! to a raw pointer to a field of the struct. + +#[cfg(not(tokio_no_addr_of))] +macro_rules! generate_addr_of_methods { + ( + impl<$($gen:ident)*> $struct_name:ty {$( + $(#[$attrs:meta])* + $vis:vis unsafe fn $fn_name:ident(self: NonNull) -> NonNull<$field_type:ty> { + &self$(.$field_name:tt)+ + } + )*} + ) => { + impl<$($gen)*> $struct_name {$( + $(#[$attrs])* + $vis unsafe fn $fn_name(me: ::core::ptr::NonNull) -> ::core::ptr::NonNull<$field_type> { + let me = me.as_ptr(); + let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ ); + ::core::ptr::NonNull::new_unchecked(field) + } + )*} + }; +} + +// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This +// version of the macro uses a workaround for older versions of rustc. +#[cfg(tokio_no_addr_of)] +macro_rules! generate_addr_of_methods { + ( + impl<$($gen:ident)*> $struct_name:ty {$( + $(#[$attrs:meta])* + $vis:vis unsafe fn $fn_name:ident(self: NonNull) -> NonNull<$field_type:ty> { + &self$(.$field_name:tt)+ + } + )*} + ) => { + impl<$($gen)*> $struct_name {$( + $(#[$attrs])* + $vis unsafe fn $fn_name(me: ::core::ptr::NonNull) -> ::core::ptr::NonNull<$field_type> { + let me = me.as_ptr(); + let me_u8 = me as *mut u8; + + let field_offset = { + let me_ref = &*me; + let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8; + field_ref_u8.offset_from(me_u8) + }; + + ::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast()) + } + )*} + }; +} diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index 45ae5f991..f72872213 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -61,6 +61,7 @@ macro_rules! cfg_fs { ($($item:item)*) => { $( #[cfg(feature = "fs")] + #[cfg(not(target_os = "wasi"))] #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] $item )* @@ -69,7 +70,11 @@ macro_rules! cfg_fs { macro_rules! cfg_io_blocking { ($($item:item)*) => { - $( #[cfg(any(feature = "io-std", feature = "fs"))] $item )* + $( #[cfg(any( + feature = "io-std", + feature = "fs", + all(windows, feature = "process"), + ))] $item )* } } @@ -78,12 +83,12 @@ macro_rules! cfg_io_driver { $( #[cfg(any( feature = "net", - feature = "process", + all(unix, feature = "process"), all(unix, feature = "signal"), ))] #[cfg_attr(docsrs, doc(cfg(any( feature = "net", - feature = "process", + all(unix, feature = "process"), all(unix, feature = "signal"), ))))] $item @@ -96,7 +101,7 @@ macro_rules! cfg_io_driver_impl { $( #[cfg(any( feature = "net", - feature = "process", + all(unix, feature = "process"), all(unix, feature = "signal"), ))] $item @@ -109,7 +114,7 @@ macro_rules! cfg_not_io_driver { $( #[cfg(not(any( feature = "net", - feature = "process", + all(unix, feature = "process"), all(unix, feature = "signal"), )))] $item @@ -247,6 +252,7 @@ macro_rules! cfg_process { #[cfg(feature = "process")] #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[cfg(not(loom))] + #[cfg(not(target_os = "wasi"))] $item )* } @@ -275,6 +281,7 @@ macro_rules! cfg_signal { #[cfg(feature = "signal")] #[cfg_attr(docsrs, doc(cfg(feature = "signal")))] #[cfg(not(loom))] + #[cfg(not(target_os = "wasi"))] $item )* } @@ -334,7 +341,7 @@ macro_rules! cfg_not_rt { macro_rules! cfg_rt_multi_thread { ($($item:item)*) => { $( - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))] $item )* @@ -451,7 +458,8 @@ macro_rules! cfg_has_atomic_u64 { target_arch = "arm", target_arch = "mips", target_arch = "powerpc", - target_arch = "riscv32" + target_arch = "riscv32", + target_arch = "wasm32" )))] $item )* @@ -465,9 +473,28 @@ macro_rules! cfg_not_has_atomic_u64 { target_arch = "arm", target_arch = "mips", target_arch = "powerpc", - target_arch = "riscv32" + target_arch = "riscv32", + target_arch = "wasm32" ))] $item )* } } + +macro_rules! cfg_not_wasi { + ($($item:item)*) => { + $( + #[cfg(not(target_os = "wasi"))] + $item + )* + } +} + +macro_rules! cfg_is_wasm_not_wasi { + ($($item:item)*) => { + $( + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] + $item + )* + } +} diff --git a/tokio/src/macros/mod.rs b/tokio/src/macros/mod.rs index a1839c830..57678c67b 100644 --- a/tokio/src/macros/mod.rs +++ b/tokio/src/macros/mod.rs @@ -15,6 +15,9 @@ mod ready; #[macro_use] mod thread_local; +#[macro_use] +mod addr_of; + cfg_trace! { #[macro_use] mod trace; diff --git a/tokio/src/net/mod.rs b/tokio/src/net/mod.rs index 0b8c1ecd1..2d317a8a2 100644 --- a/tokio/src/net/mod.rs +++ b/tokio/src/net/mod.rs @@ -23,8 +23,10 @@ //! [`UnixDatagram`]: UnixDatagram mod addr; -#[cfg(feature = "net")] -pub(crate) use addr::to_socket_addrs; +cfg_not_wasi! { + #[cfg(feature = "net")] + pub(crate) use addr::to_socket_addrs; +} pub use addr::ToSocketAddrs; cfg_net! { @@ -33,11 +35,13 @@ cfg_net! { pub mod tcp; pub use tcp::listener::TcpListener; - pub use tcp::socket::TcpSocket; pub use tcp::stream::TcpStream; + cfg_not_wasi! { + pub use tcp::socket::TcpSocket; - mod udp; - pub use udp::UdpSocket; + mod udp; + pub use udp::UdpSocket; + } } cfg_net_unix! { diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index 8aecb21aa..23e9cd233 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -1,6 +1,9 @@ use crate::io::{Interest, PollEvented}; use crate::net::tcp::TcpStream; -use crate::net::{to_socket_addrs, ToSocketAddrs}; + +cfg_not_wasi! { + use crate::net::{to_socket_addrs, ToSocketAddrs}; +} use std::convert::TryFrom; use std::fmt; @@ -55,68 +58,70 @@ cfg_net! { } impl TcpListener { - /// Creates a new TcpListener, which will be bound to the specified address. - /// - /// The returned listener is ready for accepting connections. - /// - /// Binding with a port number of 0 will request that the OS assigns a port - /// to this listener. The port allocated can be queried via the `local_addr` - /// method. - /// - /// The address type can be any implementor of the [`ToSocketAddrs`] trait. - /// If `addr` yields multiple addresses, bind will be attempted with each of - /// the addresses until one succeeds and returns the listener. If none of - /// the addresses succeed in creating a listener, the error returned from - /// the last attempt (the last address) is returned. - /// - /// This function sets the `SO_REUSEADDR` option on the socket. - /// - /// To configure the socket before binding, you can use the [`TcpSocket`] - /// type. - /// - /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs - /// [`TcpSocket`]: struct@crate::net::TcpSocket - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpListener; - /// - /// use std::io; - /// - /// #[tokio::main] - /// async fn main() -> io::Result<()> { - /// let listener = TcpListener::bind("127.0.0.1:2345").await?; - /// - /// // use the listener - /// - /// # let _ = listener; - /// Ok(()) - /// } - /// ``` - pub async fn bind(addr: A) -> io::Result { - let addrs = to_socket_addrs(addr).await?; + cfg_not_wasi! { + /// Creates a new TcpListener, which will be bound to the specified address. + /// + /// The returned listener is ready for accepting connections. + /// + /// Binding with a port number of 0 will request that the OS assigns a port + /// to this listener. The port allocated can be queried via the `local_addr` + /// method. + /// + /// The address type can be any implementor of the [`ToSocketAddrs`] trait. + /// If `addr` yields multiple addresses, bind will be attempted with each of + /// the addresses until one succeeds and returns the listener. If none of + /// the addresses succeed in creating a listener, the error returned from + /// the last attempt (the last address) is returned. + /// + /// This function sets the `SO_REUSEADDR` option on the socket. + /// + /// To configure the socket before binding, you can use the [`TcpSocket`] + /// type. + /// + /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs + /// [`TcpSocket`]: struct@crate::net::TcpSocket + /// + /// # Examples + /// + /// ```no_run + /// use tokio::net::TcpListener; + /// + /// use std::io; + /// + /// #[tokio::main] + /// async fn main() -> io::Result<()> { + /// let listener = TcpListener::bind("127.0.0.1:2345").await?; + /// + /// // use the listener + /// + /// # let _ = listener; + /// Ok(()) + /// } + /// ``` + pub async fn bind(addr: A) -> io::Result { + let addrs = to_socket_addrs(addr).await?; - let mut last_err = None; + let mut last_err = None; - for addr in addrs { - match TcpListener::bind_addr(addr) { - Ok(listener) => return Ok(listener), - Err(e) => last_err = Some(e), + for addr in addrs { + match TcpListener::bind_addr(addr) { + Ok(listener) => return Ok(listener), + Err(e) => last_err = Some(e), + } } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not resolve to any address", + ) + })) } - Err(last_err.unwrap_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "could not resolve to any address", - ) - })) - } - - fn bind_addr(addr: SocketAddr) -> io::Result { - let listener = mio::net::TcpListener::bind(addr)?; - TcpListener::new(listener) + fn bind_addr(addr: SocketAddr) -> io::Result { + let listener = mio::net::TcpListener::bind(addr)?; + TcpListener::new(listener) + } } /// Accepts a new incoming connection from this listener. @@ -216,11 +221,13 @@ impl TcpListener { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] pub fn from_std(listener: net::TcpListener) -> io::Result { let io = mio::net::TcpListener::from_std(listener); let io = PollEvented::new(io)?; @@ -267,11 +274,22 @@ impl TcpListener { .map(|io| io.into_raw_socket()) .map(|raw_socket| unsafe { std::net::TcpListener::from_raw_socket(raw_socket) }) } + + #[cfg(target_os = "wasi")] + { + use std::os::wasi::io::{FromRawFd, IntoRawFd}; + self.io + .into_inner() + .map(|io| io.into_raw_fd()) + .map(|raw_fd| unsafe { std::net::TcpListener::from_raw_fd(raw_fd) }) + } } - pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result { - let io = PollEvented::new(listener)?; - Ok(TcpListener { io }) + cfg_not_wasi! { + pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result { + let io = PollEvented::new(listener)?; + Ok(TcpListener { io }) + } } /// Returns the local address that this listener is bound to. @@ -384,6 +402,20 @@ mod sys { } } +cfg_unstable! { + #[cfg(target_os = "wasi")] + mod sys { + use super::TcpListener; + use std::os::wasi::prelude::*; + + impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.io.as_raw_fd() + } + } + } +} + #[cfg(windows)] mod sys { use super::TcpListener; diff --git a/tokio/src/net/tcp/mod.rs b/tokio/src/net/tcp/mod.rs index cb8a8b238..734eabe6d 100644 --- a/tokio/src/net/tcp/mod.rs +++ b/tokio/src/net/tcp/mod.rs @@ -2,7 +2,9 @@ pub(crate) mod listener; -pub(crate) mod socket; +cfg_not_wasi! { + pub(crate) mod socket; +} mod split; pub use split::{ReadHalf, WriteHalf}; diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index 204d9ca25..142f70bc6 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -1,8 +1,12 @@ -use crate::future::poll_fn; +cfg_not_wasi! { + use crate::future::poll_fn; + use crate::net::{to_socket_addrs, ToSocketAddrs}; + use std::time::Duration; +} + use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready}; use crate::net::tcp::split::{split, ReadHalf, WriteHalf}; use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf}; -use crate::net::{to_socket_addrs, ToSocketAddrs}; use std::convert::TryFrom; use std::fmt; @@ -10,7 +14,6 @@ use std::io; use std::net::{Shutdown, SocketAddr}; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Duration; cfg_io_util! { use bytes::BufMut; @@ -70,86 +73,88 @@ cfg_net! { } impl TcpStream { - /// Opens a TCP connection to a remote host. - /// - /// `addr` is an address of the remote host. Anything which implements the - /// [`ToSocketAddrs`] trait can be supplied as the address. If `addr` - /// yields multiple addresses, connect will be attempted with each of the - /// addresses until a connection is successful. If none of the addresses - /// result in a successful connection, the error returned from the last - /// connection attempt (the last address) is returned. - /// - /// To configure the socket before connecting, you can use the [`TcpSocket`] - /// type. - /// - /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs - /// [`TcpSocket`]: struct@crate::net::TcpSocket - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// use tokio::io::AsyncWriteExt; - /// use std::error::Error; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), Box> { - /// // Connect to a peer - /// let mut stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// // Write some data. - /// stream.write_all(b"hello world!").await?; - /// - /// Ok(()) - /// } - /// ``` - /// - /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. - /// - /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all - /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt - pub async fn connect(addr: A) -> io::Result { - let addrs = to_socket_addrs(addr).await?; + cfg_not_wasi! { + /// Opens a TCP connection to a remote host. + /// + /// `addr` is an address of the remote host. Anything which implements the + /// [`ToSocketAddrs`] trait can be supplied as the address. If `addr` + /// yields multiple addresses, connect will be attempted with each of the + /// addresses until a connection is successful. If none of the addresses + /// result in a successful connection, the error returned from the last + /// connection attempt (the last address) is returned. + /// + /// To configure the socket before connecting, you can use the [`TcpSocket`] + /// type. + /// + /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs + /// [`TcpSocket`]: struct@crate::net::TcpSocket + /// + /// # Examples + /// + /// ```no_run + /// use tokio::net::TcpStream; + /// use tokio::io::AsyncWriteExt; + /// use std::error::Error; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// // Connect to a peer + /// let mut stream = TcpStream::connect("127.0.0.1:8080").await?; + /// + /// // Write some data. + /// stream.write_all(b"hello world!").await?; + /// + /// Ok(()) + /// } + /// ``` + /// + /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. + /// + /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all + /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt + pub async fn connect(addr: A) -> io::Result { + let addrs = to_socket_addrs(addr).await?; - let mut last_err = None; + let mut last_err = None; - for addr in addrs { - match TcpStream::connect_addr(addr).await { - Ok(stream) => return Ok(stream), - Err(e) => last_err = Some(e), + for addr in addrs { + match TcpStream::connect_addr(addr).await { + Ok(stream) => return Ok(stream), + Err(e) => last_err = Some(e), + } } + + Err(last_err.unwrap_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "could not resolve to any address", + ) + })) } - Err(last_err.unwrap_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "could not resolve to any address", - ) - })) - } - - /// Establishes a connection to the specified `addr`. - async fn connect_addr(addr: SocketAddr) -> io::Result { - let sys = mio::net::TcpStream::connect(addr)?; - TcpStream::connect_mio(sys).await - } - - pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result { - let stream = TcpStream::new(sys)?; - - // Once we've connected, wait for the stream to be writable as - // that's when the actual connection has been initiated. Once we're - // writable we check for `take_socket_error` to see if the connect - // actually hit an error or not. - // - // If all that succeeded then we ship everything on up. - poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?; - - if let Some(e) = stream.io.take_error()? { - return Err(e); + /// Establishes a connection to the specified `addr`. + async fn connect_addr(addr: SocketAddr) -> io::Result { + let sys = mio::net::TcpStream::connect(addr)?; + TcpStream::connect_mio(sys).await } - Ok(stream) + pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result { + let stream = TcpStream::new(sys)?; + + // Once we've connected, wait for the stream to be writable as + // that's when the actual connection has been initiated. Once we're + // writable we check for `take_socket_error` to see if the connect + // actually hit an error or not. + // + // If all that succeeded then we ship everything on up. + poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?; + + if let Some(e) = stream.io.take_error()? { + return Err(e); + } + + Ok(stream) + } } pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result { @@ -181,11 +186,13 @@ impl TcpStream { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] pub fn from_std(stream: std::net::TcpStream) -> io::Result { let io = mio::net::TcpStream::from_std(stream); let io = PollEvented::new(io)?; @@ -244,6 +251,15 @@ impl TcpStream { .map(|io| io.into_raw_socket()) .map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(raw_socket) }) } + + #[cfg(target_os = "wasi")] + { + use std::os::wasi::io::{FromRawFd, IntoRawFd}; + self.io + .into_inner() + .map(|io| io.into_raw_fd()) + .map(|raw_fd| unsafe { std::net::TcpStream::from_raw_fd(raw_fd) }) + } } /// Returns the local address that this stream is bound to. @@ -1077,52 +1093,54 @@ impl TcpStream { self.io.set_nodelay(nodelay) } - /// Reads the linger duration for this socket by getting the `SO_LINGER` - /// option. - /// - /// For more information about this option, see [`set_linger`]. - /// - /// [`set_linger`]: TcpStream::set_linger - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// println!("{:?}", stream.linger()?); - /// # Ok(()) - /// # } - /// ``` - pub fn linger(&self) -> io::Result> { - socket2::SockRef::from(self).linger() - } + cfg_not_wasi! { + /// Reads the linger duration for this socket by getting the `SO_LINGER` + /// option. + /// + /// For more information about this option, see [`set_linger`]. + /// + /// [`set_linger`]: TcpStream::set_linger + /// + /// # Examples + /// + /// ```no_run + /// use tokio::net::TcpStream; + /// + /// # async fn dox() -> Result<(), Box> { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// + /// println!("{:?}", stream.linger()?); + /// # Ok(()) + /// # } + /// ``` + pub fn linger(&self) -> io::Result> { + socket2::SockRef::from(self).linger() + } - /// Sets the linger duration of this socket by setting the SO_LINGER option. - /// - /// This option controls the action taken when a stream has unsent messages and the stream is - /// closed. If SO_LINGER is set, the system shall block the process until it can transmit the - /// data or until the time expires. - /// - /// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a - /// way that allows the process to continue as quickly as possible. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// stream.set_linger(None)?; - /// # Ok(()) - /// # } - /// ``` - pub fn set_linger(&self, dur: Option) -> io::Result<()> { - socket2::SockRef::from(self).set_linger(dur) + /// Sets the linger duration of this socket by setting the SO_LINGER option. + /// + /// This option controls the action taken when a stream has unsent messages and the stream is + /// closed. If SO_LINGER is set, the system shall block the process until it can transmit the + /// data or until the time expires. + /// + /// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a + /// way that allows the process to continue as quickly as possible. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::net::TcpStream; + /// + /// # async fn dox() -> Result<(), Box> { + /// let stream = TcpStream::connect("127.0.0.1:8080").await?; + /// + /// stream.set_linger(None)?; + /// # Ok(()) + /// # } + /// ``` + pub fn set_linger(&self, dur: Option) -> io::Result<()> { + socket2::SockRef::from(self).set_linger(dur) + } } /// Gets the value of the `IP_TTL` option for this socket. @@ -1315,3 +1333,15 @@ mod sys { } } } + +#[cfg(all(tokio_unstable, target_os = "wasi"))] +mod sys { + use super::TcpStream; + use std::os::wasi::prelude::*; + + impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.io.as_raw_fd() + } + } +} diff --git a/tokio/src/net/udp.rs b/tokio/src/net/udp.rs index bd905e91a..218029176 100644 --- a/tokio/src/net/udp.rs +++ b/tokio/src/net/udp.rs @@ -170,6 +170,7 @@ impl UdpSocket { UdpSocket::new(sys) } + #[track_caller] fn new(socket: mio::net::UdpSocket) -> io::Result { let io = PollEvented::new(socket)?; Ok(UdpSocket { io }) @@ -210,6 +211,7 @@ impl UdpSocket { /// # Ok(()) /// # } /// ``` + #[track_caller] pub fn from_std(socket: net::UdpSocket) -> io::Result { let io = mio::net::UdpSocket::from_std(socket); UdpSocket::new(io) diff --git a/tokio/src/net/unix/datagram/socket.rs b/tokio/src/net/unix/datagram/socket.rs index def006c47..09d6fc1c1 100644 --- a/tokio/src/net/unix/datagram/socket.rs +++ b/tokio/src/net/unix/datagram/socket.rs @@ -430,7 +430,8 @@ impl UnixDatagram { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a Tokio runtime, otherwise runtime can be set @@ -457,6 +458,7 @@ impl UnixDatagram { /// # Ok(()) /// # } /// ``` + #[track_caller] pub fn from_std(datagram: net::UnixDatagram) -> io::Result { let socket = mio::net::UnixDatagram::from_std(datagram); let io = PollEvented::new(socket)?; diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs index 1785f8b0f..fbea3e76a 100644 --- a/tokio/src/net/unix/listener.rs +++ b/tokio/src/net/unix/listener.rs @@ -54,11 +54,13 @@ impl UnixListener { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] pub fn bind

(path: P) -> io::Result where P: AsRef, @@ -77,11 +79,13 @@ impl UnixListener { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] pub fn from_std(listener: net::UnixListener) -> io::Result { let listener = mio::net::UnixListener::from_std(listener); let io = PollEvented::new(listener)?; diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index fe2d825bf..82cec77fa 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -699,11 +699,13 @@ impl UnixStream { /// /// # Panics /// - /// This function panics if thread-local runtime is not set. + /// This function panics if it is not called from within a runtime with + /// IO enabled. /// /// The runtime is usually set implicitly when this function is called /// from a future driven by a tokio runtime, otherwise runtime can be set /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. + #[track_caller] pub fn from_std(stream: net::UnixStream) -> io::Result { let stream = mio::net::UnixStream::from_std(stream); let io = PollEvented::new(stream)?; diff --git a/tokio/src/net/windows/named_pipe.rs b/tokio/src/net/windows/named_pipe.rs index 695b8eb3d..34606d2a2 100644 --- a/tokio/src/net/windows/named_pipe.rs +++ b/tokio/src/net/windows/named_pipe.rs @@ -1955,6 +1955,106 @@ impl ServerOptions { self } + /// Requests permission to modify the pipe's discretionary access control list. + /// + /// This corresponds to setting [`WRITE_DAC`] in dwOpenMode. + /// + /// # Examples + /// + /// ``` + /// use std::{io, os::windows::prelude::AsRawHandle, ptr}; + // + /// use tokio::net::windows::named_pipe::ServerOptions; + /// use winapi::{ + /// shared::winerror::ERROR_SUCCESS, + /// um::{accctrl::SE_KERNEL_OBJECT, aclapi::SetSecurityInfo, winnt::DACL_SECURITY_INFORMATION}, + /// }; + /// + /// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe"; + /// + /// # #[tokio::main] async fn main() -> io::Result<()> { + /// let mut pipe_template = ServerOptions::new(); + /// pipe_template.write_dac(true); + /// let pipe = pipe_template.create(PIPE_NAME)?; + /// + /// unsafe { + /// assert_eq!( + /// ERROR_SUCCESS, + /// SetSecurityInfo( + /// pipe.as_raw_handle(), + /// SE_KERNEL_OBJECT, + /// DACL_SECURITY_INFORMATION, + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ) + /// ); + /// } + /// + /// # Ok(()) } + /// ``` + /// + /// ``` + /// use std::{io, os::windows::prelude::AsRawHandle, ptr}; + // + /// use tokio::net::windows::named_pipe::ServerOptions; + /// use winapi::{ + /// shared::winerror::ERROR_ACCESS_DENIED, + /// um::{accctrl::SE_KERNEL_OBJECT, aclapi::SetSecurityInfo, winnt::DACL_SECURITY_INFORMATION}, + /// }; + /// + /// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe_fail"; + /// + /// # #[tokio::main] async fn main() -> io::Result<()> { + /// let mut pipe_template = ServerOptions::new(); + /// pipe_template.write_dac(false); + /// let pipe = pipe_template.create(PIPE_NAME)?; + /// + /// unsafe { + /// assert_eq!( + /// ERROR_ACCESS_DENIED, + /// SetSecurityInfo( + /// pipe.as_raw_handle(), + /// SE_KERNEL_OBJECT, + /// DACL_SECURITY_INFORMATION, + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ptr::null_mut(), + /// ) + /// ); + /// } + /// + /// # Ok(()) } + /// ``` + /// + /// [`WRITE_DAC`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea + pub fn write_dac(&mut self, requested: bool) -> &mut Self { + bool_flag!(self.open_mode, requested, winnt::WRITE_DAC); + self + } + + /// Requests permission to modify the pipe's owner. + /// + /// This corresponds to setting [`WRITE_OWNER`] in dwOpenMode. + /// + /// [`WRITE_OWNER`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea + pub fn write_owner(&mut self, requested: bool) -> &mut Self { + bool_flag!(self.open_mode, requested, winnt::WRITE_OWNER); + self + } + + /// Requests permission to modify the pipe's system access control list. + /// + /// This corresponds to setting [`ACCESS_SYSTEM_SECURITY`] in dwOpenMode. + /// + /// [`ACCESS_SYSTEM_SECURITY`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea + pub fn access_system_security(&mut self, requested: bool) -> &mut Self { + bool_flag!(self.open_mode, requested, winnt::ACCESS_SYSTEM_SECURITY); + self + } + /// Indicates whether this server can accept remote clients or not. Remote /// clients are disabled by default. /// @@ -2020,6 +2120,7 @@ impl ServerOptions { /// let builder = ServerOptions::new().max_instances(255); /// # Ok(()) } /// ``` + #[track_caller] pub fn max_instances(&mut self, instances: usize) -> &mut Self { assert!(instances < 255, "cannot specify more than 254 instances"); self.max_instances = instances as DWORD; diff --git a/tokio/src/park/thread.rs b/tokio/src/park/thread.rs index 27ce20243..b42b73fe0 100644 --- a/tokio/src/park/thread.rs +++ b/tokio/src/park/thread.rs @@ -64,7 +64,11 @@ impl Park for ParkThread { } fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> { + // Wasi doesn't have threads, so just sleep. + #[cfg(not(target_os = "wasi"))] self.inner.park_timeout(duration); + #[cfg(target_os = "wasi")] + std::thread::sleep(duration); Ok(()) } diff --git a/tokio/src/process/mod.rs b/tokio/src/process/mod.rs index 719fdeee6..e5ee5db2b 100644 --- a/tokio/src/process/mod.rs +++ b/tokio/src/process/mod.rs @@ -1285,41 +1285,39 @@ impl ChildStderr { impl AsyncWrite for ChildStdin { fn poll_write( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.inner.poll_write(cx, buf) + Pin::new(&mut self.inner).poll_write(cx, buf) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) } - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_shutdown(cx) } } impl AsyncRead for ChildStdout { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - // Safety: pipes support reading into uninitialized memory - unsafe { self.inner.poll_read(cx, buf) } + Pin::new(&mut self.inner).poll_read(cx, buf) } } impl AsyncRead for ChildStderr { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - // Safety: pipes support reading into uninitialized memory - unsafe { self.inner.poll_read(cx, buf) } + Pin::new(&mut self.inner).poll_read(cx, buf) } } diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index 576fe6cb4..ba34c852b 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -29,7 +29,7 @@ use orphan::{OrphanQueue, OrphanQueueImpl, Wait}; mod reap; use reap::Reaper; -use crate::io::PollEvented; +use crate::io::{AsyncRead, AsyncWrite, PollEvented, ReadBuf}; use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::signal::unix::driver::Handle as SignalHandle; @@ -177,8 +177,8 @@ impl AsRawFd for Pipe { } } -pub(crate) fn convert_to_stdio(io: PollEvented) -> io::Result { - let mut fd = io.into_inner()?.fd; +pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result { + let mut fd = io.inner.into_inner()?.fd; // Ensure that the fd to be inherited is set to *blocking* mode, as this // is the default that virtually all programs expect to have. Those @@ -213,7 +213,50 @@ impl Source for Pipe { } } -pub(crate) type ChildStdio = PollEvented; +pub(crate) struct ChildStdio { + inner: PollEvented, +} + +impl fmt::Debug for ChildStdio { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(fmt) + } +} + +impl AsRawFd for ChildStdio { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +impl AsyncWrite for ChildStdio { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.inner.poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl AsyncRead for ChildStdio { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + // Safety: pipes support reading into uninitialized memory + unsafe { self.inner.poll_read(cx, buf) } + } +} fn set_nonblocking(fd: &mut T, nonblocking: bool) -> io::Result<()> { unsafe { @@ -238,7 +281,7 @@ fn set_nonblocking(fd: &mut T, nonblocking: bool) -> io::Result<()> Ok(()) } -pub(super) fn stdio(io: T) -> io::Result> +pub(super) fn stdio(io: T) -> io::Result where T: IntoRawFd, { @@ -246,5 +289,5 @@ where let mut pipe = Pipe::from(io); set_nonblocking(&mut pipe, true)?; - PollEvented::new(pipe) + PollEvented::new(pipe).map(|inner| ChildStdio { inner }) } diff --git a/tokio/src/process/windows.rs b/tokio/src/process/windows.rs index 136d5b0ca..651233ba3 100644 --- a/tokio/src/process/windows.rs +++ b/tokio/src/process/windows.rs @@ -15,22 +15,22 @@ //! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot //! from then on out. -use crate::io::PollEvented; +use crate::io::{blocking::Blocking, AsyncRead, AsyncWrite, ReadBuf}; use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::sync::oneshot; -use mio::windows::NamedPipe; use std::fmt; +use std::fs::File as StdFile; use std::future::Future; use std::io; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, RawHandle}; use std::pin::Pin; use std::process::Stdio; use std::process::{Child as StdChild, Command as StdCommand, ExitStatus}; use std::ptr; -use std::task::Context; -use std::task::Poll; +use std::sync::Arc; +use std::task::{Context, Poll}; use winapi::shared::minwindef::{DWORD, FALSE}; use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE}; use winapi::um::processthreadsapi::GetCurrentProcess; @@ -167,28 +167,97 @@ unsafe extern "system" fn callback(ptr: PVOID, _timer_fired: BOOLEAN) { let _ = complete.take().unwrap().send(()); } -pub(crate) type ChildStdio = PollEvented; +#[derive(Debug)] +struct ArcFile(Arc); -pub(super) fn stdio(io: T) -> io::Result> +impl io::Read for ArcFile { + fn read(&mut self, bytes: &mut [u8]) -> io::Result { + (&*self.0).read(bytes) + } +} + +impl io::Write for ArcFile { + fn write(&mut self, bytes: &[u8]) -> io::Result { + (&*self.0).write(bytes) + } + + fn flush(&mut self) -> io::Result<()> { + (&*self.0).flush() + } +} + +#[derive(Debug)] +pub(crate) struct ChildStdio { + // Used for accessing the raw handle, even if the io version is busy + raw: Arc, + // For doing I/O operations asynchronously + io: Blocking, +} + +impl AsRawHandle for ChildStdio { + fn as_raw_handle(&self) -> RawHandle { + self.raw.as_raw_handle() + } +} + +impl AsyncRead for ChildStdio { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut self.io).poll_read(cx, buf) + } +} + +impl AsyncWrite for ChildStdio { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.io).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.io).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.io).poll_shutdown(cx) + } +} + +pub(super) fn stdio(io: T) -> io::Result where T: IntoRawHandle, { - let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) }; - PollEvented::new(pipe) + use std::os::windows::prelude::FromRawHandle; + + let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) }); + let io = Blocking::new(ArcFile(raw.clone())); + Ok(ChildStdio { raw, io }) } -pub(crate) fn convert_to_stdio(io: PollEvented) -> io::Result { - let named_pipe = io.into_inner()?; +pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result { + let ChildStdio { raw, io } = child_stdio; + drop(io); // Try to drop the Arc count here + + Arc::try_unwrap(raw) + .or_else(|raw| duplicate_handle(&*raw)) + .map(Stdio::from) +} + +fn duplicate_handle(io: &T) -> io::Result { + use std::os::windows::prelude::FromRawHandle; - // Mio does not implement `IntoRawHandle` for `NamedPipe`, so we'll manually - // duplicate the handle here... unsafe { let mut dup_handle = INVALID_HANDLE_VALUE; let cur_proc = GetCurrentProcess(); let status = DuplicateHandle( cur_proc, - named_pipe.as_raw_handle(), + io.as_raw_handle(), cur_proc, &mut dup_handle, 0 as DWORD, @@ -200,6 +269,6 @@ pub(crate) fn convert_to_stdio(io: PollEvented) -> io::Result return Err(io::Error::last_os_error()); } - Ok(Stdio::from_raw_handle(dup_handle)) + Ok(StdFile::from_raw_handle(dup_handle)) } } diff --git a/tokio/src/runtime/blocking/mod.rs b/tokio/src/runtime/blocking/mod.rs index 88d5e6b6a..7633299b3 100644 --- a/tokio/src/runtime/blocking/mod.rs +++ b/tokio/src/runtime/blocking/mod.rs @@ -4,7 +4,7 @@ //! compilation. mod pool; -pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, Spawner, Task}; +pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, SpawnError, Spawner, Task}; cfg_fs! { pub(crate) use pool::spawn_mandatory_blocking; diff --git a/tokio/src/runtime/blocking/pool.rs b/tokio/src/runtime/blocking/pool.rs index f73868ee9..98edd6908 100644 --- a/tokio/src/runtime/blocking/pool.rs +++ b/tokio/src/runtime/blocking/pool.rs @@ -11,6 +11,7 @@ use crate::runtime::{Builder, Callback, ToHandle}; use std::collections::{HashMap, VecDeque}; use std::fmt; +use std::io; use std::time::Duration; pub(crate) struct BlockingPool { @@ -82,6 +83,25 @@ pub(crate) enum Mandatory { NonMandatory, } +pub(crate) enum SpawnError { + /// Pool is shutting down and the task was not scheduled + ShuttingDown, + /// There are no worker threads available to take the task + /// and the OS failed to spawn a new one + NoThreads(io::Error), +} + +impl From for io::Error { + fn from(e: SpawnError) -> Self { + match e { + SpawnError::ShuttingDown => { + io::Error::new(io::ErrorKind::Other, "blocking pool shutting down") + } + SpawnError::NoThreads(e) => e, + } + } +} + impl Task { pub(crate) fn new(task: task::UnownedTask, mandatory: Mandatory) -> Task { Task { task, mandatory } @@ -105,6 +125,7 @@ const KEEP_ALIVE: Duration = Duration::from_secs(10); /// Tasks will be scheduled as non-mandatory, meaning they may not get executed /// in case of runtime shutdown. #[track_caller] +#[cfg_attr(target_os = "wasi", allow(dead_code))] pub(crate) fn spawn_blocking(func: F) -> JoinHandle where F: FnOnce() -> R + Send + 'static, @@ -220,7 +241,7 @@ impl fmt::Debug for BlockingPool { // ===== impl Spawner ===== impl Spawner { - pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), ()> { + pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), SpawnError> { let mut shared = self.inner.shared.lock(); if shared.shutdown { @@ -230,7 +251,7 @@ impl Spawner { task.task.shutdown(); // no need to even push this task; it would never get picked up - return Err(()); + return Err(SpawnError::ShuttingDown); } shared.queue.push_back(task); @@ -261,7 +282,7 @@ impl Spawner { Err(e) => { // The OS refused to spawn the thread and there is no thread // to pick up the task that has just been pushed to the queue. - panic!("OS can't spawn worker thread: {}", e) + return Err(SpawnError::NoThreads(e)); } } } diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index 490bc96d5..fff1b3ad6 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -174,7 +174,7 @@ pub(crate) type ThreadNameFn = std::sync::Arc String + Send + Sync + pub(crate) enum Kind { CurrentThread, - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] MultiThread, } @@ -197,14 +197,16 @@ impl Builder { Builder::new(Kind::CurrentThread, 31, EVENT_INTERVAL) } - /// Returns a new builder with the multi thread scheduler selected. - /// - /// Configuration methods can be chained on the return value. - #[cfg(feature = "rt-multi-thread")] - #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))] - pub fn new_multi_thread() -> Builder { - // The number `61` is fairly arbitrary. I believe this value was copied from golang. - Builder::new(Kind::MultiThread, 61, 61) + cfg_not_wasi! { + /// Returns a new builder with the multi thread scheduler selected. + /// + /// Configuration methods can be chained on the return value. + #[cfg(feature = "rt-multi-thread")] + #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))] + pub fn new_multi_thread() -> Builder { + // The number `61` is fairly arbitrary. I believe this value was copied from golang. + Builder::new(Kind::MultiThread, 61, 61) + } } /// Returns a new runtime builder initialized with default configuration @@ -270,7 +272,11 @@ impl Builder { /// .unwrap(); /// ``` pub fn enable_all(&mut self) -> &mut Self { - #[cfg(any(feature = "net", feature = "process", all(unix, feature = "signal")))] + #[cfg(any( + feature = "net", + all(unix, feature = "process"), + all(unix, feature = "signal") + ))] self.enable_io(); #[cfg(feature = "time")] self.enable_time(); @@ -287,11 +293,7 @@ impl Builder { /// /// The default value is the number of cores available to the system. /// - /// # Panics - /// - /// When using the `current_thread` runtime this method will panic, since - /// those variants do not allow setting worker thread counts. - /// + /// When using the `current_thread` runtime this method has no effect. /// /// # Examples /// @@ -617,7 +619,7 @@ impl Builder { pub fn build(&mut self) -> io::Result { match &self.kind { Kind::CurrentThread => self.build_basic_runtime(), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Kind::MultiThread => self.build_threaded_runtime(), } } @@ -626,7 +628,7 @@ impl Builder { driver::Cfg { enable_pause_time: match self.kind { Kind::CurrentThread => true, - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Kind::MultiThread => false, }, enable_io: self.enable_io, @@ -736,7 +738,7 @@ impl Builder { /// * `UnhandledPanic::ShutdownRuntime` will force the runtime to /// shutdown immediately when a spawned task panics even if that /// task's `JoinHandle` has not been dropped. All other spawned tasks - /// will immediatetly terminate and further calls to + /// will immediately terminate and further calls to /// [`Runtime::block_on`] will panic. /// /// # Unstable diff --git a/tokio/src/runtime/context.rs b/tokio/src/runtime/context.rs index 36fa28db3..4215124fc 100644 --- a/tokio/src/runtime/context.rs +++ b/tokio/src/runtime/context.rs @@ -24,6 +24,7 @@ pub(crate) fn current() -> Handle { } cfg_io_driver! { + #[track_caller] pub(crate) fn io_handle() -> crate::runtime::driver::IoHandle { match CONTEXT.try_with(|ctx| { let ctx = ctx.borrow(); diff --git a/tokio/src/runtime/handle.rs b/tokio/src/runtime/handle.rs index 14101070c..075792a30 100644 --- a/tokio/src/runtime/handle.rs +++ b/tokio/src/runtime/handle.rs @@ -23,7 +23,11 @@ pub struct Handle { pub(crate) struct HandleInner { /// Handles to the I/O drivers #[cfg_attr( - not(any(feature = "net", feature = "process", all(unix, feature = "signal"))), + not(any( + feature = "net", + all(unix, feature = "process"), + all(unix, feature = "signal"), + )), allow(dead_code) )] pub(super) io_handle: driver::IoHandle, @@ -341,7 +345,7 @@ impl HandleInner { F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - let (join_handle, _was_spawned) = if cfg!(debug_assertions) + let (join_handle, spawn_result) = if cfg!(debug_assertions) && std::mem::size_of::() > 2048 { self.spawn_blocking_inner(Box::new(func), blocking::Mandatory::NonMandatory, None, rt) @@ -349,7 +353,14 @@ impl HandleInner { self.spawn_blocking_inner(func, blocking::Mandatory::NonMandatory, None, rt) }; - join_handle + match spawn_result { + Ok(()) => join_handle, + // Compat: do not panic here, return the join_handle even though it will never resolve + Err(blocking::SpawnError::ShuttingDown) => join_handle, + Err(blocking::SpawnError::NoThreads(e)) => { + panic!("OS can't spawn worker thread: {}", e) + } + } } cfg_fs! { @@ -363,7 +374,7 @@ impl HandleInner { F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - let (join_handle, was_spawned) = if cfg!(debug_assertions) && std::mem::size_of::() > 2048 { + let (join_handle, spawn_result) = if cfg!(debug_assertions) && std::mem::size_of::() > 2048 { self.spawn_blocking_inner( Box::new(func), blocking::Mandatory::Mandatory, @@ -379,7 +390,7 @@ impl HandleInner { ) }; - if was_spawned { + if spawn_result.is_ok() { Some(join_handle) } else { None @@ -394,7 +405,7 @@ impl HandleInner { is_mandatory: blocking::Mandatory, name: Option<&str>, rt: &dyn ToHandle, - ) -> (JoinHandle, bool) + ) -> (JoinHandle, Result<(), blocking::SpawnError>) where F: FnOnce() -> R + Send + 'static, R: Send + 'static, @@ -424,7 +435,7 @@ impl HandleInner { let spawned = self .blocking_spawner .spawn(blocking::Task::new(task, is_mandatory), rt); - (handle, spawned.is_ok()) + (handle, spawned) } } diff --git a/tokio/src/runtime/metrics/runtime.rs b/tokio/src/runtime/metrics/runtime.rs index cb0edf52a..279d70a0e 100644 --- a/tokio/src/runtime/metrics/runtime.rs +++ b/tokio/src/runtime/metrics/runtime.rs @@ -131,7 +131,7 @@ impl RuntimeMetrics { /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single - /// worker and will continue to indentify the worker throughout the lifetime + /// worker and will continue to identify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index b9e7e2de9..c0e2d96db 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -204,6 +204,7 @@ cfg_rt! { mod blocking; use blocking::BlockingPool; + #[cfg_attr(target_os = "wasi", allow(unused_imports))] pub(crate) use blocking::spawn_blocking; cfg_trace! { @@ -303,7 +304,7 @@ cfg_rt! { CurrentThread(BasicScheduler), /// Execute tasks across multiple threads. - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] ThreadPool(ThreadPool), } @@ -311,39 +312,41 @@ cfg_rt! { type Callback = std::sync::Arc; impl Runtime { - /// Creates a new runtime instance with default configuration values. - /// - /// This results in the multi threaded scheduler, I/O driver, and time driver being - /// initialized. - /// - /// Most applications will not need to call this function directly. Instead, - /// they will use the [`#[tokio::main]` attribute][main]. When a more complex - /// configuration is necessary, the [runtime builder] may be used. - /// - /// See [module level][mod] documentation for more details. - /// - /// # Examples - /// - /// Creating a new `Runtime` with default configuration values. - /// - /// ``` - /// use tokio::runtime::Runtime; - /// - /// let rt = Runtime::new() - /// .unwrap(); - /// - /// // Use the runtime... - /// ``` - /// - /// [mod]: index.html - /// [main]: ../attr.main.html - /// [threaded scheduler]: index.html#threaded-scheduler - /// [basic scheduler]: index.html#basic-scheduler - /// [runtime builder]: crate::runtime::Builder - #[cfg(feature = "rt-multi-thread")] - #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))] - pub fn new() -> std::io::Result { - Builder::new_multi_thread().enable_all().build() + cfg_not_wasi! { + /// Creates a new runtime instance with default configuration values. + /// + /// This results in the multi threaded scheduler, I/O driver, and time driver being + /// initialized. + /// + /// Most applications will not need to call this function directly. Instead, + /// they will use the [`#[tokio::main]` attribute][main]. When a more complex + /// configuration is necessary, the [runtime builder] may be used. + /// + /// See [module level][mod] documentation for more details. + /// + /// # Examples + /// + /// Creating a new `Runtime` with default configuration values. + /// + /// ``` + /// use tokio::runtime::Runtime; + /// + /// let rt = Runtime::new() + /// .unwrap(); + /// + /// // Use the runtime... + /// ``` + /// + /// [mod]: index.html + /// [main]: ../attr.main.html + /// [threaded scheduler]: index.html#threaded-scheduler + /// [basic scheduler]: index.html#basic-scheduler + /// [runtime builder]: crate::runtime::Builder + #[cfg(feature = "rt-multi-thread")] + #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))] + pub fn new() -> std::io::Result { + Builder::new_multi_thread().enable_all().build() + } } /// Returns a handle to the runtime's spawner. @@ -472,6 +475,7 @@ cfg_rt! { /// ``` /// /// [handle]: fn@Handle::block_on + #[track_caller] pub fn block_on(&self, future: F) -> F::Output { #[cfg(all(tokio_unstable, feature = "tracing"))] let future = crate::util::trace::task(future, "block_on", None, task::Id::next().as_u64()); @@ -480,7 +484,7 @@ cfg_rt! { match &self.kind { Kind::CurrentThread(exec) => exec.block_on(future), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Kind::ThreadPool(exec) => exec.block_on(future), } } @@ -573,7 +577,7 @@ cfg_rt! { /// may result in a resource leak (in that any blocking tasks are still running until they /// return. /// - /// This function is equivalent to calling `shutdown_timeout(Duration::of_nanos(0))`. + /// This function is equivalent to calling `shutdown_timeout(Duration::from_nanos(0))`. /// /// ``` /// use tokio::runtime::Runtime; @@ -610,7 +614,7 @@ cfg_rt! { }, } }, - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Kind::ThreadPool(_) => { // The threaded scheduler drops its tasks on its worker threads, which is // already in the runtime's context. diff --git a/tokio/src/runtime/spawner.rs b/tokio/src/runtime/spawner.rs index fb4d7f918..dd1f6da24 100644 --- a/tokio/src/runtime/spawner.rs +++ b/tokio/src/runtime/spawner.rs @@ -10,13 +10,13 @@ cfg_rt_multi_thread! { #[derive(Debug, Clone)] pub(crate) enum Spawner { Basic(basic_scheduler::Spawner), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] ThreadPool(thread_pool::Spawner), } impl Spawner { pub(crate) fn shutdown(&mut self) { - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] { if let Spawner::ThreadPool(spawner) = self { spawner.shutdown(); @@ -31,7 +31,7 @@ impl Spawner { { match self { Spawner::Basic(spawner) => spawner.spawn(future, id), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.spawn(future, id), } } @@ -39,7 +39,7 @@ impl Spawner { pub(crate) fn as_handle_inner(&self) -> &HandleInner { match self { Spawner::Basic(spawner) => spawner.as_handle_inner(), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.as_handle_inner(), } } @@ -52,7 +52,7 @@ cfg_metrics! { pub(crate) fn num_workers(&self) -> usize { match self { Spawner::Basic(_) => 1, - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.num_workers(), } } @@ -60,7 +60,7 @@ cfg_metrics! { pub(crate) fn scheduler_metrics(&self) -> &SchedulerMetrics { match self { Spawner::Basic(spawner) => spawner.scheduler_metrics(), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.scheduler_metrics(), } } @@ -68,7 +68,7 @@ cfg_metrics! { pub(crate) fn worker_metrics(&self, worker: usize) -> &WorkerMetrics { match self { Spawner::Basic(spawner) => spawner.worker_metrics(worker), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.worker_metrics(worker), } } @@ -76,7 +76,7 @@ cfg_metrics! { pub(crate) fn injection_queue_depth(&self) -> usize { match self { Spawner::Basic(spawner) => spawner.injection_queue_depth(), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.injection_queue_depth(), } } @@ -84,7 +84,7 @@ cfg_metrics! { pub(crate) fn worker_local_queue_depth(&self, worker: usize) -> usize { match self { Spawner::Basic(spawner) => spawner.worker_metrics(worker).queue_depth(), - #[cfg(feature = "rt-multi-thread")] + #[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] Spawner::ThreadPool(spawner) => spawner.worker_local_queue_depth(worker), } } diff --git a/tokio/src/runtime/task/core.rs b/tokio/src/runtime/task/core.rs index 548c56da3..2b7050a21 100644 --- a/tokio/src/runtime/task/core.rs +++ b/tokio/src/runtime/task/core.rs @@ -60,8 +60,6 @@ pub(crate) struct Header { /// Task state. pub(super) state: State, - pub(super) owned: UnsafeCell>, - /// Pointer to next task, used with the injection queue. pub(super) queue_next: UnsafeCell>>, @@ -89,12 +87,23 @@ pub(crate) struct Header { unsafe impl Send for Header {} unsafe impl Sync for Header {} -/// Cold data is stored after the future. +/// Cold data is stored after the future. Data is considered cold if it is only +/// used during creation or shutdown of the task. pub(super) struct Trailer { + /// Pointers for the linked list in the `OwnedTasks` that owns this task. + pub(super) owned: linked_list::Pointers

, /// Consumer task waiting on completion of this task. pub(super) waker: UnsafeCell>, } +generate_addr_of_methods! { + impl<> Trailer { + pub(super) unsafe fn addr_of_owned(self: NonNull) -> NonNull> { + &self.owned + } + } +} + /// Either the future or the output. pub(super) enum Stage { Running(T), @@ -108,10 +117,9 @@ impl Cell { pub(super) fn new(future: T, scheduler: S, state: State, task_id: Id) -> Box> { #[cfg(all(tokio_unstable, feature = "tracing"))] let id = future.id(); - Box::new(Cell { + let result = Box::new(Cell { header: Header { state, - owned: UnsafeCell::new(linked_list::Pointers::new()), queue_next: UnsafeCell::new(None), vtable: raw::vtable::(), owner_id: UnsafeCell::new(0), @@ -127,8 +135,19 @@ impl Cell { }, trailer: Trailer { waker: UnsafeCell::new(None), + owned: linked_list::Pointers::new(), }, - }) + }); + + #[cfg(debug_assertions)] + { + let trailer_addr = (&result.trailer) as *const Trailer as usize; + let trailer_ptr = unsafe { Header::get_trailer(NonNull::from(&result.header)) }; + + assert_eq!(trailer_addr, trailer_ptr.as_ptr() as usize); + } + + result } } @@ -240,6 +259,17 @@ impl Header { // the safety requirements on `set_owner_id`. unsafe { self.owner_id.with(|ptr| *ptr) } } + + /// Gets a pointer to the `Trailer` of the task containing this `Header`. + /// + /// # Safety + /// + /// The provided raw pointer must point at the header of a task. + pub(super) unsafe fn get_trailer(me: NonNull
) -> NonNull { + let offset = me.as_ref().vtable.trailer_offset; + let trailer = me.as_ptr().cast::().add(offset).cast::(); + NonNull::new_unchecked(trailer) + } } impl Trailer { diff --git a/tokio/src/runtime/task/list.rs b/tokio/src/runtime/task/list.rs index 7a1dff0bb..ca06d459c 100644 --- a/tokio/src/runtime/task/list.rs +++ b/tokio/src/runtime/task/list.rs @@ -164,7 +164,7 @@ impl OwnedTasks { // safety: We just checked that the provided task is not in some other // linked list. - unsafe { self.inner.lock().list.remove(task.header().into()) } + unsafe { self.inner.lock().list.remove(task.header_ptr()) } } pub(crate) fn is_empty(&self) -> bool { diff --git a/tokio/src/runtime/task/mod.rs b/tokio/src/runtime/task/mod.rs index e73b3f35a..46aa62b14 100644 --- a/tokio/src/runtime/task/mod.rs +++ b/tokio/src/runtime/task/mod.rs @@ -334,6 +334,10 @@ impl Task { fn header(&self) -> &Header { self.raw.header() } + + fn header_ptr(&self) -> NonNull
{ + self.raw.header_ptr() + } } impl Notified { @@ -365,7 +369,7 @@ cfg_rt_multi_thread! { } impl Task { - /// Pre-emptively cancels the task as part of the shutdown process. + /// Preemptively cancels the task as part of the shutdown process. pub(crate) fn shutdown(self) { let raw = self.raw; mem::forget(self); @@ -473,8 +477,7 @@ unsafe impl linked_list::Link for Task { } unsafe fn pointers(target: NonNull
) -> NonNull> { - // Not super great as it avoids some of looms checking... - NonNull::from(target.as_ref().owned.with_mut(|ptr| &mut *ptr)) + self::core::Trailer::addr_of_owned(Header::get_trailer(target)) } } diff --git a/tokio/src/runtime/task/raw.rs b/tokio/src/runtime/task/raw.rs index 5555298a4..a24ac44bf 100644 --- a/tokio/src/runtime/task/raw.rs +++ b/tokio/src/runtime/task/raw.rs @@ -1,4 +1,5 @@ use crate::future::Future; +use crate::runtime::task::core::{Core, Trailer}; use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State}; use std::ptr::NonNull; @@ -35,6 +36,9 @@ pub(super) struct Vtable { /// Scheduler is being shutdown. pub(super) shutdown: unsafe fn(NonNull
), + + /// The number of bytes that the `trailer` field is offset from the header. + pub(super) trailer_offset: usize, } /// Get the vtable for the requested `T` and `S` generics. @@ -48,9 +52,55 @@ pub(super) fn vtable() -> &'static Vtable { drop_abort_handle: drop_abort_handle::, remote_abort: remote_abort::, shutdown: shutdown::, + trailer_offset: TrailerOffsetHelper::::OFFSET, } } +/// Calling `get_trailer_offset` directly in vtable doesn't work because it +/// prevents the vtable from being promoted to a static reference. +/// +/// See this thread for more info: +/// +struct TrailerOffsetHelper(T, S); +impl TrailerOffsetHelper { + // Pass `size_of`/`align_of` as arguments rather than calling them directly + // inside `get_trailer_offset` because trait bounds on generic parameters + // of const fn are unstable on our MSRV. + const OFFSET: usize = get_trailer_offset( + std::mem::size_of::
(), + std::mem::size_of::>(), + std::mem::align_of::>(), + std::mem::align_of::(), + ); +} + +/// Compute the offset of the `Trailer` field in `Cell` using the +/// `#[repr(C)]` algorithm. +/// +/// Pseudo-code for the `#[repr(C)]` algorithm can be found here: +/// +const fn get_trailer_offset( + header_size: usize, + core_size: usize, + core_align: usize, + trailer_align: usize, +) -> usize { + let mut offset = header_size; + + let core_misalign = offset % core_align; + if core_misalign > 0 { + offset += core_align - core_misalign; + } + offset += core_size; + + let trailer_misalign = offset % trailer_align; + if trailer_misalign > 0 { + offset += trailer_align - trailer_misalign; + } + + offset +} + impl RawTask { pub(super) fn new(task: T, scheduler: S, id: Id) -> RawTask where diff --git a/tokio/src/runtime/task/waker.rs b/tokio/src/runtime/task/waker.rs index 74a29f4a8..a434d5be4 100644 --- a/tokio/src/runtime/task/waker.rs +++ b/tokio/src/runtime/task/waker.rs @@ -13,7 +13,7 @@ pub(super) struct WakerRef<'a, S: 'static> { _p: PhantomData<(&'a Header, S)>, } -/// Returns a `WakerRef` which avoids having to pre-emptively increase the +/// Returns a `WakerRef` which avoids having to preemptively increase the /// refcount if there is no need to do so. pub(super) fn waker_ref(header: &NonNull
) -> WakerRef<'_, S> where diff --git a/tokio/src/sync/batch_semaphore.rs b/tokio/src/sync/batch_semaphore.rs index 4db88351d..23e6e2adf 100644 --- a/tokio/src/sync/batch_semaphore.rs +++ b/tokio/src/sync/batch_semaphore.rs @@ -112,6 +112,14 @@ struct Waiter { _p: PhantomPinned, } +generate_addr_of_methods! { + impl<> Waiter { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { + &self.pointers + } + } +} + impl Semaphore { /// The maximum number of permits which a semaphore can hold. /// @@ -704,12 +712,6 @@ impl std::error::Error for TryAcquireError {} /// /// `Waiter` is forced to be !Unpin. unsafe impl linked_list::Link for Waiter { - // XXX: ideally, we would be able to use `Pin` here, to enforce the - // invariant that list entries may not move while in the list. However, we - // can't do this currently, as using `Pin<&'a mut Waiter>` as the `Handle` - // type would require `Semaphore` to be generic over a lifetime. We can't - // use `Pin<*mut Waiter>`, as raw pointers are `Unpin` regardless of whether - // or not they dereference to an `!Unpin` target. type Handle = NonNull; type Target = Waiter; @@ -721,7 +723,7 @@ unsafe impl linked_list::Link for Waiter { ptr } - unsafe fn pointers(mut target: NonNull) -> NonNull> { - NonNull::from(&mut target.as_mut().pointers) + unsafe fn pointers(target: NonNull) -> NonNull> { + Waiter::addr_of_pointers(target) } } diff --git a/tokio/src/sync/broadcast.rs b/tokio/src/sync/broadcast.rs index fa6f46b0a..69ede8f89 100644 --- a/tokio/src/sync/broadcast.rs +++ b/tokio/src/sync/broadcast.rs @@ -361,6 +361,14 @@ struct Waiter { _p: PhantomPinned, } +generate_addr_of_methods! { + impl<> Waiter { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { + &self.pointers + } + } +} + struct RecvGuard<'a, T> { slot: RwLockReadGuard<'a, Slot>, } @@ -1140,8 +1148,8 @@ unsafe impl linked_list::Link for Waiter { ptr } - unsafe fn pointers(mut target: NonNull) -> NonNull> { - NonNull::from(&mut target.as_mut().pointers) + unsafe fn pointers(target: NonNull) -> NonNull> { + Waiter::addr_of_pointers(target) } } diff --git a/tokio/src/sync/notify.rs b/tokio/src/sync/notify.rs index 2af9bacae..83bd68236 100644 --- a/tokio/src/sync/notify.rs +++ b/tokio/src/sync/notify.rs @@ -214,7 +214,6 @@ enum NotificationType { } #[derive(Debug)] -#[repr(C)] // required by `linked_list::Link` impl struct Waiter { /// Intrusive linked-list pointers. pointers: linked_list::Pointers, @@ -229,6 +228,14 @@ struct Waiter { _p: PhantomPinned, } +generate_addr_of_methods! { + impl<> Waiter { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { + &self.pointers + } + } +} + /// Future returned from [`Notify::notified()`]. /// /// This future is fused, so once it has completed, any future calls to poll @@ -950,7 +957,7 @@ unsafe impl linked_list::Link for Waiter { } unsafe fn pointers(target: NonNull) -> NonNull> { - target.cast() + Waiter::addr_of_pointers(target) } } diff --git a/tokio/src/sync/tests/atomic_waker.rs b/tokio/src/sync/tests/atomic_waker.rs index ec13cbd65..34bb9fbe7 100644 --- a/tokio/src/sync/tests/atomic_waker.rs +++ b/tokio/src/sync/tests/atomic_waker.rs @@ -12,7 +12,7 @@ impl AssertSync for AtomicWaker {} impl AssertSend for Waker {} impl AssertSync for Waker {} -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] 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 20153b7a5..858752b6f 100644 --- a/tokio/src/sync/tests/notify.rs +++ b/tokio/src/sync/tests/notify.rs @@ -4,7 +4,7 @@ use std::mem::ManuallyDrop; use std::sync::Arc; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] 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 d529a9e88..103e09dea 100644 --- a/tokio/src/sync/tests/semaphore_batch.rs +++ b/tokio/src/sync/tests/semaphore_batch.rs @@ -1,7 +1,7 @@ use crate::sync::batch_semaphore::Semaphore; use tokio_test::*; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; #[test] diff --git a/tokio/src/sync/watch.rs b/tokio/src/sync/watch.rs index 686696426..d4aeb2ef0 100644 --- a/tokio/src/sync/watch.rs +++ b/tokio/src/sync/watch.rs @@ -665,7 +665,7 @@ impl Sender { /// /// The `modify` closure must return `true` if the value has actually /// been modified during the mutable borrow. It should only return `false` - /// if the value is guaranteed to be unnmodified despite the mutable + /// if the value is guaranteed to be unmodified despite the mutable /// borrow. /// /// Receivers are only notified if the closure returned `true`. If the diff --git a/tokio/src/task/blocking.rs b/tokio/src/task/blocking.rs index 5fe358f3e..a817f83d5 100644 --- a/tokio/src/task/blocking.rs +++ b/tokio/src/task/blocking.rs @@ -102,11 +102,21 @@ cfg_rt! { /// their own. If you want to spawn an ordinary thread, you should use /// [`thread::spawn`] instead. /// - /// Closures spawned using `spawn_blocking` cannot be cancelled. When you shut - /// down the executor, it will wait indefinitely for all blocking operations to + /// Closures spawned using `spawn_blocking` cannot be cancelled abruptly; there + /// is no standard low level API to cause a thread to stop running. However, + /// a useful pattern is to pass some form of "cancellation token" into + /// the thread. This could be an [`AtomicBool`] that the task checks periodically. + /// Another approach is to have the thread primarily read or write from a channel, + /// and to exit when the channel closes; assuming the other side of the channel is dropped + /// when cancellation occurs, this will cause the blocking task thread to exit + /// soon after as well. + /// + /// When you shut down the executor, it will wait indefinitely for all blocking operations to /// finish. You can use [`shutdown_timeout`] to stop waiting for them after a /// certain timeout. Be aware that this will still not cancel the tasks — they - /// are simply allowed to keep running after the method returns. + /// are simply allowed to keep running after the method returns. It is possible + /// for a blocking task to be cancelled if it has not yet started running, but this + /// is not guaranteed. /// /// Note that if you are using the single threaded runtime, this function will /// still spawn additional threads for blocking operations. The basic @@ -140,6 +150,7 @@ cfg_rt! { /// [`thread::spawn`]: fn@std::thread::spawn /// [`shutdown_timeout`]: fn@crate::runtime::Runtime::shutdown_timeout /// [bridgesync]: https://tokio.rs/tokio/topics/bridging + /// [`AtomicBool`]: struct@std::sync::atomic::AtomicBool /// /// # Examples /// diff --git a/tokio/src/task/builder.rs b/tokio/src/task/builder.rs index ddb5c430e..5635c1a43 100644 --- a/tokio/src/task/builder.rs +++ b/tokio/src/task/builder.rs @@ -3,7 +3,7 @@ use crate::{ runtime::{context, Handle}, task::{JoinHandle, LocalSet}, }; -use std::future::Future; +use std::{future::Future, io}; /// Factory which is used to configure the properties of a new task. /// @@ -48,7 +48,7 @@ use std::future::Future; /// .spawn(async move { /// // Process each socket concurrently. /// process(socket).await -/// }); +/// })?; /// } /// } /// ``` @@ -83,12 +83,12 @@ impl<'a> Builder<'a> { /// See [`task::spawn`](crate::task::spawn) for /// more details. #[track_caller] - pub fn spawn(self, future: Fut) -> JoinHandle + pub fn spawn(self, future: Fut) -> io::Result> where Fut: Future + Send + 'static, Fut::Output: Send + 'static, { - super::spawn::spawn_inner(future, self.name) + Ok(super::spawn::spawn_inner(future, self.name)) } /// Spawn a task with this builder's settings on the provided [runtime @@ -99,12 +99,16 @@ impl<'a> Builder<'a> { /// [runtime handle]: crate::runtime::Handle /// [`Handle::spawn`]: crate::runtime::Handle::spawn #[track_caller] - pub fn spawn_on(&mut self, future: Fut, handle: &Handle) -> JoinHandle + pub fn spawn_on( + &mut self, + future: Fut, + handle: &Handle, + ) -> io::Result> where Fut: Future + Send + 'static, Fut::Output: Send + 'static, { - handle.spawn_named(future, self.name) + Ok(handle.spawn_named(future, self.name)) } /// Spawns `!Send` a task on the current [`LocalSet`] with this builder's @@ -122,12 +126,12 @@ impl<'a> Builder<'a> { /// [`task::spawn_local`]: crate::task::spawn_local /// [`LocalSet`]: crate::task::LocalSet #[track_caller] - pub fn spawn_local(self, future: Fut) -> JoinHandle + pub fn spawn_local(self, future: Fut) -> io::Result> where Fut: Future + 'static, Fut::Output: 'static, { - super::local::spawn_local_inner(future, self.name) + Ok(super::local::spawn_local_inner(future, self.name)) } /// Spawns `!Send` a task on the provided [`LocalSet`] with this builder's @@ -138,12 +142,16 @@ impl<'a> Builder<'a> { /// [`LocalSet::spawn_local`]: crate::task::LocalSet::spawn_local /// [`LocalSet`]: crate::task::LocalSet #[track_caller] - pub fn spawn_local_on(self, future: Fut, local_set: &LocalSet) -> JoinHandle + pub fn spawn_local_on( + self, + future: Fut, + local_set: &LocalSet, + ) -> io::Result> where Fut: Future + 'static, Fut::Output: 'static, { - local_set.spawn_named(future, self.name) + Ok(local_set.spawn_named(future, self.name)) } /// Spawns blocking code on the blocking threadpool. @@ -155,7 +163,10 @@ impl<'a> Builder<'a> { /// See [`task::spawn_blocking`](crate::task::spawn_blocking) /// for more details. #[track_caller] - pub fn spawn_blocking(self, function: Function) -> JoinHandle + pub fn spawn_blocking( + self, + function: Function, + ) -> io::Result> where Function: FnOnce() -> Output + Send + 'static, Output: Send + 'static, @@ -174,18 +185,20 @@ impl<'a> Builder<'a> { self, function: Function, handle: &Handle, - ) -> JoinHandle + ) -> io::Result> where Function: FnOnce() -> Output + Send + 'static, Output: Send + 'static, { use crate::runtime::Mandatory; - let (join_handle, _was_spawned) = handle.as_inner().spawn_blocking_inner( + let (join_handle, spawn_result) = handle.as_inner().spawn_blocking_inner( function, Mandatory::NonMandatory, self.name, handle, ); - join_handle + + spawn_result?; + Ok(join_handle) } } diff --git a/tokio/src/task/consume_budget.rs b/tokio/src/task/consume_budget.rs index c8b2d7e5c..7c46444b7 100644 --- a/tokio/src/task/consume_budget.rs +++ b/tokio/src/task/consume_budget.rs @@ -4,7 +4,7 @@ use std::task::Poll; /// runtime *if* the task's coop budget was exhausted. /// /// The task will only yield if its entire coop budget has been exhausted. -/// This function can can be used in order to insert optional yield points into long +/// This function can be used in order to insert optional yield points into long /// computations that do not use Tokio resources like sockets or semaphores, /// without redundantly yielding to the runtime each time. /// diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index cb08e4df0..c3767c99d 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -101,13 +101,15 @@ impl JoinSet { /// use tokio::task::JoinSet; /// /// #[tokio::main] - /// async fn main() { + /// async fn main() -> std::io::Result<()> { /// let mut set = JoinSet::new(); /// /// // Use the builder to configure a task's name before spawning it. /// set.build_task() /// .name("my_task") - /// .spawn(async { /* ... */ }); + /// .spawn(async { /* ... */ })?; + /// + /// Ok(()) /// } /// ``` #[cfg(all(tokio_unstable, feature = "tracing"))] @@ -377,13 +379,13 @@ impl<'a, T: 'static> Builder<'a, T> { /// /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] - pub fn spawn(self, future: F) -> AbortHandle + pub fn spawn(self, future: F) -> std::io::Result where F: Future, F: Send + 'static, T: Send, { - self.joinset.insert(self.builder.spawn(future)) + Ok(self.joinset.insert(self.builder.spawn(future)?)) } /// Spawn the provided task on the provided [runtime handle] with this @@ -397,13 +399,13 @@ impl<'a, T: 'static> Builder<'a, T> { /// [`AbortHandle`]: crate::task::AbortHandle /// [runtime handle]: crate::runtime::Handle #[track_caller] - pub fn spawn_on(mut self, future: F, handle: &Handle) -> AbortHandle + pub fn spawn_on(mut self, future: F, handle: &Handle) -> std::io::Result where F: Future, F: Send + 'static, T: Send, { - self.joinset.insert(self.builder.spawn_on(future, handle)) + Ok(self.joinset.insert(self.builder.spawn_on(future, handle)?)) } /// Spawn the provided task on the current [`LocalSet`] with this builder's @@ -420,12 +422,12 @@ impl<'a, T: 'static> Builder<'a, T> { /// [`LocalSet`]: crate::task::LocalSet /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] - pub fn spawn_local(self, future: F) -> AbortHandle + pub fn spawn_local(self, future: F) -> std::io::Result where F: Future, F: 'static, { - self.joinset.insert(self.builder.spawn_local(future)) + Ok(self.joinset.insert(self.builder.spawn_local(future)?)) } /// Spawn the provided task on the provided [`LocalSet`] with this builder's @@ -438,13 +440,14 @@ impl<'a, T: 'static> Builder<'a, T> { /// [`LocalSet`]: crate::task::LocalSet /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] - pub fn spawn_local_on(self, future: F, local_set: &LocalSet) -> AbortHandle + pub fn spawn_local_on(self, future: F, local_set: &LocalSet) -> std::io::Result where F: Future, F: 'static, { - self.joinset - .insert(self.builder.spawn_local_on(future, local_set)) + Ok(self + .joinset + .insert(self.builder.spawn_local_on(future, local_set)?)) } } diff --git a/tokio/src/task/local.rs b/tokio/src/task/local.rs index e3ba50a79..a5bd1bb88 100644 --- a/tokio/src/task/local.rs +++ b/tokio/src/task/local.rs @@ -10,6 +10,7 @@ use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; +use std::rc::Rc; use std::task::Poll; use pin_project_lite::pin_project; @@ -159,7 +160,7 @@ cfg_rt! { /// tokio::task::spawn_local(run_task(new_task)); /// } /// // If the while loop returns, then all the LocalSpawner - /// // objects have have been dropped. + /// // objects have been dropped. /// }); /// /// // This will return once all senders are dropped and all @@ -215,7 +216,7 @@ cfg_rt! { tick: Cell, /// State available from thread-local. - context: Context, + context: Rc, /// This type should not be Send. _not_send: PhantomData<*const ()>, @@ -260,7 +261,7 @@ pin_project! { } } -scoped_thread_local!(static CURRENT: Context); +thread_local!(static CURRENT: Cell>> = Cell::new(None)); cfg_rt! { /// Spawns a `!Send` future on the local task set. @@ -310,10 +311,12 @@ cfg_rt! { F::Output: 'static { CURRENT.with(|maybe_cx| { - let cx = maybe_cx - .expect("`spawn_local` called from outside of a `task::LocalSet`"); + let ctx = clone_rc(maybe_cx); + match ctx { + None => panic!("`spawn_local` called from outside of a `task::LocalSet`"), + Some(cx) => cx.spawn(future, name) + } - cx.spawn(future, name) }) } } @@ -327,12 +330,29 @@ const MAX_TASKS_PER_TICK: usize = 61; /// How often it check the remote queue first. const REMOTE_FIRST_INTERVAL: u8 = 31; +/// Context guard for LocalSet +pub struct LocalEnterGuard(Option>); + +impl Drop for LocalEnterGuard { + fn drop(&mut self) { + CURRENT.with(|ctx| { + ctx.replace(self.0.take()); + }) + } +} + +impl fmt::Debug for LocalEnterGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("LocalEnterGuard").finish() + } +} + impl LocalSet { /// Returns a new local task set. pub fn new() -> LocalSet { LocalSet { tick: Cell::new(0), - context: Context { + context: Rc::new(Context { owned: LocalOwnedTasks::new(), queue: VecDequeCell::with_capacity(INITIAL_CAPACITY), shared: Arc::new(Shared { @@ -342,11 +362,24 @@ impl LocalSet { unhandled_panic: crate::runtime::UnhandledPanic::Ignore, }), unhandled_panic: Cell::new(false), - }, + }), _not_send: PhantomData, } } + /// Enters the context of this `LocalSet`. + /// + /// The [`spawn_local`] method will spawn tasks on the `LocalSet` whose + /// context you are inside. + /// + /// [`spawn_local`]: fn@crate::task::spawn_local + pub fn enter(&self) -> LocalEnterGuard { + CURRENT.with(|ctx| { + let old = ctx.replace(Some(self.context.clone())); + LocalEnterGuard(old) + }) + } + /// Spawns a `!Send` task onto the local task set. /// /// This task is guaranteed to be run on the current thread. @@ -454,6 +487,7 @@ impl LocalSet { /// [`Runtime::block_on`]: method@crate::runtime::Runtime::block_on /// [in-place blocking]: fn@crate::task::block_in_place /// [`spawn_blocking`]: fn@crate::task::spawn_blocking + #[track_caller] #[cfg(feature = "rt")] #[cfg_attr(docsrs, doc(cfg(feature = "rt")))] pub fn block_on(&self, rt: &crate::runtime::Runtime, future: F) -> F::Output @@ -579,7 +613,25 @@ impl LocalSet { } fn with(&self, f: impl FnOnce() -> T) -> T { - CURRENT.set(&self.context, f) + CURRENT.with(|ctx| { + struct Reset<'a> { + ctx_ref: &'a Cell>>, + val: Option>, + } + impl<'a> Drop for Reset<'a> { + fn drop(&mut self) { + self.ctx_ref.replace(self.val.take()); + } + } + let old = ctx.replace(Some(self.context.clone())); + + let _reset = Reset { + ctx_ref: ctx, + val: old, + }; + + f() + }) } } @@ -600,7 +652,7 @@ cfg_unstable! { /// * `UnhandledPanic::ShutdownRuntime` will force the `LocalSet` to /// shutdown immediately when a spawned task panics even if that /// task's `JoinHandle` has not been dropped. All other spawned tasks - /// will immediatetly terminate and further calls to + /// will immediately terminate and further calls to /// [`LocalSet::block_on`] and [`LocalSet::run_until`] will panic. /// /// # Panics @@ -645,8 +697,9 @@ cfg_unstable! { /// [`JoinHandle`]: struct@crate::task::JoinHandle pub fn unhandled_panic(&mut self, behavior: crate::runtime::UnhandledPanic) -> &mut Self { // TODO: This should be set as a builder - Arc::get_mut(&mut self.context.shared) - .expect("TODO: we shouldn't panic") + Rc::get_mut(&mut self.context) + .and_then(|ctx| Arc::get_mut(&mut ctx.shared)) + .expect("Unhandled Panic behavior modified after starting LocalSet") .unhandled_panic = behavior; self } @@ -769,23 +822,33 @@ impl Future for RunUntil<'_, T> { } } +fn clone_rc(rc: &Cell>>) -> Option> { + let value = rc.take(); + let cloned = value.clone(); + rc.set(value); + cloned +} + impl Shared { /// Schedule the provided task on the scheduler. fn schedule(&self, task: task::Notified>) { - CURRENT.with(|maybe_cx| match maybe_cx { - Some(cx) if cx.shared.ptr_eq(self) => { - cx.queue.push_back(task); - } - _ => { - // First check whether the queue is still there (if not, the - // LocalSet is dropped). Then push to it if so, and if not, - // do nothing. - let mut lock = self.queue.lock(); + CURRENT.with(|maybe_cx| { + let ctx = clone_rc(maybe_cx); + match ctx { + Some(cx) if cx.shared.ptr_eq(self) => { + cx.queue.push_back(task); + } + _ => { + // First check whether the queue is still there (if not, the + // LocalSet is dropped). Then push to it if so, and if not, + // do nothing. + let mut lock = self.queue.lock(); - if let Some(queue) = lock.as_mut() { - queue.push_back(task); - drop(lock); - self.waker.wake(); + if let Some(queue) = lock.as_mut() { + queue.push_back(task); + drop(lock); + self.waker.wake(); + } } } }); @@ -799,9 +862,14 @@ impl Shared { impl task::Schedule for Arc { fn release(&self, task: &Task) -> Option> { CURRENT.with(|maybe_cx| { - let cx = maybe_cx.expect("scheduler context missing"); - assert!(cx.shared.ptr_eq(self)); - cx.owned.remove(task) + let ctx = clone_rc(maybe_cx); + match ctx { + None => panic!("scheduler context missing"), + Some(cx) => { + assert!(cx.shared.ptr_eq(self)); + cx.owned.remove(task) + } + } }) } @@ -821,13 +889,15 @@ impl task::Schedule for Arc { // This hook is only called from within the runtime, so // `CURRENT` should match with `&self`, i.e. there is no // opportunity for a nested scheduler to be called. - CURRENT.with(|maybe_cx| match maybe_cx { + CURRENT.with(|maybe_cx| { + let ctx = clone_rc(maybe_cx); + match ctx { Some(cx) if Arc::ptr_eq(self, &cx.shared) => { cx.unhandled_panic.set(true); cx.owned.close_and_shutdown_all(); } _ => unreachable!("runtime core not set in CURRENT thread-local"), - }) + }}) } } } diff --git a/tokio/src/task/mod.rs b/tokio/src/task/mod.rs index 8ea73fb48..d2b634de0 100644 --- a/tokio/src/task/mod.rs +++ b/tokio/src/task/mod.rs @@ -278,8 +278,10 @@ cfg_rt! { pub use crate::runtime::task::{JoinError, JoinHandle}; - mod blocking; - pub use blocking::spawn_blocking; + cfg_not_wasi! { + mod blocking; + pub use blocking::spawn_blocking; + } mod spawn; pub use spawn::spawn; @@ -297,7 +299,7 @@ cfg_rt! { } mod local; - pub use local::{spawn_local, LocalSet}; + pub use local::{spawn_local, LocalSet, LocalEnterGuard}; mod task_local; pub use task_local::LocalKey; diff --git a/tokio/src/task/task_local.rs b/tokio/src/task/task_local.rs index 924af625b..a1498ca4e 100644 --- a/tokio/src/task/task_local.rs +++ b/tokio/src/task/task_local.rs @@ -51,6 +51,7 @@ macro_rules! task_local { #[macro_export] macro_rules! __task_local_inner { ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => { + $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { std::thread_local! { static __KEY: std::cell::RefCell> = const { std::cell::RefCell::new(None) }; @@ -66,6 +67,7 @@ macro_rules! __task_local_inner { #[macro_export] macro_rules! __task_local_inner { ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => { + $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { std::thread_local! { static __KEY: std::cell::RefCell> = std::cell::RefCell::new(None); @@ -285,6 +287,7 @@ impl LocalKey { /// # Panics /// /// This function will panic if the task local doesn't have a value set. + #[track_caller] pub fn get(&'static self) -> T { self.with(|v| *v) } @@ -423,6 +426,7 @@ enum ScopeInnerErr { } impl ScopeInnerErr { + #[track_caller] fn panic(&self) -> ! { match self { Self::BorrowError => panic!("cannot enter a task-local scope while the task-local storage is borrowed"), diff --git a/tokio/src/time/driver/entry.rs b/tokio/src/time/driver/entry.rs index f0ea898e1..48856bf32 100644 --- a/tokio/src/time/driver/entry.rs +++ b/tokio/src/time/driver/entry.rs @@ -36,7 +36,7 @@ //! point than it was originally scheduled for. //! //! This is accomplished by lazily rescheduling timers. That is, we update the -//! state field field with the true expiration of the timer from the holder of +//! state field with the true expiration of the timer from the holder of //! the [`TimerEntry`]. When the driver services timers (ie, whenever it's //! walking lists of timers), it checks this "true when" value, and reschedules //! based on it. @@ -326,7 +326,7 @@ pub(super) type EntryList = crate::util::linked_list::LinkedList, @@ -339,6 +339,14 @@ pub(crate) struct TimerShared { _p: PhantomPinned, } +generate_addr_of_methods! { + impl<> TimerShared { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { + &self.driver_state.0.pointers + } + } +} + impl TimerShared { pub(super) fn new() -> Self { Self { @@ -421,7 +429,6 @@ impl TimerShared { /// padded. This contains the information that the driver thread accesses most /// frequently to minimize contention. In particular, we move it away from the /// waker, as the waker is updated on every poll. -#[repr(C)] // required by `link_list::Link` impl struct TimerSharedPadded { /// A link within the doubly-linked list of timers on a particular level and /// slot. Valid only if state is equal to Registered. @@ -476,7 +483,7 @@ unsafe impl linked_list::Link for TimerShared { unsafe fn pointers( target: NonNull, ) -> NonNull> { - target.cast() + TimerShared::addr_of_pointers(target) } } diff --git a/tokio/src/time/driver/tests/mod.rs b/tokio/src/time/driver/tests/mod.rs index 3ac8c7564..fbe000f0f 100644 --- a/tokio/src/time/driver/tests/mod.rs +++ b/tokio/src/time/driver/tests/mod.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "wasi"))] + use std::{task::Context, time::Duration}; #[cfg(not(loom))] @@ -289,11 +291,11 @@ fn balanced_incr_and_decr() { let incr_inner = inner.clone(); let decr_inner = inner.clone(); - let incr_hndle = thread::spawn(move || incr(incr_inner)); - let decr_hndle = thread::spawn(move || decr(decr_inner)); + let incr_handle = thread::spawn(move || incr(incr_inner)); + let decr_handle = thread::spawn(move || decr(decr_inner)); - incr_hndle.join().expect("should never fail"); - decr_hndle.join().expect("should never fail"); + incr_handle.join().expect("should never fail"); + decr_handle.join().expect("should never fail"); assert_eq!(inner.num(Ordering::SeqCst), 0); }) diff --git a/tokio/src/time/interval.rs b/tokio/src/time/interval.rs index 0fe420fce..82c217094 100644 --- a/tokio/src/time/interval.rs +++ b/tokio/src/time/interval.rs @@ -278,7 +278,7 @@ pub enum MissedTickBehavior { /// // 50ms after the call to `tick` up above. That is, in `tick`, when we /// // recognize that we missed a tick, we schedule the next tick to happen /// // 50ms (or whatever the `period` is) from right then, not from when - /// // were were *supposed* to tick + /// // were *supposed* to tick /// interval.tick().await; /// # } /// ``` diff --git a/tokio/src/util/idle_notified_set.rs b/tokio/src/util/idle_notified_set.rs index 71f3a32a8..ce8ff9e9a 100644 --- a/tokio/src/util/idle_notified_set.rs +++ b/tokio/src/util/idle_notified_set.rs @@ -88,10 +88,6 @@ enum List { /// move it out from this entry to prevent it from getting leaked. (Since the /// two linked lists are emptied in the destructor of `IdleNotifiedSet`, the /// value should not be leaked.) -/// -/// This type is `#[repr(C)]` because its `linked_list::Link` implementation -/// requires that `pointers` is the first field. -#[repr(C)] struct ListEntry { /// The linked list pointers of the list this entry is in. pointers: linked_list::Pointers>, @@ -105,6 +101,14 @@ struct ListEntry { _pin: PhantomPinned, } +generate_addr_of_methods! { + impl ListEntry { + unsafe fn addr_of_pointers(self: NonNull) -> NonNull>> { + &self.pointers + } + } +} + // With mutable access to the `IdleNotifiedSet`, you can get mutable access to // the values. unsafe impl Send for IdleNotifiedSet {} @@ -453,11 +457,6 @@ unsafe impl linked_list::Link for ListEntry { unsafe fn pointers( target: NonNull>, ) -> NonNull>> { - // Safety: The pointers struct is the first field and ListEntry is - // `#[repr(C)]` so this cast is safe. - // - // We do this rather than doing a field access since `std::ptr::addr_of` - // is too new for our MSRV. - target.cast() + ListEntry::addr_of_pointers(target) } } diff --git a/tokio/src/util/linked_list.rs b/tokio/src/util/linked_list.rs index e6bdde68c..af9632044 100644 --- a/tokio/src/util/linked_list.rs +++ b/tokio/src/util/linked_list.rs @@ -631,6 +631,7 @@ mod tests { } } + #[cfg(not(target_arch = "wasm32"))] fn run_fuzz(ops: Vec) { use std::collections::VecDeque; diff --git a/tokio/tests/async_send_sync.rs b/tokio/tests/async_send_sync.rs index 1f53d0db9..397e55fa5 100644 --- a/tokio/tests/async_send_sync.rs +++ b/tokio/tests/async_send_sync.rs @@ -127,70 +127,96 @@ macro_rules! assert_value { }; } -assert_value!(tokio::fs::DirBuilder: Send & Sync & Unpin); -assert_value!(tokio::fs::DirEntry: Send & Sync & Unpin); -assert_value!(tokio::fs::File: Send & Sync & Unpin); -assert_value!(tokio::fs::OpenOptions: Send & Sync & Unpin); -assert_value!(tokio::fs::ReadDir: Send & Sync & Unpin); +macro_rules! cfg_not_wasi { + ($($item:item)*) => { + $( + #[cfg(not(target_os = "wasi"))] + $item + )* + } +} -async_assert_fn!(tokio::fs::canonicalize(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::copy(&str, &str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::create_dir(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::create_dir_all(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::hard_link(&str, &str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::metadata(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::read(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::read_dir(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::read_link(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::read_to_string(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::remove_dir(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::remove_dir_all(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::remove_file(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::rename(&str, &str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::set_permissions(&str, std::fs::Permissions): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::symlink_metadata(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::write(&str, Vec): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::ReadDir::next_entry(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::OpenOptions::open(_, &str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::DirBuilder::create(_, &str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::DirEntry::metadata(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::DirEntry::file_type(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::open(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::create(&str): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::sync_all(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::sync_data(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::set_len(_, u64): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::metadata(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::try_clone(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::into_std(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::fs::File::set_permissions(_, std::fs::Permissions): Send & Sync & !Unpin); +cfg_not_wasi! { + mod fs { + use super::*; + assert_value!(tokio::fs::DirBuilder: Send & Sync & Unpin); + assert_value!(tokio::fs::DirEntry: Send & Sync & Unpin); + assert_value!(tokio::fs::File: Send & Sync & Unpin); + assert_value!(tokio::fs::OpenOptions: Send & Sync & Unpin); + assert_value!(tokio::fs::ReadDir: Send & Sync & Unpin); + + async_assert_fn!(tokio::fs::canonicalize(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::copy(&str, &str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::create_dir(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::create_dir_all(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::hard_link(&str, &str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::metadata(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::read(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::read_dir(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::read_link(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::read_to_string(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::remove_dir(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::remove_dir_all(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::remove_file(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::rename(&str, &str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::set_permissions(&str, std::fs::Permissions): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::symlink_metadata(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::write(&str, Vec): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::ReadDir::next_entry(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::OpenOptions::open(_, &str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::DirBuilder::create(_, &str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::DirEntry::metadata(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::DirEntry::file_type(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::open(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::create(&str): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::sync_all(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::sync_data(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::set_len(_, u64): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::metadata(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::try_clone(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::fs::File::into_std(_): Send & Sync & !Unpin); + async_assert_fn!( + tokio::fs::File::set_permissions(_, std::fs::Permissions): Send & Sync & !Unpin + ); + } +} + +cfg_not_wasi! { + assert_value!(tokio::net::TcpSocket: Send & Sync & Unpin); + async_assert_fn!(tokio::net::TcpListener::bind(SocketAddr): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::TcpStream::connect(SocketAddr): Send & Sync & !Unpin); +} assert_value!(tokio::net::TcpListener: Send & Sync & Unpin); -assert_value!(tokio::net::TcpSocket: Send & Sync & Unpin); assert_value!(tokio::net::TcpStream: Send & Sync & Unpin); -assert_value!(tokio::net::UdpSocket: Send & Sync & Unpin); assert_value!(tokio::net::tcp::OwnedReadHalf: Send & Sync & Unpin); assert_value!(tokio::net::tcp::OwnedWriteHalf: Send & Sync & Unpin); assert_value!(tokio::net::tcp::ReadHalf<'_>: Send & Sync & Unpin); assert_value!(tokio::net::tcp::ReuniteError: Send & Sync & Unpin); assert_value!(tokio::net::tcp::WriteHalf<'_>: Send & Sync & Unpin); async_assert_fn!(tokio::net::TcpListener::accept(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::TcpListener::bind(SocketAddr): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::TcpStream::connect(SocketAddr): Send & Sync & !Unpin); async_assert_fn!(tokio::net::TcpStream::peek(_, &mut [u8]): Send & Sync & !Unpin); async_assert_fn!(tokio::net::TcpStream::readable(_): Send & Sync & !Unpin); async_assert_fn!(tokio::net::TcpStream::ready(_, tokio::io::Interest): Send & Sync & !Unpin); async_assert_fn!(tokio::net::TcpStream::writable(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::bind(SocketAddr): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::connect(_, SocketAddr): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::peek_from(_, &mut [u8]): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::readable(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::ready(_, tokio::io::Interest): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::recv(_, &mut [u8]): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::recv_from(_, &mut [u8]): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::send(_, &[u8]): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::send_to(_, &[u8], SocketAddr): Send & Sync & !Unpin); -async_assert_fn!(tokio::net::UdpSocket::writable(_): Send & Sync & !Unpin); + +// Wasi does not support UDP +cfg_not_wasi! { + mod udp_socket { + use super::*; + assert_value!(tokio::net::UdpSocket: Send & Sync & Unpin); + async_assert_fn!(tokio::net::UdpSocket::bind(SocketAddr): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::connect(_, SocketAddr): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::peek_from(_, &mut [u8]): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::readable(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::recv(_, &mut [u8]): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::recv_from(_, &mut [u8]): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::send(_, &[u8]): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::send_to(_, &[u8], SocketAddr): Send & Sync & !Unpin); + async_assert_fn!(tokio::net::UdpSocket::writable(_): Send & Sync & !Unpin); + } +} async_assert_fn!(tokio::net::lookup_host(SocketAddr): Send & Sync & !Unpin); async_assert_fn!(tokio::net::tcp::ReadHalf::peek(_, &mut [u8]): Send & Sync & !Unpin); @@ -242,16 +268,22 @@ mod windows_named_pipe { async_assert_fn!(NamedPipeServer::writable(_): Send & Sync & !Unpin); } -assert_value!(tokio::process::Child: Send & Sync & Unpin); -assert_value!(tokio::process::ChildStderr: Send & Sync & Unpin); -assert_value!(tokio::process::ChildStdin: Send & Sync & Unpin); -assert_value!(tokio::process::ChildStdout: Send & Sync & Unpin); -assert_value!(tokio::process::Command: Send & Sync & Unpin); -async_assert_fn!(tokio::process::Child::kill(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::process::Child::wait(_): Send & Sync & !Unpin); -async_assert_fn!(tokio::process::Child::wait_with_output(_): Send & Sync & !Unpin); +cfg_not_wasi! { + mod test_process { + use super::*; + assert_value!(tokio::process::Child: Send & Sync & Unpin); + assert_value!(tokio::process::ChildStderr: Send & Sync & Unpin); + assert_value!(tokio::process::ChildStdin: Send & Sync & Unpin); + assert_value!(tokio::process::ChildStdout: Send & Sync & Unpin); + assert_value!(tokio::process::Command: Send & Sync & Unpin); + async_assert_fn!(tokio::process::Child::kill(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::process::Child::wait(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::process::Child::wait_with_output(_): Send & Sync & !Unpin); + } + + async_assert_fn!(tokio::signal::ctrl_c(): Send & Sync & !Unpin); +} -async_assert_fn!(tokio::signal::ctrl_c(): Send & Sync & !Unpin); #[cfg(unix)] mod unix_signal { use super::*; diff --git a/tokio/tests/buffered.rs b/tokio/tests/buffered.rs index 98b6d5f31..2ae110044 100644 --- a/tokio/tests/buffered.rs +++ b/tokio/tests/buffered.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind() use tokio::net::TcpListener; use tokio_test::assert_ok; diff --git a/tokio/tests/fs.rs b/tokio/tests/fs.rs index 13c44c08d..9f739f60c 100644 --- a/tokio/tests/fs.rs +++ b/tokio/tests/fs.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations use tokio::fs; use tokio_test::assert_ok; diff --git a/tokio/tests/fs_copy.rs b/tokio/tests/fs_copy.rs index 8d1632013..e32aebd93 100644 --- a/tokio/tests/fs_copy.rs +++ b/tokio/tests/fs_copy.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations use tempfile::tempdir; use tokio::fs; diff --git a/tokio/tests/fs_dir.rs b/tokio/tests/fs_dir.rs index 21efe8c0e..21391e27b 100644 --- a/tokio/tests/fs_dir.rs +++ b/tokio/tests/fs_dir.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support directory operations use tokio::fs; use tokio_test::{assert_err, assert_ok}; diff --git a/tokio/tests/fs_file.rs b/tokio/tests/fs_file.rs index f645e61ae..55ff24183 100644 --- a/tokio/tests/fs_file.rs +++ b/tokio/tests/fs_file.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations use std::io::prelude::*; use tempfile::NamedTempFile; diff --git a/tokio/tests/fs_link.rs b/tokio/tests/fs_link.rs index 2ef666fb2..9a83df523 100644 --- a/tokio/tests/fs_link.rs +++ b/tokio/tests/fs_link.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations use tokio::fs; diff --git a/tokio/tests/io_copy_bidirectional.rs b/tokio/tests/io_copy_bidirectional.rs index 0e82b29ea..679d78dc1 100644 --- a/tokio/tests/io_copy_bidirectional.rs +++ b/tokio/tests/io_copy_bidirectional.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind() use std::time::Duration; use tokio::io::{self, copy_bidirectional, AsyncReadExt, AsyncWriteExt}; diff --git a/tokio/tests/io_driver.rs b/tokio/tests/io_driver.rs index 6fb566de5..650e66c8b 100644 --- a/tokio/tests/io_driver.rs +++ b/tokio/tests/io_driver.rs @@ -1,5 +1,6 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +// Wasi does not support panic recovery or threading +#![cfg(all(feature = "full", not(target_os = "wasi")))] use tokio::net::TcpListener; use tokio::runtime; diff --git a/tokio/tests/io_driver_drop.rs b/tokio/tests/io_driver_drop.rs index 631e66e9f..c31826379 100644 --- a/tokio/tests/io_driver_drop.rs +++ b/tokio/tests/io_driver_drop.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind use tokio::net::TcpListener; use tokio::runtime; diff --git a/tokio/tests/io_fill_buf.rs b/tokio/tests/io_fill_buf.rs index 0b2ebd78f..534417c85 100644 --- a/tokio/tests/io_fill_buf.rs +++ b/tokio/tests/io_fill_buf.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support file operations use tempfile::NamedTempFile; use tokio::fs::File; diff --git a/tokio/tests/io_panic.rs b/tokio/tests/io_panic.rs index 07d824d16..9b2bf6a3b 100644 --- a/tokio/tests/io_panic.rs +++ b/tokio/tests/io_panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use std::task::{Context, Poll}; use std::{error::Error, pin::Pin}; diff --git a/tokio/tests/io_read.rs b/tokio/tests/io_read.rs index 11da5a148..6bea0ac86 100644 --- a/tokio/tests/io_read.rs +++ b/tokio/tests/io_read.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf}; use tokio_test::assert_ok; diff --git a/tokio/tests/io_split.rs b/tokio/tests/io_split.rs index a0121667f..77b77a3a0 100644 --- a/tokio/tests/io_split.rs +++ b/tokio/tests/io_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use tokio::io::{split, AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf}; diff --git a/tokio/tests/io_take.rs b/tokio/tests/io_take.rs index d623de1d1..539f17f3a 100644 --- a/tokio/tests/io_take.rs +++ b/tokio/tests/io_take.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use std::pin::Pin; use std::task::{Context, Poll}; diff --git a/tokio/tests/join_handle_panic.rs b/tokio/tests/join_handle_panic.rs index f7de92d41..055b1f6b6 100644 --- a/tokio/tests/join_handle_panic.rs +++ b/tokio/tests/join_handle_panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery struct PanicsOnDrop; diff --git a/tokio/tests/macros_join.rs b/tokio/tests/macros_join.rs index 56bd9c58f..b2969557f 100644 --- a/tokio/tests/macros_join.rs +++ b/tokio/tests/macros_join.rs @@ -2,12 +2,12 @@ #![allow(clippy::blacklisted_name)] use std::sync::Arc; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; use tokio::sync::{oneshot, Semaphore}; @@ -86,7 +86,7 @@ async fn non_cooperative_task(permits: Arc) -> usize { let mut exceeded_budget = 0; for _ in 0..5 { - // Another task should run after after this task uses its whole budget + // Another task should run after this task uses its whole budget for _ in 0..128 { let _permit = permits.clone().acquire_owned().await.unwrap(); } diff --git a/tokio/tests/macros_pin.rs b/tokio/tests/macros_pin.rs index 70c95a1c8..001528dde 100644 --- a/tokio/tests/macros_pin.rs +++ b/tokio/tests/macros_pin.rs @@ -1,9 +1,9 @@ #![cfg(feature = "macros")] -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; async fn one() {} diff --git a/tokio/tests/macros_rename_test.rs b/tokio/tests/macros_rename_test.rs index fd5554ced..a99c921e1 100644 --- a/tokio/tests/macros_rename_test.rs +++ b/tokio/tests/macros_rename_test.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threading #[allow(unused_imports)] use std as tokio; diff --git a/tokio/tests/macros_select.rs b/tokio/tests/macros_select.rs index c60a4a950..c7297b528 100644 --- a/tokio/tests/macros_select.rs +++ b/tokio/tests/macros_select.rs @@ -1,10 +1,10 @@ #![cfg(feature = "macros")] #![allow(clippy::blacklisted_name)] -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; use tokio::sync::oneshot; diff --git a/tokio/tests/macros_test.rs b/tokio/tests/macros_test.rs index c5d9d9f9b..311067ecd 100644 --- a/tokio/tests/macros_test.rs +++ b/tokio/tests/macros_test.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threading use tokio::test; diff --git a/tokio/tests/macros_try_join.rs b/tokio/tests/macros_try_join.rs index 556436d22..876de0b49 100644 --- a/tokio/tests/macros_try_join.rs +++ b/tokio/tests/macros_try_join.rs @@ -6,10 +6,10 @@ use std::sync::Arc; use tokio::sync::{oneshot, Semaphore}; use tokio_test::{assert_pending, assert_ready, task}; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; #[maybe_tokio_test] @@ -114,7 +114,7 @@ async fn non_cooperative_task(permits: Arc) -> Result let mut exceeded_budget = 0; for _ in 0..5 { - // Another task should run after after this task uses its whole budget + // Another task should run after this task uses its whole budget for _ in 0..128 { let _permit = permits.clone().acquire_owned().await.unwrap(); } diff --git a/tokio/tests/net_bind_resource.rs b/tokio/tests/net_bind_resource.rs index d4a0b8dab..d279fe4a1 100644 --- a/tokio/tests/net_bind_resource.rs +++ b/tokio/tests/net_bind_resource.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery or bind use tokio::net::TcpListener; diff --git a/tokio/tests/net_lookup_host.rs b/tokio/tests/net_lookup_host.rs index 44c8e19e0..667030c7e 100644 --- a/tokio/tests/net_lookup_host.rs +++ b/tokio/tests/net_lookup_host.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support direct socket operations use tokio::net; use tokio_test::assert_ok; diff --git a/tokio/tests/net_panic.rs b/tokio/tests/net_panic.rs new file mode 100644 index 000000000..d2d80ef50 --- /dev/null +++ b/tokio/tests/net_panic.rs @@ -0,0 +1,188 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(target_os = "wasi")))] + +use std::error::Error; +use tokio::net::{TcpListener, TcpStream}; +use tokio::runtime::{Builder, Runtime}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn udp_socket_from_std_panic_caller() -> Result<(), Box> { + use std::net::SocketAddr; + use tokio::net::UdpSocket; + + let addr = "127.0.0.1:8080".parse::().unwrap(); + let std_sock = std::net::UdpSocket::bind(addr).unwrap(); + std_sock.set_nonblocking(true).unwrap(); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _sock = UdpSocket::from_std(std_sock); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn tcp_listener_from_std_panic_caller() -> Result<(), Box> { + let std_listener = std::net::TcpListener::bind("127.0.0.1:8080").unwrap(); + std_listener.set_nonblocking(true).unwrap(); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = TcpListener::from_std(std_listener); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn tcp_stream_from_std_panic_caller() -> Result<(), Box> { + let std_listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + + let std_stream = std::net::TcpStream::connect(std_listener.local_addr().unwrap()).unwrap(); + std_stream.set_nonblocking(true).unwrap(); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = TcpStream::from_std(std_stream); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn unix_listener_bind_panic_caller() -> Result<(), Box> { + use tokio::net::UnixListener; + + let dir = tempfile::tempdir().unwrap(); + let sock_path = dir.path().join("socket"); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = UnixListener::bind(&sock_path); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn unix_listener_from_std_panic_caller() -> Result<(), Box> { + use tokio::net::UnixListener; + + let dir = tempfile::tempdir().unwrap(); + let sock_path = dir.path().join("socket"); + let std_listener = std::os::unix::net::UnixListener::bind(&sock_path).unwrap(); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = UnixListener::from_std(std_listener); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn unix_stream_from_std_panic_caller() -> Result<(), Box> { + use tokio::net::UnixStream; + + let dir = tempfile::tempdir().unwrap(); + let sock_path = dir.path().join("socket"); + let _std_listener = std::os::unix::net::UnixListener::bind(&sock_path).unwrap(); + let std_stream = std::os::unix::net::UnixStream::connect(&sock_path).unwrap(); + + let panic_location_file = test_panic(|| { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = UnixStream::from_std(std_stream); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn unix_datagram_from_std_panic_caller() -> Result<(), Box> { + use std::os::unix::net::UnixDatagram as StdUDS; + use tokio::net::UnixDatagram; + + let dir = tempfile::tempdir().unwrap(); + let sock_path = dir.path().join("socket"); + + // Bind the socket to a filesystem path + // /let socket_path = tmp.path().join("socket"); + let std_socket = StdUDS::bind(&sock_path).unwrap(); + std_socket.set_nonblocking(true).unwrap(); + + let panic_location_file = test_panic(move || { + let rt = runtime_without_io(); + rt.block_on(async { + let _ = UnixDatagram::from_std(std_socket); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(windows)] +fn server_options_max_instances_panic_caller() -> Result<(), Box> { + use tokio::net::windows::named_pipe::ServerOptions; + + let panic_location_file = test_panic(move || { + let rt = runtime_without_io(); + rt.block_on(async { + let mut options = ServerOptions::new(); + options.max_instances(255); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +// Runtime without `enable_io` so it has no IO driver set. +fn runtime_without_io() -> Runtime { + Builder::new_current_thread().build().unwrap() +} diff --git a/tokio/tests/no_rt.rs b/tokio/tests/no_rt.rs index 64e56f4d4..89c7ce0aa 100644 --- a/tokio/tests/no_rt.rs +++ b/tokio/tests/no_rt.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use tokio::net::TcpStream; use tokio::sync::oneshot; diff --git a/tokio/tests/process_smoke.rs b/tokio/tests/process_smoke.rs index fae5793fa..635b951a0 100644 --- a/tokio/tests/process_smoke.rs +++ b/tokio/tests/process_smoke.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi cannot run system commands use tokio::process::Command; use tokio_test::assert_ok; diff --git a/tokio/tests/rt_basic.rs b/tokio/tests/rt_basic.rs index 50b7e9732..73bafdc4d 100644 --- a/tokio/tests/rt_basic.rs +++ b/tokio/tests/rt_basic.rs @@ -178,6 +178,7 @@ fn drop_tasks_in_context() { } #[test] +#[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] #[should_panic(expected = "boom")] fn wake_in_drop_after_panic() { let (tx, rx) = oneshot::channel::<()>(); @@ -238,6 +239,7 @@ fn spawn_two() { } } +#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")] #[test] fn spawn_remote() { let rt = rt(); @@ -274,6 +276,7 @@ fn spawn_remote() { } #[test] +#[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] #[should_panic( expected = "A Tokio 1.x context was found, but timers are disabled. Call `enable_time` on the runtime builder to enable timers." )] @@ -312,6 +315,7 @@ mod unstable { } #[test] + #[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] fn spawns_do_nothing() { use std::sync::Arc; @@ -340,6 +344,7 @@ mod unstable { } #[test] + #[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] fn shutdown_all_concurrent_block_on() { const N: usize = 2; use std::sync::{mpsc, Arc}; diff --git a/tokio/tests/rt_common.rs b/tokio/tests/rt_common.rs index 14e190959..7ff6ae3f1 100644 --- a/tokio/tests/rt_common.rs +++ b/tokio/tests/rt_common.rs @@ -18,6 +18,7 @@ macro_rules! rt_test { } } + #[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads mod threaded_scheduler_4_threads { $($t)* @@ -31,6 +32,7 @@ macro_rules! rt_test { } } + #[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads mod threaded_scheduler_1_thread { $($t)* @@ -55,18 +57,30 @@ fn send_sync_bound() { } rt_test! { - use tokio::net::{TcpListener, TcpStream, UdpSocket}; + #[cfg(not(target_os="wasi"))] + use tokio::net::{TcpListener, TcpStream}; + #[cfg(not(target_os="wasi"))] use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use tokio::runtime::Runtime; use tokio::sync::oneshot; use tokio::{task, time}; - use tokio_test::{assert_err, assert_ok}; + + #[cfg(not(target_os="wasi"))] + use tokio_test::assert_err; + use tokio_test::assert_ok; use futures::future::poll_fn; use std::future::Future; use std::pin::Pin; - use std::sync::{mpsc, Arc}; + + #[cfg(not(target_os="wasi"))] + use std::sync::mpsc; + + use std::sync::Arc; use std::task::{Context, Poll}; + + #[cfg(not(target_os="wasi"))] use std::thread; use std::time::{Duration, Instant}; @@ -83,6 +97,7 @@ rt_test! { } + #[cfg(not(target_os="wasi"))] #[test] fn block_on_async() { let rt = rt(); @@ -164,6 +179,7 @@ rt_test! { assert_eq!(out, "ZOMG"); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_many_from_block_on() { use tokio::sync::mpsc; @@ -214,6 +230,7 @@ rt_test! { } } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_many_from_task() { use tokio::sync::mpsc; @@ -329,6 +346,7 @@ rt_test! { assert_eq!(out, "ZOMG"); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn complete_block_on_under_load() { let rt = rt(); @@ -352,6 +370,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn complete_task_under_load() { let rt = rt(); @@ -381,6 +400,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_from_other_thread_idle() { let rt = rt(); @@ -401,6 +421,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_from_other_thread_under_load() { let rt = rt(); @@ -461,6 +482,7 @@ rt_test! { assert!(now.elapsed() >= dur); } + #[cfg(not(target_os="wasi"))] // Wasi does not support bind #[test] fn block_on_socket() { let rt = rt(); @@ -481,6 +503,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_from_blocking() { let rt = rt(); @@ -496,6 +519,7 @@ rt_test! { assert_eq!(out, "hello") } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_blocking_from_blocking() { let rt = rt(); @@ -511,6 +535,7 @@ rt_test! { assert_eq!(out, "hello") } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn sleep_from_blocking() { let rt = rt(); @@ -531,6 +556,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support bind #[test] fn socket_from_blocking() { let rt = rt(); @@ -554,6 +580,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn always_active_parker() { // This test it to show that we will always have @@ -600,6 +627,7 @@ rt_test! { // concern. There also isn't a great/obvious solution to take. For now, the // test is disabled. #[cfg(not(windows))] + #[cfg(not(target_os="wasi"))] // Wasi does not support bind or threads fn io_driver_called_when_under_load() { let rt = rt(); @@ -635,6 +663,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn client_server_block_on() { let rt = rt(); @@ -646,6 +675,7 @@ rt_test! { assert_err!(rx.try_recv()); } + #[cfg_attr(target_os = "wasi", ignore = "Wasi does not support threads or panic recovery")] #[test] fn panic_in_task() { let rt = rt(); @@ -674,11 +704,13 @@ rt_test! { #[test] #[should_panic] + #[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] fn panic_in_block_on() { let rt = rt(); rt.block_on(async { panic!() }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads async fn yield_once() { let mut yielded = false; poll_fn(|cx| { @@ -816,8 +848,10 @@ rt_test! { assert!(drop_triggered.load(Ordering::Relaxed)); } + #[cfg(not(target_os="wasi"))] // Wasi doesn't support UDP or bind() #[test] fn io_notify_while_shutting_down() { + use tokio::net::UdpSocket; use std::net::Ipv6Addr; use std::sync::Arc; @@ -851,6 +885,7 @@ rt_test! { } } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn shutdown_timeout() { let (tx, rx) = oneshot::channel(); @@ -868,6 +903,7 @@ rt_test! { Arc::try_unwrap(runtime).unwrap().shutdown_timeout(Duration::from_millis(100)); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn shutdown_timeout_0() { let runtime = rt(); @@ -899,6 +935,7 @@ rt_test! { // See https://github.com/rust-lang/rust/issues/74875 #[test] #[cfg(not(windows))] + #[cfg_attr(target_os = "wasi", ignore = "Wasi does not support threads")] fn runtime_in_thread_local() { use std::cell::RefCell; use std::thread; @@ -918,6 +955,7 @@ rt_test! { }).join().unwrap(); } + #[cfg(not(target_os="wasi"))] // Wasi does not support bind async fn client_server(tx: mpsc::Sender<()>) { let server = assert_ok!(TcpListener::bind("127.0.0.1:0").await); @@ -942,6 +980,7 @@ rt_test! { tx.send(()).unwrap(); } + #[cfg(not(target_os = "wasi"))] // Wasi does not support bind #[test] fn local_set_block_on_socket() { let rt = rt(); @@ -963,6 +1002,7 @@ rt_test! { }); } + #[cfg(not(target_os = "wasi"))] // Wasi does not support bind #[test] fn local_set_client_server_block_on() { let rt = rt(); @@ -976,6 +1016,7 @@ rt_test! { assert_err!(rx.try_recv()); } + #[cfg(not(target_os = "wasi"))] // Wasi does not support bind async fn client_server_local(tx: mpsc::Sender<()>) { let server = assert_ok!(TcpListener::bind("127.0.0.1:0").await); @@ -1096,7 +1137,7 @@ rt_test! { let (spawned_tx, mut spawned_rx) = mpsc::unbounded_channel(); let mut tasks = vec![]; - // Spawn a bunch of tasks that ping ping between each other to + // Spawn a bunch of tasks that ping-pong between each other to // saturate the runtime. for _ in 0..NUM { let (tx1, mut rx1) = mpsc::unbounded_channel(); diff --git a/tokio/tests/rt_handle_block_on.rs b/tokio/tests/rt_handle_block_on.rs index 5c1d533a0..e8f3993aa 100644 --- a/tokio/tests/rt_handle_block_on.rs +++ b/tokio/tests/rt_handle_block_on.rs @@ -10,9 +10,10 @@ use std::time::Duration; use tokio::runtime::{Handle, Runtime}; use tokio::sync::mpsc; -use tokio::task::spawn_blocking; -use tokio::{fs, net, time}; +#[cfg(not(target_os = "wasi"))] +use tokio::{net, time}; +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads macro_rules! multi_threaded_rt_test { ($($t:tt)*) => { mod threaded_scheduler_4_threads_only { @@ -45,6 +46,7 @@ macro_rules! multi_threaded_rt_test { } } +#[cfg(not(target_os = "wasi"))] macro_rules! rt_test { ($($t:tt)*) => { mod current_thread_scheduler { @@ -124,7 +126,9 @@ fn unbounded_mpsc_channel() { }) } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support file operations or bind rt_test! { + use tokio::fs; // ==== spawn blocking futures ====== #[test] @@ -156,6 +160,7 @@ rt_test! { #[test] fn basic_spawn_blocking() { + use tokio::task::spawn_blocking; let rt = rt(); let _enter = rt.enter(); @@ -171,6 +176,7 @@ rt_test! { #[test] fn spawn_blocking_after_shutdown_fails() { + use tokio::task::spawn_blocking; let rt = rt(); let _enter = rt.enter(); rt.shutdown_timeout(Duration::from_secs(1000)); @@ -187,6 +193,7 @@ rt_test! { #[test] fn spawn_blocking_started_before_shutdown_continues() { + use tokio::task::spawn_blocking; let rt = rt(); let _enter = rt.enter(); @@ -412,6 +419,7 @@ rt_test! { } } +#[cfg(not(target_os = "wasi"))] multi_threaded_rt_test! { #[cfg(unix)] #[test] @@ -474,6 +482,7 @@ multi_threaded_rt_test! { // ==== utils ====== /// Create a new multi threaded runtime +#[cfg(not(target_os = "wasi"))] fn new_multi_thread(n: usize) -> Runtime { tokio::runtime::Builder::new_multi_thread() .worker_threads(n) @@ -507,6 +516,7 @@ where f(); } + #[cfg(not(target_os = "wasi"))] { println!("multi thread (1 thread) runtime"); @@ -519,6 +529,7 @@ where f(); } + #[cfg(not(target_os = "wasi"))] { println!("multi thread (4 threads) runtime"); diff --git a/tokio/tests/rt_metrics.rs b/tokio/tests/rt_metrics.rs index e86beb914..3b8070533 100644 --- a/tokio/tests/rt_metrics.rs +++ b/tokio/tests/rt_metrics.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(all(feature = "full", tokio_unstable))] +#![cfg(all(feature = "full", tokio_unstable, not(target_os = "wasi")))] use tokio::runtime::Runtime; use tokio::time::{self, Duration}; diff --git a/tokio/tests/rt_panic.rs b/tokio/tests/rt_panic.rs index 900147362..1488c9dcd 100644 --- a/tokio/tests/rt_panic.rs +++ b/tokio/tests/rt_panic.rs @@ -1,5 +1,6 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "full")] +#![cfg(not(target_os = "wasi"))] // Wasi doesn't support panic recovery use futures::future; use std::error::Error; diff --git a/tokio/tests/rt_threaded.rs b/tokio/tests/rt_threaded.rs index b2f84fd33..187cd557b 100644 --- a/tokio/tests/rt_threaded.rs +++ b/tokio/tests/rt_threaded.rs @@ -1,9 +1,9 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; -use tokio::runtime::{self, Runtime}; +use tokio::runtime; use tokio::sync::oneshot; use tokio_test::{assert_err, assert_ok}; @@ -539,6 +539,6 @@ async fn test_block_in_place4() { tokio::task::block_in_place(|| {}); } -fn rt() -> Runtime { - Runtime::new().unwrap() +fn rt() -> runtime::Runtime { + runtime::Runtime::new().unwrap() } diff --git a/tokio/tests/signal_no_rt.rs b/tokio/tests/signal_no_rt.rs index b0f32b2d1..db284e165 100644 --- a/tokio/tests/signal_no_rt.rs +++ b/tokio/tests/signal_no_rt.rs @@ -4,6 +4,7 @@ use tokio::signal::unix::{signal, SignalKind}; +#[cfg_attr(target_os = "wasi", ignore = "Wasi does not support panic recovery")] #[test] #[should_panic] fn no_runtime_panics_creating_signals() { diff --git a/tokio/tests/sync_barrier.rs b/tokio/tests/sync_barrier.rs index 5fe7ba98c..afd3ef16b 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] 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 b38b63800..7ca8281ce 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::broadcast; diff --git a/tokio/tests/sync_errors.rs b/tokio/tests/sync_errors.rs index 2eac585d4..3dd42fad2 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] 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 abbfa9d7f..a1510f57d 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; use tokio::sync::mpsc; @@ -88,7 +88,7 @@ async fn reserve_disarm() { } #[tokio::test] -#[cfg(feature = "full")] +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads async fn send_recv_stream_with_buffer() { use tokio_stream::StreamExt; @@ -192,7 +192,7 @@ async fn async_send_recv_unbounded() { } #[tokio::test] -#[cfg(feature = "full")] +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads async fn send_recv_stream_unbounded() { use tokio_stream::StreamExt; @@ -453,7 +453,7 @@ fn unconsumed_messages_are_dropped() { } #[test] -#[cfg(feature = "full")] +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads fn blocking_recv() { let (tx, mut rx) = mpsc::channel::(1); @@ -478,7 +478,7 @@ async fn blocking_recv_async() { } #[test] -#[cfg(feature = "full")] +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads fn blocking_send() { let (tx, mut rx) = mpsc::channel::(1); diff --git a/tokio/tests/sync_mutex.rs b/tokio/tests/sync_mutex.rs index bcd9b1eba..ef60a348f 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] 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 98ced1583..5fb48ec8a 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] 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 4236a91d1..4bbc71c50 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::Notify; diff --git a/tokio/tests/sync_oneshot.rs b/tokio/tests/sync_oneshot.rs index 7714f80be..76194abae 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; use tokio::sync::oneshot; diff --git a/tokio/tests/sync_panic.rs b/tokio/tests/sync_panic.rs index 9aea9a89e..fb81ad8b4 100644 --- a/tokio/tests/sync_panic.rs +++ b/tokio/tests/sync_panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] use std::error::Error; use tokio::{ diff --git a/tokio/tests/sync_rwlock.rs b/tokio/tests/sync_rwlock.rs index 01bbdbb5a..ff10fa324 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] use tokio::test as maybe_tokio_test; use std::task::Poll; @@ -172,7 +172,7 @@ async fn write_order() { } // A single RwLock is contested by tasks in multiple threads -#[cfg(feature = "full")] +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread", worker_threads = 8)] async fn multithreaded() { use futures::stream::{self, StreamExt}; diff --git a/tokio/tests/sync_semaphore.rs b/tokio/tests/sync_semaphore.rs index d92666500..fe5807c8e 100644 --- a/tokio/tests/sync_semaphore.rs +++ b/tokio/tests/sync_semaphore.rs @@ -1,6 +1,6 @@ #![cfg(feature = "sync")] -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; @@ -65,7 +65,7 @@ fn forget() { #[tokio::test] #[cfg(feature = "full")] -async fn stresstest() { +async fn stress_test() { let sem = Arc::new(Semaphore::new(5)); let mut join_handles = Vec::new(); for _ in 0..1000 { diff --git a/tokio/tests/sync_semaphore_owned.rs b/tokio/tests/sync_semaphore_owned.rs index 98c20d7b0..c01add3ca 100644 --- a/tokio/tests/sync_semaphore_owned.rs +++ b/tokio/tests/sync_semaphore_owned.rs @@ -1,6 +1,6 @@ #![cfg(feature = "sync")] -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; @@ -91,7 +91,7 @@ fn forget() { #[tokio::test] #[cfg(feature = "full")] -async fn stresstest() { +async fn stress_test() { let sem = Arc::new(Semaphore::new(5)); let mut join_handles = Vec::new(); for _ in 0..1000 { diff --git a/tokio/tests/sync_watch.rs b/tokio/tests/sync_watch.rs index d47f0df73..13aecc259 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(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::watch; diff --git a/tokio/tests/task_abort.rs b/tokio/tests/task_abort.rs index fe6b50cd4..2c165d003 100644 --- a/tokio/tests/task_abort.rs +++ b/tokio/tests/task_abort.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery use std::sync::Arc; use std::thread::sleep; diff --git a/tokio/tests/task_blocking.rs b/tokio/tests/task_blocking.rs index ee7e78ad4..46307b03f 100644 --- a/tokio/tests/task_blocking.rs +++ b/tokio/tests/task_blocking.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads use tokio::{runtime, task}; use tokio_test::assert_ok; diff --git a/tokio/tests/task_builder.rs b/tokio/tests/task_builder.rs index 1499abf19..78329ff26 100644 --- a/tokio/tests/task_builder.rs +++ b/tokio/tests/task_builder.rs @@ -11,6 +11,7 @@ mod tests { let result = Builder::new() .name("name") .spawn(async { "task executed" }) + .unwrap() .await; assert_eq!(result.unwrap(), "task executed"); @@ -21,6 +22,7 @@ mod tests { let result = Builder::new() .name("name") .spawn_blocking(|| "task executed") + .unwrap() .await; assert_eq!(result.unwrap(), "task executed"); @@ -34,6 +36,7 @@ mod tests { Builder::new() .name("name") .spawn_local(async move { unsend_data }) + .unwrap() .await }) .await; @@ -43,14 +46,20 @@ mod tests { #[test] async fn spawn_without_name() { - let result = Builder::new().spawn(async { "task executed" }).await; + let result = Builder::new() + .spawn(async { "task executed" }) + .unwrap() + .await; assert_eq!(result.unwrap(), "task executed"); } #[test] async fn spawn_blocking_without_name() { - let result = Builder::new().spawn_blocking(|| "task executed").await; + let result = Builder::new() + .spawn_blocking(|| "task executed") + .unwrap() + .await; assert_eq!(result.unwrap(), "task executed"); } @@ -59,7 +68,12 @@ mod tests { async fn spawn_local_without_name() { let unsend_data = Rc::new("task executed"); let result = LocalSet::new() - .run_until(async move { Builder::new().spawn_local(async move { unsend_data }).await }) + .run_until(async move { + Builder::new() + .spawn_local(async move { unsend_data }) + .unwrap() + .await + }) .await; assert_eq!(*result.unwrap(), "task executed"); diff --git a/tokio/tests/task_local.rs b/tokio/tests/task_local.rs index 4e33f29be..f0da060b1 100644 --- a/tokio/tests/task_local.rs +++ b/tokio/tests/task_local.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; diff --git a/tokio/tests/task_local_set.rs b/tokio/tests/task_local_set.rs index 2bd5b17b8..77920a447 100644 --- a/tokio/tests/task_local_set.rs +++ b/tokio/tests/task_local_set.rs @@ -6,14 +6,19 @@ use futures::{ FutureExt, }; -use tokio::runtime::{self, Runtime}; +use tokio::runtime; use tokio::sync::{mpsc, oneshot}; use tokio::task::{self, LocalSet}; use tokio::time; +#[cfg(not(target_os = "wasi"))] use std::cell::Cell; -use std::sync::atomic::Ordering::{self, SeqCst}; -use std::sync::atomic::{AtomicBool, AtomicUsize}; +use std::sync::atomic::AtomicBool; +#[cfg(not(target_os = "wasi"))] +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +#[cfg(not(target_os = "wasi"))] +use std::sync::atomic::Ordering::SeqCst; use std::time::Duration; #[tokio::test(flavor = "current_thread")] @@ -25,6 +30,7 @@ async fn local_basic_scheduler() { .await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn local_threadpool() { thread_local! { @@ -45,6 +51,7 @@ async fn local_threadpool() { .await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn localset_future_threadpool() { thread_local! { @@ -60,6 +67,7 @@ async fn localset_future_threadpool() { local.await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn localset_future_timers() { static RAN1: AtomicBool = AtomicBool::new(false); @@ -104,6 +112,7 @@ async fn localset_future_drives_all_local_futs() { assert!(RAN3.load(Ordering::SeqCst)); } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn local_threadpool_timer() { // This test ensures that runtime services like the timer are properly @@ -126,7 +135,23 @@ async fn local_threadpool_timer() { }) .await; } +#[test] +fn enter_guard_spawn() { + let local = LocalSet::new(); + let _guard = local.enter(); + // Run the local task set. + let join = task::spawn_local(async { true }); + let rt = runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + local.block_on(&rt, async move { + assert!(join.await.unwrap()); + }); +} + +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support panic recovery #[test] // This will panic, since the thread that calls `block_on` cannot use // in-place blocking inside of `block_on`. @@ -153,6 +178,7 @@ fn local_threadpool_blocking_in_place() { }); } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn local_threadpool_blocking_run() { thread_local! { @@ -181,6 +207,7 @@ async fn local_threadpool_blocking_run() { .await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn all_spawns_are_local() { use futures::future; @@ -207,6 +234,7 @@ async fn all_spawns_are_local() { .await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn nested_spawn_is_local() { thread_local! { @@ -242,6 +270,7 @@ async fn nested_spawn_is_local() { .await; } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support threads #[test] fn join_local_future_elsewhere() { thread_local! { @@ -355,6 +384,10 @@ fn with_timeout(timeout: Duration, f: impl FnOnce() + Send + 'static) { thread.join().expect("test thread should not panic!") } +#[cfg_attr( + target_os = "wasi", + ignore = "`unwrap()` in `with_timeout()` panics on Wasi" +)] #[test] fn drop_cancels_remote_tasks() { // This test reproduces issue #1885. @@ -377,6 +410,10 @@ fn drop_cancels_remote_tasks() { }); } +#[cfg_attr( + target_os = "wasi", + ignore = "FIXME: `task::spawn_local().await.unwrap()` panics on Wasi" +)] #[test] fn local_tasks_wake_join_all() { // This test reproduces issue #2460. @@ -398,6 +435,7 @@ fn local_tasks_wake_join_all() { }); } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support panic recovery #[test] fn local_tasks_are_polled_after_tick() { // This test depends on timing, so we run it up to five times. @@ -414,6 +452,7 @@ fn local_tasks_are_polled_after_tick() { local_tasks_are_polled_after_tick_inner(); } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support panic recovery #[tokio::main(flavor = "current_thread")] async fn local_tasks_are_polled_after_tick_inner() { // Reproduces issues #1899 and #1900 @@ -540,7 +579,7 @@ mod unstable { } } -fn rt() -> Runtime { +fn rt() -> runtime::Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() .build() diff --git a/tokio/tests/task_panic.rs b/tokio/tests/task_panic.rs new file mode 100644 index 000000000..661bbe236 --- /dev/null +++ b/tokio/tests/task_panic.rs @@ -0,0 +1,92 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(target_os = "wasi")))] + +use futures::future; +use std::error::Error; +use tokio::{runtime::Builder, spawn, task}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn local_set_block_on_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(|| { + let rt = Builder::new_current_thread().enable_all().build().unwrap(); + let local = task::LocalSet::new(); + + rt.block_on(async { + local.block_on(&rt, future::pending::<()>()); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn spawn_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(|| { + spawn(future::pending::<()>()); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn local_key_sync_scope_panic_caller() -> Result<(), Box> { + tokio::task_local! { + static NUMBER: u32; + } + + let panic_location_file = test_panic(|| { + NUMBER.sync_scope(1, || { + NUMBER.with(|_| { + let _ = NUMBER.sync_scope(1, || {}); + }); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn local_key_with_panic_caller() -> Result<(), Box> { + tokio::task_local! { + static NUMBER: u32; + } + + let panic_location_file = test_panic(|| { + NUMBER.with(|_| {}); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn local_key_get_panic_caller() -> Result<(), Box> { + tokio::task_local! { + static NUMBER: u32; + } + + let panic_location_file = test_panic(|| { + NUMBER.get(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} diff --git a/tokio/tests/tcp_accept.rs b/tokio/tests/tcp_accept.rs index 5ffb946f3..d547c48da 100644 --- a/tokio/tests/tcp_accept.rs +++ b/tokio/tests/tcp_accept.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::net::{TcpListener, TcpStream}; use tokio::sync::{mpsc, oneshot}; diff --git a/tokio/tests/tcp_connect.rs b/tokio/tests/tcp_connect.rs index cbe68fa27..269ba8ef6 100644 --- a/tokio/tests/tcp_connect.rs +++ b/tokio/tests/tcp_connect.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot; diff --git a/tokio/tests/tcp_echo.rs b/tokio/tests/tcp_echo.rs index 5bb7ff0ac..dfd4dd7b9 100644 --- a/tokio/tests/tcp_echo.rs +++ b/tokio/tests/tcp_echo.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; diff --git a/tokio/tests/tcp_into_split.rs b/tokio/tests/tcp_into_split.rs index 2e06643a0..2a030691f 100644 --- a/tokio/tests/tcp_into_split.rs +++ b/tokio/tests/tcp_into_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use std::io::{Error, ErrorKind, Result}; use std::io::{Read, Write}; diff --git a/tokio/tests/tcp_into_std.rs b/tokio/tests/tcp_into_std.rs index 4bf24c14d..58996f75c 100644 --- a/tokio/tests/tcp_into_std.rs +++ b/tokio/tests/tcp_into_std.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use std::io::Read; use std::io::Result; diff --git a/tokio/tests/tcp_peek.rs b/tokio/tests/tcp_peek.rs index aecc0ac19..606e23e7c 100644 --- a/tokio/tests/tcp_peek.rs +++ b/tokio/tests/tcp_peek.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::io::AsyncReadExt; use tokio::net::TcpStream; diff --git a/tokio/tests/tcp_shutdown.rs b/tokio/tests/tcp_shutdown.rs index 536a16130..86c1485a4 100644 --- a/tokio/tests/tcp_shutdown.rs +++ b/tokio/tests/tcp_shutdown.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; diff --git a/tokio/tests/tcp_socket.rs b/tokio/tests/tcp_socket.rs index 303041650..faead95c0 100644 --- a/tokio/tests/tcp_socket.rs +++ b/tokio/tests/tcp_socket.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use std::time::Duration; use tokio::net::TcpSocket; diff --git a/tokio/tests/tcp_split.rs b/tokio/tests/tcp_split.rs index 7171dac46..a1fc39af2 100644 --- a/tokio/tests/tcp_split.rs +++ b/tokio/tests/tcp_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use std::io::Result; use std::io::{Read, Write}; diff --git a/tokio/tests/tcp_stream.rs b/tokio/tests/tcp_stream.rs index 0b5d12ae8..f1048f938 100644 --- a/tokio/tests/tcp_stream.rs +++ b/tokio/tests/tcp_stream.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support bind use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest}; use tokio::net::{TcpListener, TcpStream}; diff --git a/tokio/tests/time_panic.rs b/tokio/tests/time_panic.rs index 3ed936f52..cb06f4452 100644 --- a/tokio/tests/time_panic.rs +++ b/tokio/tests/time_panic.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery use futures::future; use std::error::Error; diff --git a/tokio/tests/time_pause.rs b/tokio/tests/time_pause.rs index 02e050a2d..6251b4b82 100644 --- a/tokio/tests/time_pause.rs +++ b/tokio/tests/time_pause.rs @@ -4,7 +4,10 @@ use rand::SeedableRng; use rand::{rngs::StdRng, Rng}; use tokio::time::{self, Duration, Instant, Sleep}; -use tokio_test::{assert_elapsed, assert_err, assert_pending, assert_ready, assert_ready_eq, task}; +use tokio_test::{assert_elapsed, assert_pending, assert_ready, assert_ready_eq, task}; + +#[cfg(not(target_os = "wasi"))] +use tokio_test::assert_err; use std::{ future::Future, @@ -26,12 +29,14 @@ async fn pause_time_in_task() { t.await.unwrap(); } +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread", worker_threads = 1)] #[should_panic] async fn pause_time_in_main_threads() { tokio::time::pause(); } +#[cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn pause_time_in_spawn_threads() { let t = tokio::spawn(async { diff --git a/tokio/tests/time_rt.rs b/tokio/tests/time_rt.rs index 23367be41..3652b0bc4 100644 --- a/tokio/tests/time_rt.rs +++ b/tokio/tests/time_rt.rs @@ -5,6 +5,7 @@ use tokio::time::*; use std::sync::mpsc; +#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))] // Wasi doesn't support threads #[test] fn timer_with_threaded_runtime() { use tokio::runtime::Runtime; diff --git a/tokio/tests/time_sleep.rs b/tokio/tests/time_sleep.rs index 97cadecb0..787f6329a 100644 --- a/tokio/tests/time_sleep.rs +++ b/tokio/tests/time_sleep.rs @@ -168,6 +168,7 @@ async fn reset_sleep_to_past() { assert_ready!(sleep.poll()); } +#[cfg(not(target_os = "wasi"))] // Wasi doesn't support panic recovery #[test] #[should_panic] fn creating_sleep_outside_of_context() { diff --git a/tokio/tests/time_timeout.rs b/tokio/tests/time_timeout.rs index a1ff51e7d..ec871cf62 100644 --- a/tokio/tests/time_timeout.rs +++ b/tokio/tests/time_timeout.rs @@ -17,6 +17,7 @@ async fn simultaneous_deadline_future_completion() { assert_ready_ok!(fut.poll()); } +#[cfg_attr(target_os = "wasi", ignore = "FIXME: `fut.poll()` panics on Wasi")] #[tokio::test] async fn completed_future_past_deadline() { // Wrap it with a deadline diff --git a/tokio/tests/udp.rs b/tokio/tests/udp.rs index ec2a1e961..5a29525d4 100644 --- a/tokio/tests/udp.rs +++ b/tokio/tests/udp.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support bind or UDP use futures::future::poll_fn; use std::io; diff --git a/tokio/tests/unwindsafe.rs b/tokio/tests/unwindsafe.rs index 09fe83945..23fc1b2a4 100644 --- a/tokio/tests/unwindsafe.rs +++ b/tokio/tests/unwindsafe.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi does not support panic recovery use std::panic::{RefUnwindSafe, UnwindSafe};