From 6d3f92dddc510e9276191cfab1b0432ce8248589 Mon Sep 17 00:00:00 2001 From: Richard Zak <1133882+rjzak@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:20:26 -0400 Subject: [PATCH 01/18] wasm: initial support for wasm32-wasi target (#4716) This adds initial, unstable, support for the wasm32-wasi target. Not all of Tokio's features are supported yet as WASI's non-blocking APIs are still limited. Refs: tokio-rs/tokio#4827 --- .github/workflows/ci.yml | 20 +- tests-integration/tests/macros_main.rs | 6 +- tests-integration/tests/macros_select.rs | 1 + tests-integration/tests/process_stdio.rs | 2 +- tokio-stream/Cargo.toml | 1 + tokio-stream/tests/stream_panic.rs | 2 +- tokio-stream/tests/stream_stream_map.rs | 1 + tokio-util/src/lib.rs | 1 + tokio-util/src/task/mod.rs | 2 + tokio-util/tests/context.rs | 1 + tokio-util/tests/io_sync_bridge.rs | 1 + tokio-util/tests/panic.rs | 2 +- tokio-util/tests/spawn_pinned.rs | 1 + tokio-util/tests/time_delay_queue.rs | 2 + tokio-util/tests/udp.rs | 1 + tokio/Cargo.toml | 13 +- tokio/src/coop.rs | 2 +- tokio/src/io/driver/mod.rs | 10 + tokio/src/io/mod.rs | 2 + tokio/src/lib.rs | 14 ++ tokio/src/macros/cfg.rs | 29 ++- tokio/src/net/mod.rs | 14 +- tokio/src/net/tcp/listener.rs | 152 ++++++++----- tokio/src/net/tcp/mod.rs | 4 +- tokio/src/net/tcp/stream.rs | 270 +++++++++++++---------- tokio/src/park/thread.rs | 4 + tokio/src/runtime/blocking/pool.rs | 1 + tokio/src/runtime/builder.rs | 24 +- tokio/src/runtime/mod.rs | 75 ++++--- tokio/src/runtime/spawner.rs | 18 +- tokio/src/sync/tests/atomic_waker.rs | 2 +- tokio/src/sync/tests/notify.rs | 2 +- tokio/src/sync/tests/semaphore_batch.rs | 2 +- tokio/src/task/mod.rs | 6 +- tokio/src/time/driver/tests/mod.rs | 2 + tokio/src/util/linked_list.rs | 1 + tokio/tests/async_send_sync.rs | 150 ++++++++----- tokio/tests/buffered.rs | 2 +- tokio/tests/fs.rs | 2 +- tokio/tests/fs_copy.rs | 2 +- tokio/tests/fs_dir.rs | 2 +- tokio/tests/fs_file.rs | 2 +- tokio/tests/fs_link.rs | 2 +- tokio/tests/io_copy_bidirectional.rs | 2 +- tokio/tests/io_driver.rs | 3 +- tokio/tests/io_driver_drop.rs | 2 +- tokio/tests/io_fill_buf.rs | 2 +- tokio/tests/io_panic.rs | 2 +- tokio/tests/io_read.rs | 2 +- tokio/tests/io_split.rs | 2 +- tokio/tests/io_take.rs | 2 +- tokio/tests/join_handle_panic.rs | 2 +- tokio/tests/macros_join.rs | 6 +- tokio/tests/macros_pin.rs | 4 +- tokio/tests/macros_rename_test.rs | 2 +- tokio/tests/macros_select.rs | 4 +- tokio/tests/macros_test.rs | 2 +- tokio/tests/macros_try_join.rs | 4 +- tokio/tests/net_bind_resource.rs | 2 +- tokio/tests/net_lookup_host.rs | 2 +- tokio/tests/no_rt.rs | 2 +- tokio/tests/process_smoke.rs | 2 +- tokio/tests/rt_basic.rs | 5 + tokio/tests/rt_common.rs | 47 +++- tokio/tests/rt_handle_block_on.rs | 15 +- tokio/tests/rt_metrics.rs | 2 +- tokio/tests/rt_panic.rs | 1 + tokio/tests/rt_threaded.rs | 8 +- tokio/tests/signal_no_rt.rs | 1 + tokio/tests/sync_barrier.rs | 2 +- tokio/tests/sync_broadcast.rs | 2 +- tokio/tests/sync_errors.rs | 2 +- tokio/tests/sync_mpsc.rs | 14 +- tokio/tests/sync_mutex.rs | 6 +- tokio/tests/sync_mutex_owned.rs | 6 +- tokio/tests/sync_notify.rs | 2 +- tokio/tests/sync_oneshot.rs | 6 +- tokio/tests/sync_panic.rs | 2 +- tokio/tests/sync_rwlock.rs | 8 +- tokio/tests/sync_semaphore.rs | 2 +- tokio/tests/sync_semaphore_owned.rs | 2 +- tokio/tests/sync_watch.rs | 2 +- tokio/tests/task_abort.rs | 2 +- tokio/tests/task_blocking.rs | 2 +- tokio/tests/task_local.rs | 2 +- tokio/tests/task_local_set.rs | 32 ++- tokio/tests/tcp_accept.rs | 2 +- tokio/tests/tcp_connect.rs | 2 +- tokio/tests/tcp_echo.rs | 2 +- tokio/tests/tcp_into_split.rs | 2 +- tokio/tests/tcp_into_std.rs | 2 +- tokio/tests/tcp_peek.rs | 2 +- tokio/tests/tcp_shutdown.rs | 2 +- tokio/tests/tcp_socket.rs | 2 +- tokio/tests/tcp_split.rs | 2 +- tokio/tests/tcp_stream.rs | 2 +- tokio/tests/time_panic.rs | 2 +- tokio/tests/time_pause.rs | 7 +- tokio/tests/time_rt.rs | 1 + tokio/tests/time_sleep.rs | 1 + tokio/tests/time_timeout.rs | 1 + tokio/tests/udp.rs | 2 +- tokio/tests/unwindsafe.rs | 2 +- 103 files changed, 698 insertions(+), 413 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78bdfbc7e..b290da585 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -489,9 +489,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/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..1ce41013c 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; 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-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/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/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/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/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 9228b3c64..b9aa68c8f 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -51,7 +51,6 @@ net = [ "mio/os-poll", "mio/os-ext", "mio/net", - "socket2", "winapi/fileapi", "winapi/handleapi", "winapi/namedpipeapi", @@ -112,11 +111,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 +150,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/src/coop.rs b/tokio/src/coop.rs index bde1888a3..eaedae9b1 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 { diff --git a/tokio/src/io/driver/mod.rs b/tokio/src/io/driver/mod.rs index 4b420e1c8..745c73baf 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), } @@ -300,6 +309,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/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/lib.rs b/tokio/src/lib.rs index c0d7e6252..4d9678c5d 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 diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index 45ae5f991..fd3556c30 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 )* @@ -247,6 +248,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 +277,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 +337,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 +454,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 +469,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/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..d14667a93 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. @@ -267,11 +272,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 +400,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..199ac5818 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 { @@ -244,6 +249,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 +1091,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 +1331,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/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/runtime/blocking/pool.rs b/tokio/src/runtime/blocking/pool.rs index f73868ee9..7dadacc07 100644 --- a/tokio/src/runtime/blocking/pool.rs +++ b/tokio/src/runtime/blocking/pool.rs @@ -105,6 +105,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, diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index 490bc96d5..8f477a940 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 @@ -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, diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index b9e7e2de9..46a63614e 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. @@ -480,7 +483,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), } } @@ -610,7 +613,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/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/task/mod.rs b/tokio/src/task/mod.rs index 8ea73fb48..37a68b6e3 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; diff --git a/tokio/src/time/driver/tests/mod.rs b/tokio/src/time/driver/tests/mod.rs index 3ac8c7564..005a2386a 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))] 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..17eba20ef 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}; 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..c1bf6d3f1 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] 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/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..da8dc6e3a 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); 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..72b646539 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; diff --git a/tokio/tests/sync_semaphore_owned.rs b/tokio/tests/sync_semaphore_owned.rs index 98c20d7b0..5780c0e10 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; 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_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..c2bb4848c 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 @@ -127,6 +136,7 @@ async fn local_threadpool_timer() { .await; } +#[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 +163,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 +192,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 +219,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 +255,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 +369,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 +395,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 +420,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 +437,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 +564,7 @@ mod unstable { } } -fn rt() -> Runtime { +fn rt() -> runtime::Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() .build() 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}; From de686b53556928e96bd2aa12700ac805f9d1a732 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Tue, 12 Jul 2022 16:51:28 -0400 Subject: [PATCH 02/18] runtime: fix typo in `shutdown_background` docs (#4829) --- tokio/src/runtime/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index 46a63614e..cf2b927d2 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -576,7 +576,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; From 3b6c74a40ad87837ab53c105a323c2807a4e1b38 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Tue, 12 Jul 2022 15:56:33 -0700 Subject: [PATCH 03/18] Make `task::Builder::spawn*` methods fallible (#4823) --- tokio/src/runtime/blocking/mod.rs | 2 +- tokio/src/runtime/blocking/pool.rs | 26 ++++++++++++++++--- tokio/src/runtime/handle.rs | 19 +++++++++----- tokio/src/task/builder.rs | 41 ++++++++++++++++++++---------- tokio/src/task/join_set.rs | 25 ++++++++++-------- tokio/tests/task_builder.rs | 20 ++++++++++++--- 6 files changed, 95 insertions(+), 38 deletions(-) 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 7dadacc07..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 } @@ -221,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 { @@ -231,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); @@ -262,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/handle.rs b/tokio/src/runtime/handle.rs index 14101070c..961668c0a 100644 --- a/tokio/src/runtime/handle.rs +++ b/tokio/src/runtime/handle.rs @@ -341,7 +341,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 +349,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 +370,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 +386,7 @@ impl HandleInner { ) }; - if was_spawned { + if spawn_result.is_ok() { Some(join_handle) } else { None @@ -394,7 +401,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 +431,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/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/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/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"); From 8e20cfb9efe41bbda85c2814c20a5bfd1070bce9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 13 Jul 2022 10:41:13 -0400 Subject: [PATCH 04/18] task: expand on cancellation of `spawn_blocking` (#4811) I was looking at these docs again and I was thinking the "cannot be cancelled" is misleading; let's talk about approaches to do so. --- tokio/src/task/blocking.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) 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 /// From 14fca343d58207f20cc23d7867cfa7a9b0b26576 Mon Sep 17 00:00:00 2001 From: gftea <1705787+gftea@users.noreply.github.com> Date: Wed, 13 Jul 2022 18:10:09 +0200 Subject: [PATCH 05/18] task: add `LocalSet::enter` (#4736) (#4765) --- tokio/src/task/local.rs | 125 ++++++++++++++++++++++++++-------- tokio/src/task/mod.rs | 2 +- tokio/tests/task_local_set.rs | 15 ++++ 3 files changed, 113 insertions(+), 29 deletions(-) diff --git a/tokio/src/task/local.rs b/tokio/src/task/local.rs index e3ba50a79..d37607a4b 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; @@ -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. @@ -579,7 +612,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() + }) } } @@ -645,8 +696,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 +821,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 +861,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 +888,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 37a68b6e3..d2b634de0 100644 --- a/tokio/src/task/mod.rs +++ b/tokio/src/task/mod.rs @@ -299,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/tests/task_local_set.rs b/tokio/tests/task_local_set.rs index c2bb4848c..77920a447 100644 --- a/tokio/tests/task_local_set.rs +++ b/tokio/tests/task_local_set.rs @@ -135,6 +135,21 @@ 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] From 5cf22e48c55b8195ae5c2f6b167b1da5b98a3d3e Mon Sep 17 00:00:00 2001 From: Noah Kennedy Date: Wed, 13 Jul 2022 11:35:28 -0500 Subject: [PATCH 06/18] chore: allow miri to use latest nightly once again (#4833) This reverts [#4825], as miri seems to be working again on the latest nightly. I had originally thought it was this issue with rustup which was to blame, but this seems to be wrong: https://github.com/rust-lang/rustup/issues/3031 [#4825]: https://github.com/tokio-rs/tokio/pull/4825 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b290da585..f7bc422e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,7 +195,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 From 922fc91408d68f0a6e66e14c99cb47a16e8653af Mon Sep 17 00:00:00 2001 From: Arnav Singh Date: Thu, 14 Jul 2022 02:32:28 -0700 Subject: [PATCH 07/18] task: propagate attributes on task-locals (#4837) --- tokio/src/task/task_local.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tokio/src/task/task_local.rs b/tokio/src/task/task_local.rs index e92b05fdc..404472367 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); From 6f048ca954aa5f285f1cf6310d447e7c079c1c3d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 18 Jul 2022 22:47:17 +0200 Subject: [PATCH 08/18] tokio: avoid temporary references in `linked_list::Link` impls (#4841) --- tokio/build.rs | 4 +++ tokio/src/io/driver/scheduled_io.rs | 12 +++++-- tokio/src/macros/addr_of.rs | 53 +++++++++++++++++++++++++++++ tokio/src/macros/mod.rs | 3 ++ tokio/src/runtime/task/core.rs | 12 +++++-- tokio/src/runtime/task/mod.rs | 3 +- tokio/src/sync/batch_semaphore.rs | 18 +++++----- tokio/src/sync/broadcast.rs | 12 +++++-- tokio/src/sync/notify.rs | 11 ++++-- tokio/src/time/driver/entry.rs | 13 +++++-- tokio/src/util/idle_notified_set.rs | 19 +++++------ 11 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 tokio/src/macros/addr_of.rs diff --git a/tokio/build.rs b/tokio/build.rs index 4c04a84d0..6dee6c377 100644 --- a/tokio/build.rs +++ b/tokio/build.rs @@ -7,6 +7,10 @@ fn main() { if ac.probe_rustc_version(1, 59) { autocfg::emit("tokio_const_thread_local") } + + if !ac.probe_rustc_version(1, 51) { + autocfg::emit("tokio_no_addr_of") + } } Err(e) => { 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/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/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/runtime/task/core.rs b/tokio/src/runtime/task/core.rs index 548c56da3..a512921c1 100644 --- a/tokio/src/runtime/task/core.rs +++ b/tokio/src/runtime/task/core.rs @@ -60,7 +60,7 @@ pub(crate) struct Header { /// Task state. pub(super) state: State, - pub(super) owned: UnsafeCell>, + pub(super) owned: linked_list::Pointers
, /// Pointer to next task, used with the injection queue. pub(super) queue_next: UnsafeCell>>, @@ -86,6 +86,14 @@ pub(crate) struct Header { pub(super) id: Option, } +generate_addr_of_methods! { + impl<> Header { + pub(super) unsafe fn addr_of_owned(self: NonNull) -> NonNull> { + &self.owned + } + } +} + unsafe impl Send for Header {} unsafe impl Sync for Header {} @@ -111,7 +119,7 @@ impl Cell { Box::new(Cell { header: Header { state, - owned: UnsafeCell::new(linked_list::Pointers::new()), + owned: linked_list::Pointers::new(), queue_next: UnsafeCell::new(None), vtable: raw::vtable::(), owner_id: UnsafeCell::new(0), diff --git a/tokio/src/runtime/task/mod.rs b/tokio/src/runtime/task/mod.rs index e73b3f35a..c2903f840 100644 --- a/tokio/src/runtime/task/mod.rs +++ b/tokio/src/runtime/task/mod.rs @@ -473,8 +473,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)) + Header::addr_of_owned(target) } } 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/time/driver/entry.rs b/tokio/src/time/driver/entry.rs index f0ea898e1..eb33e9a2a 100644 --- a/tokio/src/time/driver/entry.rs +++ b/tokio/src/time/driver/entry.rs @@ -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/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) } } From cbdba8bd32a7a6afd63d43afd5ab978b91745814 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Mon, 18 Jul 2022 22:57:45 +0200 Subject: [PATCH 09/18] net: add track_caller to public APIs (#4805) Functions that may panic can be annotated with #[track_caller] so that in the event of a panic, the function where the user called the panicking function is shown instead of the file and line within Tokio source. This change adds #[track_caller] to all the non-unstable public net APIs in the main tokio crate where the documentation describes how the function may panic due to incorrect context or inputs. Not all panic cases can have #[track_caller] applied fully as the callstack passes through a closure which isn't yet supported by the annotation (e.g. net functions called from outside a tokio runtime). Additionally, the documentation was updated to indicate additional cases in which the public net functions may panic (the same as the io functions). Tests are included to cover each potentially panicking function. Refs: #4413 --- tokio/src/io/poll_evented.rs | 2 + tokio/src/net/tcp/listener.rs | 4 +- tokio/src/net/tcp/stream.rs | 4 +- tokio/src/net/udp.rs | 2 + tokio/src/net/unix/datagram/socket.rs | 4 +- tokio/src/net/unix/listener.rs | 8 +- tokio/src/net/unix/stream.rs | 4 +- tokio/src/net/windows/named_pipe.rs | 1 + tokio/src/runtime/context.rs | 1 + tokio/tests/net_panic.rs | 188 ++++++++++++++++++++++++++ 10 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 tokio/tests/net_panic.rs diff --git a/tokio/src/io/poll_evented.rs b/tokio/src/io/poll_evented.rs index ce4c1426a..df071897d 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()) diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index d14667a93..23e9cd233 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -221,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)?; diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index 199ac5818..142f70bc6 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -186,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)?; 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..343012f1a 100644 --- a/tokio/src/net/windows/named_pipe.rs +++ b/tokio/src/net/windows/named_pipe.rs @@ -2020,6 +2020,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/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/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() +} From 228d4fce992124fc646a2e49606a65847166413c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 19 Jul 2022 20:28:20 +0200 Subject: [PATCH 10/18] runtime: move owned field to Trailer (#4843) --- tokio/src/runtime/task/core.rs | 52 ++++++++++++++++++++++++---------- tokio/src/runtime/task/list.rs | 2 +- tokio/src/runtime/task/mod.rs | 6 +++- tokio/src/runtime/task/raw.rs | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 17 deletions(-) diff --git a/tokio/src/runtime/task/core.rs b/tokio/src/runtime/task/core.rs index a512921c1..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: linked_list::Pointers

, - /// Pointer to next task, used with the injection queue. pub(super) queue_next: UnsafeCell>>, @@ -86,23 +84,26 @@ pub(crate) struct Header { pub(super) id: Option, } +unsafe impl Send for Header {} +unsafe impl Sync for Header {} + +/// 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<> Header { + impl<> Trailer { pub(super) unsafe fn addr_of_owned(self: NonNull) -> NonNull> { &self.owned } } } -unsafe impl Send for Header {} -unsafe impl Sync for Header {} - -/// Cold data is stored after the future. -pub(super) struct Trailer { - /// Consumer task waiting on completion of this task. - pub(super) waker: UnsafeCell>, -} - /// Either the future or the output. pub(super) enum Stage { Running(T), @@ -116,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: linked_list::Pointers::new(), queue_next: UnsafeCell::new(None), vtable: raw::vtable::(), owner_id: UnsafeCell::new(0), @@ -135,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 } } @@ -248,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 c2903f840..56911a731 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 { @@ -473,7 +477,7 @@ unsafe impl linked_list::Link for Task { } unsafe fn pointers(target: NonNull
) -> NonNull> { - Header::addr_of_owned(target) + 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 From 21900bd42b2c2a11a373ebb49982f831467ef814 Mon Sep 17 00:00:00 2001 From: dwattttt Date: Wed, 20 Jul 2022 07:13:39 -0400 Subject: [PATCH 11/18] net: add security flags to named pipe `ServerOptions` (#4845) --- tokio/Cargo.toml | 2 + tokio/src/net/windows/named_pipe.rs | 100 ++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index f8a94a578..f01e79418 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -57,6 +57,8 @@ net = [ "winapi/winbase", "winapi/winnt", "winapi/minwindef", + "winapi/accctrl", + "winapi/aclapi" ] process = [ "bytes", diff --git a/tokio/src/net/windows/named_pipe.rs b/tokio/src/net/windows/named_pipe.rs index 343012f1a..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. /// From 56be5286ee9548c483b91b7593ededcd34c5da0b Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 20 Jul 2022 14:05:23 +0200 Subject: [PATCH 12/18] ci: update required ci steps (#4846) --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7bc422e8..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 From 5288e1e144d33ace0070325b16029523b1db0ffe Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Wed, 20 Jul 2022 23:17:41 +0200 Subject: [PATCH 13/18] task: add track_caller to public APIs (#4848) Functions that may panic can be annotated with `#[track_caller]` so that in the event of a panic, the function where the user called the panicking function is shown instead of the file and line within Tokio source. This change adds `#[track_caller]` to all the public APIs in the task module of the tokio crate where the documentation describes how the function may panic due to incorrect context or inputs. In cases where `#[track_caller]` does not work, it has been left out. For example, it currently does not work on async functions, blocks, or closures. So any call stack that passes through one of these before reaching the actual panic is not able to show the calling site outside of tokio as the panic location. The following functions have call stacks that pass through closures: * `task::block_in_place` * `task::local::spawn_local` Tests are included to cover each potentially panicking function. The following functions already had `#[track_caller]` applied everywhere it was needed and only tests have been added: * `task::spawn` * `task::LocalKey::sync_scope` Refs: #4413 --- tokio/src/runtime/mod.rs | 1 + tokio/src/task/local.rs | 1 + tokio/src/task/task_local.rs | 2 + tokio/tests/task_panic.rs | 92 ++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 tokio/tests/task_panic.rs diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index cf2b927d2..c0e2d96db 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -475,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()); diff --git a/tokio/src/task/local.rs b/tokio/src/task/local.rs index d37607a4b..ac77a3571 100644 --- a/tokio/src/task/local.rs +++ b/tokio/src/task/local.rs @@ -487,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 diff --git a/tokio/src/task/task_local.rs b/tokio/src/task/task_local.rs index 404472367..2f6ab3023 100644 --- a/tokio/src/task/task_local.rs +++ b/tokio/src/task/task_local.rs @@ -287,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) } @@ -425,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/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(()) +} From 8c482a5d62015af00d5dcbc62a020d2fca539a38 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Thu, 21 Jul 2022 17:25:42 +0200 Subject: [PATCH 14/18] io: add track_caller caller to no-rt io driver (#4852) This change adds a case that was missing from the original PR, #4793. The `io::driver::Handle::current` function was only covered by `#[track_caller]` in the case that the `rt` feature is enabled, however it was missing in the case that the `rt` feture isn't enabled (in which case a panic would be more common). This particular case cannot be tested in the tokio tests as they always run with all features enabled. Refs: #4413 --- tokio/src/io/driver/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tokio/src/io/driver/mod.rs b/tokio/src/io/driver/mod.rs index 745c73baf..5d9e114a6 100644 --- a/tokio/src/io/driver/mod.rs +++ b/tokio/src/io/driver/mod.rs @@ -282,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) } From 1578575db8d0bc7838f7ea2ddbe7194ac2f4aa87 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Sat, 23 Jul 2022 10:40:04 -0700 Subject: [PATCH 15/18] process: use blocking threadpool for child stdio I/O (#4824) --- tokio/Cargo.toml | 2 +- tokio/src/io/blocking.rs | 3 +- tokio/src/io/poll_evented.rs | 2 +- tokio/src/lib.rs | 7 ++- tokio/src/macros/cfg.rs | 14 +++-- tokio/src/process/mod.rs | 22 ++++---- tokio/src/process/unix/mod.rs | 55 ++++++++++++++++--- tokio/src/process/windows.rs | 99 +++++++++++++++++++++++++++++------ tokio/src/runtime/builder.rs | 6 ++- tokio/src/runtime/handle.rs | 6 ++- 10 files changed, 172 insertions(+), 44 deletions(-) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index f01e79418..6e9846c72 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -69,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"] 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/poll_evented.rs b/tokio/src/io/poll_evented.rs index df071897d..25cece627 100644 --- a/tokio/src/io/poll_evented.rs +++ b/tokio/src/io/poll_evented.rs @@ -136,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 4d9678c5d..e8ddf16b3 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -431,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/cfg.rs b/tokio/src/macros/cfg.rs index fd3556c30..f72872213 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -70,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 )* } } @@ -79,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 @@ -97,7 +101,7 @@ macro_rules! cfg_io_driver_impl { $( #[cfg(any( feature = "net", - feature = "process", + all(unix, feature = "process"), all(unix, feature = "signal"), ))] $item @@ -110,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 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/builder.rs b/tokio/src/runtime/builder.rs index 8f477a940..08c0bbd3e 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -272,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(); diff --git a/tokio/src/runtime/handle.rs b/tokio/src/runtime/handle.rs index 961668c0a..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, From 62593be2610e9adf7de6ff2589e95f886fd228be Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Sat, 23 Jul 2022 19:44:00 +0200 Subject: [PATCH 16/18] doc: remove incorrect panic section for Builder::worker_threads (#4849) The documentation for the runtime `Builder::worker_threads` function incorrectly stated that it would panic if used when constructing a `current_thread` runtime. In truth, the call to the function has no effect. Since adding the described panic to the code could cause new panics in existing code using tokio, the documentation has been modified to describe the existing behavior. Refs: #4773 --- tokio/src/runtime/builder.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index 08c0bbd3e..5ba7b7abf 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -293,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 /// From 94a7eaed514eef001f19e5cd557378fcce3a3124 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Sat, 23 Jul 2022 16:27:35 -0700 Subject: [PATCH 17/18] Revert "tests: alter integration tests for stdio to not use a handrolled cat implementation (#4803)" (#4854) This reverts commit d8cad13fd92c4360031d5bac28514eebdf94ad1d. --- tests-integration/Cargo.toml | 2 -- tests-integration/src/bin/test-cat.rs | 24 +++++++++++++++--------- tests-integration/tests/process_stdio.rs | 14 ++------------ 3 files changed, 17 insertions(+), 23 deletions(-) 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/process_stdio.rs b/tests-integration/tests/process_stdio.rs index 1ce41013c..3f8ebfbfa 100644 --- a/tests-integration/tests/process_stdio.rs +++ b/tests-integration/tests/process_stdio.rs @@ -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 } From f3e340a35b306e926e78537a0dd65b2e9b9cdc89 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:26:01 -0400 Subject: [PATCH 18/18] chore: fix spelling mistakes (#4858) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- .github/workflows/stress-test.yml | 2 +- CONTRIBUTING.md | 2 +- SECURITY.md | 8 ++++---- tokio-test/src/macros.rs | 2 +- .../src/sync/cancellation_token/tree_node.rs | 4 ++-- .../src/sync/tests/loom_cancellation_token.rs | 2 +- tokio-util/src/time/delay_queue.rs | 2 +- tokio-util/tests/framed_write.rs | 2 +- tokio-util/tests/sync_cancellation_token.rs | 14 +++++++------- tokio/docs/reactor-refactor.md | 6 +++--- tokio/src/coop.rs | 2 +- tokio/src/runtime/builder.rs | 2 +- tokio/src/runtime/metrics/runtime.rs | 2 +- tokio/src/runtime/task/mod.rs | 2 +- tokio/src/runtime/task/waker.rs | 2 +- tokio/src/sync/watch.rs | 2 +- tokio/src/task/consume_budget.rs | 2 +- tokio/src/task/local.rs | 4 ++-- tokio/src/time/driver/entry.rs | 2 +- tokio/src/time/driver/tests/mod.rs | 8 ++++---- tokio/src/time/interval.rs | 2 +- tokio/tests/macros_join.rs | 2 +- tokio/tests/macros_try_join.rs | 2 +- tokio/tests/rt_common.rs | 2 +- tokio/tests/sync_semaphore.rs | 2 +- tokio/tests/sync_semaphore_owned.rs | 2 +- 26 files changed, 42 insertions(+), 42 deletions(-) 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/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/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/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/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/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/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 eaedae9b1..1ed1d17aa 100644 --- a/tokio/src/coop.rs +++ b/tokio/src/coop.rs @@ -215,7 +215,7 @@ mod test { } #[test] - fn bugeting() { + fn budgeting() { use futures::future::poll_fn; use tokio_test::*; diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index 5ba7b7abf..fff1b3ad6 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -738,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/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/task/mod.rs b/tokio/src/runtime/task/mod.rs index 56911a731..46aa62b14 100644 --- a/tokio/src/runtime/task/mod.rs +++ b/tokio/src/runtime/task/mod.rs @@ -369,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); 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/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/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/local.rs b/tokio/src/task/local.rs index ac77a3571..a5bd1bb88 100644 --- a/tokio/src/task/local.rs +++ b/tokio/src/task/local.rs @@ -160,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 @@ -652,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 diff --git a/tokio/src/time/driver/entry.rs b/tokio/src/time/driver/entry.rs index eb33e9a2a..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. diff --git a/tokio/src/time/driver/tests/mod.rs b/tokio/src/time/driver/tests/mod.rs index 005a2386a..fbe000f0f 100644 --- a/tokio/src/time/driver/tests/mod.rs +++ b/tokio/src/time/driver/tests/mod.rs @@ -291,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/tests/macros_join.rs b/tokio/tests/macros_join.rs index 17eba20ef..b2969557f 100644 --- a/tokio/tests/macros_join.rs +++ b/tokio/tests/macros_join.rs @@ -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_try_join.rs b/tokio/tests/macros_try_join.rs index c1bf6d3f1..876de0b49 100644 --- a/tokio/tests/macros_try_join.rs +++ b/tokio/tests/macros_try_join.rs @@ -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/rt_common.rs b/tokio/tests/rt_common.rs index da8dc6e3a..7ff6ae3f1 100644 --- a/tokio/tests/rt_common.rs +++ b/tokio/tests/rt_common.rs @@ -1137,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/sync_semaphore.rs b/tokio/tests/sync_semaphore.rs index 72b646539..fe5807c8e 100644 --- a/tokio/tests/sync_semaphore.rs +++ b/tokio/tests/sync_semaphore.rs @@ -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 5780c0e10..c01add3ca 100644 --- a/tokio/tests/sync_semaphore_owned.rs +++ b/tokio/tests/sync_semaphore_owned.rs @@ -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 {