diff options
Diffstat (limited to 'vendor/tokio/tests')
109 files changed, 8938 insertions, 1015 deletions
diff --git a/vendor/tokio/tests/_require_full.rs b/vendor/tokio/tests/_require_full.rs index 98455bede..4b9698afe 100644 --- a/vendor/tokio/tests/_require_full.rs +++ b/vendor/tokio/tests/_require_full.rs @@ -1,2 +1,8 @@ -#![cfg(not(feature = "full"))] +#[cfg(not(any(feature = "full", tokio_wasm)))] compile_error!("run main Tokio tests with `--features full`"); + +// CI sets `--cfg tokio_no_parking_lot` when trying to run tests with +// `parking_lot` disabled. This check prevents "silent failure" if `parking_lot` +// accidentally gets enabled. +#[cfg(all(tokio_no_parking_lot, feature = "parking_lot"))] +compile_error!("parking_lot feature enabled when it should not be"); diff --git a/vendor/tokio/tests/async_send_sync.rs b/vendor/tokio/tests/async_send_sync.rs index 01e608186..32c03a54b 100644 --- a/vendor/tokio/tests/async_send_sync.rs +++ b/vendor/tokio/tests/async_send_sync.rs @@ -4,13 +4,30 @@ use std::cell::Cell; use std::future::Future; -use std::io::{Cursor, SeekFrom}; +use std::io::SeekFrom; use std::net::SocketAddr; use std::pin::Pin; use std::rc::Rc; use tokio::net::TcpStream; use tokio::time::{Duration, Instant}; +// The names of these structs behaves better when sorted. +// Send: Yes, Sync: Yes +#[derive(Clone)] +struct YY {} + +// Send: Yes, Sync: No +#[derive(Clone)] +struct YN { + _value: Cell<u8>, +} + +// Send: No, Sync: No +#[derive(Clone)] +struct NN { + _value: Rc<u8>, +} + #[allow(dead_code)] type BoxFutureSync<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + Sync>>; #[allow(dead_code)] @@ -19,11 +36,11 @@ type BoxFutureSend<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T>>>; #[allow(dead_code)] -type BoxAsyncRead = std::pin::Pin<Box<dyn tokio::io::AsyncBufRead>>; +type BoxAsyncRead = std::pin::Pin<Box<dyn tokio::io::AsyncBufRead + Send + Sync>>; #[allow(dead_code)] -type BoxAsyncSeek = std::pin::Pin<Box<dyn tokio::io::AsyncSeek>>; +type BoxAsyncSeek = std::pin::Pin<Box<dyn tokio::io::AsyncSeek + Send + Sync>>; #[allow(dead_code)] -type BoxAsyncWrite = std::pin::Pin<Box<dyn tokio::io::AsyncWrite>>; +type BoxAsyncWrite = std::pin::Pin<Box<dyn tokio::io::AsyncWrite + Send + Sync>>; #[allow(dead_code)] fn require_send<T: Send>(_t: &T) {} @@ -59,310 +76,671 @@ macro_rules! into_todo { x }}; } -macro_rules! assert_value { - ($type:ty: Send & Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f: $type = todo!(); - require_send(&f); - require_sync(&f); - }; - }; - ($type:ty: !Send & Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f: $type = todo!(); - AmbiguousIfSend::some_item(&f); - require_sync(&f); - }; - }; - ($type:ty: Send & !Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f: $type = todo!(); - require_send(&f); - AmbiguousIfSync::some_item(&f); - }; - }; - ($type:ty: !Send & !Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f: $type = todo!(); - AmbiguousIfSend::some_item(&f); - AmbiguousIfSync::some_item(&f); - }; + +macro_rules! async_assert_fn_send { + (Send & $(!)?Sync & $(!)?Unpin, $value:expr) => { + require_send(&$value); }; - ($type:ty: Unpin) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f: $type = todo!(); - require_unpin(&f); - }; + (!Send & $(!)?Sync & $(!)?Unpin, $value:expr) => { + AmbiguousIfSend::some_item(&$value); }; } -macro_rules! async_assert_fn { - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Send & Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - require_send(&f); - require_sync(&f); - }; +macro_rules! async_assert_fn_sync { + ($(!)?Send & Sync & $(!)?Unpin, $value:expr) => { + require_sync(&$value); }; - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Send & !Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - require_send(&f); - AmbiguousIfSync::some_item(&f); - }; + ($(!)?Send & !Sync & $(!)?Unpin, $value:expr) => { + AmbiguousIfSync::some_item(&$value); }; - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Send & Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - AmbiguousIfSend::some_item(&f); - require_sync(&f); - }; +} +macro_rules! async_assert_fn_unpin { + ($(!)?Send & $(!)?Sync & Unpin, $value:expr) => { + require_unpin(&$value); }; - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Send & !Sync) => { - #[allow(unreachable_code)] - #[allow(unused_variables)] - const _: fn() = || { - let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - AmbiguousIfSend::some_item(&f); - AmbiguousIfSync::some_item(&f); - }; + ($(!)?Send & $(!)?Sync & !Unpin, $value:expr) => { + AmbiguousIfUnpin::some_item(&$value); }; - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): !Unpin) => { +} + +macro_rules! async_assert_fn { + ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): $($tok:tt)*) => { #[allow(unreachable_code)] #[allow(unused_variables)] const _: fn() = || { let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - AmbiguousIfUnpin::some_item(&f); + async_assert_fn_send!($($tok)*, f); + async_assert_fn_sync!($($tok)*, f); + async_assert_fn_unpin!($($tok)*, f); }; }; - ($($f:ident $(< $($generic:ty),* > )? )::+($($arg:ty),*): Unpin) => { +} +macro_rules! assert_value { + ($type:ty: $($tok:tt)*) => { #[allow(unreachable_code)] #[allow(unused_variables)] const _: fn() = || { - let f = $($f $(::<$($generic),*>)? )::+( $( into_todo!($arg) ),* ); - require_unpin(&f); + let f: $type = todo!(); + async_assert_fn_send!($($tok)*, f); + async_assert_fn_sync!($($tok)*, f); + async_assert_fn_unpin!($($tok)*, f); }; }; } -async_assert_fn!(tokio::io::copy(&mut TcpStream, &mut TcpStream): Send & Sync); -async_assert_fn!(tokio::io::empty(): Send & Sync); -async_assert_fn!(tokio::io::repeat(u8): Send & Sync); -async_assert_fn!(tokio::io::sink(): Send & Sync); -async_assert_fn!(tokio::io::split(TcpStream): Send & Sync); -async_assert_fn!(tokio::io::stderr(): Send & Sync); -async_assert_fn!(tokio::io::stdin(): Send & Sync); -async_assert_fn!(tokio::io::stdout(): Send & Sync); -async_assert_fn!(tokio::io::Split<Cursor<Vec<u8>>>::next_segment(_): Send & Sync); - -async_assert_fn!(tokio::fs::canonicalize(&str): Send & Sync); -async_assert_fn!(tokio::fs::copy(&str, &str): Send & Sync); -async_assert_fn!(tokio::fs::create_dir(&str): Send & Sync); -async_assert_fn!(tokio::fs::create_dir_all(&str): Send & Sync); -async_assert_fn!(tokio::fs::hard_link(&str, &str): Send & Sync); -async_assert_fn!(tokio::fs::metadata(&str): Send & Sync); -async_assert_fn!(tokio::fs::read(&str): Send & Sync); -async_assert_fn!(tokio::fs::read_dir(&str): Send & Sync); -async_assert_fn!(tokio::fs::read_link(&str): Send & Sync); -async_assert_fn!(tokio::fs::read_to_string(&str): Send & Sync); -async_assert_fn!(tokio::fs::remove_dir(&str): Send & Sync); -async_assert_fn!(tokio::fs::remove_dir_all(&str): Send & Sync); -async_assert_fn!(tokio::fs::remove_file(&str): Send & Sync); -async_assert_fn!(tokio::fs::rename(&str, &str): Send & Sync); -async_assert_fn!(tokio::fs::set_permissions(&str, std::fs::Permissions): Send & Sync); -async_assert_fn!(tokio::fs::symlink_metadata(&str): Send & Sync); -async_assert_fn!(tokio::fs::write(&str, Vec<u8>): Send & Sync); -async_assert_fn!(tokio::fs::ReadDir::next_entry(_): Send & Sync); -async_assert_fn!(tokio::fs::OpenOptions::open(_, &str): Send & Sync); -async_assert_fn!(tokio::fs::DirEntry::metadata(_): Send & Sync); -async_assert_fn!(tokio::fs::DirEntry::file_type(_): Send & Sync); - -async_assert_fn!(tokio::fs::File::open(&str): Send & Sync); -async_assert_fn!(tokio::fs::File::create(&str): Send & Sync); -async_assert_fn!(tokio::fs::File::sync_all(_): Send & Sync); -async_assert_fn!(tokio::fs::File::sync_data(_): Send & Sync); -async_assert_fn!(tokio::fs::File::set_len(_, u64): Send & Sync); -async_assert_fn!(tokio::fs::File::metadata(_): Send & Sync); -async_assert_fn!(tokio::fs::File::try_clone(_): Send & Sync); -async_assert_fn!(tokio::fs::File::into_std(_): Send & Sync); -async_assert_fn!(tokio::fs::File::set_permissions(_, std::fs::Permissions): Send & Sync); - -async_assert_fn!(tokio::net::lookup_host(SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::TcpListener::bind(SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::TcpListener::accept(_): Send & Sync); -async_assert_fn!(tokio::net::TcpStream::connect(SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::TcpStream::peek(_, &mut [u8]): Send & Sync); -async_assert_fn!(tokio::net::tcp::ReadHalf::peek(_, &mut [u8]): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::bind(SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::connect(_, SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::send(_, &[u8]): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::recv(_, &mut [u8]): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::send_to(_, &[u8], SocketAddr): Send & Sync); -async_assert_fn!(tokio::net::UdpSocket::recv_from(_, &mut [u8]): Send & Sync); +macro_rules! cfg_not_wasi { + ($($item:item)*) => { + $( + #[cfg(not(tokio_wasi))] + $item + )* + } +} + +// Manually re-implementation of `async_assert_fn` for `poll_fn`. The macro +// doesn't work for this particular case because constructing the closure +// is too complicated. +const _: fn() = || { + let pinned = std::marker::PhantomPinned; + let f = tokio::macros::support::poll_fn(move |_| { + // Use `pinned` to take ownership of it. + let _ = &pinned; + std::task::Poll::Pending::<()> + }); + require_send(&f); + require_sync(&f); + AmbiguousIfUnpin::some_item(&f); +}; + +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<u8>): 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::TcpStream: 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::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); + +// 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); #[cfg(unix)] mod unix_datagram { use super::*; - async_assert_fn!(tokio::net::UnixListener::bind(&str): Send & Sync); - async_assert_fn!(tokio::net::UnixListener::accept(_): Send & Sync); - async_assert_fn!(tokio::net::UnixDatagram::send(_, &[u8]): Send & Sync); - async_assert_fn!(tokio::net::UnixDatagram::recv(_, &mut [u8]): Send & Sync); - async_assert_fn!(tokio::net::UnixDatagram::send_to(_, &[u8], &str): Send & Sync); - async_assert_fn!(tokio::net::UnixDatagram::recv_from(_, &mut [u8]): Send & Sync); - async_assert_fn!(tokio::net::UnixStream::connect(&str): Send & Sync); + use tokio::net::*; + assert_value!(UnixDatagram: Send & Sync & Unpin); + assert_value!(UnixListener: Send & Sync & Unpin); + assert_value!(UnixStream: Send & Sync & Unpin); + assert_value!(unix::OwnedReadHalf: Send & Sync & Unpin); + assert_value!(unix::OwnedWriteHalf: Send & Sync & Unpin); + assert_value!(unix::ReadHalf<'_>: Send & Sync & Unpin); + assert_value!(unix::ReuniteError: Send & Sync & Unpin); + assert_value!(unix::SocketAddr: Send & Sync & Unpin); + assert_value!(unix::UCred: Send & Sync & Unpin); + assert_value!(unix::WriteHalf<'_>: Send & Sync & Unpin); + async_assert_fn!(UnixDatagram::readable(_): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::recv(_, &mut [u8]): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::recv_from(_, &mut [u8]): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::send(_, &[u8]): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::send_to(_, &[u8], &str): Send & Sync & !Unpin); + async_assert_fn!(UnixDatagram::writable(_): Send & Sync & !Unpin); + async_assert_fn!(UnixListener::accept(_): Send & Sync & !Unpin); + async_assert_fn!(UnixStream::connect(&str): Send & Sync & !Unpin); + async_assert_fn!(UnixStream::readable(_): Send & Sync & !Unpin); + async_assert_fn!(UnixStream::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(UnixStream::writable(_): Send & Sync & !Unpin); } -async_assert_fn!(tokio::process::Child::wait_with_output(_): Send & Sync); -async_assert_fn!(tokio::signal::ctrl_c(): Send & Sync); #[cfg(unix)] -async_assert_fn!(tokio::signal::unix::Signal::recv(_): Send & Sync); - -async_assert_fn!(tokio::sync::Barrier::wait(_): Send & Sync); -async_assert_fn!(tokio::sync::Mutex<u8>::lock(_): Send & Sync); -async_assert_fn!(tokio::sync::Mutex<Cell<u8>>::lock(_): Send & Sync); -async_assert_fn!(tokio::sync::Mutex<Rc<u8>>::lock(_): !Send & !Sync); -async_assert_fn!(tokio::sync::Mutex<u8>::lock_owned(_): Send & Sync); -async_assert_fn!(tokio::sync::Mutex<Cell<u8>>::lock_owned(_): Send & Sync); -async_assert_fn!(tokio::sync::Mutex<Rc<u8>>::lock_owned(_): !Send & !Sync); -async_assert_fn!(tokio::sync::Notify::notified(_): Send & Sync); -async_assert_fn!(tokio::sync::RwLock<u8>::read(_): Send & Sync); -async_assert_fn!(tokio::sync::RwLock<u8>::write(_): Send & Sync); -async_assert_fn!(tokio::sync::RwLock<Cell<u8>>::read(_): !Send & !Sync); -async_assert_fn!(tokio::sync::RwLock<Cell<u8>>::write(_): !Send & !Sync); -async_assert_fn!(tokio::sync::RwLock<Rc<u8>>::read(_): !Send & !Sync); -async_assert_fn!(tokio::sync::RwLock<Rc<u8>>::write(_): !Send & !Sync); -async_assert_fn!(tokio::sync::Semaphore::acquire(_): Send & Sync); - -async_assert_fn!(tokio::sync::broadcast::Receiver<u8>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::broadcast::Receiver<Cell<u8>>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::broadcast::Receiver<Rc<u8>>::recv(_): !Send & !Sync); - -async_assert_fn!(tokio::sync::mpsc::Receiver<u8>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::mpsc::Receiver<Cell<u8>>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::mpsc::Receiver<Rc<u8>>::recv(_): !Send & !Sync); -async_assert_fn!(tokio::sync::mpsc::Sender<u8>::send(_, u8): Send & Sync); -async_assert_fn!(tokio::sync::mpsc::Sender<Cell<u8>>::send(_, Cell<u8>): Send & !Sync); -async_assert_fn!(tokio::sync::mpsc::Sender<Rc<u8>>::send(_, Rc<u8>): !Send & !Sync); - -async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<u8>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<Cell<u8>>::recv(_): Send & Sync); -async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<Rc<u8>>::recv(_): !Send & !Sync); - -async_assert_fn!(tokio::sync::watch::Receiver<u8>::changed(_): Send & Sync); -async_assert_fn!(tokio::sync::watch::Sender<u8>::closed(_): Send & Sync); -async_assert_fn!(tokio::sync::watch::Sender<Cell<u8>>::closed(_): !Send & !Sync); -async_assert_fn!(tokio::sync::watch::Sender<Rc<u8>>::closed(_): !Send & !Sync); - -async_assert_fn!(tokio::sync::OnceCell<u8>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = u8> + Send + Sync>>): Send & Sync); -async_assert_fn!(tokio::sync::OnceCell<u8>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = u8> + Send>>): Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<u8>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = u8>>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Cell<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Cell<u8>> + Send + Sync>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Cell<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Cell<u8>> + Send>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Cell<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Cell<u8>>>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Rc<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Rc<u8>> + Send + Sync>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Rc<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Rc<u8>> + Send>>): !Send & !Sync); -async_assert_fn!(tokio::sync::OnceCell<Rc<u8>>::get_or_init( - _, fn() -> Pin<Box<dyn Future<Output = Rc<u8>>>>): !Send & !Sync); -assert_value!(tokio::sync::OnceCell<u8>: Send & Sync); -assert_value!(tokio::sync::OnceCell<Cell<u8>>: Send & !Sync); -assert_value!(tokio::sync::OnceCell<Rc<u8>>: !Send & !Sync); - -async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFutureSync<()>): Send & Sync); -async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFutureSend<()>): Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFuture<()>): !Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFutureSync<()>): Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFutureSend<()>): Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFuture<()>): !Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFutureSync<()>): !Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFutureSend<()>): !Send & !Sync); -async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFuture<()>): !Send & !Sync); -async_assert_fn!(tokio::task::LocalSet::run_until(_, BoxFutureSync<()>): !Send & !Sync); -assert_value!(tokio::task::LocalSet: !Send & !Sync); - -async_assert_fn!(tokio::time::advance(Duration): Send & Sync); -async_assert_fn!(tokio::time::sleep(Duration): Send & Sync); -async_assert_fn!(tokio::time::sleep_until(Instant): Send & Sync); -async_assert_fn!(tokio::time::timeout(Duration, BoxFutureSync<()>): Send & Sync); -async_assert_fn!(tokio::time::timeout(Duration, BoxFutureSend<()>): Send & !Sync); -async_assert_fn!(tokio::time::timeout(Duration, BoxFuture<()>): !Send & !Sync); -async_assert_fn!(tokio::time::timeout_at(Instant, BoxFutureSync<()>): Send & Sync); -async_assert_fn!(tokio::time::timeout_at(Instant, BoxFutureSend<()>): Send & !Sync); -async_assert_fn!(tokio::time::timeout_at(Instant, BoxFuture<()>): !Send & !Sync); -async_assert_fn!(tokio::time::Interval::tick(_): Send & Sync); - -assert_value!(tokio::time::Interval: Unpin); -async_assert_fn!(tokio::time::sleep(Duration): !Unpin); -async_assert_fn!(tokio::time::sleep_until(Instant): !Unpin); -async_assert_fn!(tokio::time::timeout(Duration, BoxFuture<()>): !Unpin); -async_assert_fn!(tokio::time::timeout_at(Instant, BoxFuture<()>): !Unpin); -async_assert_fn!(tokio::time::Interval::tick(_): !Unpin); -async_assert_fn!(tokio::io::AsyncBufReadExt::read_until(&mut BoxAsyncRead, u8, &mut Vec<u8>): !Unpin); -async_assert_fn!(tokio::io::AsyncBufReadExt::read_line(&mut BoxAsyncRead, &mut String): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read(&mut BoxAsyncRead, &mut [u8]): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_exact(&mut BoxAsyncRead, &mut [u8]): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u8(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i8(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u16(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i16(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u32(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i32(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u64(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i64(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u128(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i128(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u16_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i16_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u32_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i32_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u64_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i64_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_u128_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_i128_le(&mut BoxAsyncRead): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_to_end(&mut BoxAsyncRead, &mut Vec<u8>): !Unpin); -async_assert_fn!(tokio::io::AsyncReadExt::read_to_string(&mut BoxAsyncRead, &mut String): !Unpin); -async_assert_fn!(tokio::io::AsyncSeekExt::seek(&mut BoxAsyncSeek, SeekFrom): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write(&mut BoxAsyncWrite, &[u8]): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_all(&mut BoxAsyncWrite, &[u8]): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u8(&mut BoxAsyncWrite, u8): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i8(&mut BoxAsyncWrite, i8): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u16(&mut BoxAsyncWrite, u16): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i16(&mut BoxAsyncWrite, i16): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u32(&mut BoxAsyncWrite, u32): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i32(&mut BoxAsyncWrite, i32): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u64(&mut BoxAsyncWrite, u64): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i64(&mut BoxAsyncWrite, i64): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u128(&mut BoxAsyncWrite, u128): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i128(&mut BoxAsyncWrite, i128): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u16_le(&mut BoxAsyncWrite, u16): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i16_le(&mut BoxAsyncWrite, i16): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u32_le(&mut BoxAsyncWrite, u32): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i32_le(&mut BoxAsyncWrite, i32): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u64_le(&mut BoxAsyncWrite, u64): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i64_le(&mut BoxAsyncWrite, i64): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_u128_le(&mut BoxAsyncWrite, u128): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::write_i128_le(&mut BoxAsyncWrite, i128): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::flush(&mut BoxAsyncWrite): !Unpin); -async_assert_fn!(tokio::io::AsyncWriteExt::shutdown(&mut BoxAsyncWrite): !Unpin); +mod unix_pipe { + use super::*; + use tokio::net::unix::pipe::*; + assert_value!(OpenOptions: Send & Sync & Unpin); + assert_value!(Receiver: Send & Sync & Unpin); + assert_value!(Sender: Send & Sync & Unpin); + async_assert_fn!(Receiver::readable(_): Send & Sync & !Unpin); + async_assert_fn!(Receiver::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(Sender::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(Sender::writable(_): Send & Sync & !Unpin); +} + +#[cfg(windows)] +mod windows_named_pipe { + use super::*; + use tokio::net::windows::named_pipe::*; + assert_value!(ClientOptions: Send & Sync & Unpin); + assert_value!(NamedPipeClient: Send & Sync & Unpin); + assert_value!(NamedPipeServer: Send & Sync & Unpin); + assert_value!(PipeEnd: Send & Sync & Unpin); + assert_value!(PipeInfo: Send & Sync & Unpin); + assert_value!(PipeMode: Send & Sync & Unpin); + assert_value!(ServerOptions: Send & Sync & Unpin); + async_assert_fn!(NamedPipeClient::readable(_): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeClient::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeClient::writable(_): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeServer::connect(_): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeServer::readable(_): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeServer::ready(_, tokio::io::Interest): Send & Sync & !Unpin); + async_assert_fn!(NamedPipeServer::writable(_): 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); +} + +#[cfg(unix)] +mod unix_signal { + use super::*; + assert_value!(tokio::signal::unix::Signal: Send & Sync & Unpin); + assert_value!(tokio::signal::unix::SignalKind: Send & Sync & Unpin); + async_assert_fn!(tokio::signal::unix::Signal::recv(_): Send & Sync & !Unpin); +} +#[cfg(windows)] +mod windows_signal { + use super::*; + assert_value!(tokio::signal::windows::CtrlC: Send & Sync & Unpin); + assert_value!(tokio::signal::windows::CtrlBreak: Send & Sync & Unpin); + async_assert_fn!(tokio::signal::windows::CtrlC::recv(_): Send & Sync & !Unpin); + async_assert_fn!(tokio::signal::windows::CtrlBreak::recv(_): Send & Sync & !Unpin); +} + +assert_value!(tokio::sync::AcquireError: Send & Sync & Unpin); +assert_value!(tokio::sync::Barrier: Send & Sync & Unpin); +assert_value!(tokio::sync::BarrierWaitResult: Send & Sync & Unpin); +assert_value!(tokio::sync::MappedMutexGuard<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::MappedMutexGuard<'_, YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::MappedMutexGuard<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::Mutex<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::Mutex<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::Mutex<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::MutexGuard<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::MutexGuard<'_, YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::MutexGuard<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::Notify: Send & Sync & Unpin); +assert_value!(tokio::sync::OnceCell<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OnceCell<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::OnceCell<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedMutexGuard<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMutexGuard<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMutexGuard<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<NN,NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<NN,YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<NN,YY>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YN,NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YN,YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YN,YY>: Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YY,NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YY,YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedMappedMutexGuard<YY,YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockMappedWriteGuard<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockMappedWriteGuard<YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockMappedWriteGuard<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockReadGuard<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockReadGuard<YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockReadGuard<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockWriteGuard<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockWriteGuard<YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::OwnedRwLockWriteGuard<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::OwnedSemaphorePermit: Send & Sync & Unpin); +assert_value!(tokio::sync::RwLock<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLock<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLock<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::RwLockMappedWriteGuard<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockMappedWriteGuard<'_, YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockMappedWriteGuard<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::RwLockReadGuard<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockReadGuard<'_, YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockReadGuard<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::RwLockWriteGuard<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockWriteGuard<'_, YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::RwLockWriteGuard<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::Semaphore: Send & Sync & Unpin); +assert_value!(tokio::sync::SemaphorePermit<'_>: Send & Sync & Unpin); +assert_value!(tokio::sync::TryAcquireError: Send & Sync & Unpin); +assert_value!(tokio::sync::TryLockError: Send & Sync & Unpin); +assert_value!(tokio::sync::broadcast::Receiver<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::broadcast::Receiver<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::broadcast::Receiver<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::broadcast::Sender<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::broadcast::Sender<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::broadcast::Sender<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::futures::Notified<'_>: Send & Sync & !Unpin); +assert_value!(tokio::sync::mpsc::OwnedPermit<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::OwnedPermit<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::OwnedPermit<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Permit<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::Permit<'_, YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Permit<'_, YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Receiver<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::Receiver<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Receiver<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Sender<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::Sender<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::Sender<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedReceiver<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedReceiver<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedReceiver<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedSender<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedSender<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::UnboundedSender<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendError<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendError<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendError<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendTimeoutError<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendTimeoutError<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::SendTimeoutError<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::TrySendError<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::TrySendError<YN>: Send & !Sync & Unpin); +assert_value!(tokio::sync::mpsc::error::TrySendError<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::oneshot::Receiver<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::oneshot::Receiver<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::oneshot::Receiver<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::oneshot::Sender<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::oneshot::Sender<YN>: Send & Sync & Unpin); +assert_value!(tokio::sync::oneshot::Sender<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::watch::Receiver<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Receiver<YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Receiver<YY>: Send & Sync & Unpin); +assert_value!(tokio::sync::watch::Ref<'_, NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Ref<'_, YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Ref<'_, YY>: !Send & Sync & Unpin); +assert_value!(tokio::sync::watch::Sender<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Sender<YN>: !Send & !Sync & Unpin); +assert_value!(tokio::sync::watch::Sender<YY>: Send & Sync & Unpin); +assert_value!(tokio::task::JoinError: Send & Sync & Unpin); +assert_value!(tokio::task::JoinHandle<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::task::JoinHandle<YN>: Send & Sync & Unpin); +assert_value!(tokio::task::JoinHandle<YY>: Send & Sync & Unpin); +assert_value!(tokio::task::JoinSet<NN>: !Send & !Sync & Unpin); +assert_value!(tokio::task::JoinSet<YN>: Send & Sync & Unpin); +assert_value!(tokio::task::JoinSet<YY>: Send & Sync & Unpin); +assert_value!(tokio::task::LocalSet: !Send & !Sync & Unpin); +async_assert_fn!(tokio::sync::Barrier::wait(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<NN>::lock(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<NN>::lock_owned(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<YN>::lock(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<YN>::lock_owned(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<YY>::lock(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Mutex<YY>::lock_owned(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Notify::notified(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = NN> + Send + Sync>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = NN> + Send>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = NN>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<NN>> + Send + Sync>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<NN>> + Send>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<NN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<NN>>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YN> + Send + Sync>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YN> + Send>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YN>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YN>> + Send + Sync>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YN>> + Send>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YN>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YN>>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YY> + Send + Sync>>): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YY> + Send>>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_init( _, fn() -> Pin<Box<dyn Future<Output = YY>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YY>> + Send + Sync>>): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YY>> + Send>>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::OnceCell<YY>::get_or_try_init( _, fn() -> Pin<Box<dyn Future<Output = std::io::Result<YY>>>>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<NN>::read(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<NN>::write(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<YN>::read(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<YN>::write(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<YY>::read(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::RwLock<YY>::write(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Semaphore::acquire(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Semaphore::acquire_many(_, u32): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Semaphore::acquire_many_owned(_, u32): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::Semaphore::acquire_owned(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::broadcast::Receiver<NN>::recv(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::broadcast::Receiver<YN>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::broadcast::Receiver<YY>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Receiver<NN>::recv(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Receiver<YN>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Receiver<YY>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<NN>::closed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<NN>::reserve(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<NN>::reserve_owned(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<NN>::send(_, NN): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<NN>::send_timeout(_, NN, Duration): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YN>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YN>::reserve(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YN>::reserve_owned(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YN>::send(_, YN): Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YN>::send_timeout(_, YN, Duration): Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YY>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YY>::reserve(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YY>::reserve_owned(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YY>::send(_, YY): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::Sender<YY>::send_timeout(_, YY, Duration): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<NN>::recv(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<YN>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedReceiver<YY>::recv(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedSender<NN>::closed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedSender<YN>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::mpsc::UnboundedSender<YY>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::oneshot::Sender<NN>::closed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::oneshot::Sender<YN>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::oneshot::Sender<YY>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Receiver<NN>::changed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Receiver<YN>::changed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Receiver<YY>::changed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Sender<NN>::closed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Sender<YN>::closed(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::sync::watch::Sender<YY>::closed(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<Cell<u32>>::join_next(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<Cell<u32>>::shutdown(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<Rc<u32>>::join_next(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<Rc<u32>>::shutdown(_): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<u32>::join_next(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::JoinSet<u32>::shutdown(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFuture<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFutureSend<()>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Cell<u32>>::scope(_, Cell<u32>, BoxFutureSync<()>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFuture<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFutureSend<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<Rc<u32>>::scope(_, Rc<u32>, BoxFutureSync<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFuture<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFutureSend<()>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::LocalKey<u32>::scope(_, u32, BoxFutureSync<()>): Send & Sync & !Unpin); +async_assert_fn!(tokio::task::LocalSet::run_until(_, BoxFutureSync<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::task::unconstrained(BoxFuture<()>): !Send & !Sync & Unpin); +async_assert_fn!(tokio::task::unconstrained(BoxFutureSend<()>): Send & !Sync & Unpin); +async_assert_fn!(tokio::task::unconstrained(BoxFutureSync<()>): Send & Sync & Unpin); + +assert_value!(tokio::runtime::Builder: Send & Sync & Unpin); +assert_value!(tokio::runtime::EnterGuard<'_>: !Send & Sync & Unpin); +assert_value!(tokio::runtime::Handle: Send & Sync & Unpin); +assert_value!(tokio::runtime::Runtime: Send & Sync & Unpin); + +assert_value!(tokio::time::Interval: Send & Sync & Unpin); +assert_value!(tokio::time::Instant: Send & Sync & Unpin); +assert_value!(tokio::time::Sleep: Send & Sync & !Unpin); +assert_value!(tokio::time::Timeout<BoxFutureSync<()>>: Send & Sync & !Unpin); +assert_value!(tokio::time::Timeout<BoxFutureSend<()>>: Send & !Sync & !Unpin); +assert_value!(tokio::time::Timeout<BoxFuture<()>>: !Send & !Sync & !Unpin); +assert_value!(tokio::time::error::Elapsed: Send & Sync & Unpin); +assert_value!(tokio::time::error::Error: Send & Sync & Unpin); +async_assert_fn!(tokio::time::advance(Duration): Send & Sync & !Unpin); +async_assert_fn!(tokio::time::sleep(Duration): Send & Sync & !Unpin); +async_assert_fn!(tokio::time::sleep_until(Instant): Send & Sync & !Unpin); +async_assert_fn!(tokio::time::timeout(Duration, BoxFutureSync<()>): Send & Sync & !Unpin); +async_assert_fn!(tokio::time::timeout(Duration, BoxFutureSend<()>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::time::timeout(Duration, BoxFuture<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::time::timeout_at(Instant, BoxFutureSync<()>): Send & Sync & !Unpin); +async_assert_fn!(tokio::time::timeout_at(Instant, BoxFutureSend<()>): Send & !Sync & !Unpin); +async_assert_fn!(tokio::time::timeout_at(Instant, BoxFuture<()>): !Send & !Sync & !Unpin); +async_assert_fn!(tokio::time::Interval::tick(_): Send & Sync & !Unpin); + +assert_value!(tokio::io::BufReader<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::BufStream<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::BufWriter<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::DuplexStream: Send & Sync & Unpin); +assert_value!(tokio::io::Empty: Send & Sync & Unpin); +assert_value!(tokio::io::Interest: Send & Sync & Unpin); +assert_value!(tokio::io::Lines<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::ReadBuf<'_>: Send & Sync & Unpin); +assert_value!(tokio::io::ReadHalf<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::Ready: Send & Sync & Unpin); +assert_value!(tokio::io::Repeat: Send & Sync & Unpin); +assert_value!(tokio::io::Sink: Send & Sync & Unpin); +assert_value!(tokio::io::Split<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::Stderr: Send & Sync & Unpin); +assert_value!(tokio::io::Stdin: Send & Sync & Unpin); +assert_value!(tokio::io::Stdout: Send & Sync & Unpin); +assert_value!(tokio::io::Take<TcpStream>: Send & Sync & Unpin); +assert_value!(tokio::io::WriteHalf<TcpStream>: Send & Sync & Unpin); +async_assert_fn!(tokio::io::copy(&mut TcpStream, &mut TcpStream): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::copy_bidirectional(&mut TcpStream, &mut TcpStream): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::copy_buf(&mut tokio::io::BufReader<TcpStream>, &mut TcpStream): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::empty(): Send & Sync & Unpin); +async_assert_fn!(tokio::io::repeat(u8): Send & Sync & Unpin); +async_assert_fn!(tokio::io::sink(): Send & Sync & Unpin); +async_assert_fn!(tokio::io::split(TcpStream): Send & Sync & Unpin); +async_assert_fn!(tokio::io::stderr(): Send & Sync & Unpin); +async_assert_fn!(tokio::io::stdin(): Send & Sync & Unpin); +async_assert_fn!(tokio::io::stdout(): Send & Sync & Unpin); +async_assert_fn!(tokio::io::Split<tokio::io::BufReader<TcpStream>>::next_segment(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::Lines<tokio::io::BufReader<TcpStream>>::next_line(_): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncBufReadExt::read_until(&mut BoxAsyncRead, u8, &mut Vec<u8>): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::AsyncBufReadExt::read_line(&mut BoxAsyncRead, &mut String): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::AsyncBufReadExt::fill_buf(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read(&mut BoxAsyncRead, &mut [u8]): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_buf(&mut BoxAsyncRead, &mut Vec<u8>): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::AsyncReadExt::read_exact(&mut BoxAsyncRead, &mut [u8]): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::AsyncReadExt::read_u8(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i8(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u16(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i16(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u32(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i32(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u64(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i64(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u128(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i128(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_f32(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_f64(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u16_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i16_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u32_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i32_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u64_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i64_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_u128_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_i128_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_f32_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_f64_le(&mut BoxAsyncRead): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncReadExt::read_to_end(&mut BoxAsyncRead, &mut Vec<u8>): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::AsyncReadExt::read_to_string(&mut BoxAsyncRead, &mut String): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::AsyncSeekExt::seek(&mut BoxAsyncSeek, SeekFrom): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncSeekExt::stream_position(&mut BoxAsyncSeek): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncWriteExt::write(&mut BoxAsyncWrite, &[u8]): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_vectored(&mut BoxAsyncWrite, _): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_buf(&mut BoxAsyncWrite, &mut bytes::Bytes): Send + & Sync + & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_all_buf(&mut BoxAsyncWrite, &mut bytes::Bytes): Send + & Sync + & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_all(&mut BoxAsyncWrite, &[u8]): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::AsyncWriteExt::write_u8(&mut BoxAsyncWrite, u8): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncWriteExt::write_i8(&mut BoxAsyncWrite, i8): Send & Sync & !Unpin); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u16(&mut BoxAsyncWrite, u16): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i16(&mut BoxAsyncWrite, i16): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u32(&mut BoxAsyncWrite, u32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i32(&mut BoxAsyncWrite, i32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u64(&mut BoxAsyncWrite, u64): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i64(&mut BoxAsyncWrite, i64): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u128(&mut BoxAsyncWrite, u128): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i128(&mut BoxAsyncWrite, i128): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_f32(&mut BoxAsyncWrite, f32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_f64(&mut BoxAsyncWrite, f64): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u16_le(&mut BoxAsyncWrite, u16): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i16_le(&mut BoxAsyncWrite, i16): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u32_le(&mut BoxAsyncWrite, u32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i32_le(&mut BoxAsyncWrite, i32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u64_le(&mut BoxAsyncWrite, u64): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i64_le(&mut BoxAsyncWrite, i64): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_u128_le(&mut BoxAsyncWrite, u128): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_i128_le(&mut BoxAsyncWrite, i128): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_f32_le(&mut BoxAsyncWrite, f32): Send & Sync & !Unpin +); +async_assert_fn!( + tokio::io::AsyncWriteExt::write_f64_le(&mut BoxAsyncWrite, f64): Send & Sync & !Unpin +); +async_assert_fn!(tokio::io::AsyncWriteExt::flush(&mut BoxAsyncWrite): Send & Sync & !Unpin); +async_assert_fn!(tokio::io::AsyncWriteExt::shutdown(&mut BoxAsyncWrite): Send & Sync & !Unpin); + +#[cfg(unix)] +mod unix_asyncfd { + use super::*; + use tokio::io::unix::*; + + struct ImplsFd<T> { + _t: T, + } + impl<T> std::os::unix::io::AsRawFd for ImplsFd<T> { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + unreachable!() + } + } + + assert_value!(AsyncFd<ImplsFd<YY>>: Send & Sync & Unpin); + assert_value!(AsyncFd<ImplsFd<YN>>: Send & !Sync & Unpin); + assert_value!(AsyncFd<ImplsFd<NN>>: !Send & !Sync & Unpin); + assert_value!(AsyncFdReadyGuard<'_, ImplsFd<YY>>: Send & Sync & Unpin); + assert_value!(AsyncFdReadyGuard<'_, ImplsFd<YN>>: !Send & !Sync & Unpin); + assert_value!(AsyncFdReadyGuard<'_, ImplsFd<NN>>: !Send & !Sync & Unpin); + assert_value!(AsyncFdReadyMutGuard<'_, ImplsFd<YY>>: Send & Sync & Unpin); + assert_value!(AsyncFdReadyMutGuard<'_, ImplsFd<YN>>: Send & !Sync & Unpin); + assert_value!(AsyncFdReadyMutGuard<'_, ImplsFd<NN>>: !Send & !Sync & Unpin); + assert_value!(TryIoError: Send & Sync & Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YY>>::readable(_): Send & Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YY>>::readable_mut(_): Send & Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YY>>::writable(_): Send & Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YY>>::writable_mut(_): Send & Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YN>>::readable(_): !Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YN>>::readable_mut(_): Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YN>>::writable(_): !Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<YN>>::writable_mut(_): Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<NN>>::readable(_): !Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<NN>>::readable_mut(_): !Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<NN>>::writable(_): !Send & !Sync & !Unpin); + async_assert_fn!(AsyncFd<ImplsFd<NN>>::writable_mut(_): !Send & !Sync & !Unpin); +} diff --git a/vendor/tokio/tests/buffered.rs b/vendor/tokio/tests/buffered.rs index 98b6d5f31..4251c3fcc 100644 --- a/vendor/tokio/tests/buffered.rs +++ b/vendor/tokio/tests/buffered.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support bind() use tokio::net::TcpListener; use tokio_test::assert_ok; @@ -18,10 +18,10 @@ async fn echo_server() { let msg = "foo bar baz"; let t = thread::spawn(move || { - let mut s = assert_ok!(TcpStream::connect(&addr)); + let mut s = assert_ok!(TcpStream::connect(addr)); let t2 = thread::spawn(move || { - let mut s = assert_ok!(TcpStream::connect(&addr)); + let mut s = assert_ok!(TcpStream::connect(addr)); let mut b = vec![0; msg.len() * N]; assert_ok!(s.read_exact(&mut b)); b diff --git a/vendor/tokio/tests/dump.rs b/vendor/tokio/tests/dump.rs new file mode 100644 index 000000000..658ee4b9b --- /dev/null +++ b/vendor/tokio/tests/dump.rs @@ -0,0 +1,99 @@ +#![cfg(all( + tokio_unstable, + tokio_taskdump, + target_os = "linux", + any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64") +))] + +use std::hint::black_box; +use tokio::runtime::{self, Handle}; + +#[inline(never)] +async fn a() { + black_box(b()).await +} + +#[inline(never)] +async fn b() { + black_box(c()).await +} + +#[inline(never)] +async fn c() { + loop { + black_box(tokio::task::yield_now()).await + } +} + +#[test] +fn current_thread() { + let rt = runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + async fn dump() { + let handle = Handle::current(); + let dump = handle.dump().await; + + let tasks: Vec<_> = dump.tasks().iter().collect(); + + assert_eq!(tasks.len(), 3); + + for task in tasks { + let trace = task.trace().to_string(); + eprintln!("\n\n{trace}\n\n"); + assert!(trace.contains("dump::a")); + assert!(trace.contains("dump::b")); + assert!(trace.contains("dump::c")); + assert!(trace.contains("tokio::task::yield_now")); + } + } + + rt.block_on(async { + tokio::select!( + biased; + _ = tokio::spawn(a()) => {}, + _ = tokio::spawn(a()) => {}, + _ = tokio::spawn(a()) => {}, + _ = dump() => {}, + ); + }); +} + +#[test] +fn multi_thread() { + let rt = runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(3) + .build() + .unwrap(); + + async fn dump() { + let handle = Handle::current(); + let dump = handle.dump().await; + + let tasks: Vec<_> = dump.tasks().iter().collect(); + + assert_eq!(tasks.len(), 3); + + for task in tasks { + let trace = task.trace().to_string(); + eprintln!("\n\n{trace}\n\n"); + assert!(trace.contains("dump::a")); + assert!(trace.contains("dump::b")); + assert!(trace.contains("dump::c")); + assert!(trace.contains("tokio::task::yield_now")); + } + } + + rt.block_on(async { + tokio::select!( + biased; + _ = tokio::spawn(a()) => {}, + _ = tokio::spawn(a()) => {}, + _ = tokio::spawn(a()) => {}, + _ = dump() => {}, + ); + }); +} diff --git a/vendor/tokio/tests/fs.rs b/vendor/tokio/tests/fs.rs index 13c44c08d..ba38b719f 100644 --- a/vendor/tokio/tests/fs.rs +++ b/vendor/tokio/tests/fs.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support file operations use tokio::fs; use tokio_test::assert_ok; diff --git a/vendor/tokio/tests/fs_canonicalize_dir.rs b/vendor/tokio/tests/fs_canonicalize_dir.rs new file mode 100644 index 000000000..83f0d8137 --- /dev/null +++ b/vendor/tokio/tests/fs_canonicalize_dir.rs @@ -0,0 +1,22 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use tokio::fs; + +#[tokio::test] +#[cfg(unix)] +async fn canonicalize_root_dir_unix() { + assert_eq!(fs::canonicalize("/.").await.unwrap().to_str().unwrap(), "/"); +} + +#[tokio::test] +#[cfg(windows)] +async fn canonicalize_root_dir_windows() { + // 2-step let bindings due to Rust memory semantics + let dir_path = fs::canonicalize("C:\\.\\").await.unwrap(); + + let dir_name = dir_path.to_str().unwrap(); + + assert!(dir_name.starts_with("\\\\")); + assert!(dir_name.ends_with("C:\\")); +} diff --git a/vendor/tokio/tests/fs_copy.rs b/vendor/tokio/tests/fs_copy.rs index 8d1632013..e6e5b666f 100644 --- a/vendor/tokio/tests/fs_copy.rs +++ b/vendor/tokio/tests/fs_copy.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations use tempfile::tempdir; use tokio::fs; diff --git a/vendor/tokio/tests/fs_dir.rs b/vendor/tokio/tests/fs_dir.rs index 21efe8c0e..5f653cb21 100644 --- a/vendor/tokio/tests/fs_dir.rs +++ b/vendor/tokio/tests/fs_dir.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations use tokio::fs; use tokio_test::{assert_err, assert_ok}; @@ -46,6 +46,27 @@ async fn build_dir() { } #[tokio::test] +#[cfg(unix)] +async fn build_dir_mode_read_only() { + let base_dir = tempdir().unwrap(); + let new_dir = base_dir.path().join("abc"); + + assert_ok!( + fs::DirBuilder::new() + .recursive(true) + .mode(0o444) + .create(&new_dir) + .await + ); + + assert!(fs::metadata(new_dir) + .await + .expect("metadata result") + .permissions() + .readonly()); +} + +#[tokio::test] async fn remove() { let base_dir = tempdir().unwrap(); let new_dir = base_dir.path().join("foo"); @@ -85,3 +106,21 @@ async fn read_inherent() { vec!["aa".to_string(), "bb".to_string(), "cc".to_string()] ); } + +#[tokio::test] +async fn read_dir_entry_info() { + let temp_dir = tempdir().unwrap(); + + let file_path = temp_dir.path().join("a.txt"); + + fs::write(&file_path, b"Hello File!").await.unwrap(); + + let mut dir = fs::read_dir(temp_dir.path()).await.unwrap(); + + let first_entry = dir.next_entry().await.unwrap().unwrap(); + + assert_eq!(first_entry.path(), file_path); + assert_eq!(first_entry.file_name(), "a.txt"); + assert!(first_entry.metadata().await.unwrap().is_file()); + assert!(first_entry.file_type().await.unwrap().is_file()); +} diff --git a/vendor/tokio/tests/fs_file.rs b/vendor/tokio/tests/fs_file.rs index bf2f1d7b5..94d872a57 100644 --- a/vendor/tokio/tests/fs_file.rs +++ b/vendor/tokio/tests/fs_file.rs @@ -1,12 +1,11 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] - -use tokio::fs::File; -use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; -use tokio_test::task; +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations use std::io::prelude::*; use tempfile::NamedTempFile; +use tokio::fs::File; +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt, SeekFrom}; +use tokio_test::task; const HELLO: &[u8] = b"hello world..."; @@ -51,6 +50,19 @@ async fn basic_write_and_shutdown() { } #[tokio::test] +async fn rewind_seek_position() { + let tempfile = tempfile(); + + let mut file = File::create(tempfile.path()).await.unwrap(); + + file.seek(SeekFrom::Current(10)).await.unwrap(); + + file.rewind().await.unwrap(); + + assert_eq!(file.stream_position().await.unwrap(), 0); +} + +#[tokio::test] async fn coop() { let mut tempfile = tempfile(); tempfile.write_all(HELLO).unwrap(); @@ -61,7 +73,7 @@ async fn coop() { let mut buf = [0; 1024]; loop { - file.read(&mut buf).await.unwrap(); + let _ = file.read(&mut buf).await.unwrap(); file.seek(std::io::SeekFrom::Start(0)).await.unwrap(); } }); @@ -75,13 +87,93 @@ async fn coop() { panic!("did not yield"); } +#[tokio::test] +async fn write_to_clone() { + let tempfile = tempfile(); + + let file = File::create(tempfile.path()).await.unwrap(); + let mut clone = file.try_clone().await.unwrap(); + + clone.write_all(HELLO).await.unwrap(); + clone.flush().await.unwrap(); + + let contents = std::fs::read(tempfile.path()).unwrap(); + assert_eq!(contents, HELLO); +} + +#[tokio::test] +async fn write_into_std() { + let tempfile = tempfile(); + + let file = File::create(tempfile.path()).await.unwrap(); + let mut std_file = file.into_std().await; + + std_file.write_all(HELLO).unwrap(); + + let contents = std::fs::read(tempfile.path()).unwrap(); + assert_eq!(contents, HELLO); +} + +#[tokio::test] +async fn write_into_std_immediate() { + let tempfile = tempfile(); + + let file = File::create(tempfile.path()).await.unwrap(); + let mut std_file = file.try_into_std().unwrap(); + + std_file.write_all(HELLO).unwrap(); + + let contents = std::fs::read(tempfile.path()).unwrap(); + assert_eq!(contents, HELLO); +} + +#[tokio::test] +async fn read_file_from_std() { + let mut tempfile = tempfile(); + tempfile.write_all(HELLO).unwrap(); + + let std_file = std::fs::File::open(tempfile.path()).unwrap(); + let mut file = File::from(std_file); + + let mut buf = [0; 1024]; + let n = file.read(&mut buf).await.unwrap(); + assert_eq!(n, HELLO.len()); + assert_eq!(&buf[..n], HELLO); +} + fn tempfile() -> NamedTempFile { NamedTempFile::new().unwrap() } #[tokio::test] #[cfg(unix)] -async fn unix_fd() { +async fn file_debug_fmt() { + let tempfile = tempfile(); + + let file = File::open(tempfile.path()).await.unwrap(); + + assert_eq!( + &format!("{:?}", file)[0..33], + "tokio::fs::File { std: File { fd:" + ); +} + +#[tokio::test] +#[cfg(windows)] +async fn file_debug_fmt() { + let tempfile = tempfile(); + + let file = File::open(tempfile.path()).await.unwrap(); + + assert_eq!( + &format!("{:?}", file)[0..37], + "tokio::fs::File { std: File { handle:" + ); +} + +#[tokio::test] +#[cfg(unix)] +async fn unix_fd_is_valid() { use std::os::unix::io::AsRawFd; let tempfile = tempfile(); @@ -90,6 +182,26 @@ async fn unix_fd() { } #[tokio::test] +#[cfg(unix)] +async fn read_file_from_unix_fd() { + use std::os::unix::io::{FromRawFd, IntoRawFd}; + + let mut tempfile = tempfile(); + tempfile.write_all(HELLO).unwrap(); + + let file1 = File::open(tempfile.path()).await.unwrap(); + let raw_fd = file1.into_std().await.into_raw_fd(); + assert!(raw_fd > 0); + + let mut file2 = unsafe { File::from_raw_fd(raw_fd) }; + + let mut buf = [0; 1024]; + let n = file2.read(&mut buf).await.unwrap(); + assert_eq!(n, HELLO.len()); + assert_eq!(&buf[..n], HELLO); +} + +#[tokio::test] #[cfg(windows)] async fn windows_handle() { use std::os::windows::io::AsRawHandle; diff --git a/vendor/tokio/tests/fs_link.rs b/vendor/tokio/tests/fs_link.rs index 2ef666fb2..681b56660 100644 --- a/vendor/tokio/tests/fs_link.rs +++ b/vendor/tokio/tests/fs_link.rs @@ -1,10 +1,9 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations use tokio::fs; -use std::io::prelude::*; -use std::io::BufReader; +use std::io::Write; use tempfile::tempdir; #[tokio::test] @@ -13,24 +12,23 @@ async fn test_hard_link() { let src = dir.path().join("src.txt"); let dst = dir.path().join("dst.txt"); - { - let mut file = std::fs::File::create(&src).unwrap(); - file.write_all(b"hello").unwrap(); - } + std::fs::File::create(&src) + .unwrap() + .write_all(b"hello") + .unwrap(); - let dst_2 = dst.clone(); + fs::hard_link(&src, &dst).await.unwrap(); - assert!(fs::hard_link(src, dst_2.clone()).await.is_ok()); + std::fs::File::create(&src) + .unwrap() + .write_all(b"new-data") + .unwrap(); - let mut content = String::new(); + let content = fs::read(&dst).await.unwrap(); + assert_eq!(content, b"new-data"); - { - let file = std::fs::File::open(dst).unwrap(); - let mut reader = BufReader::new(file); - reader.read_to_string(&mut content).unwrap(); - } - - assert!(content == "hello"); + // test that this is not a symlink: + assert!(fs::read_link(&dst).await.is_err()); } #[cfg(unix)] @@ -40,25 +38,20 @@ async fn test_symlink() { let src = dir.path().join("src.txt"); let dst = dir.path().join("dst.txt"); - { - let mut file = std::fs::File::create(&src).unwrap(); - file.write_all(b"hello").unwrap(); - } - - let src_2 = src.clone(); - let dst_2 = dst.clone(); - - assert!(fs::symlink(src_2.clone(), dst_2.clone()).await.is_ok()); + std::fs::File::create(&src) + .unwrap() + .write_all(b"hello") + .unwrap(); - let mut content = String::new(); + fs::symlink(&src, &dst).await.unwrap(); - { - let file = std::fs::File::open(dst.clone()).unwrap(); - let mut reader = BufReader::new(file); - reader.read_to_string(&mut content).unwrap(); - } + std::fs::File::create(&src) + .unwrap() + .write_all(b"new-data") + .unwrap(); - assert!(content == "hello"); + let content = fs::read(&dst).await.unwrap(); + assert_eq!(content, b"new-data"); let read = fs::read_link(dst.clone()).await.unwrap(); assert!(read == src); diff --git a/vendor/tokio/tests/fs_open_options.rs b/vendor/tokio/tests/fs_open_options.rs new file mode 100644 index 000000000..e8d8b51b3 --- /dev/null +++ b/vendor/tokio/tests/fs_open_options.rs @@ -0,0 +1,80 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use std::io::Write; +use tempfile::NamedTempFile; +use tokio::fs::OpenOptions; +use tokio::io::AsyncReadExt; + +const HELLO: &[u8] = b"hello world..."; + +#[tokio::test] +async fn open_with_open_options_and_read() { + let mut tempfile = NamedTempFile::new().unwrap(); + tempfile.write_all(HELLO).unwrap(); + + let mut file = OpenOptions::new().read(true).open(tempfile).await.unwrap(); + + let mut buf = [0; 1024]; + let n = file.read(&mut buf).await.unwrap(); + + assert_eq!(n, HELLO.len()); + assert_eq!(&buf[..n], HELLO); +} + +#[tokio::test] +async fn open_options_write() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().write(true)).contains("write: true")); +} + +#[tokio::test] +async fn open_options_append() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().append(true)).contains("append: true")); +} + +#[tokio::test] +async fn open_options_truncate() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().truncate(true)).contains("truncate: true")); +} + +#[tokio::test] +async fn open_options_create() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().create(true)).contains("create: true")); +} + +#[tokio::test] +async fn open_options_create_new() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().create_new(true)).contains("create_new: true")); +} + +#[tokio::test] +#[cfg(unix)] +async fn open_options_mode() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().mode(0o644)).contains("mode: 420 ")); +} + +#[tokio::test] +#[cfg(target_os = "linux")] +async fn open_options_custom_flags_linux() { + // TESTING HACK: use Debug output to check the stored data + assert!( + format!("{:?}", OpenOptions::new().custom_flags(libc::O_TRUNC)) + .contains("custom_flags: 512,") + ); +} + +#[tokio::test] +#[cfg(any(target_os = "freebsd", target_os = "macos"))] +async fn open_options_custom_flags_bsd_family() { + // TESTING HACK: use Debug output to check the stored data + assert!( + format!("{:?}", OpenOptions::new().custom_flags(libc::O_NOFOLLOW)) + .contains("custom_flags: 256,") + ); +} diff --git a/vendor/tokio/tests/fs_open_options_windows.rs b/vendor/tokio/tests/fs_open_options_windows.rs new file mode 100644 index 000000000..398e6a12b --- /dev/null +++ b/vendor/tokio/tests/fs_open_options_windows.rs @@ -0,0 +1,51 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations +#![cfg(windows)] + +use tokio::fs::OpenOptions; +use windows_sys::Win32::Storage::FileSystem; + +#[tokio::test] +#[cfg(windows)] +async fn open_options_windows_access_mode() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().access_mode(0)).contains("access_mode: Some(0)")); +} + +#[tokio::test] +#[cfg(windows)] +async fn open_options_windows_share_mode() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!("{:?}", OpenOptions::new().share_mode(0)).contains("share_mode: 0,")); +} + +#[tokio::test] +#[cfg(windows)] +async fn open_options_windows_custom_flags() { + // TESTING HACK: use Debug output to check the stored data + assert!(format!( + "{:?}", + OpenOptions::new().custom_flags(FileSystem::FILE_FLAG_DELETE_ON_CLOSE) + ) + .contains("custom_flags: 67108864,")); +} + +#[tokio::test] +#[cfg(windows)] +async fn open_options_windows_attributes() { + assert!(format!( + "{:?}", + OpenOptions::new().attributes(FileSystem::FILE_ATTRIBUTE_HIDDEN) + ) + .contains("attributes: 2,")); +} + +#[tokio::test] +#[cfg(windows)] +async fn open_options_windows_security_qos_flags() { + assert!(format!( + "{:?}", + OpenOptions::new().security_qos_flags(FileSystem::SECURITY_IDENTIFICATION) + ) + .contains("security_qos_flags: 1114112,")); +} diff --git a/vendor/tokio/tests/fs_remove_dir_all.rs b/vendor/tokio/tests/fs_remove_dir_all.rs new file mode 100644 index 000000000..53a2fd34f --- /dev/null +++ b/vendor/tokio/tests/fs_remove_dir_all.rs @@ -0,0 +1,31 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn remove_dir_all() { + let temp_dir = tempdir().unwrap(); + + let test_dir = temp_dir.path().join("test"); + fs::create_dir(&test_dir).await.unwrap(); + + let file_path = test_dir.as_path().join("a.txt"); + + fs::write(&file_path, b"Hello File!").await.unwrap(); + + fs::remove_dir_all(test_dir.as_path()).await.unwrap(); + + // test dir should no longer exist + match fs::try_exists(test_dir).await { + Ok(exists) => assert!(!exists), + Err(_) => println!("ignored try_exists error after remove_dir_all"), + }; + + // contents should no longer exist + match fs::try_exists(file_path).await { + Ok(exists) => assert!(!exists), + Err(_) => println!("ignored try_exists error after remove_dir_all"), + }; +} diff --git a/vendor/tokio/tests/fs_remove_file.rs b/vendor/tokio/tests/fs_remove_file.rs new file mode 100644 index 000000000..14fd680da --- /dev/null +++ b/vendor/tokio/tests/fs_remove_file.rs @@ -0,0 +1,24 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn remove_file() { + let temp_dir = tempdir().unwrap(); + + let file_path = temp_dir.path().join("a.txt"); + + fs::write(&file_path, b"Hello File!").await.unwrap(); + + assert!(fs::try_exists(&file_path).await.unwrap()); + + fs::remove_file(&file_path).await.unwrap(); + + // should no longer exist + match fs::try_exists(file_path).await { + Ok(exists) => assert!(!exists), + Err(_) => println!("ignored try_exists error after remove_file"), + }; +} diff --git a/vendor/tokio/tests/fs_rename.rs b/vendor/tokio/tests/fs_rename.rs new file mode 100644 index 000000000..382fea079 --- /dev/null +++ b/vendor/tokio/tests/fs_rename.rs @@ -0,0 +1,28 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn rename_file() { + let temp_dir = tempdir().unwrap(); + + let file_path = temp_dir.path().join("a.txt"); + + fs::write(&file_path, b"Hello File!").await.unwrap(); + + assert!(fs::try_exists(&file_path).await.unwrap()); + + let new_file_path = temp_dir.path().join("b.txt"); + + fs::rename(&file_path, &new_file_path).await.unwrap(); + + assert!(fs::try_exists(new_file_path).await.unwrap()); + + // original file should no longer exist + match fs::try_exists(file_path).await { + Ok(exists) => assert!(!exists), + Err(_) => println!("ignored try_exists error after rename"), + }; +} diff --git a/vendor/tokio/tests/fs_symlink_dir_windows.rs b/vendor/tokio/tests/fs_symlink_dir_windows.rs new file mode 100644 index 000000000..d80354268 --- /dev/null +++ b/vendor/tokio/tests/fs_symlink_dir_windows.rs @@ -0,0 +1,31 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations +#![cfg(windows)] + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn symlink_file_windows() { + const FILE_NAME: &str = "abc.txt"; + + let temp_dir = tempdir().unwrap(); + + let dir1 = temp_dir.path().join("a"); + fs::create_dir(&dir1).await.unwrap(); + + let file1 = dir1.as_path().join(FILE_NAME); + fs::write(&file1, b"Hello File!").await.unwrap(); + + let dir2 = temp_dir.path().join("b"); + fs::symlink_dir(&dir1, &dir2).await.unwrap(); + + fs::write(&file1, b"new data!").await.unwrap(); + + let file2 = dir2.as_path().join(FILE_NAME); + + let from = fs::read(&file1).await.unwrap(); + let to = fs::read(&file2).await.unwrap(); + + assert_eq!(from, to); +} diff --git a/vendor/tokio/tests/fs_symlink_file_windows.rs b/vendor/tokio/tests/fs_symlink_file_windows.rs new file mode 100644 index 000000000..50eaf8aab --- /dev/null +++ b/vendor/tokio/tests/fs_symlink_file_windows.rs @@ -0,0 +1,24 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations +#![cfg(windows)] + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn symlink_file_windows() { + let dir = tempdir().unwrap(); + + let source_path = dir.path().join("foo.txt"); + let dest_path = dir.path().join("bar.txt"); + + fs::write(&source_path, b"Hello File!").await.unwrap(); + fs::symlink_file(&source_path, &dest_path).await.unwrap(); + + fs::write(&source_path, b"new data!").await.unwrap(); + + let from = fs::read(&source_path).await.unwrap(); + let to = fs::read(&dest_path).await.unwrap(); + + assert_eq!(from, to); +} diff --git a/vendor/tokio/tests/fs_try_exists.rs b/vendor/tokio/tests/fs_try_exists.rs new file mode 100644 index 000000000..00d236500 --- /dev/null +++ b/vendor/tokio/tests/fs_try_exists.rs @@ -0,0 +1,44 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // WASI does not support all fs operations + +use tempfile::tempdir; +use tokio::fs; + +#[tokio::test] +async fn try_exists() { + let dir = tempdir().unwrap(); + + let existing_path = dir.path().join("foo.txt"); + fs::write(&existing_path, b"Hello File!").await.unwrap(); + let nonexisting_path = dir.path().join("bar.txt"); + + assert!(fs::try_exists(existing_path).await.unwrap()); + assert!(!fs::try_exists(nonexisting_path).await.unwrap()); + // FreeBSD root user always has permission to stat. + #[cfg(all(unix, not(target_os = "freebsd")))] + { + use std::os::unix::prelude::PermissionsExt; + let permission_denied_directory_path = dir.path().join("baz"); + fs::create_dir(&permission_denied_directory_path) + .await + .unwrap(); + let permission_denied_file_path = permission_denied_directory_path.join("baz.txt"); + fs::write(&permission_denied_file_path, b"Hello File!") + .await + .unwrap(); + let mut perms = tokio::fs::metadata(&permission_denied_directory_path) + .await + .unwrap() + .permissions(); + + perms.set_mode(0o244); + fs::set_permissions(&permission_denied_directory_path, perms) + .await + .unwrap(); + let permission_denied_result = fs::try_exists(permission_denied_file_path).await; + assert_eq!( + permission_denied_result.err().unwrap().kind(), + std::io::ErrorKind::PermissionDenied + ); + } +} diff --git a/vendor/tokio/tests/io_async_fd.rs b/vendor/tokio/tests/io_async_fd.rs index dc21e426f..cdbfbacd0 100644 --- a/vendor/tokio/tests/io_async_fd.rs +++ b/vendor/tokio/tests/io_async_fd.rs @@ -15,7 +15,7 @@ use std::{ use nix::unistd::{close, read, write}; -use futures::{poll, FutureExt}; +use futures::poll; use tokio::io::unix::{AsyncFd, AsyncFdReadyGuard}; use tokio_test::{assert_err, assert_pending}; @@ -163,10 +163,11 @@ async fn initially_writable() { afd_a.writable().await.unwrap().clear_ready(); afd_b.writable().await.unwrap().clear_ready(); - futures::select_biased! { - _ = tokio::time::sleep(Duration::from_millis(10)).fuse() => {}, - _ = afd_a.readable().fuse() => panic!("Unexpected readable state"), - _ = afd_b.readable().fuse() => panic!("Unexpected readable state"), + tokio::select! { + biased; + _ = tokio::time::sleep(Duration::from_millis(10)) => {}, + _ = afd_a.readable() => panic!("Unexpected readable state"), + _ = afd_b.readable() => panic!("Unexpected readable state"), } } @@ -353,12 +354,13 @@ async fn multiple_waiters() { futures::future::pending::<()>().await; }; - futures::select_biased! { - guard = afd_a.readable().fuse() => { + tokio::select! { + biased; + guard = afd_a.readable() => { tokio::task::yield_now().await; guard.unwrap().clear_ready() }, - _ = notify_barrier.fuse() => unreachable!(), + _ = notify_barrier => unreachable!(), } std::mem::drop(afd_a); @@ -597,3 +599,89 @@ fn driver_shutdown_wakes_poll_race() { assert_err!(futures::executor::block_on(poll_writable(&afd_a))); } } + +#[tokio::test] +#[cfg(any(target_os = "linux", target_os = "android"))] +async fn priority_event_on_oob_data() { + use std::net::SocketAddr; + + use tokio::io::Interest; + + let addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); + + let listener = std::net::TcpListener::bind(addr).unwrap(); + let addr = listener.local_addr().unwrap(); + + let client = std::net::TcpStream::connect(addr).unwrap(); + let client = AsyncFd::with_interest(client, Interest::PRIORITY).unwrap(); + + let (stream, _) = listener.accept().unwrap(); + + // Sending out of band data should trigger priority event. + send_oob_data(&stream, b"hello").unwrap(); + + let _ = client.ready(Interest::PRIORITY).await.unwrap(); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn send_oob_data<S: AsRawFd>(stream: &S, data: &[u8]) -> io::Result<usize> { + unsafe { + let res = libc::send( + stream.as_raw_fd(), + data.as_ptr().cast(), + data.len(), + libc::MSG_OOB, + ); + if res == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(res as usize) + } + } +} + +#[tokio::test] +async fn clear_ready_matching_clears_ready() { + use tokio::io::{Interest, Ready}; + + let (a, mut b) = socketpair(); + + let afd_a = AsyncFd::new(a).unwrap(); + b.write_all(b"0").unwrap(); + + let mut guard = afd_a + .ready(Interest::READABLE | Interest::WRITABLE) + .await + .unwrap(); + + assert_eq!(guard.ready(), Ready::READABLE | Ready::WRITABLE); + + guard.clear_ready_matching(Ready::READABLE); + assert_eq!(guard.ready(), Ready::WRITABLE); + + guard.clear_ready_matching(Ready::WRITABLE); + assert_eq!(guard.ready(), Ready::EMPTY); +} + +#[tokio::test] +async fn clear_ready_matching_clears_ready_mut() { + use tokio::io::{Interest, Ready}; + + let (a, mut b) = socketpair(); + + let mut afd_a = AsyncFd::new(a).unwrap(); + b.write_all(b"0").unwrap(); + + let mut guard = afd_a + .ready_mut(Interest::READABLE | Interest::WRITABLE) + .await + .unwrap(); + + assert_eq!(guard.ready(), Ready::READABLE | Ready::WRITABLE); + + guard.clear_ready_matching(Ready::READABLE); + assert_eq!(guard.ready(), Ready::WRITABLE); + + guard.clear_ready_matching(Ready::WRITABLE); + assert_eq!(guard.ready(), Ready::EMPTY); +} diff --git a/vendor/tokio/tests/io_buf_reader.rs b/vendor/tokio/tests/io_buf_reader.rs index c72c058d7..0d3f6bafc 100644 --- a/vendor/tokio/tests/io_buf_reader.rs +++ b/vendor/tokio/tests/io_buf_reader.rs @@ -8,9 +8,11 @@ use std::cmp; use std::io::{self, Cursor}; use std::pin::Pin; use tokio::io::{ - AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, BufReader, - ReadBuf, SeekFrom, + AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWriteExt, + BufReader, ReadBuf, SeekFrom, }; +use tokio_test::task::spawn; +use tokio_test::{assert_pending, assert_ready}; macro_rules! run_fill_buf { ($reader:expr) => {{ @@ -348,3 +350,30 @@ async fn maybe_pending_seek() { Pin::new(&mut reader).consume(1); assert_eq!(reader.seek(SeekFrom::Current(-2)).await.unwrap(), 3); } + +// This tests the AsyncBufReadExt::fill_buf wrapper. +#[tokio::test] +async fn test_fill_buf_wrapper() { + let (mut write, read) = tokio::io::duplex(16); + + let mut read = BufReader::new(read); + write.write_all(b"hello world").await.unwrap(); + + assert_eq!(read.fill_buf().await.unwrap(), b"hello world"); + read.consume(b"hello ".len()); + assert_eq!(read.fill_buf().await.unwrap(), b"world"); + assert_eq!(read.fill_buf().await.unwrap(), b"world"); + read.consume(b"world".len()); + + let mut fill = spawn(read.fill_buf()); + assert_pending!(fill.poll()); + + write.write_all(b"foo bar").await.unwrap(); + assert_eq!(assert_ready!(fill.poll()).unwrap(), b"foo bar"); + drop(fill); + + drop(write); + assert_eq!(read.fill_buf().await.unwrap(), b"foo bar"); + read.consume(b"foo bar".len()); + assert_eq!(read.fill_buf().await.unwrap(), b""); +} diff --git a/vendor/tokio/tests/io_buf_writer.rs b/vendor/tokio/tests/io_buf_writer.rs index 47a0d466f..d3acf62c7 100644 --- a/vendor/tokio/tests/io_buf_writer.rs +++ b/vendor/tokio/tests/io_buf_writer.rs @@ -70,15 +70,15 @@ where async fn buf_writer() { let mut writer = BufWriter::with_capacity(2, Vec::new()); - writer.write(&[0, 1]).await.unwrap(); + assert_eq!(writer.write(&[0, 1]).await.unwrap(), 2); assert_eq!(writer.buffer(), []); assert_eq!(*writer.get_ref(), [0, 1]); - writer.write(&[2]).await.unwrap(); + assert_eq!(writer.write(&[2]).await.unwrap(), 1); assert_eq!(writer.buffer(), [2]); assert_eq!(*writer.get_ref(), [0, 1]); - writer.write(&[3]).await.unwrap(); + assert_eq!(writer.write(&[3]).await.unwrap(), 1); assert_eq!(writer.buffer(), [2, 3]); assert_eq!(*writer.get_ref(), [0, 1]); @@ -86,20 +86,20 @@ async fn buf_writer() { assert_eq!(writer.buffer(), []); assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - writer.write(&[4]).await.unwrap(); - writer.write(&[5]).await.unwrap(); + assert_eq!(writer.write(&[4]).await.unwrap(), 1); + assert_eq!(writer.write(&[5]).await.unwrap(), 1); assert_eq!(writer.buffer(), [4, 5]); assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - writer.write(&[6]).await.unwrap(); + assert_eq!(writer.write(&[6]).await.unwrap(), 1); assert_eq!(writer.buffer(), [6]); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); - writer.write(&[7, 8]).await.unwrap(); + assert_eq!(writer.write(&[7, 8]).await.unwrap(), 2); assert_eq!(writer.buffer(), []); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); - writer.write(&[9, 10, 11]).await.unwrap(); + assert_eq!(writer.write(&[9, 10, 11]).await.unwrap(), 3); assert_eq!(writer.buffer(), []); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); @@ -111,7 +111,7 @@ async fn buf_writer() { #[tokio::test] async fn buf_writer_inner_flushes() { let mut w = BufWriter::with_capacity(3, Vec::new()); - w.write(&[0, 1]).await.unwrap(); + assert_eq!(w.write(&[0, 1]).await.unwrap(), 2); assert_eq!(*w.get_ref(), []); w.flush().await.unwrap(); let w = w.into_inner(); @@ -135,15 +135,15 @@ async fn buf_writer_seek() { async fn maybe_pending_buf_writer() { let mut writer = BufWriter::with_capacity(2, MaybePending::new(Vec::new())); - writer.write(&[0, 1]).await.unwrap(); + assert_eq!(writer.write(&[0, 1]).await.unwrap(), 2); assert_eq!(writer.buffer(), []); assert_eq!(&writer.get_ref().inner, &[0, 1]); - writer.write(&[2]).await.unwrap(); + assert_eq!(writer.write(&[2]).await.unwrap(), 1); assert_eq!(writer.buffer(), [2]); assert_eq!(&writer.get_ref().inner, &[0, 1]); - writer.write(&[3]).await.unwrap(); + assert_eq!(writer.write(&[3]).await.unwrap(), 1); assert_eq!(writer.buffer(), [2, 3]); assert_eq!(&writer.get_ref().inner, &[0, 1]); @@ -151,20 +151,20 @@ async fn maybe_pending_buf_writer() { assert_eq!(writer.buffer(), []); assert_eq!(&writer.get_ref().inner, &[0, 1, 2, 3]); - writer.write(&[4]).await.unwrap(); - writer.write(&[5]).await.unwrap(); + assert_eq!(writer.write(&[4]).await.unwrap(), 1); + assert_eq!(writer.write(&[5]).await.unwrap(), 1); assert_eq!(writer.buffer(), [4, 5]); assert_eq!(&writer.get_ref().inner, &[0, 1, 2, 3]); - writer.write(&[6]).await.unwrap(); + assert_eq!(writer.write(&[6]).await.unwrap(), 1); assert_eq!(writer.buffer(), [6]); assert_eq!(writer.get_ref().inner, &[0, 1, 2, 3, 4, 5]); - writer.write(&[7, 8]).await.unwrap(); + assert_eq!(writer.write(&[7, 8]).await.unwrap(), 2); assert_eq!(writer.buffer(), []); assert_eq!(writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8]); - writer.write(&[9, 10, 11]).await.unwrap(); + assert_eq!(writer.write(&[9, 10, 11]).await.unwrap(), 3); assert_eq!(writer.buffer(), []); assert_eq!( writer.get_ref().inner, @@ -182,7 +182,7 @@ async fn maybe_pending_buf_writer() { #[tokio::test] async fn maybe_pending_buf_writer_inner_flushes() { let mut w = BufWriter::with_capacity(3, MaybePending::new(Vec::new())); - w.write(&[0, 1]).await.unwrap(); + assert_eq!(w.write(&[0, 1]).await.unwrap(), 2); assert_eq!(&w.get_ref().inner, &[]); w.flush().await.unwrap(); let w = w.into_inner().inner; diff --git a/vendor/tokio/tests/io_copy.rs b/vendor/tokio/tests/io_copy.rs index 9ed7995c7..005e17011 100644 --- a/vendor/tokio/tests/io_copy.rs +++ b/vendor/tokio/tests/io_copy.rs @@ -1,7 +1,9 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "full")] -use tokio::io::{self, AsyncRead, ReadBuf}; +use bytes::BytesMut; +use futures::ready; +use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use tokio_test::assert_ok; use std::pin::Pin; @@ -34,3 +36,52 @@ async fn copy() { assert_eq!(n, 11); assert_eq!(wr, b"hello world"); } + +#[tokio::test] +async fn proxy() { + struct BufferedWd { + buf: BytesMut, + writer: io::DuplexStream, + } + + impl AsyncWrite for BufferedWd { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll<io::Result<usize>> { + self.get_mut().buf.extend_from_slice(buf); + Poll::Ready(Ok(buf.len())) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { + let this = self.get_mut(); + + while !this.buf.is_empty() { + let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &this.buf))?; + let _ = this.buf.split_to(n); + } + + Pin::new(&mut this.writer).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { + Pin::new(&mut self.writer).poll_shutdown(cx) + } + } + + let (rd, wd) = io::duplex(1024); + let mut rd = rd.take(1024); + let mut wd = BufferedWd { + buf: BytesMut::new(), + writer: wd, + }; + + // write start bytes + assert_ok!(wd.write_all(&[0x42; 512]).await); + assert_ok!(wd.flush().await); + + let n = assert_ok!(io::copy(&mut rd, &mut wd).await); + + assert_eq!(n, 1024); +} diff --git a/vendor/tokio/tests/io_copy_bidirectional.rs b/vendor/tokio/tests/io_copy_bidirectional.rs index 0ce7f85c5..c54967595 100644 --- a/vendor/tokio/tests/io_copy_bidirectional.rs +++ b/vendor/tokio/tests/io_copy_bidirectional.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support bind() use std::time::Duration; use tokio::io::{self, copy_bidirectional, AsyncReadExt, AsyncWriteExt}; @@ -26,7 +26,7 @@ async fn block_write(s: &mut TcpStream) -> usize { result = s.write(&BUF) => { copied += result.expect("write error") }, - _ = tokio::time::sleep(Duration::from_millis(100)) => { + _ = tokio::time::sleep(Duration::from_millis(10)) => { break; } } @@ -111,18 +111,30 @@ async fn blocking_one_side_does_not_block_other() { } #[tokio::test] -async fn immediate_exit_on_error() { - symmetric(|handle, mut a, mut b| async move { - block_write(&mut a).await; +async fn immediate_exit_on_write_error() { + let payload = b"here, take this"; + let error = || io::Error::new(io::ErrorKind::Other, "no thanks!"); - // Fill up the b->copy->a path. We expect that this will _not_ drain - // before we exit the copy task. - let _bytes_written = block_write(&mut b).await; + let mut a = tokio_test::io::Builder::new() + .read(payload) + .write_error(error()) + .build(); - // Drop b. We should not wait for a to consume the data buffered in the - // copy loop, since b will be failing writes. - drop(b); - assert!(handle.await.unwrap().is_err()); - }) - .await + let mut b = tokio_test::io::Builder::new() + .read(payload) + .write_error(error()) + .build(); + + assert!(copy_bidirectional(&mut a, &mut b).await.is_err()); +} + +#[tokio::test] +async fn immediate_exit_on_read_error() { + let error = || io::Error::new(io::ErrorKind::Other, "got nothing!"); + + let mut a = tokio_test::io::Builder::new().read_error(error()).build(); + + let mut b = tokio_test::io::Builder::new().read_error(error()).build(); + + assert!(copy_bidirectional(&mut a, &mut b).await.is_err()); } diff --git a/vendor/tokio/tests/io_driver.rs b/vendor/tokio/tests/io_driver.rs index 6fb566de5..97018e0f9 100644 --- a/vendor/tokio/tests/io_driver.rs +++ b/vendor/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(tokio_wasi)))] use tokio::net::TcpListener; use tokio::runtime; @@ -79,7 +80,7 @@ fn test_drop_on_notify() { drop(task); // Establish a connection to the acceptor - let _s = TcpStream::connect(&addr).unwrap(); + let _s = TcpStream::connect(addr).unwrap(); // Force the reactor to turn rt.block_on(async {}); diff --git a/vendor/tokio/tests/io_driver_drop.rs b/vendor/tokio/tests/io_driver_drop.rs index 631e66e9f..0a384d419 100644 --- a/vendor/tokio/tests/io_driver_drop.rs +++ b/vendor/tokio/tests/io_driver_drop.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support bind use tokio::net::TcpListener; use tokio::runtime; diff --git a/vendor/tokio/tests/io_fill_buf.rs b/vendor/tokio/tests/io_fill_buf.rs new file mode 100644 index 000000000..62c3f1b37 --- /dev/null +++ b/vendor/tokio/tests/io_fill_buf.rs @@ -0,0 +1,34 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support file operations + +use tempfile::NamedTempFile; +use tokio::fs::File; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio_test::assert_ok; + +#[tokio::test] +async fn fill_buf_file() { + let file = NamedTempFile::new().unwrap(); + + assert_ok!(std::fs::write(file.path(), b"hello")); + + let file = assert_ok!(File::open(file.path()).await); + let mut file = BufReader::new(file); + + let mut contents = Vec::new(); + + loop { + let consumed = { + let buffer = assert_ok!(file.fill_buf().await); + if buffer.is_empty() { + break; + } + contents.extend_from_slice(buffer); + buffer.len() + }; + + file.consume(consumed); + } + + assert_eq!(contents, b"hello"); +} diff --git a/vendor/tokio/tests/io_mem_stream.rs b/vendor/tokio/tests/io_mem_stream.rs index 01baa5369..e79ec4e9d 100644 --- a/vendor/tokio/tests/io_mem_stream.rs +++ b/vendor/tokio/tests/io_mem_stream.rs @@ -100,3 +100,22 @@ async fn max_write_size() { // drop b only after task t1 finishes writing drop(b); } + +#[tokio::test] +async fn duplex_is_cooperative() { + let (mut tx, mut rx) = tokio::io::duplex(1024 * 8); + + tokio::select! { + biased; + + _ = async { + loop { + let buf = [3u8; 4096]; + tx.write_all(&buf).await.unwrap(); + let mut buf = [0u8; 4096]; + let _ = rx.read(&mut buf).await.unwrap(); + } + } => {}, + _ = tokio::task::yield_now() => {} + } +} diff --git a/vendor/tokio/tests/io_panic.rs b/vendor/tokio/tests/io_panic.rs new file mode 100644 index 000000000..922d7c076 --- /dev/null +++ b/vendor/tokio/tests/io_panic.rs @@ -0,0 +1,176 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery + +use std::task::{Context, Poll}; +use std::{error::Error, pin::Pin}; +use tokio::io::{self, split, AsyncRead, AsyncWrite, ReadBuf}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +struct RW; + +impl AsyncRead for RW { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll<io::Result<()>> { + buf.put_slice(&[b'z']); + Poll::Ready(Ok(())) + } +} + +impl AsyncWrite for RW { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll<Result<usize, io::Error>> { + Poll::Ready(Ok(1)) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { + Poll::Ready(Ok(())) + } +} + +#[cfg(unix)] +mod unix { + use std::os::unix::prelude::{AsRawFd, RawFd}; + + pub struct MockFd; + + impl AsRawFd for MockFd { + fn as_raw_fd(&self) -> RawFd { + 0 + } + } +} + +#[test] +fn read_buf_initialize_unfilled_to_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let mut buffer = Vec::<u8>::new(); + let mut read_buf = ReadBuf::new(&mut buffer); + + read_buf.initialize_unfilled_to(2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn read_buf_advance_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let mut buffer = Vec::<u8>::new(); + let mut read_buf = ReadBuf::new(&mut buffer); + + read_buf.advance(2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn read_buf_set_filled_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let mut buffer = Vec::<u8>::new(); + let mut read_buf = ReadBuf::new(&mut buffer); + + read_buf.set_filled(2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn read_buf_put_slice_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let mut buffer = Vec::<u8>::new(); + let mut read_buf = ReadBuf::new(&mut buffer); + + let new_slice = [0x40_u8, 0x41_u8]; + + read_buf.put_slice(&new_slice); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn unsplit_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let (r1, _w1) = split(RW); + let (_r2, w2) = split(RW); + r1.unsplit(w2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn async_fd_new_panic_caller() -> Result<(), Box<dyn Error>> { + use tokio::io::unix::AsyncFd; + use tokio::runtime::Builder; + + let panic_location_file = test_panic(|| { + // Runtime without `enable_io` so it has no IO driver set. + let rt = Builder::new_current_thread().build().unwrap(); + rt.block_on(async { + let fd = unix::MockFd; + + let _ = AsyncFd::new(fd); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +#[cfg(unix)] +fn async_fd_with_interest_panic_caller() -> Result<(), Box<dyn Error>> { + use tokio::io::unix::AsyncFd; + use tokio::io::Interest; + use tokio::runtime::Builder; + + let panic_location_file = test_panic(|| { + // Runtime without `enable_io` so it has no IO driver set. + let rt = Builder::new_current_thread().build().unwrap(); + rt.block_on(async { + let fd = unix::MockFd; + + let _ = AsyncFd::with_interest(fd, Interest::READABLE); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} diff --git a/vendor/tokio/tests/io_poll_aio.rs b/vendor/tokio/tests/io_poll_aio.rs new file mode 100644 index 000000000..f044af5cc --- /dev/null +++ b/vendor/tokio/tests/io_poll_aio.rs @@ -0,0 +1,375 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(target_os = "freebsd", feature = "net"))] + +use mio_aio::{AioCb, AioFsyncMode, LioCb}; +use std::{ + future::Future, + mem, + os::unix::io::{AsRawFd, RawFd}, + pin::Pin, + task::{Context, Poll}, +}; +use tempfile::tempfile; +use tokio::io::bsd::{Aio, AioSource}; +use tokio_test::assert_pending; + +mod aio { + use super::*; + + /// Adapts mio_aio::AioCb (which implements mio::event::Source) to AioSource + struct WrappedAioCb<'a>(AioCb<'a>); + impl<'a> AioSource for WrappedAioCb<'a> { + fn register(&mut self, kq: RawFd, token: usize) { + self.0.register_raw(kq, token) + } + fn deregister(&mut self) { + self.0.deregister_raw() + } + } + + /// A very crude implementation of an AIO-based future + struct FsyncFut(Aio<WrappedAioCb<'static>>); + + impl Future for FsyncFut { + type Output = std::io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + let poll_result = self.0.poll_ready(cx); + match poll_result { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok(_ev)) => { + // At this point, we could clear readiness. But there's no + // point, since we're about to drop the Aio. + let result = (*self.0).0.aio_return(); + match result { + Ok(_) => Poll::Ready(Ok(())), + Err(e) => Poll::Ready(Err(e.into())), + } + } + } + } + } + + /// Low-level AIO Source + /// + /// An example bypassing mio_aio and Nix to demonstrate how the kevent + /// registration actually works, under the hood. + struct LlSource(Pin<Box<libc::aiocb>>); + + impl AioSource for LlSource { + fn register(&mut self, kq: RawFd, token: usize) { + let mut sev: libc::sigevent = unsafe { mem::MaybeUninit::zeroed().assume_init() }; + sev.sigev_notify = libc::SIGEV_KEVENT; + sev.sigev_signo = kq; + sev.sigev_value = libc::sigval { + sival_ptr: token as *mut libc::c_void, + }; + self.0.aio_sigevent = sev; + } + + fn deregister(&mut self) { + unsafe { + self.0.aio_sigevent = mem::zeroed(); + } + } + } + + struct LlFut(Aio<LlSource>); + + impl Future for LlFut { + type Output = std::io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + let poll_result = self.0.poll_ready(cx); + match poll_result { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok(_ev)) => { + let r = unsafe { libc::aio_return(self.0 .0.as_mut().get_unchecked_mut()) }; + assert_eq!(0, r); + Poll::Ready(Ok(())) + } + } + } + } + + /// A very simple object that can implement AioSource and can be reused. + /// + /// mio_aio normally assumes that each AioCb will be consumed on completion. + /// This somewhat contrived example shows how an Aio object can be reused + /// anyway. + struct ReusableFsyncSource { + aiocb: Pin<Box<AioCb<'static>>>, + fd: RawFd, + token: usize, + } + impl ReusableFsyncSource { + fn fsync(&mut self) { + self.aiocb.register_raw(self.fd, self.token); + self.aiocb.fsync(AioFsyncMode::O_SYNC).unwrap(); + } + fn new(aiocb: AioCb<'static>) -> Self { + ReusableFsyncSource { + aiocb: Box::pin(aiocb), + fd: 0, + token: 0, + } + } + fn reset(&mut self, aiocb: AioCb<'static>) { + self.aiocb = Box::pin(aiocb); + } + } + impl AioSource for ReusableFsyncSource { + fn register(&mut self, kq: RawFd, token: usize) { + self.fd = kq; + self.token = token; + } + fn deregister(&mut self) { + self.fd = 0; + } + } + + struct ReusableFsyncFut<'a>(&'a mut Aio<ReusableFsyncSource>); + impl<'a> Future for ReusableFsyncFut<'a> { + type Output = std::io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + let poll_result = self.0.poll_ready(cx); + match poll_result { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok(ev)) => { + // Since this future uses a reusable Aio, we must clear + // its readiness here. That makes the future + // non-idempotent; the caller can't poll it repeatedly after + // it has already returned Ready. But that's ok; most + // futures behave this way. + self.0.clear_ready(ev); + let result = (*self.0).aiocb.aio_return(); + match result { + Ok(_) => Poll::Ready(Ok(())), + Err(e) => Poll::Ready(Err(e.into())), + } + } + } + } + } + + #[tokio::test] + async fn fsync() { + let f = tempfile().unwrap(); + let fd = f.as_raw_fd(); + let aiocb = AioCb::from_fd(fd, 0); + let source = WrappedAioCb(aiocb); + let mut poll_aio = Aio::new_for_aio(source).unwrap(); + (*poll_aio).0.fsync(AioFsyncMode::O_SYNC).unwrap(); + let fut = FsyncFut(poll_aio); + fut.await.unwrap(); + } + + #[tokio::test] + async fn ll_fsync() { + let f = tempfile().unwrap(); + let fd = f.as_raw_fd(); + let mut aiocb: libc::aiocb = unsafe { mem::MaybeUninit::zeroed().assume_init() }; + aiocb.aio_fildes = fd; + let source = LlSource(Box::pin(aiocb)); + let mut poll_aio = Aio::new_for_aio(source).unwrap(); + let r = unsafe { + let p = (*poll_aio).0.as_mut().get_unchecked_mut(); + libc::aio_fsync(libc::O_SYNC, p) + }; + assert_eq!(0, r); + let fut = LlFut(poll_aio); + fut.await.unwrap(); + } + + /// A suitably crafted future type can reuse an Aio object + #[tokio::test] + async fn reuse() { + let f = tempfile().unwrap(); + let fd = f.as_raw_fd(); + let aiocb0 = AioCb::from_fd(fd, 0); + let source = ReusableFsyncSource::new(aiocb0); + let mut poll_aio = Aio::new_for_aio(source).unwrap(); + poll_aio.fsync(); + let fut0 = ReusableFsyncFut(&mut poll_aio); + fut0.await.unwrap(); + + let aiocb1 = AioCb::from_fd(fd, 0); + poll_aio.reset(aiocb1); + let mut ctx = Context::from_waker(futures::task::noop_waker_ref()); + assert_pending!(poll_aio.poll_ready(&mut ctx)); + poll_aio.fsync(); + let fut1 = ReusableFsyncFut(&mut poll_aio); + fut1.await.unwrap(); + } +} + +mod lio { + use super::*; + + struct WrappedLioCb<'a>(LioCb<'a>); + impl<'a> AioSource for WrappedLioCb<'a> { + fn register(&mut self, kq: RawFd, token: usize) { + self.0.register_raw(kq, token) + } + fn deregister(&mut self) { + self.0.deregister_raw() + } + } + + /// A very crude lio_listio-based Future + struct LioFut(Option<Aio<WrappedLioCb<'static>>>); + + impl Future for LioFut { + type Output = std::io::Result<Vec<isize>>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + let poll_result = self.0.as_mut().unwrap().poll_ready(cx); + match poll_result { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok(_ev)) => { + // At this point, we could clear readiness. But there's no + // point, since we're about to drop the Aio. + let r = self.0.take().unwrap().into_inner().0.into_results(|iter| { + iter.map(|lr| lr.result.unwrap()).collect::<Vec<isize>>() + }); + Poll::Ready(Ok(r)) + } + } + } + } + + /// Minimal example demonstrating reuse of an Aio object with lio + /// readiness. mio_aio::LioCb actually does something similar under the + /// hood. + struct ReusableLioSource { + liocb: Option<LioCb<'static>>, + fd: RawFd, + token: usize, + } + impl ReusableLioSource { + fn new(liocb: LioCb<'static>) -> Self { + ReusableLioSource { + liocb: Some(liocb), + fd: 0, + token: 0, + } + } + fn reset(&mut self, liocb: LioCb<'static>) { + self.liocb = Some(liocb); + } + fn submit(&mut self) { + self.liocb + .as_mut() + .unwrap() + .register_raw(self.fd, self.token); + self.liocb.as_mut().unwrap().submit().unwrap(); + } + } + impl AioSource for ReusableLioSource { + fn register(&mut self, kq: RawFd, token: usize) { + self.fd = kq; + self.token = token; + } + fn deregister(&mut self) { + self.fd = 0; + } + } + struct ReusableLioFut<'a>(&'a mut Aio<ReusableLioSource>); + impl<'a> Future for ReusableLioFut<'a> { + type Output = std::io::Result<Vec<isize>>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + let poll_result = self.0.poll_ready(cx); + match poll_result { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Ready(Ok(ev)) => { + // Since this future uses a reusable Aio, we must clear + // its readiness here. That makes the future + // non-idempotent; the caller can't poll it repeatedly after + // it has already returned Ready. But that's ok; most + // futures behave this way. + self.0.clear_ready(ev); + let r = (*self.0).liocb.take().unwrap().into_results(|iter| { + iter.map(|lr| lr.result.unwrap()).collect::<Vec<isize>>() + }); + Poll::Ready(Ok(r)) + } + } + } + } + + /// An lio_listio operation with one write element + #[tokio::test] + async fn onewrite() { + const WBUF: &[u8] = b"abcdef"; + let f = tempfile().unwrap(); + + let mut builder = mio_aio::LioCbBuilder::with_capacity(1); + builder = builder.emplace_slice( + f.as_raw_fd(), + 0, + &WBUF[..], + 0, + mio_aio::LioOpcode::LIO_WRITE, + ); + let liocb = builder.finish(); + let source = WrappedLioCb(liocb); + let mut poll_aio = Aio::new_for_lio(source).unwrap(); + + // Send the operation to the kernel + (*poll_aio).0.submit().unwrap(); + let fut = LioFut(Some(poll_aio)); + let v = fut.await.unwrap(); + assert_eq!(v.len(), 1); + assert_eq!(v[0] as usize, WBUF.len()); + } + + /// A suitably crafted future type can reuse an Aio object + #[tokio::test] + async fn reuse() { + const WBUF: &[u8] = b"abcdef"; + let f = tempfile().unwrap(); + + let mut builder0 = mio_aio::LioCbBuilder::with_capacity(1); + builder0 = builder0.emplace_slice( + f.as_raw_fd(), + 0, + &WBUF[..], + 0, + mio_aio::LioOpcode::LIO_WRITE, + ); + let liocb0 = builder0.finish(); + let source = ReusableLioSource::new(liocb0); + let mut poll_aio = Aio::new_for_aio(source).unwrap(); + poll_aio.submit(); + let fut0 = ReusableLioFut(&mut poll_aio); + let v = fut0.await.unwrap(); + assert_eq!(v.len(), 1); + assert_eq!(v[0] as usize, WBUF.len()); + + // Now reuse the same Aio + let mut builder1 = mio_aio::LioCbBuilder::with_capacity(1); + builder1 = builder1.emplace_slice( + f.as_raw_fd(), + 0, + &WBUF[..], + 0, + mio_aio::LioOpcode::LIO_WRITE, + ); + let liocb1 = builder1.finish(); + poll_aio.reset(liocb1); + let mut ctx = Context::from_waker(futures::task::noop_waker_ref()); + assert_pending!(poll_aio.poll_ready(&mut ctx)); + poll_aio.submit(); + let fut1 = ReusableLioFut(&mut poll_aio); + let v = fut1.await.unwrap(); + assert_eq!(v.len(), 1); + assert_eq!(v[0] as usize, WBUF.len()); + } +} diff --git a/vendor/tokio/tests/io_read.rs b/vendor/tokio/tests/io_read.rs index cb1aa7052..7f74c5e85 100644 --- a/vendor/tokio/tests/io_read.rs +++ b/vendor/tokio/tests/io_read.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf}; use tokio_test::assert_ok; @@ -8,6 +8,11 @@ use std::io; use std::pin::Pin; use std::task::{Context, Poll}; +mod support { + pub(crate) mod leaked_buffers; +} +use support::leaked_buffers::LeakedBuffers; + #[tokio::test] async fn read() { #[derive(Default)] @@ -37,16 +42,27 @@ async fn read() { assert_eq!(buf[..], b"hello world"[..]); } -struct BadAsyncRead; +struct BadAsyncRead { + leaked_buffers: LeakedBuffers, +} + +impl BadAsyncRead { + fn new() -> Self { + Self { + leaked_buffers: LeakedBuffers::new(), + } + } +} impl AsyncRead for BadAsyncRead { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>> { - *buf = ReadBuf::new(Box::leak(vec![0; buf.capacity()].into_boxed_slice())); + *buf = ReadBuf::new(unsafe { self.leaked_buffers.create(buf.capacity()) }); buf.advance(buf.capacity()); + Poll::Ready(Ok(())) } } @@ -55,5 +71,5 @@ impl AsyncRead for BadAsyncRead { #[should_panic] async fn read_buf_bad_async_read() { let mut buf = Vec::with_capacity(10); - BadAsyncRead.read_buf(&mut buf).await.unwrap(); + BadAsyncRead::new().read_buf(&mut buf).await.unwrap(); } diff --git a/vendor/tokio/tests/io_read_buf.rs b/vendor/tokio/tests/io_read_buf.rs index 0328168d7..49a4f86f8 100644 --- a/vendor/tokio/tests/io_read_buf.rs +++ b/vendor/tokio/tests/io_read_buf.rs @@ -34,3 +34,61 @@ async fn read_buf() { assert_eq!(n, 11); assert_eq!(buf[..], b"hello world"[..]); } + +#[tokio::test] +#[cfg(feature = "io-util")] +async fn issue_5588() { + use bytes::BufMut; + + // steps to zero + let mut buf = [0; 8]; + let mut read_buf = ReadBuf::new(&mut buf); + assert_eq!(read_buf.remaining_mut(), 8); + assert_eq!(read_buf.chunk_mut().len(), 8); + unsafe { + read_buf.advance_mut(1); + } + assert_eq!(read_buf.remaining_mut(), 7); + assert_eq!(read_buf.chunk_mut().len(), 7); + unsafe { + read_buf.advance_mut(5); + } + assert_eq!(read_buf.remaining_mut(), 2); + assert_eq!(read_buf.chunk_mut().len(), 2); + unsafe { + read_buf.advance_mut(2); + } + assert_eq!(read_buf.remaining_mut(), 0); + assert_eq!(read_buf.chunk_mut().len(), 0); + + // directly to zero + let mut buf = [0; 8]; + let mut read_buf = ReadBuf::new(&mut buf); + assert_eq!(read_buf.remaining_mut(), 8); + assert_eq!(read_buf.chunk_mut().len(), 8); + unsafe { + read_buf.advance_mut(8); + } + assert_eq!(read_buf.remaining_mut(), 0); + assert_eq!(read_buf.chunk_mut().len(), 0); + + // uninit + let mut buf = [std::mem::MaybeUninit::new(1); 8]; + let mut uninit = ReadBuf::uninit(&mut buf); + assert_eq!(uninit.remaining_mut(), 8); + assert_eq!(uninit.chunk_mut().len(), 8); + + let mut buf = [std::mem::MaybeUninit::uninit(); 8]; + let mut uninit = ReadBuf::uninit(&mut buf); + unsafe { + uninit.advance_mut(4); + } + assert_eq!(uninit.remaining_mut(), 4); + assert_eq!(uninit.chunk_mut().len(), 4); + uninit.put_u8(1); + assert_eq!(uninit.remaining_mut(), 3); + assert_eq!(uninit.chunk_mut().len(), 3); + uninit.put_slice(&[1, 2, 3]); + assert_eq!(uninit.remaining_mut(), 0); + assert_eq!(uninit.chunk_mut().len(), 0); +} diff --git a/vendor/tokio/tests/io_read_to_end.rs b/vendor/tokio/tests/io_read_to_end.rs index 171e6d648..29b60bece 100644 --- a/vendor/tokio/tests/io_read_to_end.rs +++ b/vendor/tokio/tests/io_read_to_end.rs @@ -5,6 +5,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf}; use tokio_test::assert_ok; +use tokio_test::io::Builder; #[tokio::test] async fn read_to_end() { @@ -76,3 +77,48 @@ async fn read_to_end_uninit() { test.read_to_end(&mut buf).await.unwrap(); assert_eq!(buf.len(), 33); } + +#[tokio::test] +async fn read_to_end_doesnt_grow_with_capacity() { + let arr: Vec<u8> = (0..100).collect(); + + // We only test from 32 since we allocate at least 32 bytes each time + for len in 32..100 { + let bytes = &arr[..len]; + for split in 0..len { + for cap in 0..101 { + let mut mock = if split == 0 { + Builder::new().read(bytes).build() + } else { + Builder::new() + .read(&bytes[..split]) + .read(&bytes[split..]) + .build() + }; + let mut buf = Vec::with_capacity(cap); + AsyncReadExt::read_to_end(&mut mock, &mut buf) + .await + .unwrap(); + // It has the right data. + assert_eq!(buf.as_slice(), bytes); + // Unless cap was smaller than length, then we did not reallocate. + if cap >= len { + assert_eq!(buf.capacity(), cap); + } + } + } + } +} + +#[tokio::test] +async fn read_to_end_grows_capacity_if_unfit() { + let bytes = b"the_vector_startingcap_will_be_smaller"; + let mut mock = Builder::new().read(bytes).build(); + let initial_capacity = bytes.len() - 4; + let mut buf = Vec::with_capacity(initial_capacity); + AsyncReadExt::read_to_end(&mut mock, &mut buf) + .await + .unwrap(); + // *4 since it doubles when it doesn't fit and again when reaching EOF + assert_eq!(buf.capacity(), initial_capacity * 4); +} diff --git a/vendor/tokio/tests/io_split.rs b/vendor/tokio/tests/io_split.rs index db168e9f8..4cbd2a2d1 100644 --- a/vendor/tokio/tests/io_split.rs +++ b/vendor/tokio/tests/io_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery use tokio::io::{split, AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf}; @@ -50,10 +50,10 @@ fn is_send_and_sync() { fn split_stream_id() { let (r1, w1) = split(RW); let (r2, w2) = split(RW); - assert_eq!(r1.is_pair_of(&w1), true); - assert_eq!(r1.is_pair_of(&w2), false); - assert_eq!(r2.is_pair_of(&w2), true); - assert_eq!(r2.is_pair_of(&w1), false); + assert!(r1.is_pair_of(&w1)); + assert!(!r1.is_pair_of(&w2)); + assert!(r2.is_pair_of(&w2)); + assert!(!r2.is_pair_of(&w1)); } #[test] diff --git a/vendor/tokio/tests/io_take.rs b/vendor/tokio/tests/io_take.rs index 683606f72..fba01d2e0 100644 --- a/vendor/tokio/tests/io_take.rs +++ b/vendor/tokio/tests/io_take.rs @@ -1,9 +1,16 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery -use tokio::io::AsyncReadExt; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::{self, AsyncRead, AsyncReadExt, ReadBuf}; use tokio_test::assert_ok; +mod support { + pub(crate) mod leaked_buffers; +} +use support::leaked_buffers::LeakedBuffers; + #[tokio::test] async fn take() { let mut buf = [0; 6]; @@ -14,3 +21,54 @@ async fn take() { assert_eq!(n, 4); assert_eq!(&buf, &b"hell\0\0"[..]); } + +#[tokio::test] +async fn issue_4435() { + let mut buf = [0; 8]; + let rd: &[u8] = b"hello world"; + + let rd = rd.take(4); + tokio::pin!(rd); + + let mut read_buf = ReadBuf::new(&mut buf); + read_buf.put_slice(b"AB"); + + futures::future::poll_fn(|cx| rd.as_mut().poll_read(cx, &mut read_buf)) + .await + .unwrap(); + assert_eq!(&buf, &b"ABhell\0\0"[..]); +} + +struct BadReader { + leaked_buffers: LeakedBuffers, +} + +impl BadReader { + fn new() -> Self { + Self { + leaked_buffers: LeakedBuffers::new(), + } + } +} + +impl AsyncRead for BadReader { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + read_buf: &mut ReadBuf<'_>, + ) -> Poll<io::Result<()>> { + let mut buf = ReadBuf::new(unsafe { self.leaked_buffers.create(10) }); + buf.put_slice(&[123; 10]); + *read_buf = buf; + + Poll::Ready(Ok(())) + } +} + +#[tokio::test] +#[should_panic] +async fn bad_reader_fails() { + let mut buf = Vec::with_capacity(10); + + BadReader::new().take(10).read_buf(&mut buf).await.unwrap(); +} diff --git a/vendor/tokio/tests/io_util_empty.rs b/vendor/tokio/tests/io_util_empty.rs new file mode 100644 index 000000000..e49cd17fc --- /dev/null +++ b/vendor/tokio/tests/io_util_empty.rs @@ -0,0 +1,32 @@ +#![cfg(feature = "full")] +use tokio::io::{AsyncBufReadExt, AsyncReadExt}; + +#[tokio::test] +async fn empty_read_is_cooperative() { + tokio::select! { + biased; + + _ = async { + loop { + let mut buf = [0u8; 4096]; + let _ = tokio::io::empty().read(&mut buf).await; + } + } => {}, + _ = tokio::task::yield_now() => {} + } +} + +#[tokio::test] +async fn empty_buf_reads_are_cooperative() { + tokio::select! { + biased; + + _ = async { + loop { + let mut buf = String::new(); + let _ = tokio::io::empty().read_line(&mut buf).await; + } + } => {}, + _ = tokio::task::yield_now() => {} + } +} diff --git a/vendor/tokio/tests/io_write_all_buf.rs b/vendor/tokio/tests/io_write_all_buf.rs index b49a58ebe..7c8b61935 100644 --- a/vendor/tokio/tests/io_write_all_buf.rs +++ b/vendor/tokio/tests/io_write_all_buf.rs @@ -52,7 +52,7 @@ async fn write_all_buf() { assert_eq!(wr.buf, b"helloworld"[..]); // expect 4 writes, [hell],[o],[worl],[d] assert_eq!(wr.cnt, 4); - assert_eq!(buf.has_remaining(), false); + assert!(!buf.has_remaining()); } #[tokio::test] diff --git a/vendor/tokio/tests/join_handle_panic.rs b/vendor/tokio/tests/join_handle_panic.rs new file mode 100644 index 000000000..bc34fd0ea --- /dev/null +++ b/vendor/tokio/tests/join_handle_panic.rs @@ -0,0 +1,20 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery + +struct PanicsOnDrop; + +impl Drop for PanicsOnDrop { + fn drop(&mut self) { + panic!("I told you so"); + } +} + +#[tokio::test] +async fn test_panics_do_not_propagate_when_dropping_join_handle() { + let join_handle = tokio::spawn(async move { PanicsOnDrop }); + + // only drop the JoinHandle when the task has completed + // (which is difficult to synchronize precisely) + tokio::time::sleep(std::time::Duration::from_millis(3)).await; + drop(join_handle); +} diff --git a/vendor/tokio/tests/macros_join.rs b/vendor/tokio/tests/macros_join.rs index 169e898f9..7866ac086 100644 --- a/vendor/tokio/tests/macros_join.rs +++ b/vendor/tokio/tests/macros_join.rs @@ -1,36 +1,48 @@ -#![allow(clippy::blacklisted_name)] -use tokio::sync::oneshot; +#![cfg(feature = "macros")] +#![allow(clippy::disallowed_names)] +use std::sync::Arc; + +#[cfg(tokio_wasm_not_wasi)] +#[cfg(target_pointer_width = "64")] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; + +use tokio::sync::{oneshot, Semaphore}; use tokio_test::{assert_pending, assert_ready, task}; -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_comma() { let foo = tokio::join!(async { 1 },); assert_eq!(foo, (1,)); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_no_comma() { let foo = tokio::join!(async { 1 }); assert_eq!(foo, (1,)); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_two_lit_expr_comma() { let foo = tokio::join!(async { 1 }, async { 2 },); assert_eq!(foo, (1, 2)); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_two_lit_expr_no_comma() { let foo = tokio::join!(async { 1 }, async { 2 }); assert_eq!(foo, (1, 2)); } -#[tokio::test] +#[maybe_tokio_test] async fn two_await() { let (tx1, rx1) = oneshot::channel::<&str>(); let (tx2, rx2) = oneshot::channel::<u32>(); @@ -53,6 +65,7 @@ async fn two_await() { } #[test] +#[cfg(target_pointer_width = "64")] fn join_size() { use futures::future; use std::mem; @@ -61,12 +74,88 @@ fn join_size() { let ready = future::ready(0i32); tokio::join!(ready) }; - assert_eq!(mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 32); let fut = async { let ready1 = future::ready(0i32); let ready2 = future::ready(0i32); tokio::join!(ready1, ready2) }; - assert_eq!(mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 40); +} + +async fn non_cooperative_task(permits: Arc<Semaphore>) -> usize { + let mut exceeded_budget = 0; + + for _ in 0..5 { + // Another task should run after this task uses its whole budget + for _ in 0..128 { + let _permit = permits.clone().acquire_owned().await.unwrap(); + } + + exceeded_budget += 1; + } + + exceeded_budget +} + +async fn poor_little_task(permits: Arc<Semaphore>) -> usize { + let mut how_many_times_i_got_to_run = 0; + + for _ in 0..5 { + let _permit = permits.clone().acquire_owned().await.unwrap(); + how_many_times_i_got_to_run += 1; + } + + how_many_times_i_got_to_run +} + +#[tokio::test] +async fn join_does_not_allow_tasks_to_starve() { + let permits = Arc::new(Semaphore::new(1)); + + // non_cooperative_task should yield after its budget is exceeded and then poor_little_task should run. + let (non_cooperative_result, little_task_result) = tokio::join!( + non_cooperative_task(Arc::clone(&permits)), + poor_little_task(permits) + ); + + assert_eq!(5, non_cooperative_result); + assert_eq!(5, little_task_result); +} + +#[tokio::test] +async fn a_different_future_is_polled_first_every_time_poll_fn_is_polled() { + let poll_order = Arc::new(std::sync::Mutex::new(vec![])); + + let fut = |x, poll_order: Arc<std::sync::Mutex<Vec<i32>>>| async move { + for _ in 0..4 { + { + let mut guard = poll_order.lock().unwrap(); + + guard.push(x); + } + + tokio::task::yield_now().await; + } + }; + + tokio::join!( + fut(1, Arc::clone(&poll_order)), + fut(2, Arc::clone(&poll_order)), + fut(3, Arc::clone(&poll_order)), + ); + + // Each time the future created by join! is polled, it should start + // by polling a different future first. + assert_eq!( + vec![1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 2, 3], + *poll_order.lock().unwrap() + ); +} + +#[tokio::test] +#[allow(clippy::unit_cmp)] +async fn empty_join() { + assert_eq!(tokio::join!(), ()); } diff --git a/vendor/tokio/tests/macros_pin.rs b/vendor/tokio/tests/macros_pin.rs index da6e0be6e..e99a3ee20 100644 --- a/vendor/tokio/tests/macros_pin.rs +++ b/vendor/tokio/tests/macros_pin.rs @@ -1,7 +1,15 @@ +#![cfg(feature = "macros")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; + async fn one() {} async fn two() {} -#[tokio::test] +#[maybe_tokio_test] async fn multi_pin() { tokio::pin! { let f1 = one(); diff --git a/vendor/tokio/tests/macros_rename_test.rs b/vendor/tokio/tests/macros_rename_test.rs new file mode 100644 index 000000000..6c2ce2f57 --- /dev/null +++ b/vendor/tokio/tests/macros_rename_test.rs @@ -0,0 +1,35 @@ +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threading + +#[allow(unused_imports)] +use std as tokio; + +use ::tokio as tokio1; + +mod test { + pub use ::tokio; +} + +async fn compute() -> usize { + let join = tokio1::spawn(async { 1 }); + join.await.unwrap() +} + +#[tokio1::main(crate = "tokio1")] +async fn compute_main() -> usize { + compute().await +} + +#[test] +fn crate_rename_main() { + assert_eq!(1, compute_main()); +} + +#[tokio1::test(crate = "tokio1")] +async fn crate_rename_test() { + assert_eq!(1, compute().await); +} + +#[test::tokio::test(crate = "test::tokio")] +async fn crate_path_test() { + assert_eq!(1, compute().await); +} diff --git a/vendor/tokio/tests/macros_select.rs b/vendor/tokio/tests/macros_select.rs index a089602cb..0514c864d 100644 --- a/vendor/tokio/tests/macros_select.rs +++ b/vendor/tokio/tests/macros_select.rs @@ -1,12 +1,19 @@ -#![allow(clippy::blacklisted_name)] -use tokio::sync::{mpsc, oneshot}; -use tokio::task; +#![cfg(feature = "macros")] +#![allow(clippy::disallowed_names)] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; + +use tokio::sync::oneshot; use tokio_test::{assert_ok, assert_pending, assert_ready}; use futures::future::poll_fn; use std::task::Poll::Ready; -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_comma() { let foo = tokio::select! { foo = async { 1 } => foo, @@ -15,7 +22,7 @@ async fn sync_one_lit_expr_comma() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn nested_one() { let foo = tokio::select! { foo = async { 1 } => tokio::select! { @@ -26,7 +33,7 @@ async fn nested_one() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_no_comma() { let foo = tokio::select! { foo = async { 1 } => foo @@ -35,7 +42,7 @@ async fn sync_one_lit_expr_no_comma() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_block() { let foo = tokio::select! { foo = async { 1 } => { foo } @@ -44,7 +51,7 @@ async fn sync_one_lit_expr_block() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_await() { let foo = tokio::select! { foo = one() => foo, @@ -53,7 +60,7 @@ async fn sync_one_await() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_ident() { let one = one(); @@ -64,7 +71,7 @@ async fn sync_one_ident() { assert_eq!(foo, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_two() { use std::cell::Cell; @@ -85,7 +92,7 @@ async fn sync_two() { assert!(res == 1 || res == 2); } -#[tokio::test] +#[maybe_tokio_test] async fn drop_in_fut() { let s = "hello".to_string(); @@ -100,7 +107,8 @@ async fn drop_in_fut() { assert_eq!(res, 1); } -#[tokio::test] +#[maybe_tokio_test] +#[cfg(feature = "full")] async fn one_ready() { let (tx1, rx1) = oneshot::channel::<i32>(); let (_tx2, rx2) = oneshot::channel::<i32>(); @@ -117,20 +125,23 @@ async fn one_ready() { assert_eq!(1, v); } -#[tokio::test] +#[maybe_tokio_test] +#[cfg(feature = "full")] async fn select_streams() { + use tokio::sync::mpsc; + let (tx1, mut rx1) = mpsc::unbounded_channel::<i32>(); let (tx2, mut rx2) = mpsc::unbounded_channel::<i32>(); tokio::spawn(async move { assert_ok!(tx2.send(1)); - task::yield_now().await; + tokio::task::yield_now().await; assert_ok!(tx1.send(2)); - task::yield_now().await; + tokio::task::yield_now().await; assert_ok!(tx2.send(3)); - task::yield_now().await; + tokio::task::yield_now().await; drop((tx1, tx2)); }); @@ -156,7 +167,7 @@ async fn select_streams() { assert_eq!(&msgs[..], &[1, 2, 3]); } -#[tokio::test] +#[maybe_tokio_test] async fn move_uncompleted_futures() { let (tx1, mut rx1) = oneshot::channel::<i32>(); let (tx2, mut rx2) = oneshot::channel::<i32>(); @@ -182,7 +193,7 @@ async fn move_uncompleted_futures() { assert!(ran); } -#[tokio::test] +#[maybe_tokio_test] async fn nested() { let res = tokio::select! { x = async { 1 } => { @@ -195,49 +206,59 @@ async fn nested() { assert_eq!(res, 3); } -#[tokio::test] -async fn struct_size() { +#[cfg(target_pointer_width = "64")] +mod pointer_64_tests { + use super::maybe_tokio_test; use futures::future; use std::mem; - let fut = async { - let ready = future::ready(0i32); + #[maybe_tokio_test] + async fn struct_size_1() { + let fut = async { + let ready = future::ready(0i32); - tokio::select! { - _ = ready => {}, - } - }; + tokio::select! { + _ = ready => {}, + } + }; - assert!(mem::size_of_val(&fut) <= 32); + assert_eq!(mem::size_of_val(&fut), 32); + } - let fut = async { - let ready1 = future::ready(0i32); - let ready2 = future::ready(0i32); + #[maybe_tokio_test] + async fn struct_size_2() { + let fut = async { + let ready1 = future::ready(0i32); + let ready2 = future::ready(0i32); - tokio::select! { - _ = ready1 => {}, - _ = ready2 => {}, - } - }; + tokio::select! { + _ = ready1 => {}, + _ = ready2 => {}, + } + }; - assert!(mem::size_of_val(&fut) <= 40); + assert_eq!(mem::size_of_val(&fut), 40); + } - let fut = async { - let ready1 = future::ready(0i32); - let ready2 = future::ready(0i32); - let ready3 = future::ready(0i32); + #[maybe_tokio_test] + async fn struct_size_3() { + let fut = async { + let ready1 = future::ready(0i32); + let ready2 = future::ready(0i32); + let ready3 = future::ready(0i32); - tokio::select! { - _ = ready1 => {}, - _ = ready2 => {}, - _ = ready3 => {}, - } - }; + tokio::select! { + _ = ready1 => {}, + _ = ready2 => {}, + _ = ready3 => {}, + } + }; - assert!(mem::size_of_val(&fut) <= 48); + assert_eq!(mem::size_of_val(&fut), 48); + } } -#[tokio::test] +#[maybe_tokio_test] async fn mutable_borrowing_future_with_same_borrow_in_block() { let mut value = 234; @@ -251,7 +272,7 @@ async fn mutable_borrowing_future_with_same_borrow_in_block() { assert!(value >= 234); } -#[tokio::test] +#[maybe_tokio_test] async fn mutable_borrowing_future_with_same_borrow_in_block_and_else() { let mut value = 234; @@ -268,7 +289,7 @@ async fn mutable_borrowing_future_with_same_borrow_in_block_and_else() { assert!(value >= 234); } -#[tokio::test] +#[maybe_tokio_test] async fn future_panics_after_poll() { use tokio_test::task; @@ -298,7 +319,7 @@ async fn future_panics_after_poll() { assert_eq!(1, res); } -#[tokio::test] +#[maybe_tokio_test] async fn disable_with_if() { use tokio_test::task; @@ -320,7 +341,7 @@ async fn disable_with_if() { assert_ready!(f.poll()); } -#[tokio::test] +#[maybe_tokio_test] async fn join_with_select() { use tokio_test::task; @@ -356,11 +377,12 @@ async fn join_with_select() { } #[tokio::test] +#[cfg(feature = "full")] async fn use_future_in_if_condition() { use tokio::time::{self, Duration}; tokio::select! { - _ = time::sleep(Duration::from_millis(50)), if false => { + _ = time::sleep(Duration::from_millis(10)), if false => { panic!("if condition ignored") } _ = async { 1u32 } => { @@ -369,6 +391,21 @@ async fn use_future_in_if_condition() { } #[tokio::test] +#[cfg(feature = "full")] +async fn use_future_in_if_condition_biased() { + use tokio::time::{self, Duration}; + + tokio::select! { + biased; + _ = time::sleep(Duration::from_millis(10)), if false => { + panic!("if condition ignored") + } + _ = async { 1u32 } => { + } + } +} + +#[maybe_tokio_test] async fn many_branches() { let num = tokio::select! { x = async { 1 } => x, @@ -434,12 +471,13 @@ async fn many_branches() { x = async { 1 } => x, x = async { 1 } => x, x = async { 1 } => x, + x = async { 1 } => x, }; assert_eq!(1, num); } -#[tokio::test] +#[maybe_tokio_test] async fn never_branch_no_warnings() { let t = tokio::select! { _ = async_never() => 0, @@ -456,14 +494,11 @@ async fn require_mutable(_: &mut i32) {} async fn async_noop() {} async fn async_never() -> ! { - use tokio::time::Duration; - loop { - tokio::time::sleep(Duration::from_millis(10)).await; - } + futures::future::pending().await } // From https://github.com/tokio-rs/tokio/issues/2857 -#[tokio::test] +#[maybe_tokio_test] async fn mut_on_left_hand_side() { let v = async move { let ok = async { 1 }; @@ -479,7 +514,7 @@ async fn mut_on_left_hand_side() { assert_eq!(v, 2); } -#[tokio::test] +#[maybe_tokio_test] async fn biased_one_not_ready() { let (_tx1, rx1) = oneshot::channel::<i32>(); let (tx2, rx2) = oneshot::channel::<i32>(); @@ -503,7 +538,8 @@ async fn biased_one_not_ready() { assert_eq!(2, v); } -#[tokio::test] +#[maybe_tokio_test] +#[cfg(feature = "full")] async fn biased_eventually_ready() { use tokio::task::yield_now; @@ -547,3 +583,92 @@ pub async fn default_numeric_fallback() { else => (), } } + +// https://github.com/tokio-rs/tokio/issues/4182 +#[maybe_tokio_test] +async fn mut_ref_patterns() { + tokio::select! { + Some(mut foo) = async { Some("1".to_string()) } => { + assert_eq!(foo, "1"); + foo = "2".to_string(); + assert_eq!(foo, "2"); + }, + }; + + tokio::select! { + Some(ref foo) = async { Some("1".to_string()) } => { + assert_eq!(*foo, "1"); + }, + }; + + tokio::select! { + Some(ref mut foo) = async { Some("1".to_string()) } => { + assert_eq!(*foo, "1"); + *foo = "2".to_string(); + assert_eq!(*foo, "2"); + }, + }; +} + +#[cfg(tokio_unstable)] +mod unstable { + use tokio::runtime::RngSeed; + + #[test] + fn deterministic_select_current_thread() { + let seed = b"bytes used to generate seed"; + let rt1 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt1_values = rt1.block_on(async { (select_0_to_9().await, select_0_to_9().await) }); + + let rt2 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt2_values = rt2.block_on(async { (select_0_to_9().await, select_0_to_9().await) }); + + assert_eq!(rt1_values, rt2_values); + } + + #[test] + #[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))] + fn deterministic_select_multi_thread() { + let seed = b"bytes used to generate seed"; + let rt1 = tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt1_values = rt1.block_on(async { + let _ = tokio::spawn(async { (select_0_to_9().await, select_0_to_9().await) }).await; + }); + + let rt2 = tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt2_values = rt2.block_on(async { + let _ = tokio::spawn(async { (select_0_to_9().await, select_0_to_9().await) }).await; + }); + + assert_eq!(rt1_values, rt2_values); + } + + async fn select_0_to_9() -> u32 { + tokio::select!( + x = async { 0 } => x, + x = async { 1 } => x, + x = async { 2 } => x, + x = async { 3 } => x, + x = async { 4 } => x, + x = async { 5 } => x, + x = async { 6 } => x, + x = async { 7 } => x, + x = async { 8 } => x, + x = async { 9 } => x, + ) + } +} diff --git a/vendor/tokio/tests/macros_test.rs b/vendor/tokio/tests/macros_test.rs index bca2c9198..85279b7ed 100644 --- a/vendor/tokio/tests/macros_test.rs +++ b/vendor/tokio/tests/macros_test.rs @@ -1,3 +1,5 @@ +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threading + use tokio::test; #[test] @@ -46,3 +48,41 @@ pub async fn issue_4175_test() -> std::io::Result<()> { return Ok(()); panic!(); } + +// https://github.com/tokio-rs/tokio/issues/4175 +#[allow(clippy::let_unit_value)] +pub mod clippy_semicolon_if_nothing_returned { + #![deny(clippy::semicolon_if_nothing_returned)] + + #[tokio::main] + pub async fn local() { + let _x = (); + } + #[tokio::main] + pub async fn item() { + fn _f() {} + } + #[tokio::main] + pub async fn semi() { + panic!(); + } + #[tokio::main] + pub async fn empty() { + // To trigger clippy::semicolon_if_nothing_returned lint, the block needs to contain newline. + } +} + +// https://github.com/tokio-rs/tokio/issues/5243 +pub mod issue_5243 { + macro_rules! mac { + (async fn $name:ident() $b:block) => { + #[::tokio::test] + async fn $name() { + $b + } + }; + } + mac!( + async fn foo() {} + ); +} diff --git a/vendor/tokio/tests/macros_try_join.rs b/vendor/tokio/tests/macros_try_join.rs index a92515326..74b1c9f94 100644 --- a/vendor/tokio/tests/macros_try_join.rs +++ b/vendor/tokio/tests/macros_try_join.rs @@ -1,36 +1,46 @@ -#![allow(clippy::blacklisted_name)] -use tokio::sync::oneshot; +#![cfg(feature = "macros")] +#![allow(clippy::disallowed_names)] + +use std::sync::Arc; + +use tokio::sync::{oneshot, Semaphore}; use tokio_test::{assert_pending, assert_ready, task}; -#[tokio::test] +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; + +#[maybe_tokio_test] async fn sync_one_lit_expr_comma() { let foo = tokio::try_join!(async { ok(1) },); assert_eq!(foo, Ok((1,))); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_one_lit_expr_no_comma() { let foo = tokio::try_join!(async { ok(1) }); assert_eq!(foo, Ok((1,))); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_two_lit_expr_comma() { let foo = tokio::try_join!(async { ok(1) }, async { ok(2) },); assert_eq!(foo, Ok((1, 2))); } -#[tokio::test] +#[maybe_tokio_test] async fn sync_two_lit_expr_no_comma() { let foo = tokio::try_join!(async { ok(1) }, async { ok(2) }); assert_eq!(foo, Ok((1, 2))); } -#[tokio::test] +#[maybe_tokio_test] async fn two_await() { let (tx1, rx1) = oneshot::channel::<&str>(); let (tx2, rx2) = oneshot::channel::<u32>(); @@ -51,7 +61,7 @@ async fn two_await() { assert_eq!(Ok(("hello", 123)), res); } -#[tokio::test] +#[maybe_tokio_test] async fn err_abort_early() { let (tx1, rx1) = oneshot::channel::<&str>(); let (tx2, rx2) = oneshot::channel::<u32>(); @@ -78,6 +88,7 @@ async fn err_abort_early() { } #[test] +#[cfg(target_pointer_width = "64")] fn join_size() { use futures::future; use std::mem; @@ -86,16 +97,94 @@ fn join_size() { let ready = future::ready(ok(0i32)); tokio::try_join!(ready) }; - assert_eq!(mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 32); let fut = async { let ready1 = future::ready(ok(0i32)); let ready2 = future::ready(ok(0i32)); tokio::try_join!(ready1, ready2) }; - assert_eq!(mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 48); } fn ok<T>(val: T) -> Result<T, ()> { Ok(val) } + +async fn non_cooperative_task(permits: Arc<Semaphore>) -> Result<usize, String> { + let mut exceeded_budget = 0; + + for _ in 0..5 { + // Another task should run after this task uses its whole budget + for _ in 0..128 { + let _permit = permits.clone().acquire_owned().await.unwrap(); + } + + exceeded_budget += 1; + } + + Ok(exceeded_budget) +} + +async fn poor_little_task(permits: Arc<Semaphore>) -> Result<usize, String> { + let mut how_many_times_i_got_to_run = 0; + + for _ in 0..5 { + let _permit = permits.clone().acquire_owned().await.unwrap(); + + how_many_times_i_got_to_run += 1; + } + + Ok(how_many_times_i_got_to_run) +} + +#[tokio::test] +async fn try_join_does_not_allow_tasks_to_starve() { + let permits = Arc::new(Semaphore::new(10)); + + // non_cooperative_task should yield after its budget is exceeded and then poor_little_task should run. + let result = tokio::try_join!( + non_cooperative_task(Arc::clone(&permits)), + poor_little_task(permits) + ); + + let (non_cooperative_result, little_task_result) = result.unwrap(); + + assert_eq!(5, non_cooperative_result); + assert_eq!(5, little_task_result); +} + +#[tokio::test] +async fn a_different_future_is_polled_first_every_time_poll_fn_is_polled() { + let poll_order = Arc::new(std::sync::Mutex::new(vec![])); + + let fut = |x, poll_order: Arc<std::sync::Mutex<Vec<i32>>>| async move { + for _ in 0..4 { + { + let mut guard = poll_order.lock().unwrap(); + + guard.push(x); + } + + tokio::task::yield_now().await; + } + }; + + tokio::join!( + fut(1, Arc::clone(&poll_order)), + fut(2, Arc::clone(&poll_order)), + fut(3, Arc::clone(&poll_order)), + ); + + // Each time the future created by join! is polled, it should start + // by polling a different future first. + assert_eq!( + vec![1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 2, 3], + *poll_order.lock().unwrap() + ); +} + +#[tokio::test] +async fn empty_try_join() { + assert_eq!(tokio::try_join!() as Result<_, ()>, Ok(())); +} diff --git a/vendor/tokio/tests/net_bind_resource.rs b/vendor/tokio/tests/net_bind_resource.rs index d4a0b8dab..76378b3ea 100644 --- a/vendor/tokio/tests/net_bind_resource.rs +++ b/vendor/tokio/tests/net_bind_resource.rs @@ -1,9 +1,8 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support panic recovery or bind use tokio::net::TcpListener; -use std::convert::TryFrom; use std::net; #[test] diff --git a/vendor/tokio/tests/net_lookup_host.rs b/vendor/tokio/tests/net_lookup_host.rs index 4d0640298..2b346e65e 100644 --- a/vendor/tokio/tests/net_lookup_host.rs +++ b/vendor/tokio/tests/net_lookup_host.rs @@ -1,3 +1,5 @@ +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support direct socket operations + use tokio::net; use tokio_test::assert_ok; diff --git a/vendor/tokio/tests/named_pipe.rs b/vendor/tokio/tests/net_named_pipe.rs index fd33203f8..02eda48e5 100644 --- a/vendor/tokio/tests/named_pipe.rs +++ b/vendor/tokio/tests/net_named_pipe.rs @@ -2,13 +2,11 @@ #![cfg(all(windows))] use std::io; -use std::mem; -use std::os::windows::io::AsRawHandle; use std::time::Duration; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::windows::named_pipe::{ClientOptions, PipeMode, ServerOptions}; use tokio::time; -use winapi::shared::winerror; +use windows_sys::Win32::Foundation::{ERROR_NO_DATA, ERROR_PIPE_BUSY}; #[tokio::test] async fn test_named_pipe_client_drop() -> io::Result<()> { @@ -16,8 +14,6 @@ async fn test_named_pipe_client_drop() -> io::Result<()> { let mut server = ServerOptions::new().create(PIPE_NAME)?; - assert_eq!(num_instances("test-named-pipe-client-drop")?, 1); - let client = ClientOptions::new().open(PIPE_NAME)?; server.connect().await?; @@ -25,7 +21,7 @@ async fn test_named_pipe_client_drop() -> io::Result<()> { // instance will be broken because client is gone match server.write_all(b"ping").await { - Err(e) if e.raw_os_error() == Some(winerror::ERROR_NO_DATA as i32) => (), + Err(e) if e.raw_os_error() == Some(ERROR_NO_DATA as i32) => (), x => panic!("{:?}", x), } @@ -120,13 +116,13 @@ async fn test_named_pipe_multi_client() -> io::Result<()> { let client = loop { match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, - Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (), + Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), Err(e) if e.kind() == io::ErrorKind::NotFound => (), Err(e) => return Err(e), } // Wait for a named pipe to become available. - time::sleep(Duration::from_millis(50)).await; + time::sleep(Duration::from_millis(10)).await; }; let mut client = BufReader::new(client); @@ -249,13 +245,13 @@ async fn test_named_pipe_multi_client_ready() -> io::Result<()> { let client = loop { match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, - Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (), + Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), Err(e) if e.kind() == io::ErrorKind::NotFound => (), Err(e) => return Err(e), } // Wait for a named pipe to become available. - time::sleep(Duration::from_millis(50)).await; + time::sleep(Duration::from_millis(10)).await; }; let mut read_buf = [0u8; 5]; @@ -327,67 +323,77 @@ async fn test_named_pipe_multi_client_ready() -> io::Result<()> { Ok(()) } -// This tests what happens when a client tries to disconnect. +// This tests that message mode works as expected. #[tokio::test] async fn test_named_pipe_mode_message() -> io::Result<()> { - const PIPE_NAME: &str = r"\\.\pipe\test-named-pipe-mode-message"; + // it's easy to accidentally get a seemingly working test here because byte pipes + // often return contents at write boundaries. to make sure we're doing the right thing we + // explicitly test that it doesn't work in byte mode. + _named_pipe_mode_message(PipeMode::Message).await?; + _named_pipe_mode_message(PipeMode::Byte).await +} + +async fn _named_pipe_mode_message(mode: PipeMode) -> io::Result<()> { + let pipe_name = format!( + r"\\.\pipe\test-named-pipe-mode-message-{}", + matches!(mode, PipeMode::Message) + ); + let mut buf = [0u8; 32]; - let server = ServerOptions::new() - .pipe_mode(PipeMode::Message) - .create(PIPE_NAME)?; + let mut server = ServerOptions::new() + .first_pipe_instance(true) + .pipe_mode(mode) + .create(&pipe_name)?; + + let mut client = ClientOptions::new().pipe_mode(mode).open(&pipe_name)?; - let _ = ClientOptions::new().open(PIPE_NAME)?; server.connect().await?; + + // this needs a few iterations, presumably Windows waits for a few calls before merging buffers + for _ in 0..10 { + client.write_all(b"hello").await?; + server.write_all(b"world").await?; + } + for _ in 0..10 { + let n = server.read(&mut buf).await?; + if buf[..n] != b"hello"[..] { + assert!(matches!(mode, PipeMode::Byte)); + return Ok(()); + } + let n = client.read(&mut buf).await?; + if buf[..n] != b"world"[..] { + assert!(matches!(mode, PipeMode::Byte)); + return Ok(()); + } + } + // byte mode should have errored before. + assert!(matches!(mode, PipeMode::Message)); Ok(()) } -fn num_instances(pipe_name: impl AsRef<str>) -> io::Result<u32> { - use ntapi::ntioapi; - use winapi::shared::ntdef; - - let mut name = pipe_name.as_ref().encode_utf16().collect::<Vec<_>>(); - let mut name = ntdef::UNICODE_STRING { - Length: (name.len() * mem::size_of::<u16>()) as u16, - MaximumLength: (name.len() * mem::size_of::<u16>()) as u16, - Buffer: name.as_mut_ptr(), - }; - let root = std::fs::File::open(r"\\.\Pipe\")?; - let mut io_status_block = unsafe { mem::zeroed() }; - let mut file_directory_information = [0_u8; 1024]; - - let status = unsafe { - ntioapi::NtQueryDirectoryFile( - root.as_raw_handle(), - std::ptr::null_mut(), - None, - std::ptr::null_mut(), - &mut io_status_block, - &mut file_directory_information as *mut _ as *mut _, - 1024, - ntioapi::FileDirectoryInformation, - 0, - &mut name, - 0, - ) - }; - - if status as u32 != winerror::NO_ERROR { - return Err(io::Error::last_os_error()); +// This tests `NamedPipeServer::connect` with various access settings. +#[tokio::test] +async fn test_named_pipe_access() -> io::Result<()> { + const PIPE_NAME: &str = r"\\.\pipe\test-named-pipe-access"; + + for (inb, outb) in [(true, true), (true, false), (false, true)] { + let (tx, rx) = tokio::sync::oneshot::channel(); + let server = tokio::spawn(async move { + let s = ServerOptions::new() + .access_inbound(inb) + .access_outbound(outb) + .create(PIPE_NAME)?; + let mut connect_fut = tokio_test::task::spawn(s.connect()); + assert!(connect_fut.poll().is_pending()); + tx.send(()).unwrap(); + connect_fut.await + }); + + // Wait for the server to call connect. + rx.await.unwrap(); + let _ = ClientOptions::new().read(outb).write(inb).open(PIPE_NAME)?; + + server.await??; } - - let info = unsafe { - mem::transmute::<_, &ntioapi::FILE_DIRECTORY_INFORMATION>(&file_directory_information) - }; - let raw_name = unsafe { - std::slice::from_raw_parts( - info.FileName.as_ptr(), - info.FileNameLength as usize / mem::size_of::<u16>(), - ) - }; - let name = String::from_utf16(raw_name).unwrap(); - let num_instances = unsafe { *info.EndOfFile.QuadPart() }; - - assert_eq!(name, pipe_name.as_ref()); - - Ok(num_instances as u32) + Ok(()) } diff --git a/vendor/tokio/tests/net_panic.rs b/vendor/tokio/tests/net_panic.rs new file mode 100644 index 000000000..fc2209ad4 --- /dev/null +++ b/vendor/tokio/tests/net_panic.rs @@ -0,0 +1,188 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_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<dyn Error>> { + use std::net::SocketAddr; + use tokio::net::UdpSocket; + + let addr = "127.0.0.1:0".parse::<SocketAddr>().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<dyn Error>> { + let std_listener = std::net::TcpListener::bind("127.0.0.1:0").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<dyn Error>> { + 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<dyn Error>> { + 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<dyn Error>> { + 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<dyn Error>> { + 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<dyn Error>> { + 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<dyn Error>> { + use tokio::net::windows::named_pipe::ServerOptions; + + let panic_location_file = test_panic(move || { + let rt = runtime_without_io(); + rt.block_on(async { + let mut options = ServerOptions::new(); + options.max_instances(255); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +// Runtime without `enable_io` so it has no IO driver set. +fn runtime_without_io() -> Runtime { + Builder::new_current_thread().build().unwrap() +} diff --git a/vendor/tokio/tests/net_unix_pipe.rs b/vendor/tokio/tests/net_unix_pipe.rs new file mode 100644 index 000000000..c96d6e70f --- /dev/null +++ b/vendor/tokio/tests/net_unix_pipe.rs @@ -0,0 +1,429 @@ +#![cfg(feature = "full")] +#![cfg(unix)] + +use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest}; +use tokio::net::unix::pipe; +use tokio_test::task; +use tokio_test::{assert_err, assert_ok, assert_pending, assert_ready_ok}; + +use std::fs::File; +use std::io; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::AsRawFd; +use std::path::{Path, PathBuf}; + +/// Helper struct which will clean up temporary files once dropped. +struct TempFifo { + path: PathBuf, + _dir: tempfile::TempDir, +} + +impl TempFifo { + fn new(name: &str) -> io::Result<TempFifo> { + let dir = tempfile::Builder::new() + .prefix("tokio-fifo-tests") + .tempdir()?; + let path = dir.path().join(name); + nix::unistd::mkfifo(&path, nix::sys::stat::Mode::S_IRWXU)?; + + Ok(TempFifo { path, _dir: dir }) + } +} + +impl AsRef<Path> for TempFifo { + fn as_ref(&self) -> &Path { + self.path.as_ref() + } +} + +#[tokio::test] +async fn fifo_simple_send() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + let fifo = TempFifo::new("simple_send")?; + + // Create a reading task which should wait for data from the pipe. + let mut reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let mut read_fut = task::spawn(async move { + let mut buf = vec![0; DATA.len()]; + reader.read_exact(&mut buf).await?; + Ok::<_, io::Error>(buf) + }); + assert_pending!(read_fut.poll()); + + let mut writer = pipe::OpenOptions::new().open_sender(&fifo)?; + writer.write_all(DATA).await?; + + // Let the IO driver poll events for the reader. + while !read_fut.is_woken() { + tokio::task::yield_now().await; + } + + // Reading task should be ready now. + let read_data = assert_ready_ok!(read_fut.poll()); + assert_eq!(&read_data, DATA); + + Ok(()) +} + +#[tokio::test] +#[cfg(target_os = "linux")] +async fn fifo_simple_send_sender_first() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + // Create a new fifo file with *no reading ends open*. + let fifo = TempFifo::new("simple_send_sender_first")?; + + // Simple `open_sender` should fail with ENXIO (no such device or address). + let err = assert_err!(pipe::OpenOptions::new().open_sender(&fifo)); + assert_eq!(err.raw_os_error(), Some(libc::ENXIO)); + + // `open_sender` in read-write mode should succeed and the pipe should be ready to write. + let mut writer = pipe::OpenOptions::new() + .read_write(true) + .open_sender(&fifo)?; + writer.write_all(DATA).await?; + + // Read the written data and validate. + let mut reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let mut read_data = vec![0; DATA.len()]; + reader.read_exact(&mut read_data).await?; + assert_eq!(&read_data, DATA); + + Ok(()) +} + +// Opens a FIFO file, write and *close the writer*. +async fn write_and_close(path: impl AsRef<Path>, msg: &[u8]) -> io::Result<()> { + let mut writer = pipe::OpenOptions::new().open_sender(path)?; + writer.write_all(msg).await?; + drop(writer); // Explicit drop. + Ok(()) +} + +/// Checks EOF behavior with single reader and writers sequentially opening +/// and closing a FIFO. +#[tokio::test] +async fn fifo_multiple_writes() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + let fifo = TempFifo::new("fifo_multiple_writes")?; + + let mut reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + + write_and_close(&fifo, DATA).await?; + let ev = reader.ready(Interest::READABLE).await?; + assert!(ev.is_readable()); + let mut read_data = vec![0; DATA.len()]; + assert_ok!(reader.read_exact(&mut read_data).await); + + // Check that reader hits EOF. + let err = assert_err!(reader.read_exact(&mut read_data).await); + assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof); + + // Write more data and read again. + write_and_close(&fifo, DATA).await?; + assert_ok!(reader.read_exact(&mut read_data).await); + + Ok(()) +} + +/// Checks behavior of a resilient reader (Receiver in O_RDWR access mode) +/// with writers sequentially opening and closing a FIFO. +#[tokio::test] +#[cfg(target_os = "linux")] +async fn fifo_resilient_reader() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + let fifo = TempFifo::new("fifo_resilient_reader")?; + + // Open reader in read-write access mode. + let mut reader = pipe::OpenOptions::new() + .read_write(true) + .open_receiver(&fifo)?; + + write_and_close(&fifo, DATA).await?; + let ev = reader.ready(Interest::READABLE).await?; + let mut read_data = vec![0; DATA.len()]; + reader.read_exact(&mut read_data).await?; + + // Check that reader didn't hit EOF. + assert!(!ev.is_read_closed()); + + // Resilient reader can asynchronously wait for the next writer. + let mut second_read_fut = task::spawn(reader.read_exact(&mut read_data)); + assert_pending!(second_read_fut.poll()); + + // Write more data and read again. + write_and_close(&fifo, DATA).await?; + assert_ok!(second_read_fut.await); + + Ok(()) +} + +#[tokio::test] +async fn open_detects_not_a_fifo() -> io::Result<()> { + let dir = tempfile::Builder::new() + .prefix("tokio-fifo-tests") + .tempdir() + .unwrap(); + let path = dir.path().join("not_a_fifo"); + + // Create an ordinary file. + File::create(&path)?; + + // Check if Sender detects invalid file type. + let err = assert_err!(pipe::OpenOptions::new().open_sender(&path)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + // Check if Receiver detects invalid file type. + let err = assert_err!(pipe::OpenOptions::new().open_sender(&path)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + Ok(()) +} + +#[tokio::test] +async fn from_file() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + let fifo = TempFifo::new("from_file")?; + + // Construct a Receiver from a File. + let file = std::fs::OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK) + .open(&fifo)?; + let mut reader = pipe::Receiver::from_file(file)?; + + // Construct a Sender from a File. + let file = std::fs::OpenOptions::new() + .write(true) + .custom_flags(libc::O_NONBLOCK) + .open(&fifo)?; + let mut writer = pipe::Sender::from_file(file)?; + + // Write and read some data to test async. + let mut read_fut = task::spawn(async move { + let mut buf = vec![0; DATA.len()]; + reader.read_exact(&mut buf).await?; + Ok::<_, io::Error>(buf) + }); + assert_pending!(read_fut.poll()); + + writer.write_all(DATA).await?; + + let read_data = assert_ok!(read_fut.await); + assert_eq!(&read_data, DATA); + + Ok(()) +} + +#[tokio::test] +async fn from_file_detects_not_a_fifo() -> io::Result<()> { + let dir = tempfile::Builder::new() + .prefix("tokio-fifo-tests") + .tempdir() + .unwrap(); + let path = dir.path().join("not_a_fifo"); + + // Create an ordinary file. + File::create(&path)?; + + // Check if Sender detects invalid file type. + let file = std::fs::OpenOptions::new().write(true).open(&path)?; + let err = assert_err!(pipe::Sender::from_file(file)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + // Check if Receiver detects invalid file type. + let file = std::fs::OpenOptions::new().read(true).open(&path)?; + let err = assert_err!(pipe::Receiver::from_file(file)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + Ok(()) +} + +#[tokio::test] +async fn from_file_detects_wrong_access_mode() -> io::Result<()> { + let fifo = TempFifo::new("wrong_access_mode")?; + + // Open a read end to open the fifo for writing. + let _reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + + // Check if Receiver detects write-only access mode. + let wronly = std::fs::OpenOptions::new() + .write(true) + .custom_flags(libc::O_NONBLOCK) + .open(&fifo)?; + let err = assert_err!(pipe::Receiver::from_file(wronly)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + // Check if Sender detects read-only access mode. + let rdonly = std::fs::OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK) + .open(&fifo)?; + let err = assert_err!(pipe::Sender::from_file(rdonly)); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + + Ok(()) +} + +fn is_nonblocking<T: AsRawFd>(fd: &T) -> io::Result<bool> { + let flags = nix::fcntl::fcntl(fd.as_raw_fd(), nix::fcntl::F_GETFL)?; + Ok((flags & libc::O_NONBLOCK) != 0) +} + +#[tokio::test] +async fn from_file_sets_nonblock() -> io::Result<()> { + let fifo = TempFifo::new("sets_nonblock")?; + + // Open read and write ends to let blocking files open. + let _reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let _writer = pipe::OpenOptions::new().open_sender(&fifo)?; + + // Check if Receiver sets the pipe in non-blocking mode. + let rdonly = std::fs::OpenOptions::new().read(true).open(&fifo)?; + assert!(!is_nonblocking(&rdonly)?); + let reader = pipe::Receiver::from_file(rdonly)?; + assert!(is_nonblocking(&reader)?); + + // Check if Sender sets the pipe in non-blocking mode. + let wronly = std::fs::OpenOptions::new().write(true).open(&fifo)?; + assert!(!is_nonblocking(&wronly)?); + let writer = pipe::Sender::from_file(wronly)?; + assert!(is_nonblocking(&writer)?); + + Ok(()) +} + +fn writable_by_poll(writer: &pipe::Sender) -> bool { + task::spawn(writer.writable()).poll().is_ready() +} + +#[tokio::test] +async fn try_read_write() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + // Create a pipe pair over a fifo file. + let fifo = TempFifo::new("try_read_write")?; + let reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let writer = pipe::OpenOptions::new().open_sender(&fifo)?; + + // Fill the pipe buffer with `try_write`. + let mut write_data = Vec::new(); + while writable_by_poll(&writer) { + match writer.try_write(DATA) { + Ok(n) => write_data.extend(&DATA[..n]), + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + break; + } + } + } + + // Drain the pipe buffer with `try_read`. + let mut read_data = vec![0; write_data.len()]; + let mut i = 0; + while i < write_data.len() { + reader.readable().await?; + match reader.try_read(&mut read_data[i..]) { + Ok(n) => i += n, + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + continue; + } + } + } + + assert_eq!(read_data, write_data); + + Ok(()) +} + +#[tokio::test] +async fn try_read_write_vectored() -> io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + // Create a pipe pair over a fifo file. + let fifo = TempFifo::new("try_read_write_vectored")?; + let reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let writer = pipe::OpenOptions::new().open_sender(&fifo)?; + + let write_bufs: Vec<_> = DATA.chunks(3).map(io::IoSlice::new).collect(); + + // Fill the pipe buffer with `try_write_vectored`. + let mut write_data = Vec::new(); + while writable_by_poll(&writer) { + match writer.try_write_vectored(&write_bufs) { + Ok(n) => write_data.extend(&DATA[..n]), + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + break; + } + } + } + + // Drain the pipe buffer with `try_read_vectored`. + let mut read_data = vec![0; write_data.len()]; + let mut i = 0; + while i < write_data.len() { + reader.readable().await?; + + let mut read_bufs: Vec<_> = read_data[i..] + .chunks_mut(0x10000) + .map(io::IoSliceMut::new) + .collect(); + match reader.try_read_vectored(&mut read_bufs) { + Ok(n) => i += n, + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + continue; + } + } + } + + assert_eq!(read_data, write_data); + + Ok(()) +} + +#[tokio::test] +async fn try_read_buf() -> std::io::Result<()> { + const DATA: &[u8] = b"this is some data to write to the fifo"; + + // Create a pipe pair over a fifo file. + let fifo = TempFifo::new("try_read_write_vectored")?; + let reader = pipe::OpenOptions::new().open_receiver(&fifo)?; + let writer = pipe::OpenOptions::new().open_sender(&fifo)?; + + // Fill the pipe buffer with `try_write`. + let mut write_data = Vec::new(); + while writable_by_poll(&writer) { + match writer.try_write(DATA) { + Ok(n) => write_data.extend(&DATA[..n]), + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + break; + } + } + } + + // Drain the pipe buffer with `try_read_buf`. + let mut read_data = vec![0; write_data.len()]; + let mut i = 0; + while i < write_data.len() { + reader.readable().await?; + match reader.try_read_buf(&mut read_data) { + Ok(n) => i += n, + Err(e) => { + assert_eq!(e.kind(), io::ErrorKind::WouldBlock); + continue; + } + } + } + + assert_eq!(read_data, write_data); + + Ok(()) +} diff --git a/vendor/tokio/tests/no_rt.rs b/vendor/tokio/tests/no_rt.rs index 8437b8046..747fab6af 100644 --- a/vendor/tokio/tests/no_rt.rs +++ b/vendor/tokio/tests/no_rt.rs @@ -1,3 +1,5 @@ +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery + use tokio::net::TcpStream; use tokio::sync::oneshot; use tokio::time::{timeout, Duration}; @@ -26,7 +28,7 @@ fn panics_when_no_reactor() { async fn timeout_value() { let (_tx, rx) = oneshot::channel::<()>(); - let dur = Duration::from_millis(20); + let dur = Duration::from_millis(10); let _ = timeout(dur, rx).await; } diff --git a/vendor/tokio/tests/process_arg0.rs b/vendor/tokio/tests/process_arg0.rs new file mode 100644 index 000000000..4fabea0fe --- /dev/null +++ b/vendor/tokio/tests/process_arg0.rs @@ -0,0 +1,13 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", unix))] + +use tokio::process::Command; + +#[tokio::test] +async fn arg0() { + let mut cmd = Command::new("sh"); + cmd.arg0("test_string").arg("-c").arg("echo $0"); + + let output = cmd.output().await.unwrap(); + assert_eq!(output.stdout, b"test_string\n"); +} diff --git a/vendor/tokio/tests/process_kill_on_drop.rs b/vendor/tokio/tests/process_kill_on_drop.rs index 00f5c6dc6..d919b1a27 100644 --- a/vendor/tokio/tests/process_kill_on_drop.rs +++ b/vendor/tokio/tests/process_kill_on_drop.rs @@ -1,6 +1,7 @@ #![cfg(all(unix, feature = "process"))] #![warn(rust_2018_idioms)] +use std::io::ErrorKind; use std::process::Stdio; use std::time::Duration; use tokio::io::AsyncReadExt; @@ -11,7 +12,7 @@ use tokio_test::assert_ok; #[tokio::test] async fn kill_on_drop() { let mut cmd = Command::new("bash"); - cmd.args(&[ + cmd.args([ "-c", " # Fork another child that won't get killed @@ -24,11 +25,12 @@ async fn kill_on_drop() { ", ]); - let mut child = cmd - .kill_on_drop(true) - .stdout(Stdio::piped()) - .spawn() - .unwrap(); + let e = cmd.kill_on_drop(true).stdout(Stdio::piped()).spawn(); + if e.is_err() && e.as_ref().unwrap_err().kind() == ErrorKind::NotFound { + println!("bash not available; skipping test"); + return; + } + let mut child = e.unwrap(); sleep(Duration::from_secs(2)).await; diff --git a/vendor/tokio/tests/process_raw_handle.rs b/vendor/tokio/tests/process_raw_handle.rs new file mode 100644 index 000000000..fbe22b07c --- /dev/null +++ b/vendor/tokio/tests/process_raw_handle.rs @@ -0,0 +1,23 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] +#![cfg(windows)] + +use tokio::process::Command; +use windows_sys::Win32::System::Threading::GetProcessId; + +#[tokio::test] +async fn obtain_raw_handle() { + let mut cmd = Command::new("cmd"); + cmd.kill_on_drop(true); + cmd.arg("/c"); + cmd.arg("pause"); + + let child = cmd.spawn().unwrap(); + + let orig_id = child.id().expect("missing id"); + assert!(orig_id > 0); + + let handle = child.raw_handle().expect("process stopped"); + let handled_id = unsafe { GetProcessId(handle as _) }; + assert_eq!(handled_id, orig_id); +} diff --git a/vendor/tokio/tests/process_smoke.rs b/vendor/tokio/tests/process_smoke.rs index fae5793fa..81d2020a0 100644 --- a/vendor/tokio/tests/process_smoke.rs +++ b/vendor/tokio/tests/process_smoke.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi cannot run system commands use tokio::process::Command; use tokio_test::assert_ok; diff --git a/vendor/tokio/tests/rt_basic.rs b/vendor/tokio/tests/rt_basic.rs index 4b1bdadc1..6caf0a44b 100644 --- a/vendor/tokio/tests/rt_basic.rs +++ b/vendor/tokio/tests/rt_basic.rs @@ -3,15 +3,28 @@ use tokio::runtime::Runtime; use tokio::sync::oneshot; +use tokio::time::{timeout, Duration}; use tokio_test::{assert_err, assert_ok}; +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::task::{Context, Poll}; use std::thread; -use tokio::time::{timeout, Duration}; mod support { pub(crate) mod mpsc_stream; } +macro_rules! cfg_metrics { + ($($t:tt)*) => { + #[cfg(tokio_unstable)] + { + $( $t )* + } + } +} + #[test] fn spawned_task_does_not_progress_without_block_on() { let (tx, mut rx) = oneshot::channel(); @@ -136,6 +149,134 @@ fn acquire_mutex_in_drop() { } #[test] +fn drop_tasks_in_context() { + static SUCCESS: AtomicBool = AtomicBool::new(false); + + struct ContextOnDrop; + + impl Future for ContextOnDrop { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + Poll::Pending + } + } + + impl Drop for ContextOnDrop { + fn drop(&mut self) { + if tokio::runtime::Handle::try_current().is_ok() { + SUCCESS.store(true, Ordering::SeqCst); + } + } + } + + let rt = rt(); + rt.spawn(ContextOnDrop); + drop(rt); + + assert!(SUCCESS.load(Ordering::SeqCst)); +} + +#[test] +#[cfg_attr(tokio_wasi, ignore = "Wasi does not support panic recovery")] +#[should_panic(expected = "boom")] +fn wake_in_drop_after_panic() { + let (tx, rx) = oneshot::channel::<()>(); + + struct WakeOnDrop(Option<oneshot::Sender<()>>); + + impl Drop for WakeOnDrop { + fn drop(&mut self) { + self.0.take().unwrap().send(()).unwrap(); + } + } + + let rt = rt(); + + rt.spawn(async move { + let _wake_on_drop = WakeOnDrop(Some(tx)); + // wait forever + futures::future::pending::<()>().await; + }); + + let _join = rt.spawn(async move { rx.await }); + + rt.block_on(async { + tokio::task::yield_now().await; + panic!("boom"); + }); +} + +#[test] +fn spawn_two() { + let rt = rt(); + + let out = rt.block_on(async { + let (tx, rx) = oneshot::channel(); + + tokio::spawn(async move { + tokio::spawn(async move { + tx.send("ZOMG").unwrap(); + }); + }); + + assert_ok!(rx.await) + }); + + assert_eq!(out, "ZOMG"); + + cfg_metrics! { + let metrics = rt.metrics(); + drop(rt); + assert_eq!(0, metrics.remote_schedule_count()); + + let mut local = 0; + for i in 0..metrics.num_workers() { + local += metrics.worker_local_schedule_count(i); + } + + assert_eq!(2, local); + } +} + +#[cfg_attr(tokio_wasi, ignore = "WASI: std::thread::spawn not supported")] +#[test] +fn spawn_remote() { + let rt = rt(); + + let out = rt.block_on(async { + let (tx, rx) = oneshot::channel(); + + let handle = tokio::spawn(async move { + std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(10)); + tx.send("ZOMG").unwrap(); + }); + + rx.await.unwrap() + }); + + handle.await.unwrap() + }); + + assert_eq!(out, "ZOMG"); + + cfg_metrics! { + let metrics = rt.metrics(); + drop(rt); + assert_eq!(1, metrics.remote_schedule_count()); + + let mut local = 0; + for i in 0..metrics.num_workers() { + local += metrics.worker_local_schedule_count(i); + } + + assert_eq!(1, local); + } +} + +#[test] +#[cfg_attr(tokio_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." )] @@ -150,6 +291,155 @@ fn timeout_panics_when_no_time_handle() { }); } +#[cfg(tokio_unstable)] +mod unstable { + use tokio::runtime::{Builder, RngSeed, UnhandledPanic}; + + #[test] + #[should_panic( + expected = "a spawned task panicked and the runtime is configured to shut down on unhandled panic" + )] + fn shutdown_on_panic() { + let rt = Builder::new_current_thread() + .unhandled_panic(UnhandledPanic::ShutdownRuntime) + .build() + .unwrap(); + + rt.block_on(async { + tokio::spawn(async { + panic!("boom"); + }); + + futures::future::pending::<()>().await; + }) + } + + #[test] + #[cfg_attr(tokio_wasi, ignore = "Wasi does not support panic recovery")] + fn spawns_do_nothing() { + use std::sync::Arc; + + let rt = Builder::new_current_thread() + .unhandled_panic(UnhandledPanic::ShutdownRuntime) + .build() + .unwrap(); + + let rt1 = Arc::new(rt); + let rt2 = rt1.clone(); + + let _ = std::thread::spawn(move || { + rt2.block_on(async { + tokio::spawn(async { + panic!("boom"); + }); + + futures::future::pending::<()>().await; + }) + }) + .join(); + + let task = rt1.spawn(async {}); + let res = futures::executor::block_on(task); + assert!(res.is_err()); + } + + #[test] + #[cfg_attr(tokio_wasi, ignore = "Wasi does not support panic recovery")] + fn shutdown_all_concurrent_block_on() { + const N: usize = 2; + use std::sync::{mpsc, Arc}; + + let rt = Builder::new_current_thread() + .unhandled_panic(UnhandledPanic::ShutdownRuntime) + .build() + .unwrap(); + + let rt = Arc::new(rt); + let mut ths = vec![]; + let (tx, rx) = mpsc::channel(); + + for _ in 0..N { + let rt = rt.clone(); + let tx = tx.clone(); + ths.push(std::thread::spawn(move || { + rt.block_on(async { + tx.send(()).unwrap(); + futures::future::pending::<()>().await; + }); + })); + } + + for _ in 0..N { + rx.recv().unwrap(); + } + + rt.spawn(async { + panic!("boom"); + }); + + for th in ths { + assert!(th.join().is_err()); + } + } + + #[test] + fn rng_seed() { + let seed = b"bytes used to generate seed"; + let rt1 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt1_values = rt1.block_on(async { + let rand_1 = tokio::macros::support::thread_rng_n(100); + let rand_2 = tokio::macros::support::thread_rng_n(100); + + (rand_1, rand_2) + }); + + let rt2 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt2_values = rt2.block_on(async { + let rand_1 = tokio::macros::support::thread_rng_n(100); + let rand_2 = tokio::macros::support::thread_rng_n(100); + + (rand_1, rand_2) + }); + + assert_eq!(rt1_values, rt2_values); + } + + #[test] + fn rng_seed_multi_enter() { + let seed = b"bytes used to generate seed"; + + fn two_rand_values() -> (u32, u32) { + let rand_1 = tokio::macros::support::thread_rng_n(100); + let rand_2 = tokio::macros::support::thread_rng_n(100); + + (rand_1, rand_2) + } + + let rt1 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt1_values_1 = rt1.block_on(async { two_rand_values() }); + let rt1_values_2 = rt1.block_on(async { two_rand_values() }); + + let rt2 = tokio::runtime::Builder::new_current_thread() + .rng_seed(RngSeed::from_bytes(seed)) + .build() + .unwrap(); + let rt2_values_1 = rt2.block_on(async { two_rand_values() }); + let rt2_values_2 = rt2.block_on(async { two_rand_values() }); + + assert_eq!(rt1_values_1, rt2_values_1); + assert_eq!(rt1_values_2, rt2_values_2); + } +} + fn rt() -> Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() diff --git a/vendor/tokio/tests/rt_common.rs b/vendor/tokio/tests/rt_common.rs index cb1d0f661..9c6add047 100644 --- a/vendor/tokio/tests/rt_common.rs +++ b/vendor/tokio/tests/rt_common.rs @@ -2,13 +2,16 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "full")] -// Tests to run on both current-thread & thread-pool runtime variants. +// Tests to run on both current-thread & multi-thread runtime variants. macro_rules! rt_test { ($($t:tt)*) => { mod current_thread_scheduler { $($t)* + #[cfg(not(target_os="wasi"))] + const NUM_WORKERS: usize = 1; + fn rt() -> Arc<Runtime> { tokio::runtime::Builder::new_current_thread() .enable_all() @@ -18,9 +21,12 @@ macro_rules! rt_test { } } + #[cfg(not(tokio_wasi))] // Wasi doesn't support threads mod threaded_scheduler_4_threads { $($t)* + const NUM_WORKERS: usize = 4; + fn rt() -> Arc<Runtime> { tokio::runtime::Builder::new_multi_thread() .worker_threads(4) @@ -31,9 +37,12 @@ macro_rules! rt_test { } } + #[cfg(not(tokio_wasi))] // Wasi doesn't support threads mod threaded_scheduler_1_thread { $($t)* + const NUM_WORKERS: usize = 1; + fn rt() -> Arc<Runtime> { tokio::runtime::Builder::new_multi_thread() .worker_threads(1) @@ -55,18 +64,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 +104,7 @@ rt_test! { } + #[cfg(not(target_os="wasi"))] #[test] fn block_on_async() { let rt = rt(); @@ -164,6 +186,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 +237,7 @@ rt_test! { } } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_many_from_task() { use tokio::sync::mpsc; @@ -226,14 +250,6 @@ rt_test! { tokio::spawn(async move { let (done_tx, mut done_rx) = mpsc::unbounded_channel(); - /* - for _ in 0..100 { - tokio::spawn(async move { }); - } - - tokio::task::yield_now().await; - */ - let mut txs = (0..ITER) .map(|i| { let (tx, rx) = oneshot::channel(); @@ -275,6 +291,31 @@ rt_test! { } #[test] + fn spawn_one_from_block_on_called_on_handle() { + let rt = rt(); + let (tx, rx) = oneshot::channel(); + + #[allow(clippy::async_yields_async)] + let handle = rt.handle().block_on(async { + tokio::spawn(async move { + tx.send("ZOMG").unwrap(); + "DONE" + }) + }); + + let out = rt.block_on(async { + let msg = assert_ok!(rx.await); + + let out = assert_ok!(handle.await); + assert_eq!(out, "DONE"); + + msg + }); + + assert_eq!(out, "ZOMG"); + } + + #[test] fn spawn_await_chain() { let rt = rt(); @@ -329,6 +370,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 +394,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn complete_task_under_load() { let rt = rt(); @@ -381,6 +424,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 +445,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 +506,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 +527,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn spawn_from_blocking() { let rt = rt(); @@ -496,6 +543,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 +559,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 +580,7 @@ rt_test! { }); } + #[cfg(not(target_os="wasi"))] // Wasi does not support bind #[test] fn socket_from_blocking() { let rt = rt(); @@ -554,6 +604,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 +651,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(); @@ -607,7 +659,12 @@ rt_test! { for _ in 0..100 { rt.spawn(async { loop { - tokio::task::yield_now().await; + // Don't use Tokio's `yield_now()` to avoid special defer + // logic. + futures::future::poll_fn::<(), _>(|cx| { + cx.waker().wake_by_ref(); + std::task::Poll::Pending + }).await; } }); } @@ -635,6 +692,113 @@ rt_test! { }); } + /// Tests that yielded tasks are not scheduled until **after** resource + /// drivers are polled. + /// + /// The OS does not guarantee when I/O events are delivered, so there may be + /// more yields than anticipated. This makes the test slightly flaky. To + /// help avoid flakiness, we run the test 10 times and only fail it after + /// 10 failures in a row. + /// + /// Note that if the test fails by panicking rather than by returning false, + /// then we fail it immediately. That kind of failure should not happen + /// spuriously. + #[test] + #[cfg(not(target_os="wasi"))] + fn yield_defers_until_park() { + for _ in 0..10 { + if yield_defers_until_park_inner() { + // test passed + return; + } + + // Wait a bit and run the test again. + std::thread::sleep(std::time::Duration::from_secs(2)); + } + + panic!("yield_defers_until_park is failing consistently"); + } + + /// Implementation of `yield_defers_until_park` test. Returns `true` if the + /// test passed. + #[cfg(not(target_os="wasi"))] + fn yield_defers_until_park_inner() -> bool { + use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; + use std::sync::Barrier; + + let rt = rt(); + + let flag = Arc::new(AtomicBool::new(false)); + let barrier = Arc::new(Barrier::new(NUM_WORKERS)); + + rt.block_on(async { + // Make sure other workers cannot steal tasks + #[allow(clippy::reversed_empty_ranges)] + for _ in 0..(NUM_WORKERS-1) { + let flag = flag.clone(); + let barrier = barrier.clone(); + + tokio::spawn(async move { + barrier.wait(); + + while !flag.load(SeqCst) { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + }); + } + + barrier.wait(); + + let (fail_test, fail_test_recv) = oneshot::channel::<()>(); + + let jh = tokio::spawn(async move { + // Create a TCP litener + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::join!( + async { + // Done in a blocking manner intentionally. + let _socket = std::net::TcpStream::connect(addr).unwrap(); + + // Yield until connected + let mut cnt = 0; + while !flag.load(SeqCst){ + tokio::task::yield_now().await; + cnt += 1; + + if cnt >= 10 { + // yielded too many times; report failure and + // sleep forever so that the `fail_test` branch + // of the `select!` below triggers. + let _ = fail_test.send(()); + futures::future::pending::<()>().await; + break; + } + } + }, + async { + let _ = listener.accept().await.unwrap(); + flag.store(true, SeqCst); + } + ); + }); + + // Wait until the spawned task completes or fails. If no message is + // sent on `fail_test`, then the test succeeds. Otherwise, it fails. + let success = fail_test_recv.await.is_err(); + + if success { + // Check for panics in spawned task. + jh.abort(); + jh.await.unwrap(); + } + + success + }) + } + + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn client_server_block_on() { let rt = rt(); @@ -646,6 +810,7 @@ rt_test! { assert_err!(rx.try_recv()); } + #[cfg_attr(tokio_wasi, ignore = "Wasi does not support threads or panic recovery")] #[test] fn panic_in_task() { let rt = rt(); @@ -674,11 +839,13 @@ rt_test! { #[test] #[should_panic] + #[cfg_attr(tokio_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| { @@ -748,7 +915,11 @@ rt_test! { #[test] fn wake_while_rt_is_dropping() { - use tokio::task; + use tokio::sync::Barrier; + use core::sync::atomic::{AtomicBool, Ordering}; + + let drop_triggered = Arc::new(AtomicBool::new(false)); + let set_drop_triggered = drop_triggered.clone(); struct OnDrop<F: FnMut()>(F); @@ -762,17 +933,21 @@ rt_test! { let (tx2, rx2) = oneshot::channel(); let (tx3, rx3) = oneshot::channel(); - let rt = rt(); + let barrier = Arc::new(Barrier::new(4)); + let barrier1 = barrier.clone(); + let barrier2 = barrier.clone(); + let barrier3 = barrier.clone(); - let h1 = rt.clone(); + let rt = rt(); rt.spawn(async move { // Ensure a waker gets stored in oneshot 1. - let _ = rx1.await; + let _ = tokio::join!(rx1, barrier1.wait()); tx3.send(()).unwrap(); }); rt.spawn(async move { + let h1 = tokio::runtime::Handle::current(); // When this task is dropped, we'll be "closing remotes". // We spawn a new task that owns the `tx1`, to move its Drop // out of here. @@ -785,36 +960,40 @@ rt_test! { h1.spawn(async move { tx1.send(()).unwrap(); }); + // Just a sanity check that this entire thing actually happened + set_drop_triggered.store(true, Ordering::Relaxed); }); - let _ = rx2.await; + let _ = tokio::join!(rx2, barrier2.wait()); }); rt.spawn(async move { - let _ = rx3.await; + let _ = tokio::join!(rx3, barrier3.wait()); // We'll never get here, but once task 3 drops, this will // force task 2 to re-schedule since it's waiting on oneshot 2. tx2.send(()).unwrap(); }); - // Tick the loop - rt.block_on(async { - task::yield_now().await; - }); + // Wait until every oneshot channel has been polled. + rt.block_on(barrier.wait()); // Drop the rt drop(rt); + + // Make sure that the spawn actually happened + 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 std::net::Ipv6Addr; + use tokio::net::UdpSocket; use std::sync::Arc; for _ in 1..10 { let runtime = rt(); runtime.block_on(async { - let socket = UdpSocket::bind((Ipv6Addr::LOCALHOST, 0)).await.unwrap(); + let socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); let addr = socket.local_addr().unwrap(); let send_half = Arc::new(socket); let recv_half = send_half.clone(); @@ -840,6 +1019,7 @@ rt_test! { } } + #[cfg(not(target_os="wasi"))] // Wasi does not support threads #[test] fn shutdown_timeout() { let (tx, rx) = oneshot::channel(); @@ -857,6 +1037,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(); @@ -888,6 +1069,7 @@ rt_test! { // See https://github.com/rust-lang/rust/issues/74875 #[test] #[cfg(not(windows))] + #[cfg_attr(tokio_wasi, ignore = "Wasi does not support threads")] fn runtime_in_thread_local() { use std::cell::RefCell; use std::thread; @@ -907,6 +1089,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); @@ -931,6 +1114,7 @@ rt_test! { tx.send(()).unwrap(); } + #[cfg(not(tokio_wasi))] // Wasi does not support bind #[test] fn local_set_block_on_socket() { let rt = rt(); @@ -952,6 +1136,7 @@ rt_test! { }); } + #[cfg(not(tokio_wasi))] // Wasi does not support bind #[test] fn local_set_client_server_block_on() { let rt = rt(); @@ -965,6 +1150,7 @@ rt_test! { assert_err!(rx.try_recv()); } + #[cfg(not(tokio_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); @@ -992,22 +1178,22 @@ rt_test! { #[test] fn coop() { use std::task::Poll::Ready; + use tokio::sync::mpsc; let rt = rt(); rt.block_on(async { - // Create a bunch of tasks - let mut tasks = (0..1_000).map(|_| { - tokio::spawn(async { }) - }).collect::<Vec<_>>(); + let (send, mut recv) = mpsc::unbounded_channel(); - // Hope that all the tasks complete... - time::sleep(Duration::from_millis(100)).await; + // Send a bunch of messages. + for _ in 0..1_000 { + send.send(()).unwrap(); + } poll_fn(|cx| { - // At least one task should not be ready - for task in &mut tasks { - if Pin::new(task).poll(cx).is_pending() { + // At least one response should return pending. + for _ in 0..1_000 { + if recv.poll_recv(cx).is_pending() { return Ready(()); } } @@ -1020,22 +1206,22 @@ rt_test! { #[test] fn coop_unconstrained() { use std::task::Poll::Ready; + use tokio::sync::mpsc; let rt = rt(); rt.block_on(async { - // Create a bunch of tasks - let mut tasks = (0..1_000).map(|_| { - tokio::spawn(async { }) - }).collect::<Vec<_>>(); + let (send, mut recv) = mpsc::unbounded_channel(); - // Hope that all the tasks complete... - time::sleep(Duration::from_millis(100)).await; + // Send a bunch of messages. + for _ in 0..1_000 { + send.send(()).unwrap(); + } tokio::task::unconstrained(poll_fn(|cx| { - // All the tasks should be ready - for task in &mut tasks { - assert!(Pin::new(task).poll(cx).is_ready()); + // All the responses should be ready. + for _ in 0..1_000 { + assert_eq!(recv.poll_recv(cx), Poll::Ready(Some(()))); } Ready(()) @@ -1043,6 +1229,31 @@ rt_test! { }); } + #[cfg(tokio_unstable)] + #[test] + fn coop_consume_budget() { + let rt = rt(); + + rt.block_on(async { + poll_fn(|cx| { + let counter = Arc::new(std::sync::Mutex::new(0)); + let counter_clone = Arc::clone(&counter); + let mut worker = Box::pin(async move { + // Consume the budget until a yield happens + for _ in 0..1000 { + *counter.lock().unwrap() += 1; + task::consume_budget().await + } + }); + // Assert that the worker was yielded and it didn't manage + // to finish the whole work (assuming the total budget of 128) + assert!(Pin::new(&mut worker).poll(cx).is_pending()); + assert!(*counter_clone.lock().unwrap() < 1000); + std::task::Poll::Ready(()) + }).await; + }); + } + // Tests that the "next task" scheduler optimization is not able to starve // other tasks. #[test] @@ -1060,7 +1271,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(); @@ -1106,4 +1317,39 @@ rt_test! { } }); } + + #[test] + #[cfg(not(target_os="wasi"))] + fn shutdown_concurrent_spawn() { + const NUM_TASKS: usize = 10_000; + for _ in 0..5 { + let (tx, rx) = std::sync::mpsc::channel(); + let rt = rt(); + + let mut txs = vec![]; + + for _ in 0..NUM_TASKS { + let (tx, rx) = tokio::sync::oneshot::channel(); + txs.push(tx); + rt.spawn(async move { + rx.await.unwrap(); + }); + } + + // Prime the tasks + rt.block_on(async { tokio::task::yield_now().await }); + + let th = std::thread::spawn(move || { + tx.send(()).unwrap(); + for tx in txs.drain(..) { + let _ = tx.send(()); + } + }); + + rx.recv().unwrap(); + drop(rt); + + th.join().unwrap(); + } + } } diff --git a/vendor/tokio/tests/rt_handle.rs b/vendor/tokio/tests/rt_handle.rs new file mode 100644 index 000000000..34c99cdae --- /dev/null +++ b/vendor/tokio/tests/rt_handle.rs @@ -0,0 +1,67 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] + +use tokio::runtime::Runtime; + +#[test] +fn basic_enter() { + let rt1 = rt(); + let rt2 = rt(); + + let enter1 = rt1.enter(); + let enter2 = rt2.enter(); + + drop(enter2); + drop(enter1); +} + +#[test] +#[should_panic] +fn interleave_enter_different_rt() { + let rt1 = rt(); + let rt2 = rt(); + + let enter1 = rt1.enter(); + let enter2 = rt2.enter(); + + drop(enter1); + drop(enter2); +} + +#[test] +#[should_panic] +fn interleave_enter_same_rt() { + let rt1 = rt(); + + let _enter1 = rt1.enter(); + let enter2 = rt1.enter(); + let enter3 = rt1.enter(); + + drop(enter2); + drop(enter3); +} + +#[test] +#[cfg(not(tokio_wasi))] +fn interleave_then_enter() { + let _ = std::panic::catch_unwind(|| { + let rt1 = rt(); + let rt2 = rt(); + + let enter1 = rt1.enter(); + let enter2 = rt2.enter(); + + drop(enter1); + drop(enter2); + }); + + // Can still enter + let rt3 = rt(); + let _enter = rt3.enter(); +} + +fn rt() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .build() + .unwrap() +} diff --git a/vendor/tokio/tests/rt_handle_block_on.rs b/vendor/tokio/tests/rt_handle_block_on.rs index 17878c8d2..5ec783e55 100644 --- a/vendor/tokio/tests/rt_handle_block_on.rs +++ b/vendor/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(tokio_wasi))] +use tokio::{net, time}; +#[cfg(not(tokio_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(tokio_wasi))] macro_rules! rt_test { ($($t:tt)*) => { mod current_thread_scheduler { @@ -124,7 +126,9 @@ fn unbounded_mpsc_channel() { }) } +#[cfg(not(tokio_wasi))] // Wasi doesn't support file operations or bind rt_test! { + use tokio::fs; // ==== spawn blocking futures ====== #[test] @@ -135,7 +139,7 @@ rt_test! { let contents = Handle::current() .block_on(fs::read_to_string("Cargo.toml")) .unwrap(); - assert!(contents.contains("Cargo.toml")); + assert!(contents.contains("https://tokio.rs")); } #[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(tokio_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(tokio_wasi))] fn new_multi_thread(n: usize) -> Runtime { tokio::runtime::Builder::new_multi_thread() .worker_threads(n) @@ -496,37 +505,30 @@ where F: Fn(), { { - println!("current thread runtime"); - let rt = new_current_thread(); let _enter = rt.enter(); f(); - println!("current thread runtime after shutdown"); rt.shutdown_timeout(Duration::from_secs(1000)); f(); } + #[cfg(not(tokio_wasi))] { - println!("multi thread (1 thread) runtime"); - let rt = new_multi_thread(1); let _enter = rt.enter(); f(); - println!("multi thread runtime after shutdown"); rt.shutdown_timeout(Duration::from_secs(1000)); f(); } + #[cfg(not(tokio_wasi))] { - println!("multi thread (4 threads) runtime"); - let rt = new_multi_thread(4); let _enter = rt.enter(); f(); - println!("multi thread runtime after shutdown"); rt.shutdown_timeout(Duration::from_secs(1000)); f(); } diff --git a/vendor/tokio/tests/rt_metrics.rs b/vendor/tokio/tests/rt_metrics.rs new file mode 100644 index 000000000..0fe839a2f --- /dev/null +++ b/vendor/tokio/tests/rt_metrics.rs @@ -0,0 +1,718 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", tokio_unstable, not(tokio_wasi)))] + +use std::future::Future; +use std::sync::{Arc, Mutex}; +use std::task::Poll; +use tokio::macros::support::poll_fn; + +use tokio::runtime::Runtime; +use tokio::task::consume_budget; +use tokio::time::{self, Duration}; + +#[test] +fn num_workers() { + let rt = current_thread(); + assert_eq!(1, rt.metrics().num_workers()); + + let rt = threaded(); + assert_eq!(2, rt.metrics().num_workers()); +} + +#[test] +fn num_blocking_threads() { + let rt = current_thread(); + assert_eq!(0, rt.metrics().num_blocking_threads()); + let _ = rt.block_on(rt.spawn_blocking(move || {})); + assert_eq!(1, rt.metrics().num_blocking_threads()); +} + +#[test] +fn num_idle_blocking_threads() { + let rt = current_thread(); + assert_eq!(0, rt.metrics().num_idle_blocking_threads()); + let _ = rt.block_on(rt.spawn_blocking(move || {})); + rt.block_on(async { + time::sleep(Duration::from_millis(5)).await; + }); + + // We need to wait until the blocking thread has become idle. Usually 5ms is + // enough for this to happen, but not always. When it isn't enough, sleep + // for another second. We don't always wait for a whole second since we want + // the test suite to finish quickly. + // + // Note that the timeout for idle threads to be killed is 10 seconds. + if 0 == rt.metrics().num_idle_blocking_threads() { + rt.block_on(async { + time::sleep(Duration::from_secs(1)).await; + }); + } + + assert_eq!(1, rt.metrics().num_idle_blocking_threads()); +} + +#[test] +fn blocking_queue_depth() { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .max_blocking_threads(1) + .build() + .unwrap(); + + assert_eq!(0, rt.metrics().blocking_queue_depth()); + + let ready = Arc::new(Mutex::new(())); + let guard = ready.lock().unwrap(); + + let ready_cloned = ready.clone(); + let wait_until_ready = move || { + let _unused = ready_cloned.lock().unwrap(); + }; + + let h1 = rt.spawn_blocking(wait_until_ready.clone()); + let h2 = rt.spawn_blocking(wait_until_ready); + assert!(rt.metrics().blocking_queue_depth() > 0); + + drop(guard); + + let _ = rt.block_on(h1); + let _ = rt.block_on(h2); + + assert_eq!(0, rt.metrics().blocking_queue_depth()); +} + +#[test] +fn active_tasks_count() { + let rt = current_thread(); + let metrics = rt.metrics(); + assert_eq!(0, metrics.active_tasks_count()); + rt.spawn(async move { + assert_eq!(1, metrics.active_tasks_count()); + }); + + let rt = threaded(); + let metrics = rt.metrics(); + assert_eq!(0, metrics.active_tasks_count()); + rt.spawn(async move { + assert_eq!(1, metrics.active_tasks_count()); + }); +} + +#[test] +fn remote_schedule_count() { + use std::thread; + + let rt = current_thread(); + let handle = rt.handle().clone(); + let task = thread::spawn(move || { + handle.spawn(async { + // DO nothing + }) + }) + .join() + .unwrap(); + + rt.block_on(task).unwrap(); + + assert_eq!(1, rt.metrics().remote_schedule_count()); + + let rt = threaded(); + let handle = rt.handle().clone(); + let task = thread::spawn(move || { + handle.spawn(async { + // DO nothing + }) + }) + .join() + .unwrap(); + + rt.block_on(task).unwrap(); + + assert_eq!(1, rt.metrics().remote_schedule_count()); +} + +#[test] +fn worker_park_count() { + let rt = current_thread(); + let metrics = rt.metrics(); + rt.block_on(async { + time::sleep(Duration::from_millis(1)).await; + }); + drop(rt); + assert!(1 <= metrics.worker_park_count(0)); + + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async { + time::sleep(Duration::from_millis(1)).await; + }); + drop(rt); + assert!(1 <= metrics.worker_park_count(0)); + assert!(1 <= metrics.worker_park_count(1)); +} + +#[test] +fn worker_noop_count() { + // There isn't really a great way to generate no-op parks as they happen as + // false-positive events under concurrency. + + let rt = current_thread(); + let metrics = rt.metrics(); + rt.block_on(async { + time::sleep(Duration::from_millis(1)).await; + }); + drop(rt); + assert!(0 < metrics.worker_noop_count(0)); + + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async { + time::sleep(Duration::from_millis(1)).await; + }); + drop(rt); + assert!(0 < metrics.worker_noop_count(0)); + assert!(0 < metrics.worker_noop_count(1)); +} + +#[test] +fn worker_steal_count() { + // This metric only applies to the multi-threaded runtime. + // + // We use a blocking channel to backup one worker thread. + use std::sync::mpsc::channel; + + let rt = threaded(); + let metrics = rt.metrics(); + + rt.block_on(async { + let (tx, rx) = channel(); + + // Move to the runtime. + tokio::spawn(async move { + // Spawn the task that sends to the channel + tokio::spawn(async move { + tx.send(()).unwrap(); + }); + + // Spawn a task that bumps the previous task out of the "next + // scheduled" slot. + tokio::spawn(async {}); + + // Blocking receive on the channel. + rx.recv().unwrap(); + }) + .await + .unwrap(); + }); + + drop(rt); + + let n: u64 = (0..metrics.num_workers()) + .map(|i| metrics.worker_steal_count(i)) + .sum(); + + assert_eq!(1, n); +} + +#[test] +fn worker_poll_count() { + const N: u64 = 5; + + let rt = current_thread(); + let metrics = rt.metrics(); + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async {}).await.unwrap(); + } + }); + drop(rt); + assert_eq!(N, metrics.worker_poll_count(0)); + + // Does not populate the histogram + assert!(!metrics.poll_count_histogram_enabled()); + for i in 0..10 { + assert_eq!(0, metrics.poll_count_histogram_bucket_count(0, i)); + } + + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async {}).await.unwrap(); + } + }); + drop(rt); + // Account for the `block_on` task + let n = (0..metrics.num_workers()) + .map(|i| metrics.worker_poll_count(i)) + .sum(); + + assert_eq!(N, n); + + // Does not populate the histogram + assert!(!metrics.poll_count_histogram_enabled()); + for n in 0..metrics.num_workers() { + for i in 0..10 { + assert_eq!(0, metrics.poll_count_histogram_bucket_count(n, i)); + } + } +} + +#[test] +fn worker_poll_count_histogram() { + const N: u64 = 5; + + let rts = [ + tokio::runtime::Builder::new_current_thread() + .enable_all() + .enable_metrics_poll_count_histogram() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Linear) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(Duration::from_millis(50)) + .build() + .unwrap(), + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .enable_metrics_poll_count_histogram() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Linear) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(Duration::from_millis(50)) + .build() + .unwrap(), + ]; + + for rt in rts { + let metrics = rt.metrics(); + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async {}).await.unwrap(); + } + }); + drop(rt); + + let num_workers = metrics.num_workers(); + let num_buckets = metrics.poll_count_histogram_num_buckets(); + + assert!(metrics.poll_count_histogram_enabled()); + assert_eq!(num_buckets, 3); + + let n = (0..num_workers) + .flat_map(|i| (0..num_buckets).map(move |j| (i, j))) + .map(|(worker, bucket)| metrics.poll_count_histogram_bucket_count(worker, bucket)) + .sum(); + assert_eq!(N, n); + } +} + +#[test] +fn worker_poll_count_histogram_range() { + let max = Duration::from_nanos(u64::MAX); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .enable_metrics_poll_count_histogram() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Linear) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(us(50)) + .build() + .unwrap(); + let metrics = rt.metrics(); + + assert_eq!(metrics.poll_count_histogram_bucket_range(0), us(0)..us(50)); + assert_eq!( + metrics.poll_count_histogram_bucket_range(1), + us(50)..us(100) + ); + assert_eq!(metrics.poll_count_histogram_bucket_range(2), us(100)..max); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .enable_metrics_poll_count_histogram() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Log) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(us(50)) + .build() + .unwrap(); + let metrics = rt.metrics(); + + let a = Duration::from_nanos(50000_u64.next_power_of_two()); + let b = a * 2; + + assert_eq!(metrics.poll_count_histogram_bucket_range(0), us(0)..a); + assert_eq!(metrics.poll_count_histogram_bucket_range(1), a..b); + assert_eq!(metrics.poll_count_histogram_bucket_range(2), b..max); +} + +#[test] +fn worker_poll_count_histogram_disabled_without_explicit_enable() { + let rts = [ + tokio::runtime::Builder::new_current_thread() + .enable_all() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Linear) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(Duration::from_millis(50)) + .build() + .unwrap(), + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .metrics_poll_count_histogram_scale(tokio::runtime::HistogramScale::Linear) + .metrics_poll_count_histogram_buckets(3) + .metrics_poll_count_histogram_resolution(Duration::from_millis(50)) + .build() + .unwrap(), + ]; + + for rt in rts { + let metrics = rt.metrics(); + assert!(!metrics.poll_count_histogram_enabled()); + } +} + +#[test] +fn worker_total_busy_duration() { + const N: usize = 5; + + let zero = Duration::from_millis(0); + + let rt = current_thread(); + let metrics = rt.metrics(); + + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async { + tokio::task::yield_now().await; + }) + .await + .unwrap(); + } + }); + + drop(rt); + + assert!(zero < metrics.worker_total_busy_duration(0)); + + let rt = threaded(); + let metrics = rt.metrics(); + + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async { + tokio::task::yield_now().await; + }) + .await + .unwrap(); + } + }); + + drop(rt); + + for i in 0..metrics.num_workers() { + assert!(zero < metrics.worker_total_busy_duration(i)); + } +} + +#[test] +fn worker_local_schedule_count() { + let rt = current_thread(); + let metrics = rt.metrics(); + rt.block_on(async { + tokio::spawn(async {}).await.unwrap(); + }); + drop(rt); + + assert_eq!(1, metrics.worker_local_schedule_count(0)); + assert_eq!(0, metrics.remote_schedule_count()); + + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async { + // Move to the runtime + tokio::spawn(async { + tokio::spawn(async {}).await.unwrap(); + }) + .await + .unwrap(); + }); + drop(rt); + + let n: u64 = (0..metrics.num_workers()) + .map(|i| metrics.worker_local_schedule_count(i)) + .sum(); + + assert_eq!(2, n); + assert_eq!(1, metrics.remote_schedule_count()); +} + +#[test] +fn worker_overflow_count() { + // Only applies to the threaded worker + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async { + // Move to the runtime + tokio::spawn(async { + let (tx1, rx1) = std::sync::mpsc::channel(); + let (tx2, rx2) = std::sync::mpsc::channel(); + + // First, we need to block the other worker until all tasks have + // been spawned. + tokio::spawn(async move { + tx1.send(()).unwrap(); + rx2.recv().unwrap(); + }); + + // Bump the next-run spawn + tokio::spawn(async {}); + + rx1.recv().unwrap(); + + // Spawn many tasks + for _ in 0..300 { + tokio::spawn(async {}); + } + + tx2.send(()).unwrap(); + }) + .await + .unwrap(); + }); + drop(rt); + + let n: u64 = (0..metrics.num_workers()) + .map(|i| metrics.worker_overflow_count(i)) + .sum(); + + assert_eq!(1, n); +} + +#[test] +fn injection_queue_depth() { + use std::thread; + + let rt = current_thread(); + let handle = rt.handle().clone(); + let metrics = rt.metrics(); + + thread::spawn(move || { + handle.spawn(async {}); + }) + .join() + .unwrap(); + + assert_eq!(1, metrics.injection_queue_depth()); + + let rt = threaded(); + let handle = rt.handle().clone(); + let metrics = rt.metrics(); + + // First we need to block the runtime workers + let (tx1, rx1) = std::sync::mpsc::channel(); + let (tx2, rx2) = std::sync::mpsc::channel(); + let (tx3, rx3) = std::sync::mpsc::channel(); + let rx3 = Arc::new(Mutex::new(rx3)); + + rt.spawn(async move { rx1.recv().unwrap() }); + rt.spawn(async move { rx2.recv().unwrap() }); + + // Spawn some more to make sure there are items + for _ in 0..10 { + let rx = rx3.clone(); + rt.spawn(async move { + rx.lock().unwrap().recv().unwrap(); + }); + } + + thread::spawn(move || { + handle.spawn(async {}); + }) + .join() + .unwrap(); + + let n = metrics.injection_queue_depth(); + assert!(1 <= n, "{}", n); + assert!(15 >= n, "{}", n); + + for _ in 0..10 { + tx3.send(()).unwrap(); + } + + tx1.send(()).unwrap(); + tx2.send(()).unwrap(); +} + +#[test] +fn worker_local_queue_depth() { + const N: usize = 100; + + let rt = current_thread(); + let metrics = rt.metrics(); + rt.block_on(async { + for _ in 0..N { + tokio::spawn(async {}); + } + + assert_eq!(N, metrics.worker_local_queue_depth(0)); + }); + + let rt = threaded(); + let metrics = rt.metrics(); + rt.block_on(async move { + // Move to the runtime + tokio::spawn(async move { + let (tx1, rx1) = std::sync::mpsc::channel(); + let (tx2, rx2) = std::sync::mpsc::channel(); + + // First, we need to block the other worker until all tasks have + // been spawned. + tokio::spawn(async move { + tx1.send(()).unwrap(); + rx2.recv().unwrap(); + }); + + // Bump the next-run spawn + tokio::spawn(async {}); + + rx1.recv().unwrap(); + + // Spawn some tasks + for _ in 0..100 { + tokio::spawn(async {}); + } + + let n: usize = (0..metrics.num_workers()) + .map(|i| metrics.worker_local_queue_depth(i)) + .sum(); + + assert_eq!(n, N); + + tx2.send(()).unwrap(); + }) + .await + .unwrap(); + }); +} + +#[test] +fn budget_exhaustion_yield() { + let rt = current_thread(); + let metrics = rt.metrics(); + + assert_eq!(0, metrics.budget_forced_yield_count()); + + let mut did_yield = false; + + // block on a task which consumes budget until it yields + rt.block_on(poll_fn(|cx| loop { + if did_yield { + return Poll::Ready(()); + } + + let fut = consume_budget(); + tokio::pin!(fut); + + if fut.poll(cx).is_pending() { + did_yield = true; + return Poll::Pending; + } + })); + + assert_eq!(1, rt.metrics().budget_forced_yield_count()); +} + +#[test] +fn budget_exhaustion_yield_with_joins() { + let rt = current_thread(); + let metrics = rt.metrics(); + + assert_eq!(0, metrics.budget_forced_yield_count()); + + let mut did_yield_1 = false; + let mut did_yield_2 = false; + + // block on a task which consumes budget until it yields + rt.block_on(async { + tokio::join!( + poll_fn(|cx| loop { + if did_yield_1 { + return Poll::Ready(()); + } + + let fut = consume_budget(); + tokio::pin!(fut); + + if fut.poll(cx).is_pending() { + did_yield_1 = true; + return Poll::Pending; + } + }), + poll_fn(|cx| loop { + if did_yield_2 { + return Poll::Ready(()); + } + + let fut = consume_budget(); + tokio::pin!(fut); + + if fut.poll(cx).is_pending() { + did_yield_2 = true; + return Poll::Pending; + } + }) + ) + }); + + assert_eq!(1, rt.metrics().budget_forced_yield_count()); +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +#[test] +fn io_driver_fd_count() { + let rt = current_thread(); + let metrics = rt.metrics(); + + assert_eq!(metrics.io_driver_fd_registered_count(), 0); + + let stream = tokio::net::TcpStream::connect("google.com:80"); + let stream = rt.block_on(async move { stream.await.unwrap() }); + + assert_eq!(metrics.io_driver_fd_registered_count(), 1); + assert_eq!(metrics.io_driver_fd_deregistered_count(), 0); + + drop(stream); + + assert_eq!(metrics.io_driver_fd_deregistered_count(), 1); + assert_eq!(metrics.io_driver_fd_registered_count(), 1); +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +#[test] +fn io_driver_ready_count() { + let rt = current_thread(); + let metrics = rt.metrics(); + + let stream = tokio::net::TcpStream::connect("google.com:80"); + let _stream = rt.block_on(async move { stream.await.unwrap() }); + + assert_eq!(metrics.io_driver_ready_count(), 1); +} + +fn current_thread() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() +} + +fn threaded() -> Runtime { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .unwrap() +} + +fn us(n: u64) -> Duration { + Duration::from_micros(n) +} diff --git a/vendor/tokio/tests/rt_panic.rs b/vendor/tokio/tests/rt_panic.rs new file mode 100644 index 000000000..f9a684fda --- /dev/null +++ b/vendor/tokio/tests/rt_panic.rs @@ -0,0 +1,77 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] +#![cfg(not(tokio_wasi))] // Wasi doesn't support panic recovery + +use futures::future; +use std::error::Error; +use tokio::runtime::{Builder, Handle, Runtime}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn current_handle_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = Handle::current(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn into_panic_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(move || { + let rt = current_thread(); + rt.block_on(async { + let handle = tokio::spawn(future::pending::<()>()); + + handle.abort(); + + let err = handle.await.unwrap_err(); + assert!(!&err.is_panic()); + + let _ = err.into_panic(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn builder_worker_threads_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = Builder::new_multi_thread().worker_threads(0).build(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn builder_max_blocking_threads_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = Builder::new_multi_thread().max_blocking_threads(0).build(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +fn current_thread() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() +} diff --git a/vendor/tokio/tests/rt_threaded.rs b/vendor/tokio/tests/rt_threaded.rs index 9e76c4ed0..69b186947 100644 --- a/vendor/tokio/tests/rt_threaded.rs +++ b/vendor/tokio/tests/rt_threaded.rs @@ -1,9 +1,9 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_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}; @@ -15,13 +15,23 @@ use std::sync::atomic::Ordering::Relaxed; use std::sync::{mpsc, Arc, Mutex}; use std::task::{Context, Poll, Waker}; +macro_rules! cfg_metrics { + ($($t:tt)*) => { + #[cfg(tokio_unstable)] + { + $( $t )* + } + } +} + #[test] fn single_thread() { // No panic when starting a runtime w/ a single thread let _ = runtime::Builder::new_multi_thread() .enable_all() .worker_threads(1) - .build(); + .build() + .unwrap(); } #[test] @@ -54,6 +64,39 @@ fn many_oneshot_futures() { drop(rt); } } + +#[test] +fn spawn_two() { + let rt = rt(); + + let out = rt.block_on(async { + let (tx, rx) = oneshot::channel(); + + tokio::spawn(async move { + tokio::spawn(async move { + tx.send("ZOMG").unwrap(); + }); + }); + + assert_ok!(rx.await) + }); + + assert_eq!(out, "ZOMG"); + + cfg_metrics! { + let metrics = rt.metrics(); + drop(rt); + assert_eq!(1, metrics.remote_schedule_count()); + + let mut local = 0; + for i in 0..metrics.num_workers() { + local += metrics.worker_local_schedule_count(i); + } + + assert_eq!(1, local); + } +} + #[test] fn many_multishot_futures() { const CHAIN: usize = 200; @@ -119,6 +162,32 @@ fn many_multishot_futures() { } #[test] +fn lifo_slot_budget() { + async fn my_fn() { + spawn_another(); + } + + fn spawn_another() { + tokio::spawn(my_fn()); + } + + let rt = runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(1) + .build() + .unwrap(); + + let (send, recv) = oneshot::channel(); + + rt.spawn(async move { + tokio::spawn(my_fn()); + let _ = send.send(()); + }); + + let _ = rt.block_on(recv); +} + +#[test] fn spawn_shutdown() { let rt = rt(); let (tx, rx) = mpsc::channel(); @@ -373,6 +442,32 @@ fn coop_and_block_in_place() { }); } +#[test] +fn yield_after_block_in_place() { + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .build() + .unwrap(); + + rt.block_on(async { + tokio::spawn(async move { + // Block in place then enter a new runtime + tokio::task::block_in_place(|| { + let rt = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + + rt.block_on(async {}); + }); + + // Yield, then complete + tokio::task::yield_now().await; + }) + .await + .unwrap() + }); +} + // Testing this does not panic #[test] fn max_blocking_threads() { @@ -438,9 +533,7 @@ fn wake_during_shutdown() { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { let me = Pin::into_inner(self); let mut lock = me.shared.lock().unwrap(); - println!("poll {}", me.put_waker); if me.put_waker { - println!("putting"); lock.waker = Some(cx.waker().clone()); } Poll::Pending @@ -449,13 +542,11 @@ fn wake_during_shutdown() { impl Drop for MyFuture { fn drop(&mut self) { - println!("drop {} start", self.put_waker); let mut lock = self.shared.lock().unwrap(); if !self.put_waker { lock.waker.take().unwrap().wake(); } drop(lock); - println!("drop {} stop", self.put_waker); } } @@ -473,6 +564,202 @@ fn wake_during_shutdown() { rt.block_on(async { tokio::time::sleep(tokio::time::Duration::from_millis(20)).await }); } -fn rt() -> Runtime { - Runtime::new().unwrap() +#[should_panic] +#[tokio::test] +async fn test_block_in_place1() { + tokio::task::block_in_place(|| {}); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_block_in_place2() { + tokio::task::block_in_place(|| {}); +} + +#[should_panic] +#[tokio::main(flavor = "current_thread")] +#[test] +async fn test_block_in_place3() { + tokio::task::block_in_place(|| {}); +} + +#[tokio::main] +#[test] +async fn test_block_in_place4() { + tokio::task::block_in_place(|| {}); +} + +// Repro for tokio-rs/tokio#5239 +#[test] +fn test_nested_block_in_place_with_block_on_between() { + let rt = runtime::Builder::new_multi_thread() + .worker_threads(1) + // Needs to be more than 0 + .max_blocking_threads(1) + .build() + .unwrap(); + + // Triggered by a race condition, so run a few times to make sure it is OK. + for _ in 0..100 { + let h = rt.handle().clone(); + + rt.block_on(async move { + tokio::spawn(async move { + tokio::task::block_in_place(|| { + h.block_on(async { + tokio::task::block_in_place(|| {}); + }); + }) + }) + .await + .unwrap() + }); + } +} + +// Testing the tuning logic is tricky as it is inherently timing based, and more +// of a heuristic than an exact behavior. This test checks that the interval +// changes over time based on load factors. There are no assertions, completion +// is sufficient. If there is a regression, this test will hang. In theory, we +// could add limits, but that would be likely to fail on CI. +#[test] +#[cfg(not(tokio_no_tuning_tests))] +fn test_tuning() { + use std::sync::atomic::AtomicBool; + use std::time::Duration; + + let rt = runtime::Builder::new_multi_thread() + .worker_threads(1) + .build() + .unwrap(); + + fn iter(flag: Arc<AtomicBool>, counter: Arc<AtomicUsize>, stall: bool) { + if flag.load(Relaxed) { + if stall { + std::thread::sleep(Duration::from_micros(5)); + } + + counter.fetch_add(1, Relaxed); + tokio::spawn(async move { iter(flag, counter, stall) }); + } + } + + let flag = Arc::new(AtomicBool::new(true)); + let counter = Arc::new(AtomicUsize::new(61)); + let interval = Arc::new(AtomicUsize::new(61)); + + { + let flag = flag.clone(); + let counter = counter.clone(); + rt.spawn(async move { iter(flag, counter, true) }); + } + + // Now, hammer the injection queue until the interval drops. + let mut n = 0; + loop { + let curr = interval.load(Relaxed); + + if curr <= 8 { + n += 1; + } else { + n = 0; + } + + // Make sure we get a few good rounds. Jitter in the tuning could result + // in one "good" value without being representative of reaching a good + // state. + if n == 3 { + break; + } + + if Arc::strong_count(&interval) < 5_000 { + let counter = counter.clone(); + let interval = interval.clone(); + + rt.spawn(async move { + let prev = counter.swap(0, Relaxed); + interval.store(prev, Relaxed); + }); + + std::thread::yield_now(); + } + } + + flag.store(false, Relaxed); + + let w = Arc::downgrade(&interval); + drop(interval); + + while w.strong_count() > 0 { + std::thread::sleep(Duration::from_micros(500)); + } + + // Now, run it again with a faster task + let flag = Arc::new(AtomicBool::new(true)); + // Set it high, we know it shouldn't ever really be this high + let counter = Arc::new(AtomicUsize::new(10_000)); + let interval = Arc::new(AtomicUsize::new(10_000)); + + { + let flag = flag.clone(); + let counter = counter.clone(); + rt.spawn(async move { iter(flag, counter, false) }); + } + + // Now, hammer the injection queue until the interval reaches the expected range. + let mut n = 0; + loop { + let curr = interval.load(Relaxed); + + if curr <= 1_000 && curr > 32 { + n += 1; + } else { + n = 0; + } + + if n == 3 { + break; + } + + if Arc::strong_count(&interval) <= 5_000 { + let counter = counter.clone(); + let interval = interval.clone(); + + rt.spawn(async move { + let prev = counter.swap(0, Relaxed); + interval.store(prev, Relaxed); + }); + } + + std::thread::yield_now(); + } + + flag.store(false, Relaxed); +} + +fn rt() -> runtime::Runtime { + runtime::Runtime::new().unwrap() +} + +#[cfg(tokio_unstable)] +mod unstable { + use super::*; + + #[test] + fn test_disable_lifo_slot() { + let rt = runtime::Builder::new_multi_thread() + .disable_lifo_slot() + .worker_threads(2) + .build() + .unwrap(); + + rt.block_on(async { + tokio::spawn(async { + // Spawn another task and block the thread until completion. If the LIFO slot + // is used then the test doesn't complete. + futures::executor::block_on(tokio::spawn(async {})).unwrap(); + }) + .await + .unwrap(); + }) + } } diff --git a/vendor/tokio/tests/rt_time_start_paused.rs b/vendor/tokio/tests/rt_time_start_paused.rs new file mode 100644 index 000000000..283f4748a --- /dev/null +++ b/vendor/tokio/tests/rt_time_start_paused.rs @@ -0,0 +1,14 @@ +#![cfg(all(feature = "full"))] + +use tokio::time::{Duration, Instant}; + +#[tokio::test(start_paused = true)] +async fn test_start_paused() { + let now = Instant::now(); + + // Pause a few times w/ std sleep and ensure `now` stays the same + for _ in 0..5 { + std::thread::sleep(Duration::from_millis(1)); + assert_eq!(now, Instant::now()); + } +} diff --git a/vendor/tokio/tests/signal_no_rt.rs b/vendor/tokio/tests/signal_no_rt.rs index b0f32b2d1..ffcc6651c 100644 --- a/vendor/tokio/tests/signal_no_rt.rs +++ b/vendor/tokio/tests/signal_no_rt.rs @@ -4,6 +4,7 @@ use tokio::signal::unix::{signal, SignalKind}; +#[cfg_attr(tokio_wasi, ignore = "Wasi does not support panic recovery")] #[test] #[should_panic] fn no_runtime_panics_creating_signals() { diff --git a/vendor/tokio/tests/signal_panic.rs b/vendor/tokio/tests/signal_panic.rs new file mode 100644 index 000000000..ce1ec3e4a --- /dev/null +++ b/vendor/tokio/tests/signal_panic.rs @@ -0,0 +1,29 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] +#![cfg(unix)] + +use std::error::Error; +use tokio::runtime::Builder; +use tokio::signal::unix::{signal, SignalKind}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn signal_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = Builder::new_current_thread().build().unwrap(); + + rt.block_on(async { + let kind = SignalKind::from_raw(-1); + let _ = signal(kind); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} diff --git a/vendor/tokio/tests/support/leaked_buffers.rs b/vendor/tokio/tests/support/leaked_buffers.rs new file mode 100644 index 000000000..a6079fb70 --- /dev/null +++ b/vendor/tokio/tests/support/leaked_buffers.rs @@ -0,0 +1,26 @@ +/// Can create buffers of arbitrary lifetime. +/// Frees created buffers when dropped. +/// +/// This struct is of course unsafe and the fact that +/// it must outlive the created slices has to be ensured by +/// the programmer. +/// +/// Used at certain test scenarios as a safer version of +/// Vec::leak, to satisfy the address sanitizer. +pub struct LeakedBuffers { + leaked_vecs: Vec<Box<[u8]>>, +} + +impl LeakedBuffers { + pub fn new() -> Self { + Self { + leaked_vecs: vec![], + } + } + pub unsafe fn create<'a>(&mut self, size: usize) -> &'a mut [u8] { + let new_mem = vec![0u8; size].into_boxed_slice(); + self.leaked_vecs.push(new_mem); + let new_mem = self.leaked_vecs.last_mut().unwrap(); + std::slice::from_raw_parts_mut(new_mem.as_mut_ptr(), new_mem.len()) + } +} diff --git a/vendor/tokio/tests/support/panic.rs b/vendor/tokio/tests/support/panic.rs new file mode 100644 index 000000000..df2f59d30 --- /dev/null +++ b/vendor/tokio/tests/support/panic.rs @@ -0,0 +1,34 @@ +use std::panic; +use std::sync::{Arc, Mutex}; + +pub fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> { + static PANIC_MUTEX: Mutex<()> = Mutex::new(()); + + { + let _guard = PANIC_MUTEX.lock(); + let panic_file: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None)); + + let prev_hook = panic::take_hook(); + { + let panic_file = panic_file.clone(); + panic::set_hook(Box::new(move |panic_info| { + let panic_location = panic_info.location().unwrap(); + panic_file + .lock() + .unwrap() + .clone_from(&Some(panic_location.file().to_string())); + })); + } + + let result = panic::catch_unwind(func); + // Return to the previously set panic hook (maybe default) so that we get nice error + // messages in the tests. + panic::set_hook(prev_hook); + + if result.is_err() { + panic_file.lock().unwrap().clone() + } else { + None + } + } +} diff --git a/vendor/tokio/tests/sync_barrier.rs b/vendor/tokio/tests/sync_barrier.rs index f280fe860..2001c3598 100644 --- a/vendor/tokio/tests/sync_barrier.rs +++ b/vendor/tokio/tests/sync_barrier.rs @@ -1,6 +1,9 @@ #![allow(clippy::unnecessary_operation)] #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::Barrier; diff --git a/vendor/tokio/tests/sync_broadcast.rs b/vendor/tokio/tests/sync_broadcast.rs index 5f79800a7..feed03148 100644 --- a/vendor/tokio/tests/sync_broadcast.rs +++ b/vendor/tokio/tests/sync_broadcast.rs @@ -2,6 +2,9 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "sync")] +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; + use tokio::sync::broadcast; use tokio_test::task; use tokio_test::{ @@ -44,7 +47,7 @@ macro_rules! assert_closed { ($e:expr) => { match assert_err!($e) { broadcast::error::TryRecvError::Closed => {} - _ => panic!("did not lag"), + _ => panic!("is not closed"), } }; } @@ -273,12 +276,14 @@ fn send_no_rx() { #[test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding fn zero_capacity() { broadcast::channel::<()>(0); } #[test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding fn capacity_too_big() { use std::usize; @@ -286,6 +291,8 @@ fn capacity_too_big() { } #[test] +#[cfg(panic = "unwind")] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding fn panic_in_clone() { use std::panic::{self, AssertUnwindSafe}; @@ -451,6 +458,186 @@ fn lagging_receiver_recovers_after_wrap_open() { assert_empty!(rx); } +#[test] +fn receiver_len_with_lagged() { + let (tx, mut rx) = broadcast::channel(3); + + tx.send(10).unwrap(); + tx.send(20).unwrap(); + tx.send(30).unwrap(); + tx.send(40).unwrap(); + + assert_eq!(rx.len(), 4); + assert_eq!(assert_recv!(rx), 10); + + tx.send(50).unwrap(); + tx.send(60).unwrap(); + + assert_eq!(rx.len(), 5); + assert_lagged!(rx.try_recv(), 1); +} + fn is_closed(err: broadcast::error::RecvError) -> bool { matches!(err, broadcast::error::RecvError::Closed) } + +#[test] +fn resubscribe_points_to_tail() { + let (tx, mut rx) = broadcast::channel(3); + tx.send(1).unwrap(); + + let mut rx_resub = rx.resubscribe(); + + // verify we're one behind at the start + assert_empty!(rx_resub); + assert_eq!(assert_recv!(rx), 1); + + // verify we do not affect rx + tx.send(2).unwrap(); + assert_eq!(assert_recv!(rx_resub), 2); + tx.send(3).unwrap(); + assert_eq!(assert_recv!(rx), 2); + assert_eq!(assert_recv!(rx), 3); + assert_empty!(rx); + + assert_eq!(assert_recv!(rx_resub), 3); + assert_empty!(rx_resub); +} + +#[test] +fn resubscribe_lagged() { + let (tx, mut rx) = broadcast::channel(1); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + let mut rx_resub = rx.resubscribe(); + assert_lagged!(rx.try_recv(), 1); + assert_empty!(rx_resub); + + assert_eq!(assert_recv!(rx), 2); + assert_empty!(rx); + assert_empty!(rx_resub); +} + +#[test] +fn resubscribe_to_closed_channel() { + let (tx, rx) = tokio::sync::broadcast::channel::<u32>(2); + drop(tx); + + let mut rx_resub = rx.resubscribe(); + assert_closed!(rx_resub.try_recv()); +} + +#[test] +fn sender_len() { + let (tx, mut rx1) = broadcast::channel(4); + let mut rx2 = tx.subscribe(); + + assert_eq!(tx.len(), 0); + assert!(tx.is_empty()); + + tx.send(1).unwrap(); + tx.send(2).unwrap(); + tx.send(3).unwrap(); + + assert_eq!(tx.len(), 3); + assert!(!tx.is_empty()); + + assert_recv!(rx1); + assert_recv!(rx1); + + assert_eq!(tx.len(), 3); + assert!(!tx.is_empty()); + + assert_recv!(rx2); + + assert_eq!(tx.len(), 2); + assert!(!tx.is_empty()); + + tx.send(4).unwrap(); + tx.send(5).unwrap(); + tx.send(6).unwrap(); + + assert_eq!(tx.len(), 4); + assert!(!tx.is_empty()); +} + +#[test] +#[cfg(not(tokio_wasm_not_wasi))] +fn sender_len_random() { + use rand::Rng; + + let (tx, mut rx1) = broadcast::channel(16); + let mut rx2 = tx.subscribe(); + + for _ in 0..1000 { + match rand::thread_rng().gen_range(0..4) { + 0 => { + let _ = rx1.try_recv(); + } + 1 => { + let _ = rx2.try_recv(); + } + _ => { + tx.send(0).unwrap(); + } + } + + let expected_len = usize::min(usize::max(rx1.len(), rx2.len()), 16); + assert_eq!(tx.len(), expected_len); + } +} + +#[test] +fn send_in_waker_drop() { + use futures::task::ArcWake; + use std::future::Future; + use std::task::Context; + + struct SendOnDrop(broadcast::Sender<()>); + + impl Drop for SendOnDrop { + fn drop(&mut self) { + let _ = self.0.send(()); + } + } + + impl ArcWake for SendOnDrop { + fn wake_by_ref(_arc_self: &Arc<Self>) {} + } + + // Test if there is no deadlock when replacing the old waker. + + let (tx, mut rx) = broadcast::channel(16); + + let mut fut = Box::pin(async { + let _ = rx.recv().await; + }); + + // Store our special waker in the receiving future. + let waker = futures::task::waker(Arc::new(SendOnDrop(tx))); + let mut cx = Context::from_waker(&waker); + assert!(fut.as_mut().poll(&mut cx).is_pending()); + drop(waker); + + // Second poll shouldn't deadlock. + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let _ = fut.as_mut().poll(&mut cx); + + // Test if there is no deadlock when calling waker.wake(). + + let (tx, mut rx) = broadcast::channel(16); + + let mut fut = Box::pin(async { + let _ = rx.recv().await; + }); + + // Store our special waker in the receiving future. + let waker = futures::task::waker(Arc::new(SendOnDrop(tx.clone()))); + let mut cx = Context::from_waker(&waker); + assert!(fut.as_mut().poll(&mut cx).is_pending()); + drop(waker); + + // Shouldn't deadlock. + let _ = tx.send(()); +} diff --git a/vendor/tokio/tests/sync_errors.rs b/vendor/tokio/tests/sync_errors.rs index 66e8f0c09..bf0a6cf73 100644 --- a/vendor/tokio/tests/sync_errors.rs +++ b/vendor/tokio/tests/sync_errors.rs @@ -1,5 +1,8 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; fn is_error<T: std::error::Error + Send + Sync>() {} diff --git a/vendor/tokio/tests/sync_mpsc.rs b/vendor/tokio/tests/sync_mpsc.rs index cd43ad438..6e8709641 100644 --- a/vendor/tokio/tests/sync_mpsc.rs +++ b/vendor/tokio/tests/sync_mpsc.rs @@ -1,18 +1,21 @@ #![allow(clippy::redundant_clone)] #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] -use std::thread; -use tokio::runtime::Runtime; -use tokio::sync::mpsc; -use tokio::sync::mpsc::error::TrySendError; -use tokio_test::task; -use tokio_test::{ - assert_err, assert_ok, assert_pending, assert_ready, assert_ready_err, assert_ready_ok, -}; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; +use std::fmt; use std::sync::Arc; +use tokio::sync::mpsc; +use tokio::sync::mpsc::error::{TryRecvError, TrySendError}; +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; +use tokio_test::*; +#[cfg(not(tokio_wasm))] mod support { pub(crate) mod mpsc_stream; } @@ -21,7 +24,7 @@ trait AssertSend: Send {} impl AssertSend for mpsc::Sender<i32> {} impl AssertSend for mpsc::Receiver<i32> {} -#[tokio::test] +#[maybe_tokio_test] async fn send_recv_with_buffer() { let (tx, mut rx) = mpsc::channel::<i32>(16); @@ -46,6 +49,7 @@ async fn send_recv_with_buffer() { } #[tokio::test] +#[cfg(feature = "full")] async fn reserve_disarm() { let (tx, mut rx) = mpsc::channel::<i32>(2); let tx1 = tx.clone(); @@ -58,10 +62,10 @@ async fn reserve_disarm() { let permit2 = assert_ok!(tx2.reserve().await); // But a third should not be ready - let mut r3 = task::spawn(tx3.reserve()); + let mut r3 = tokio_test::task::spawn(tx3.reserve()); assert_pending!(r3.poll()); - let mut r4 = task::spawn(tx4.reserve()); + let mut r4 = tokio_test::task::spawn(tx4.reserve()); assert_pending!(r4.poll()); // Using one of the reserved slots should allow a new handle to become ready @@ -78,11 +82,12 @@ async fn reserve_disarm() { drop(permit2); assert!(r4.is_woken()); - let mut r1 = task::spawn(tx1.reserve()); + let mut r1 = tokio_test::task::spawn(tx1.reserve()); assert_pending!(r1.poll()); } #[tokio::test] +#[cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads async fn send_recv_stream_with_buffer() { use tokio_stream::StreamExt; @@ -100,6 +105,7 @@ async fn send_recv_stream_with_buffer() { } #[tokio::test] +#[cfg(feature = "full")] async fn async_send_recv_with_buffer() { let (tx, mut rx) = mpsc::channel(16); @@ -114,10 +120,11 @@ async fn async_send_recv_with_buffer() { } #[tokio::test] +#[cfg(feature = "full")] async fn start_send_past_cap() { use std::future::Future; - let mut t1 = task::spawn(()); + let mut t1 = tokio_test::task::spawn(()); let (tx1, mut rx) = mpsc::channel(1); let tx2 = tx1.clone(); @@ -128,7 +135,7 @@ async fn start_send_past_cap() { t1.enter(|cx, _| assert_pending!(r1.as_mut().poll(cx))); { - let mut r2 = task::spawn(tx2.reserve()); + let mut r2 = tokio_test::task::spawn(tx2.reserve()); assert_pending!(r2.poll()); drop(r1); @@ -147,11 +154,12 @@ async fn start_send_past_cap() { #[test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding fn buffer_gteq_one() { mpsc::channel::<i32>(0); } -#[tokio::test] +#[maybe_tokio_test] async fn send_recv_unbounded() { let (tx, mut rx) = mpsc::unbounded_channel::<i32>(); @@ -168,6 +176,7 @@ async fn send_recv_unbounded() { } #[tokio::test] +#[cfg(feature = "full")] async fn async_send_recv_unbounded() { let (tx, mut rx) = mpsc::unbounded_channel(); @@ -182,6 +191,7 @@ async fn async_send_recv_unbounded() { } #[tokio::test] +#[cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads async fn send_recv_stream_unbounded() { use tokio_stream::StreamExt; @@ -199,32 +209,32 @@ async fn send_recv_stream_unbounded() { assert_eq!(None, rx.next().await); } -#[tokio::test] +#[maybe_tokio_test] async fn no_t_bounds_buffer() { struct NoImpls; let (tx, mut rx) = mpsc::channel(100); // sender should be Debug even though T isn't Debug - println!("{:?}", tx); + is_debug(&tx); // same with Receiver - println!("{:?}", rx); + is_debug(&rx); // and sender should be Clone even though T isn't Clone assert!(tx.clone().try_send(NoImpls).is_ok()); assert!(rx.recv().await.is_some()); } -#[tokio::test] +#[maybe_tokio_test] async fn no_t_bounds_unbounded() { struct NoImpls; let (tx, mut rx) = mpsc::unbounded_channel(); // sender should be Debug even though T isn't Debug - println!("{:?}", tx); + is_debug(&tx); // same with Receiver - println!("{:?}", rx); + is_debug(&rx); // and sender should be Clone even though T isn't Clone assert!(tx.clone().send(NoImpls).is_ok()); @@ -232,6 +242,7 @@ async fn no_t_bounds_unbounded() { } #[tokio::test] +#[cfg(feature = "full")] async fn send_recv_buffer_limited() { let (tx, mut rx) = mpsc::channel::<i32>(1); @@ -242,7 +253,7 @@ async fn send_recv_buffer_limited() { p1.send(1); // Not ready - let mut p2 = task::spawn(tx.reserve()); + let mut p2 = tokio_test::task::spawn(tx.reserve()); assert_pending!(p2.poll()); // Take the value @@ -261,7 +272,7 @@ async fn send_recv_buffer_limited() { assert!(rx.recv().await.is_some()); } -#[tokio::test] +#[maybe_tokio_test] async fn recv_close_gets_none_idle() { let (tx, mut rx) = mpsc::channel::<i32>(10); @@ -273,12 +284,13 @@ async fn recv_close_gets_none_idle() { } #[tokio::test] +#[cfg(feature = "full")] async fn recv_close_gets_none_reserved() { let (tx1, mut rx) = mpsc::channel::<i32>(1); let tx2 = tx1.clone(); let permit1 = assert_ok!(tx1.reserve().await); - let mut permit2 = task::spawn(tx2.reserve()); + let mut permit2 = tokio_test::task::spawn(tx2.reserve()); assert_pending!(permit2.poll()); rx.close(); @@ -287,7 +299,7 @@ async fn recv_close_gets_none_reserved() { assert_ready_err!(permit2.poll()); { - let mut recv = task::spawn(rx.recv()); + let mut recv = tokio_test::task::spawn(rx.recv()); assert_pending!(recv.poll()); permit1.send(123); @@ -300,13 +312,13 @@ async fn recv_close_gets_none_reserved() { assert!(rx.recv().await.is_none()); } -#[tokio::test] +#[maybe_tokio_test] async fn tx_close_gets_none() { let (_, mut rx) = mpsc::channel::<i32>(10); assert!(rx.recv().await.is_none()); } -#[tokio::test] +#[maybe_tokio_test] async fn try_send_fail() { let (tx, mut rx) = mpsc::channel(1); @@ -327,7 +339,28 @@ async fn try_send_fail() { assert!(rx.recv().await.is_none()); } -#[tokio::test] +#[maybe_tokio_test] +async fn try_send_fail_with_try_recv() { + let (tx, mut rx) = mpsc::channel(1); + + tx.try_send("hello").unwrap(); + + // This should fail + match assert_err!(tx.try_send("fail")) { + TrySendError::Full(..) => {} + _ => panic!(), + } + + assert_eq!(rx.try_recv(), Ok("hello")); + + assert_ok!(tx.try_send("goodbye")); + drop(tx); + + assert_eq!(rx.try_recv(), Ok("goodbye")); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[maybe_tokio_test] async fn try_reserve_fails() { let (tx, mut rx) = mpsc::channel(1); @@ -351,6 +384,7 @@ async fn try_reserve_fails() { } #[tokio::test] +#[cfg(feature = "full")] async fn drop_permit_releases_permit() { // poll_ready reserves capacity, ensure that the capacity is released if tx // is dropped w/o sending a value. @@ -359,7 +393,7 @@ async fn drop_permit_releases_permit() { let permit = assert_ok!(tx1.reserve().await); - let mut reserve2 = task::spawn(tx2.reserve()); + let mut reserve2 = tokio_test::task::spawn(tx2.reserve()); assert_pending!(reserve2.poll()); drop(permit); @@ -368,7 +402,7 @@ async fn drop_permit_releases_permit() { assert_ready_ok!(reserve2.poll()); } -#[tokio::test] +#[maybe_tokio_test] async fn dropping_rx_closes_channel() { let (tx, rx) = mpsc::channel(100); @@ -389,13 +423,15 @@ fn dropping_rx_closes_channel_for_try() { drop(rx); - { - let err = assert_err!(tx.try_send(msg.clone())); - match err { - TrySendError::Closed(..) => {} - _ => panic!(), - } - } + assert!(matches!( + tx.try_send(msg.clone()), + Err(TrySendError::Closed(_)) + )); + assert!(matches!(tx.try_reserve(), Err(TrySendError::Closed(_)))); + assert!(matches!( + tx.try_reserve_owned(), + Err(TrySendError::Closed(_)) + )); assert_eq!(1, Arc::strong_count(&msg)); } @@ -416,48 +452,57 @@ fn unconsumed_messages_are_dropped() { } #[test] +#[cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads fn blocking_recv() { let (tx, mut rx) = mpsc::channel::<u8>(1); - let sync_code = thread::spawn(move || { + let sync_code = std::thread::spawn(move || { assert_eq!(Some(10), rx.blocking_recv()); }); - Runtime::new().unwrap().block_on(async move { - let _ = tx.send(10).await; - }); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let _ = tx.send(10).await; + }); sync_code.join().unwrap() } #[tokio::test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding async fn blocking_recv_async() { let (_tx, mut rx) = mpsc::channel::<()>(1); let _ = rx.blocking_recv(); } #[test] +#[cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads fn blocking_send() { let (tx, mut rx) = mpsc::channel::<u8>(1); - let sync_code = thread::spawn(move || { + let sync_code = std::thread::spawn(move || { tx.blocking_send(10).unwrap(); }); - Runtime::new().unwrap().block_on(async move { - assert_eq!(Some(10), rx.recv().await); - }); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + assert_eq!(Some(10), rx.recv().await); + }); sync_code.join().unwrap() } #[tokio::test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding async fn blocking_send_async() { let (tx, _rx) = mpsc::channel::<()>(1); let _ = tx.blocking_send(()); } #[tokio::test] +#[cfg(feature = "full")] async fn ready_close_cancel_bounded() { let (tx, mut rx) = mpsc::channel::<()>(100); let _tx2 = tx.clone(); @@ -466,7 +511,7 @@ async fn ready_close_cancel_bounded() { rx.close(); - let mut recv = task::spawn(rx.recv()); + let mut recv = tokio_test::task::spawn(rx.recv()); assert_pending!(recv.poll()); drop(permit); @@ -477,13 +522,14 @@ async fn ready_close_cancel_bounded() { } #[tokio::test] +#[cfg(feature = "full")] async fn permit_available_not_acquired_close() { let (tx1, mut rx) = mpsc::channel::<()>(1); let tx2 = tx1.clone(); let permit1 = assert_ok!(tx1.reserve().await); - let mut permit2 = task::spawn(tx2.reserve()); + let mut permit2 = tokio_test::task::spawn(tx2.reserve()); assert_pending!(permit2.poll()); rx.close(); @@ -494,3 +540,140 @@ async fn permit_available_not_acquired_close() { drop(permit2); assert!(rx.recv().await.is_none()); } + +#[test] +fn try_recv_bounded() { + let (tx, mut rx) = mpsc::channel(5); + + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + assert!(tx.try_send("hello").is_err()); + + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Err(TryRecvError::Empty), rx.try_recv()); + + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + assert_eq!(Ok("hello"), rx.try_recv()); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + assert!(tx.try_send("hello").is_err()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Err(TryRecvError::Empty), rx.try_recv()); + + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + tx.try_send("hello").unwrap(); + drop(tx); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Ok("hello"), rx.try_recv()); + assert_eq!(Err(TryRecvError::Disconnected), rx.try_recv()); +} + +#[test] +fn try_recv_unbounded() { + for num in 0..100 { + let (tx, mut rx) = mpsc::unbounded_channel(); + + for i in 0..num { + tx.send(i).unwrap(); + } + + for i in 0..num { + assert_eq!(rx.try_recv(), Ok(i)); + } + + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + } +} + +#[test] +fn try_recv_close_while_empty_bounded() { + let (tx, mut rx) = mpsc::channel::<()>(5); + + assert_eq!(Err(TryRecvError::Empty), rx.try_recv()); + drop(tx); + assert_eq!(Err(TryRecvError::Disconnected), rx.try_recv()); +} + +#[test] +fn try_recv_close_while_empty_unbounded() { + let (tx, mut rx) = mpsc::unbounded_channel::<()>(); + + assert_eq!(Err(TryRecvError::Empty), rx.try_recv()); + drop(tx); + assert_eq!(Err(TryRecvError::Disconnected), rx.try_recv()); +} + +#[tokio::test(start_paused = true)] +#[cfg(feature = "full")] +async fn recv_timeout() { + use tokio::sync::mpsc::error::SendTimeoutError::{Closed, Timeout}; + use tokio::time::Duration; + + let (tx, rx) = mpsc::channel(5); + + assert_eq!(tx.send_timeout(10, Duration::from_secs(1)).await, Ok(())); + assert_eq!(tx.send_timeout(20, Duration::from_secs(1)).await, Ok(())); + assert_eq!(tx.send_timeout(30, Duration::from_secs(1)).await, Ok(())); + assert_eq!(tx.send_timeout(40, Duration::from_secs(1)).await, Ok(())); + assert_eq!(tx.send_timeout(50, Duration::from_secs(1)).await, Ok(())); + assert_eq!( + tx.send_timeout(60, Duration::from_secs(1)).await, + Err(Timeout(60)) + ); + + drop(rx); + assert_eq!( + tx.send_timeout(70, Duration::from_secs(1)).await, + Err(Closed(70)) + ); +} + +#[test] +#[should_panic = "there is no reactor running, must be called from the context of a Tokio 1.x runtime"] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding +fn recv_timeout_panic() { + use futures::future::FutureExt; + use tokio::time::Duration; + + let (tx, _rx) = mpsc::channel(5); + tx.send_timeout(10, Duration::from_secs(1)).now_or_never(); +} + +// Tests that channel `capacity` changes and `max_capacity` stays the same +#[tokio::test] +async fn test_tx_capacity() { + let (tx, _rx) = mpsc::channel::<()>(10); + // both capacities are same before + assert_eq!(tx.capacity(), 10); + assert_eq!(tx.max_capacity(), 10); + + let _permit = tx.reserve().await.unwrap(); + // after reserve, only capacity should drop by one + assert_eq!(tx.capacity(), 9); + assert_eq!(tx.max_capacity(), 10); + + tx.send(()).await.unwrap(); + // after send, capacity should drop by one again + assert_eq!(tx.capacity(), 8); + assert_eq!(tx.max_capacity(), 10); +} + +fn is_debug<T: fmt::Debug>(_: &T) {} diff --git a/vendor/tokio/tests/sync_mpsc_weak.rs b/vendor/tokio/tests/sync_mpsc_weak.rs new file mode 100644 index 000000000..0fdfc0070 --- /dev/null +++ b/vendor/tokio/tests/sync_mpsc_weak.rs @@ -0,0 +1,513 @@ +#![allow(clippy::redundant_clone)] +#![warn(rust_2018_idioms)] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{Acquire, Release}; +use tokio::sync::mpsc::{self, channel, unbounded_channel}; +use tokio::sync::oneshot; + +#[tokio::test] +async fn weak_sender() { + let (tx, mut rx) = channel(11); + + let tx_weak = tokio::spawn(async move { + let tx_weak = tx.clone().downgrade(); + + for i in 0..10 { + if tx.send(i).await.is_err() { + return None; + } + } + + let tx2 = tx_weak + .upgrade() + .expect("expected to be able to upgrade tx_weak"); + let _ = tx2.send(20).await; + let tx_weak = tx2.downgrade(); + + Some(tx_weak) + }) + .await + .unwrap(); + + for i in 0..12 { + let recvd = rx.recv().await; + + match recvd { + Some(msg) => { + if i == 10 { + assert_eq!(msg, 20); + } + } + None => { + assert_eq!(i, 11); + break; + } + } + } + + let tx_weak = tx_weak.unwrap(); + let upgraded = tx_weak.upgrade(); + assert!(upgraded.is_none()); +} + +#[tokio::test] +async fn actor_weak_sender() { + pub struct MyActor { + receiver: mpsc::Receiver<ActorMessage>, + sender: mpsc::WeakSender<ActorMessage>, + next_id: u32, + pub received_self_msg: bool, + } + + enum ActorMessage { + GetUniqueId { respond_to: oneshot::Sender<u32> }, + SelfMessage {}, + } + + impl MyActor { + fn new( + receiver: mpsc::Receiver<ActorMessage>, + sender: mpsc::WeakSender<ActorMessage>, + ) -> Self { + MyActor { + receiver, + sender, + next_id: 0, + received_self_msg: false, + } + } + + fn handle_message(&mut self, msg: ActorMessage) { + match msg { + ActorMessage::GetUniqueId { respond_to } => { + self.next_id += 1; + + // The `let _ =` ignores any errors when sending. + // + // This can happen if the `select!` macro is used + // to cancel waiting for the response. + let _ = respond_to.send(self.next_id); + } + ActorMessage::SelfMessage { .. } => { + self.received_self_msg = true; + } + } + } + + async fn send_message_to_self(&mut self) { + let msg = ActorMessage::SelfMessage {}; + + let sender = self.sender.clone(); + + // cannot move self.sender here + if let Some(sender) = sender.upgrade() { + let _ = sender.send(msg).await; + self.sender = sender.downgrade(); + } + } + + async fn run(&mut self) { + let mut i = 0; + while let Some(msg) = self.receiver.recv().await { + self.handle_message(msg); + + if i == 0 { + self.send_message_to_self().await; + } + + i += 1 + } + + assert!(self.received_self_msg); + } + } + + #[derive(Clone)] + pub struct MyActorHandle { + sender: mpsc::Sender<ActorMessage>, + } + + impl MyActorHandle { + pub fn new() -> (Self, MyActor) { + let (sender, receiver) = mpsc::channel(8); + let actor = MyActor::new(receiver, sender.clone().downgrade()); + + (Self { sender }, actor) + } + + pub async fn get_unique_id(&self) -> u32 { + let (send, recv) = oneshot::channel(); + let msg = ActorMessage::GetUniqueId { respond_to: send }; + + // Ignore send errors. If this send fails, so does the + // recv.await below. There's no reason to check the + // failure twice. + let _ = self.sender.send(msg).await; + recv.await.expect("Actor task has been killed") + } + } + + let (handle, mut actor) = MyActorHandle::new(); + + let actor_handle = tokio::spawn(async move { actor.run().await }); + + let _ = tokio::spawn(async move { + let _ = handle.get_unique_id().await; + drop(handle); + }) + .await; + + let _ = actor_handle.await; +} + +static NUM_DROPPED: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug)] +struct Msg; + +impl Drop for Msg { + fn drop(&mut self) { + NUM_DROPPED.fetch_add(1, Release); + } +} + +// Tests that no pending messages are put onto the channel after `Rx` was +// dropped. +// +// Note: After the introduction of `WeakSender`, which internally +// used `Arc` and doesn't call a drop of the channel after the last strong +// `Sender` was dropped while more than one `WeakSender` remains, we want to +// ensure that no messages are kept in the channel, which were sent after +// the receiver was dropped. +#[tokio::test] +async fn test_msgs_dropped_on_rx_drop() { + let (tx, mut rx) = mpsc::channel(3); + + tx.send(Msg {}).await.unwrap(); + tx.send(Msg {}).await.unwrap(); + + // This msg will be pending and should be dropped when `rx` is dropped + let sent_fut = tx.send(Msg {}); + + let _ = rx.recv().await.unwrap(); + let _ = rx.recv().await.unwrap(); + + sent_fut.await.unwrap(); + + drop(rx); + + assert_eq!(NUM_DROPPED.load(Acquire), 3); + + // This msg will not be put onto `Tx` list anymore, since `Rx` is closed. + assert!(tx.send(Msg {}).await.is_err()); + + assert_eq!(NUM_DROPPED.load(Acquire), 4); +} + +// Tests that a `WeakSender` is upgradeable when other `Sender`s exist. +#[test] +fn downgrade_upgrade_sender_success() { + let (tx, _rx) = mpsc::channel::<i32>(1); + let weak_tx = tx.downgrade(); + assert!(weak_tx.upgrade().is_some()); +} + +// Tests that a `WeakSender` fails to upgrade when no other `Sender` exists. +#[test] +fn downgrade_upgrade_sender_failure() { + let (tx, _rx) = mpsc::channel::<i32>(1); + let weak_tx = tx.downgrade(); + drop(tx); + assert!(weak_tx.upgrade().is_none()); +} + +// Tests that a `WeakSender` cannot be upgraded after a `Sender` was dropped, +// which existed at the time of the `downgrade` call. +#[test] +fn downgrade_drop_upgrade() { + let (tx, _rx) = mpsc::channel::<i32>(1); + + // the cloned `Tx` is dropped right away + let weak_tx = tx.clone().downgrade(); + drop(tx); + assert!(weak_tx.upgrade().is_none()); +} + +// Tests that we can upgrade a weak sender with an outstanding permit +// but no other strong senders. +#[tokio::test] +async fn downgrade_get_permit_upgrade_no_senders() { + let (tx, _rx) = mpsc::channel::<i32>(1); + let weak_tx = tx.downgrade(); + let _permit = tx.reserve_owned().await.unwrap(); + assert!(weak_tx.upgrade().is_some()); +} + +// Tests that you can downgrade and upgrade a sender with an outstanding permit +// but no other senders left. +#[tokio::test] +async fn downgrade_upgrade_get_permit_no_senders() { + let (tx, _rx) = mpsc::channel::<i32>(1); + let tx2 = tx.clone(); + let _permit = tx.reserve_owned().await.unwrap(); + let weak_tx = tx2.downgrade(); + drop(tx2); + assert!(weak_tx.upgrade().is_some()); +} + +// Tests that `downgrade` does not change the `tx_count` of the channel. +#[test] +fn test_tx_count_weak_sender() { + let (tx, _rx) = mpsc::channel::<i32>(1); + let tx_weak = tx.downgrade(); + let tx_weak2 = tx.downgrade(); + drop(tx); + + assert!(tx_weak.upgrade().is_none() && tx_weak2.upgrade().is_none()); +} + +#[tokio::test] +async fn weak_unbounded_sender() { + let (tx, mut rx) = unbounded_channel(); + + let tx_weak = tokio::spawn(async move { + let tx_weak = tx.clone().downgrade(); + + for i in 0..10 { + if tx.send(i).is_err() { + return None; + } + } + + let tx2 = tx_weak + .upgrade() + .expect("expected to be able to upgrade tx_weak"); + let _ = tx2.send(20); + let tx_weak = tx2.downgrade(); + + Some(tx_weak) + }) + .await + .unwrap(); + + for i in 0..12 { + let recvd = rx.recv().await; + + match recvd { + Some(msg) => { + if i == 10 { + assert_eq!(msg, 20); + } + } + None => { + assert_eq!(i, 11); + break; + } + } + } + + let tx_weak = tx_weak.unwrap(); + let upgraded = tx_weak.upgrade(); + assert!(upgraded.is_none()); +} + +#[tokio::test] +async fn actor_weak_unbounded_sender() { + pub struct MyActor { + receiver: mpsc::UnboundedReceiver<ActorMessage>, + sender: mpsc::WeakUnboundedSender<ActorMessage>, + next_id: u32, + pub received_self_msg: bool, + } + + enum ActorMessage { + GetUniqueId { respond_to: oneshot::Sender<u32> }, + SelfMessage {}, + } + + impl MyActor { + fn new( + receiver: mpsc::UnboundedReceiver<ActorMessage>, + sender: mpsc::WeakUnboundedSender<ActorMessage>, + ) -> Self { + MyActor { + receiver, + sender, + next_id: 0, + received_self_msg: false, + } + } + + fn handle_message(&mut self, msg: ActorMessage) { + match msg { + ActorMessage::GetUniqueId { respond_to } => { + self.next_id += 1; + + // The `let _ =` ignores any errors when sending. + // + // This can happen if the `select!` macro is used + // to cancel waiting for the response. + let _ = respond_to.send(self.next_id); + } + ActorMessage::SelfMessage { .. } => { + self.received_self_msg = true; + } + } + } + + async fn send_message_to_self(&mut self) { + let msg = ActorMessage::SelfMessage {}; + + let sender = self.sender.clone(); + + // cannot move self.sender here + if let Some(sender) = sender.upgrade() { + let _ = sender.send(msg); + self.sender = sender.downgrade(); + } + } + + async fn run(&mut self) { + let mut i = 0; + while let Some(msg) = self.receiver.recv().await { + self.handle_message(msg); + + if i == 0 { + self.send_message_to_self().await; + } + + i += 1 + } + + assert!(self.received_self_msg); + } + } + + #[derive(Clone)] + pub struct MyActorHandle { + sender: mpsc::UnboundedSender<ActorMessage>, + } + + impl MyActorHandle { + pub fn new() -> (Self, MyActor) { + let (sender, receiver) = mpsc::unbounded_channel(); + let actor = MyActor::new(receiver, sender.clone().downgrade()); + + (Self { sender }, actor) + } + + pub async fn get_unique_id(&self) -> u32 { + let (send, recv) = oneshot::channel(); + let msg = ActorMessage::GetUniqueId { respond_to: send }; + + // Ignore send errors. If this send fails, so does the + // recv.await below. There's no reason to check the + // failure twice. + let _ = self.sender.send(msg); + recv.await.expect("Actor task has been killed") + } + } + + let (handle, mut actor) = MyActorHandle::new(); + + let actor_handle = tokio::spawn(async move { actor.run().await }); + + let _ = tokio::spawn(async move { + let _ = handle.get_unique_id().await; + drop(handle); + }) + .await; + + let _ = actor_handle.await; +} + +static NUM_DROPPED_UNBOUNDED: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug)] +struct MsgUnbounded; + +impl Drop for MsgUnbounded { + fn drop(&mut self) { + NUM_DROPPED_UNBOUNDED.fetch_add(1, Release); + } +} + +// Tests that no pending messages are put onto the channel after `Rx` was +// dropped. +// +// Note: After the introduction of `UnboundedWeakSender`, which internally +// used `Arc` and doesn't call a drop of the channel after the last strong +// `UnboundedSender` was dropped while more than one `UnboundedWeakSender` +// remains, we want to ensure that no messages are kept in the channel, which +// were sent after the receiver was dropped. +#[tokio::test] +async fn test_msgs_dropped_on_unbounded_rx_drop() { + let (tx, mut rx) = mpsc::unbounded_channel(); + + tx.send(MsgUnbounded {}).unwrap(); + tx.send(MsgUnbounded {}).unwrap(); + + // This msg will be pending and should be dropped when `rx` is dropped + let sent = tx.send(MsgUnbounded {}); + + let _ = rx.recv().await.unwrap(); + let _ = rx.recv().await.unwrap(); + + sent.unwrap(); + + drop(rx); + + assert_eq!(NUM_DROPPED_UNBOUNDED.load(Acquire), 3); + + // This msg will not be put onto `Tx` list anymore, since `Rx` is closed. + assert!(tx.send(MsgUnbounded {}).is_err()); + + assert_eq!(NUM_DROPPED_UNBOUNDED.load(Acquire), 4); +} + +// Tests that an `WeakUnboundedSender` is upgradeable when other +// `UnboundedSender`s exist. +#[test] +fn downgrade_upgrade_unbounded_sender_success() { + let (tx, _rx) = mpsc::unbounded_channel::<i32>(); + let weak_tx = tx.downgrade(); + assert!(weak_tx.upgrade().is_some()); +} + +// Tests that a `WeakUnboundedSender` fails to upgrade when no other +// `UnboundedSender` exists. +#[test] +fn downgrade_upgrade_unbounded_sender_failure() { + let (tx, _rx) = mpsc::unbounded_channel::<i32>(); + let weak_tx = tx.downgrade(); + drop(tx); + assert!(weak_tx.upgrade().is_none()); +} + +// Tests that an `WeakUnboundedSender` cannot be upgraded after an +// `UnboundedSender` was dropped, which existed at the time of the `downgrade` call. +#[test] +fn downgrade_drop_upgrade_unbounded() { + let (tx, _rx) = mpsc::unbounded_channel::<i32>(); + + // the cloned `Tx` is dropped right away + let weak_tx = tx.clone().downgrade(); + drop(tx); + assert!(weak_tx.upgrade().is_none()); +} + +// Tests that `downgrade` does not change the `tx_count` of the channel. +#[test] +fn test_tx_count_weak_unbounded_sender() { + let (tx, _rx) = mpsc::unbounded_channel::<i32>(); + let tx_weak = tx.downgrade(); + let tx_weak2 = tx.downgrade(); + drop(tx); + + assert!(tx_weak.upgrade().is_none() && tx_weak2.upgrade().is_none()); +} diff --git a/vendor/tokio/tests/sync_mutex.rs b/vendor/tokio/tests/sync_mutex.rs index 0ddb203d8..1e35a558c 100644 --- a/vendor/tokio/tests/sync_mutex.rs +++ b/vendor/tokio/tests/sync_mutex.rs @@ -1,13 +1,19 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; use tokio::sync::Mutex; -use tokio::time::{interval, timeout}; use tokio_test::task::spawn; use tokio_test::{assert_pending, assert_ready}; use std::sync::Arc; -use std::time::Duration; #[test] fn straight_execution() { @@ -47,7 +53,7 @@ fn readiness() { // But once g unlocks, we can acquire it drop(g); assert!(t2.is_woken()); - assert_ready!(t2.poll()); + let _t2 = assert_ready!(t2.poll()); } /* @@ -82,10 +88,14 @@ fn lock() { } */ -#[tokio::test] /// Ensure a mutex is unlocked if a future holding the lock /// is aborted prematurely. +#[tokio::test] +#[cfg(feature = "full")] async fn aborted_future_1() { + use std::time::Duration; + use tokio::time::{interval, timeout}; + let m1: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); { let m2 = m1.clone(); @@ -93,7 +103,7 @@ async fn aborted_future_1() { timeout(Duration::from_millis(1u64), async move { let iv = interval(Duration::from_millis(1000)); tokio::pin!(iv); - m2.lock().await; + let _g = m2.lock().await; iv.as_mut().tick().await; iv.as_mut().tick().await; }) @@ -102,16 +112,20 @@ async fn aborted_future_1() { } // This should succeed as there is no lock left for the mutex. timeout(Duration::from_millis(1u64), async move { - m1.lock().await; + let _g = m1.lock().await; }) .await .expect("Mutex is locked"); } -#[tokio::test] /// This test is similar to `aborted_future_1` but this time the /// aborted future is waiting for the lock. +#[tokio::test] +#[cfg(feature = "full")] async fn aborted_future_2() { + use std::time::Duration; + use tokio::time::timeout; + let m1: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); { // Lock mutex @@ -120,7 +134,7 @@ async fn aborted_future_2() { let m2 = m1.clone(); // Try to lock mutex in a future that is aborted prematurely timeout(Duration::from_millis(1u64), async move { - m2.lock().await; + let _g = m2.lock().await; }) .await .unwrap_err(); @@ -128,7 +142,7 @@ async fn aborted_future_2() { } // This should succeed as there is no lock left for the mutex. timeout(Duration::from_millis(1u64), async move { - m1.lock().await; + let _g = m1.lock().await; }) .await .expect("Mutex is locked"); @@ -139,22 +153,22 @@ fn try_lock() { let m: Mutex<usize> = Mutex::new(0); { let g1 = m.try_lock(); - assert_eq!(g1.is_ok(), true); + assert!(g1.is_ok()); let g2 = m.try_lock(); - assert_eq!(g2.is_ok(), false); + assert!(g2.is_err()); } let g3 = m.try_lock(); - assert_eq!(g3.is_ok(), true); + assert!(g3.is_ok()); } -#[tokio::test] +#[maybe_tokio_test] async fn debug_format() { let s = "debug"; let m = Mutex::new(s.to_string()); assert_eq!(format!("{:?}", s), format!("{:?}", m.lock().await)); } -#[tokio::test] +#[maybe_tokio_test] async fn mutex_debug() { let s = "data"; let m = Mutex::new(s.to_string()); diff --git a/vendor/tokio/tests/sync_mutex_owned.rs b/vendor/tokio/tests/sync_mutex_owned.rs index 0f1399c43..ba472fee7 100644 --- a/vendor/tokio/tests/sync_mutex_owned.rs +++ b/vendor/tokio/tests/sync_mutex_owned.rs @@ -1,13 +1,19 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; use tokio::sync::Mutex; -use tokio::time::{interval, timeout}; use tokio_test::task::spawn; use tokio_test::{assert_pending, assert_ready}; use std::sync::Arc; -use std::time::Duration; #[test] fn straight_execution() { @@ -49,10 +55,14 @@ fn readiness() { assert_ready!(t2.poll()); } -#[tokio::test] /// Ensure a mutex is unlocked if a future holding the lock /// is aborted prematurely. +#[tokio::test] +#[cfg(feature = "full")] async fn aborted_future_1() { + use std::time::Duration; + use tokio::time::{interval, timeout}; + let m1: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); { let m2 = m1.clone(); @@ -75,10 +85,14 @@ async fn aborted_future_1() { .expect("Mutex is locked"); } -#[tokio::test] /// This test is similar to `aborted_future_1` but this time the /// aborted future is waiting for the lock. +#[tokio::test] +#[cfg(feature = "full")] async fn aborted_future_2() { + use std::time::Duration; + use tokio::time::timeout; + let m1: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); { // Lock mutex @@ -106,15 +120,15 @@ fn try_lock_owned() { let m: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); { let g1 = m.clone().try_lock_owned(); - assert_eq!(g1.is_ok(), true); + assert!(g1.is_ok()); let g2 = m.clone().try_lock_owned(); - assert_eq!(g2.is_ok(), false); + assert!(g2.is_err()); } let g3 = m.try_lock_owned(); - assert_eq!(g3.is_ok(), true); + assert!(g3.is_ok()); } -#[tokio::test] +#[maybe_tokio_test] async fn debug_format() { let s = "debug"; let m = Arc::new(Mutex::new(s.to_string())); diff --git a/vendor/tokio/tests/sync_notify.rs b/vendor/tokio/tests/sync_notify.rs index 6c6620b2f..aa58039fa 100644 --- a/vendor/tokio/tests/sync_notify.rs +++ b/vendor/tokio/tests/sync_notify.rs @@ -1,5 +1,8 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::Notify; use tokio_test::task::spawn; @@ -151,3 +154,74 @@ fn notify_one_after_dropped_all() { assert_ready!(notified2.poll()); } + +#[test] +fn test_notify_one_not_enabled() { + let notify = Notify::new(); + let mut future = spawn(notify.notified()); + + notify.notify_one(); + assert_ready!(future.poll()); +} + +#[test] +fn test_notify_one_after_enable() { + let notify = Notify::new(); + let mut future = spawn(notify.notified()); + + future.enter(|_, fut| assert!(!fut.enable())); + + notify.notify_one(); + assert_ready!(future.poll()); + future.enter(|_, fut| assert!(fut.enable())); +} + +#[test] +fn test_poll_after_enable() { + let notify = Notify::new(); + let mut future = spawn(notify.notified()); + + future.enter(|_, fut| assert!(!fut.enable())); + assert_pending!(future.poll()); +} + +#[test] +fn test_enable_after_poll() { + let notify = Notify::new(); + let mut future = spawn(notify.notified()); + + assert_pending!(future.poll()); + future.enter(|_, fut| assert!(!fut.enable())); +} + +#[test] +fn test_enable_consumes_permit() { + let notify = Notify::new(); + + // Add a permit. + notify.notify_one(); + + let mut future1 = spawn(notify.notified()); + future1.enter(|_, fut| assert!(fut.enable())); + + let mut future2 = spawn(notify.notified()); + future2.enter(|_, fut| assert!(!fut.enable())); +} + +#[test] +fn test_waker_update() { + use futures::task::noop_waker; + use std::future::Future; + use std::task::Context; + + let notify = Notify::new(); + let mut future = spawn(notify.notified()); + + let noop = noop_waker(); + future.enter(|_, fut| assert_pending!(fut.poll(&mut Context::from_waker(&noop)))); + + assert_pending!(future.poll()); + notify.notify_one(); + + assert!(future.is_woken()); +} diff --git a/vendor/tokio/tests/sync_once_cell.rs b/vendor/tokio/tests/sync_once_cell.rs index 18eaf9382..38dfa7ca0 100644 --- a/vendor/tokio/tests/sync_once_cell.rs +++ b/vendor/tokio/tests/sync_once_cell.rs @@ -4,178 +4,7 @@ use std::mem; use std::ops::Drop; use std::sync::atomic::{AtomicU32, Ordering}; -use std::time::Duration; -use tokio::runtime; -use tokio::sync::{OnceCell, SetError}; -use tokio::time; - -async fn func1() -> u32 { - 5 -} - -async fn func2() -> u32 { - time::sleep(Duration::from_millis(1)).await; - 10 -} - -async fn func_err() -> Result<u32, ()> { - Err(()) -} - -async fn func_ok() -> Result<u32, ()> { - Ok(10) -} - -async fn func_panic() -> u32 { - time::sleep(Duration::from_millis(1)).await; - panic!(); -} - -async fn sleep_and_set() -> u32 { - // Simulate sleep by pausing time and waiting for another thread to - // resume clock when calling `set`, then finding the cell being initialized - // by this call - time::sleep(Duration::from_millis(2)).await; - 5 -} - -async fn advance_time_and_set(cell: &'static OnceCell<u32>, v: u32) -> Result<(), SetError<u32>> { - time::advance(Duration::from_millis(1)).await; - cell.set(v) -} - -#[test] -fn get_or_init() { - let rt = runtime::Builder::new_current_thread() - .enable_time() - .start_paused(true) - .build() - .unwrap(); - - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - rt.block_on(async { - let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await }); - let handle2 = rt.spawn(async { ONCE.get_or_init(func2).await }); - - time::advance(Duration::from_millis(1)).await; - time::resume(); - - let result1 = handle1.await.unwrap(); - let result2 = handle2.await.unwrap(); - - assert_eq!(*result1, 5); - assert_eq!(*result2, 5); - }); -} - -#[test] -fn get_or_init_panic() { - let rt = runtime::Builder::new_current_thread() - .enable_time() - .build() - .unwrap(); - - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - rt.block_on(async { - time::pause(); - - let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await }); - let handle2 = rt.spawn(async { ONCE.get_or_init(func_panic).await }); - - time::advance(Duration::from_millis(1)).await; - - let result1 = handle1.await.unwrap(); - let result2 = handle2.await.unwrap(); - - assert_eq!(*result1, 5); - assert_eq!(*result2, 5); - }); -} - -#[test] -fn set_and_get() { - let rt = runtime::Builder::new_current_thread() - .enable_time() - .build() - .unwrap(); - - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - rt.block_on(async { - let _ = rt.spawn(async { ONCE.set(5) }).await; - let value = ONCE.get().unwrap(); - assert_eq!(*value, 5); - }); -} - -#[test] -fn get_uninit() { - static ONCE: OnceCell<u32> = OnceCell::const_new(); - let uninit = ONCE.get(); - assert!(uninit.is_none()); -} - -#[test] -fn set_twice() { - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - let first = ONCE.set(5); - assert_eq!(first, Ok(())); - let second = ONCE.set(6); - assert!(second.err().unwrap().is_already_init_err()); -} - -#[test] -fn set_while_initializing() { - let rt = runtime::Builder::new_current_thread() - .enable_time() - .build() - .unwrap(); - - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - rt.block_on(async { - time::pause(); - - let handle1 = rt.spawn(async { ONCE.get_or_init(sleep_and_set).await }); - let handle2 = rt.spawn(async { advance_time_and_set(&ONCE, 10).await }); - - time::advance(Duration::from_millis(2)).await; - - let result1 = handle1.await.unwrap(); - let result2 = handle2.await.unwrap(); - - assert_eq!(*result1, 5); - assert!(result2.err().unwrap().is_initializing_err()); - }); -} - -#[test] -fn get_or_try_init() { - let rt = runtime::Builder::new_current_thread() - .enable_time() - .start_paused(true) - .build() - .unwrap(); - - static ONCE: OnceCell<u32> = OnceCell::const_new(); - - rt.block_on(async { - let handle1 = rt.spawn(async { ONCE.get_or_try_init(func_err).await }); - let handle2 = rt.spawn(async { ONCE.get_or_try_init(func_ok).await }); - - time::advance(Duration::from_millis(1)).await; - time::resume(); - - let result1 = handle1.await.unwrap(); - assert!(result1.is_err()); - - let result2 = handle2.await.unwrap(); - assert_eq!(*result2.unwrap(), 10); - }); -} +use tokio::sync::OnceCell; #[test] fn drop_cell() { @@ -272,3 +101,185 @@ fn from() { let cell = OnceCell::from(2); assert_eq!(*cell.get().unwrap(), 2); } + +#[cfg(feature = "parking_lot")] +mod parking_lot { + use super::*; + + use tokio::runtime; + use tokio::sync::SetError; + use tokio::time; + + use std::time::Duration; + + async fn func1() -> u32 { + 5 + } + + async fn func2() -> u32 { + time::sleep(Duration::from_millis(1)).await; + 10 + } + + async fn func_err() -> Result<u32, ()> { + Err(()) + } + + async fn func_ok() -> Result<u32, ()> { + Ok(10) + } + + async fn func_panic() -> u32 { + time::sleep(Duration::from_millis(1)).await; + panic!(); + } + + async fn sleep_and_set() -> u32 { + // Simulate sleep by pausing time and waiting for another thread to + // resume clock when calling `set`, then finding the cell being initialized + // by this call + time::sleep(Duration::from_millis(2)).await; + 5 + } + + async fn advance_time_and_set( + cell: &'static OnceCell<u32>, + v: u32, + ) -> Result<(), SetError<u32>> { + time::advance(Duration::from_millis(1)).await; + cell.set(v) + } + + #[test] + fn get_or_init() { + let rt = runtime::Builder::new_current_thread() + .enable_time() + .start_paused(true) + .build() + .unwrap(); + + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + rt.block_on(async { + let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await }); + let handle2 = rt.spawn(async { ONCE.get_or_init(func2).await }); + + time::advance(Duration::from_millis(1)).await; + time::resume(); + + let result1 = handle1.await.unwrap(); + let result2 = handle2.await.unwrap(); + + assert_eq!(*result1, 5); + assert_eq!(*result2, 5); + }); + } + + #[test] + fn get_or_init_panic() { + let rt = runtime::Builder::new_current_thread() + .enable_time() + .build() + .unwrap(); + + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + rt.block_on(async { + time::pause(); + + let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await }); + let handle2 = rt.spawn(async { ONCE.get_or_init(func_panic).await }); + + time::advance(Duration::from_millis(1)).await; + + let result1 = handle1.await.unwrap(); + let result2 = handle2.await.unwrap(); + + assert_eq!(*result1, 5); + assert_eq!(*result2, 5); + }); + } + + #[test] + fn set_and_get() { + let rt = runtime::Builder::new_current_thread() + .enable_time() + .build() + .unwrap(); + + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + rt.block_on(async { + let _ = rt.spawn(async { ONCE.set(5) }).await; + let value = ONCE.get().unwrap(); + assert_eq!(*value, 5); + }); + } + + #[test] + fn get_uninit() { + static ONCE: OnceCell<u32> = OnceCell::const_new(); + let uninit = ONCE.get(); + assert!(uninit.is_none()); + } + + #[test] + fn set_twice() { + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + let first = ONCE.set(5); + assert_eq!(first, Ok(())); + let second = ONCE.set(6); + assert!(second.err().unwrap().is_already_init_err()); + } + + #[test] + fn set_while_initializing() { + let rt = runtime::Builder::new_current_thread() + .enable_time() + .build() + .unwrap(); + + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + rt.block_on(async { + time::pause(); + + let handle1 = rt.spawn(async { ONCE.get_or_init(sleep_and_set).await }); + let handle2 = rt.spawn(async { advance_time_and_set(&ONCE, 10).await }); + + time::advance(Duration::from_millis(2)).await; + + let result1 = handle1.await.unwrap(); + let result2 = handle2.await.unwrap(); + + assert_eq!(*result1, 5); + assert!(result2.err().unwrap().is_initializing_err()); + }); + } + + #[test] + fn get_or_try_init() { + let rt = runtime::Builder::new_current_thread() + .enable_time() + .start_paused(true) + .build() + .unwrap(); + + static ONCE: OnceCell<u32> = OnceCell::const_new(); + + rt.block_on(async { + let handle1 = rt.spawn(async { ONCE.get_or_try_init(func_err).await }); + let handle2 = rt.spawn(async { ONCE.get_or_try_init(func_ok).await }); + + time::advance(Duration::from_millis(1)).await; + time::resume(); + + let result1 = handle1.await.unwrap(); + assert!(result1.is_err()); + + let result2 = handle2.await.unwrap(); + assert_eq!(*result2.unwrap(), 10); + }); + } +} diff --git a/vendor/tokio/tests/sync_oneshot.rs b/vendor/tokio/tests/sync_oneshot.rs index 1aab810e1..15b692370 100644 --- a/vendor/tokio/tests/sync_oneshot.rs +++ b/vendor/tokio/tests/sync_oneshot.rs @@ -1,5 +1,13 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; use tokio::sync::oneshot; use tokio::sync::oneshot::error::TryRecvError; @@ -40,7 +48,7 @@ fn send_recv() { assert_eq!(val, 1); } -#[tokio::test] +#[maybe_tokio_test] async fn async_send_recv() { let (tx, rx) = oneshot::channel(); @@ -86,6 +94,7 @@ fn close_rx() { } #[tokio::test] +#[cfg(feature = "full")] async fn async_rx_closed() { let (mut tx, rx) = oneshot::channel::<()>(); @@ -170,6 +179,7 @@ fn explicit_close_try_recv() { #[test] #[should_panic] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding fn close_try_recv_poll() { let (_tx, rx) = oneshot::channel::<i32>(); let mut rx = task::spawn(rx); @@ -203,6 +213,18 @@ fn try_recv_after_completion() { } #[test] +fn try_recv_after_completion_await() { + let (tx, rx) = oneshot::channel::<i32>(); + let mut rx = task::spawn(rx); + + tx.send(17).unwrap(); + + assert_eq!(Ok(17), assert_ready!(rx.poll())); + assert_eq!(Err(TryRecvError::Closed), rx.try_recv()); + rx.close(); +} + +#[test] fn drops_tasks() { let (mut tx, mut rx) = oneshot::channel::<i32>(); let mut tx_task = task::spawn(()); diff --git a/vendor/tokio/tests/sync_panic.rs b/vendor/tokio/tests/sync_panic.rs new file mode 100644 index 000000000..6c2366499 --- /dev/null +++ b/vendor/tokio/tests/sync_panic.rs @@ -0,0 +1,197 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] + +use std::{error::Error, sync::Arc}; +use tokio::{ + runtime::{Builder, Runtime}, + sync::{broadcast, mpsc, oneshot, Mutex, RwLock, Semaphore}, +}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn broadcast_channel_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let (_, _) = broadcast::channel::<u32>(0); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn mutex_blocking_lock_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + rt.block_on(async { + let mutex = Mutex::new(5_u32); + let _g = mutex.blocking_lock(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn oneshot_blocking_recv_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + rt.block_on(async { + let (_tx, rx) = oneshot::channel::<u8>(); + let _ = rx.blocking_recv(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn rwlock_with_max_readers_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = RwLock::<u8>::with_max_readers(0, (u32::MAX >> 3) + 1); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn rwlock_blocking_read_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + rt.block_on(async { + let lock = RwLock::<u8>::new(0); + let _ = lock.blocking_read(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn rwlock_blocking_write_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + rt.block_on(async { + let lock = RwLock::<u8>::new(0); + let _ = lock.blocking_write(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn mpsc_bounded_channel_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let (_, _) = mpsc::channel::<u8>(0); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn mpsc_bounded_receiver_blocking_recv_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + let (_tx, mut rx) = mpsc::channel::<u8>(1); + rt.block_on(async { + let _ = rx.blocking_recv(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn mpsc_bounded_sender_blocking_send_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + let (tx, _rx) = mpsc::channel::<u8>(1); + rt.block_on(async { + let _ = tx.blocking_send(3); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn mpsc_unbounded_receiver_blocking_recv_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + let (_tx, mut rx) = mpsc::unbounded_channel::<u8>(); + rt.block_on(async { + let _ = rx.blocking_recv(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn semaphore_merge_unrelated_owned_permits() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let sem1 = Arc::new(Semaphore::new(42)); + let sem2 = Arc::new(Semaphore::new(42)); + let mut p1 = sem1.try_acquire_owned().unwrap(); + let p2 = sem2.try_acquire_owned().unwrap(); + p1.merge(p2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn semaphore_merge_unrelated_permits() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let sem1 = Semaphore::new(42); + let sem2 = Semaphore::new(42); + let mut p1 = sem1.try_acquire().unwrap(); + let p2 = sem2.try_acquire().unwrap(); + p1.merge(p2); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +fn current_thread() -> Runtime { + Builder::new_current_thread().enable_all().build().unwrap() +} diff --git a/vendor/tokio/tests/sync_rwlock.rs b/vendor/tokio/tests/sync_rwlock.rs index 7d05086bf..667f62172 100644 --- a/vendor/tokio/tests/sync_rwlock.rs +++ b/vendor/tokio/tests/sync_rwlock.rs @@ -1,13 +1,19 @@ #![warn(rust_2018_idioms)] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as maybe_tokio_test; + +#[cfg(not(tokio_wasm_not_wasi))] +use tokio::test as maybe_tokio_test; -use std::sync::Arc; use std::task::Poll; use futures::future::FutureExt; -use futures::stream; -use futures::stream::StreamExt; -use tokio::sync::{Barrier, RwLock}; +use tokio::sync::{RwLock, RwLockWriteGuard}; use tokio_test::task::spawn; use tokio_test::{assert_pending, assert_ready}; @@ -25,7 +31,7 @@ fn read_shared() { let mut t1 = spawn(rwlock.read()); let _g1 = assert_ready!(t1.poll()); let mut t2 = spawn(rwlock.read()); - assert_ready!(t2.poll()); + let _g2 = assert_ready!(t2.poll()); } // When there is an active shared owner, exclusive access should not be possible @@ -69,7 +75,7 @@ fn exhaust_reading() { let g2 = reads.pop().unwrap(); drop(g2); assert!(t1.is_woken()); - assert_ready!(t1.poll()); + let _g1 = assert_ready!(t1.poll()); } // When there is an active exclusive owner, subsequent exclusive access should not be possible @@ -94,7 +100,7 @@ fn write_shared_drop() { assert_pending!(t2.poll()); drop(g1); assert!(t2.is_woken()); - assert_ready!(t2.poll()); + let _g2 = assert_ready!(t2.poll()); } // when there is an active shared owner, and exclusive access is triggered, @@ -106,7 +112,7 @@ fn write_read_shared_pending() { let _g1 = assert_ready!(t1.poll()); let mut t2 = spawn(rwlock.read()); - assert_ready!(t2.poll()); + let _g2 = assert_ready!(t2.poll()); let mut t3 = spawn(rwlock.write()); assert_pending!(t3.poll()); @@ -131,11 +137,11 @@ fn write_read_shared_drop_pending() { drop(t2); assert!(t3.is_woken()); - assert_ready!(t3.poll()); + let _t3 = assert_ready!(t3.poll()); } // Acquire an RwLock nonexclusively by a single task -#[tokio::test] +#[maybe_tokio_test] async fn read_uncontested() { let rwlock = RwLock::new(100); let result = *rwlock.read().await; @@ -144,7 +150,7 @@ async fn read_uncontested() { } // Acquire an uncontested RwLock in exclusive mode -#[tokio::test] +#[maybe_tokio_test] async fn write_uncontested() { let rwlock = RwLock::new(100); let mut result = rwlock.write().await; @@ -153,7 +159,7 @@ async fn write_uncontested() { } // RwLocks should be acquired in the order that their Futures are waited upon. -#[tokio::test] +#[maybe_tokio_test] async fn write_order() { let rwlock = RwLock::<Vec<u32>>::new(vec![]); let fut2 = rwlock.write().map(|mut guard| guard.push(2)); @@ -166,8 +172,13 @@ async fn write_order() { } // A single RwLock is contested by tasks in multiple threads +#[cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread", worker_threads = 8)] async fn multithreaded() { + use futures::stream::{self, StreamExt}; + use std::sync::Arc; + use tokio::sync::Barrier; + let barrier = Arc::new(Barrier::new(5)); let rwlock = Arc::new(RwLock::<u32>::new(0)); let rwclone1 = rwlock.clone(); @@ -236,7 +247,7 @@ async fn multithreaded() { assert_eq!(*g, 17_000); } -#[tokio::test] +#[maybe_tokio_test] async fn try_write() { let lock = RwLock::new(0); let read_guard = lock.read().await; @@ -268,3 +279,53 @@ fn try_read_try_write() { assert_eq!(*lock.try_read().unwrap(), 1515); } + +#[maybe_tokio_test] +async fn downgrade_map() { + let lock = RwLock::new(0); + let write_guard = lock.write().await; + let mut read_t = spawn(lock.read()); + + // We can't create a read when a write exists + assert_pending!(read_t.poll()); + + // During the call to `f`, `read_t` doesn't have access yet. + let read_guard1 = RwLockWriteGuard::downgrade_map(write_guard, |v| { + assert_pending!(read_t.poll()); + v + }); + + // After the downgrade, `read_t` got the lock + let read_guard2 = assert_ready!(read_t.poll()); + + // Ensure they're equal, as we return the original value + assert_eq!(&*read_guard1 as *const _, &*read_guard2 as *const _); +} + +#[maybe_tokio_test] +async fn try_downgrade_map() { + let lock = RwLock::new(0); + let write_guard = lock.write().await; + let mut read_t = spawn(lock.read()); + + // We can't create a read when a write exists + assert_pending!(read_t.poll()); + + // During the call to `f`, `read_t` doesn't have access yet. + let write_guard = RwLockWriteGuard::try_downgrade_map(write_guard, |_| { + assert_pending!(read_t.poll()); + None::<&()> + }) + .expect_err("downgrade didn't fail"); + + // After `f` returns `None`, `read_t` doesn't have access + assert_pending!(read_t.poll()); + + // After `f` returns `Some`, `read_t` does have access + let read_guard1 = RwLockWriteGuard::try_downgrade_map(write_guard, |v| Some(v)) + .expect("downgrade didn't succeed"); + let read_guard2 = assert_ready!(read_t.poll()); + + // Ensure they're equal, as we return the original value + assert_eq!(&*read_guard1 as *const _, &*read_guard2 as *const _); +} diff --git a/vendor/tokio/tests/sync_semaphore.rs b/vendor/tokio/tests/sync_semaphore.rs index a33b878b3..3d47ed0ed 100644 --- a/vendor/tokio/tests/sync_semaphore.rs +++ b/vendor/tokio/tests/sync_semaphore.rs @@ -1,4 +1,7 @@ -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; use tokio::sync::Semaphore; @@ -23,6 +26,7 @@ fn try_acquire() { } #[tokio::test] +#[cfg(feature = "full")] async fn acquire() { let sem = Arc::new(Semaphore::new(1)); let p1 = sem.try_acquire().unwrap(); @@ -35,6 +39,7 @@ async fn acquire() { } #[tokio::test] +#[cfg(feature = "full")] async fn add_permits() { let sem = Arc::new(Semaphore::new(0)); let sem_clone = sem.clone(); @@ -58,8 +63,34 @@ fn forget() { assert!(sem.try_acquire().is_err()); } +#[test] +fn merge() { + let sem = Arc::new(Semaphore::new(3)); + { + let mut p1 = sem.try_acquire().unwrap(); + assert_eq!(sem.available_permits(), 2); + let p2 = sem.try_acquire_many(2).unwrap(); + assert_eq!(sem.available_permits(), 0); + p1.merge(p2); + assert_eq!(sem.available_permits(), 0); + } + assert_eq!(sem.available_permits(), 3); +} + +#[test] +#[cfg(not(tokio_wasm))] // No stack unwinding on wasm targets +#[should_panic] +fn merge_unrelated_permits() { + let sem1 = Arc::new(Semaphore::new(3)); + let sem2 = Arc::new(Semaphore::new(3)); + let mut p1 = sem1.try_acquire().unwrap(); + let p2 = sem2.try_acquire().unwrap(); + p1.merge(p2); +} + #[tokio::test] -async fn stresstest() { +#[cfg(feature = "full")] +async fn stress_test() { let sem = Arc::new(Semaphore::new(5)); let mut join_handles = Vec::new(); for _ in 0..1000 { @@ -83,13 +114,37 @@ async fn stresstest() { #[test] fn add_max_amount_permits() { let s = tokio::sync::Semaphore::new(0); - s.add_permits(usize::MAX >> 3); - assert_eq!(s.available_permits(), usize::MAX >> 3); + s.add_permits(tokio::sync::Semaphore::MAX_PERMITS); + assert_eq!(s.available_permits(), tokio::sync::Semaphore::MAX_PERMITS); } +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding #[test] #[should_panic] -fn add_more_than_max_amount_permits() { +fn add_more_than_max_amount_permits1() { let s = tokio::sync::Semaphore::new(1); - s.add_permits(usize::MAX >> 3); + s.add_permits(tokio::sync::Semaphore::MAX_PERMITS); +} + +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding +#[test] +#[should_panic] +fn add_more_than_max_amount_permits2() { + let s = Semaphore::new(Semaphore::MAX_PERMITS - 1); + s.add_permits(1); + s.add_permits(1); +} + +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding +#[test] +#[should_panic] +fn panic_when_exceeds_maxpermits() { + let _ = Semaphore::new(Semaphore::MAX_PERMITS + 1); +} + +#[test] +fn no_panic_at_maxpermits() { + let _ = Semaphore::new(Semaphore::MAX_PERMITS); + let s = Semaphore::new(Semaphore::MAX_PERMITS - 1); + s.add_permits(1); } diff --git a/vendor/tokio/tests/sync_semaphore_owned.rs b/vendor/tokio/tests/sync_semaphore_owned.rs index 478c3a332..f69457647 100644 --- a/vendor/tokio/tests/sync_semaphore_owned.rs +++ b/vendor/tokio/tests/sync_semaphore_owned.rs @@ -1,4 +1,7 @@ -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; use std::sync::Arc; use tokio::sync::Semaphore; @@ -33,6 +36,7 @@ fn try_acquire_many() { } #[tokio::test] +#[cfg(feature = "full")] async fn acquire() { let sem = Arc::new(Semaphore::new(1)); let p1 = sem.clone().try_acquire_owned().unwrap(); @@ -45,6 +49,7 @@ async fn acquire() { } #[tokio::test] +#[cfg(feature = "full")] async fn acquire_many() { let semaphore = Arc::new(Semaphore::new(42)); let permit32 = semaphore.clone().try_acquire_many_owned(32).unwrap(); @@ -60,6 +65,7 @@ async fn acquire_many() { } #[tokio::test] +#[cfg(feature = "full")] async fn add_permits() { let sem = Arc::new(Semaphore::new(0)); let sem_clone = sem.clone(); @@ -83,8 +89,34 @@ fn forget() { assert!(sem.try_acquire_owned().is_err()); } +#[test] +fn merge() { + let sem = Arc::new(Semaphore::new(3)); + { + let mut p1 = sem.clone().try_acquire_owned().unwrap(); + assert_eq!(sem.available_permits(), 2); + let p2 = sem.clone().try_acquire_many_owned(2).unwrap(); + assert_eq!(sem.available_permits(), 0); + p1.merge(p2); + assert_eq!(sem.available_permits(), 0); + } + assert_eq!(sem.available_permits(), 3); +} + +#[test] +#[cfg(not(tokio_wasm))] // No stack unwinding on wasm targets +#[should_panic] +fn merge_unrelated_permits() { + let sem1 = Arc::new(Semaphore::new(3)); + let sem2 = Arc::new(Semaphore::new(3)); + let mut p1 = sem1.try_acquire_owned().unwrap(); + let p2 = sem2.try_acquire_owned().unwrap(); + p1.merge(p2) +} + #[tokio::test] -async fn stresstest() { +#[cfg(feature = "full")] +async fn stress_test() { let sem = Arc::new(Semaphore::new(5)); let mut join_handles = Vec::new(); for _ in 0..1000 { diff --git a/vendor/tokio/tests/sync_watch.rs b/vendor/tokio/tests/sync_watch.rs index a2a276d8b..d4f8ce87d 100644 --- a/vendor/tokio/tests/sync_watch.rs +++ b/vendor/tokio/tests/sync_watch.rs @@ -1,6 +1,9 @@ #![allow(clippy::cognitive_complexity)] #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(feature = "sync")] + +#[cfg(tokio_wasm_not_wasi)] +use wasm_bindgen_test::wasm_bindgen_test as test; use tokio::sync::watch; use tokio_test::task::spawn; @@ -174,15 +177,67 @@ fn poll_close() { fn borrow_and_update() { let (tx, mut rx) = watch::channel("one"); + assert!(!rx.has_changed().unwrap()); + tx.send("two").unwrap(); + assert!(rx.has_changed().unwrap()); assert_ready!(spawn(rx.changed()).poll()).unwrap(); assert_pending!(spawn(rx.changed()).poll()); + assert!(!rx.has_changed().unwrap()); tx.send("three").unwrap(); + assert!(rx.has_changed().unwrap()); assert_eq!(*rx.borrow_and_update(), "three"); assert_pending!(spawn(rx.changed()).poll()); + assert!(!rx.has_changed().unwrap()); drop(tx); assert_eq!(*rx.borrow_and_update(), "three"); assert_ready!(spawn(rx.changed()).poll()).unwrap_err(); + assert!(rx.has_changed().is_err()); +} + +#[test] +fn reopened_after_subscribe() { + let (tx, rx) = watch::channel("one"); + assert!(!tx.is_closed()); + + drop(rx); + assert!(tx.is_closed()); + + let rx = tx.subscribe(); + assert!(!tx.is_closed()); + + drop(rx); + assert!(tx.is_closed()); +} + +#[test] +#[cfg(panic = "unwind")] +#[cfg(not(tokio_wasm))] // wasm currently doesn't support unwinding +fn send_modify_panic() { + let (tx, mut rx) = watch::channel("one"); + + tx.send_modify(|old| *old = "two"); + assert_eq!(*rx.borrow_and_update(), "two"); + + let mut rx2 = rx.clone(); + assert_eq!(*rx2.borrow_and_update(), "two"); + + let mut task = spawn(rx2.changed()); + + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + tx.send_modify(|old| { + *old = "panicked"; + panic!(); + }) + })); + assert!(result.is_err()); + + assert_pending!(task.poll()); + assert_eq!(*rx.borrow(), "panicked"); + + tx.send_modify(|old| *old = "three"); + assert_ready_ok!(task.poll()); + assert_eq!(*rx.borrow_and_update(), "three"); } diff --git a/vendor/tokio/tests/task_abort.rs b/vendor/tokio/tests/task_abort.rs index cdaa405b8..492f8b551 100644 --- a/vendor/tokio/tests/task_abort.rs +++ b/vendor/tokio/tests/task_abort.rs @@ -1,9 +1,9 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support panic recovery use std::sync::Arc; use std::thread::sleep; -use std::time::Duration; +use tokio::time::Duration; use tokio::runtime::Builder; @@ -26,13 +26,10 @@ fn test_abort_without_panic_3157() { .unwrap(); rt.block_on(async move { - let handle = tokio::spawn(async move { - println!("task started"); - tokio::time::sleep(std::time::Duration::new(100, 0)).await - }); + let handle = tokio::spawn(async move { tokio::time::sleep(Duration::new(100, 0)).await }); // wait for task to sleep. - tokio::time::sleep(std::time::Duration::new(1, 0)).await; + tokio::time::sleep(Duration::from_millis(10)).await; handle.abort(); let _ = handle.await; @@ -89,7 +86,7 @@ fn test_abort_without_panic_3662() { // Note: We do the following to trigger a deferred task cleanup. // // The relevant piece of code you want to look at is in: - // `Inner::block_on` of `basic_scheduler.rs`. + // `Inner::block_on` of `scheduler/current_thread.rs`. // // We cause the cleanup to happen by having a poll return Pending once // so that the scheduler can go into the "auxiliary tasks" mode, at @@ -138,7 +135,7 @@ fn remote_abort_local_set_3929() { }); let jh2 = std::thread::spawn(move || { - sleep(Duration::from_millis(50)); + sleep(Duration::from_millis(10)); jh.abort(); }); @@ -159,18 +156,17 @@ fn test_abort_wakes_task_3964() { let handle = tokio::spawn(async move { // Make sure the Arc is moved into the task let _notify_dropped = notify_dropped; - println!("task started"); - tokio::time::sleep(std::time::Duration::new(100, 0)).await + tokio::time::sleep(Duration::new(100, 0)).await }); // wait for task to sleep. - tokio::time::sleep(std::time::Duration::from_millis(10)).await; + tokio::time::sleep(Duration::from_millis(10)).await; handle.abort(); drop(handle); // wait for task to abort. - tokio::time::sleep(std::time::Duration::from_millis(10)).await; + tokio::time::sleep(Duration::from_millis(10)).await; // Check that the Arc has been dropped. assert!(weak_notify_dropped.upgrade().is_none()); @@ -187,18 +183,17 @@ fn test_abort_task_that_panics_on_drop_contained() { let handle = tokio::spawn(async move { // Make sure the Arc is moved into the task let _panic_dropped = PanicOnDrop; - println!("task started"); - tokio::time::sleep(std::time::Duration::new(100, 0)).await + tokio::time::sleep(Duration::new(100, 0)).await }); // wait for task to sleep. - tokio::time::sleep(std::time::Duration::from_millis(10)).await; + tokio::time::sleep(Duration::from_millis(10)).await; handle.abort(); drop(handle); // wait for task to abort. - tokio::time::sleep(std::time::Duration::from_millis(10)).await; + tokio::time::sleep(Duration::from_millis(10)).await; }); } @@ -211,12 +206,11 @@ fn test_abort_task_that_panics_on_drop_returned() { let handle = tokio::spawn(async move { // Make sure the Arc is moved into the task let _panic_dropped = PanicOnDrop; - println!("task started"); - tokio::time::sleep(std::time::Duration::new(100, 0)).await + tokio::time::sleep(Duration::new(100, 0)).await }); // wait for task to sleep. - tokio::time::sleep(std::time::Duration::from_millis(10)).await; + tokio::time::sleep(Duration::from_millis(10)).await; handle.abort(); assert!(handle.await.unwrap_err().is_panic()); diff --git a/vendor/tokio/tests/task_blocking.rs b/vendor/tokio/tests/task_blocking.rs index ee7e78ad4..2999758ff 100644 --- a/vendor/tokio/tests/task_blocking.rs +++ b/vendor/tokio/tests/task_blocking.rs @@ -1,7 +1,7 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads -use tokio::{runtime, task}; +use tokio::{runtime, task, time}; use tokio_test::assert_ok; use std::thread; @@ -77,7 +77,7 @@ async fn block_in_block() { #[tokio::test(flavor = "current_thread")] #[should_panic] -async fn no_block_in_basic_scheduler() { +async fn no_block_in_current_thread_scheduler() { task::block_in_place(|| {}); } @@ -91,7 +91,7 @@ fn yes_block_in_threaded_block_on() { #[test] #[should_panic] -fn no_block_in_basic_block_on() { +fn no_block_in_current_thread_block_on() { let rt = runtime::Builder::new_current_thread().build().unwrap(); rt.block_on(async { task::block_in_place(|| {}); @@ -99,7 +99,7 @@ fn no_block_in_basic_block_on() { } #[test] -fn can_enter_basic_rt_from_within_block_in_place() { +fn can_enter_current_thread_rt_from_within_block_in_place() { let outer = tokio::runtime::Runtime::new().unwrap(); outer.block_on(async { @@ -226,3 +226,84 @@ fn coop_disabled_in_block_in_place_in_block_on() { done_rx.recv().unwrap().unwrap(); } + +#[cfg(feature = "test-util")] +#[tokio::test(start_paused = true)] +async fn blocking_when_paused() { + // Do not auto-advance time when we have started a blocking task that has + // not yet finished. + time::timeout( + Duration::from_secs(3), + task::spawn_blocking(|| thread::sleep(Duration::from_millis(1))), + ) + .await + .expect("timeout should not trigger") + .expect("blocking task should finish"); + + // Really: Do not auto-advance time, even if the timeout is short and the + // blocking task runs for longer than that. It doesn't matter: Tokio time + // is paused; system time is not. + time::timeout( + Duration::from_millis(1), + task::spawn_blocking(|| thread::sleep(Duration::from_millis(50))), + ) + .await + .expect("timeout should not trigger") + .expect("blocking task should finish"); +} + +#[cfg(feature = "test-util")] +#[tokio::test(start_paused = true)] +async fn blocking_task_wakes_paused_runtime() { + let t0 = std::time::Instant::now(); + time::timeout( + Duration::from_secs(15), + task::spawn_blocking(|| thread::sleep(Duration::from_millis(1))), + ) + .await + .expect("timeout should not trigger") + .expect("blocking task should finish"); + assert!( + t0.elapsed() < Duration::from_secs(10), + "completing a spawn_blocking should wake the scheduler if it's parked while time is paused" + ); +} + +#[cfg(feature = "test-util")] +#[tokio::test(start_paused = true)] +async fn unawaited_blocking_task_wakes_paused_runtime() { + let t0 = std::time::Instant::now(); + + // When this task finishes, time should auto-advance, even though the + // JoinHandle has not been awaited yet. + let a = task::spawn_blocking(|| { + thread::sleep(Duration::from_millis(1)); + }); + + crate::time::sleep(Duration::from_secs(15)).await; + a.await.expect("blocking task should finish"); + assert!( + t0.elapsed() < Duration::from_secs(10), + "completing a spawn_blocking should wake the scheduler if it's parked while time is paused" + ); +} + +#[cfg(feature = "test-util")] +#[tokio::test(start_paused = true)] +async fn panicking_blocking_task_wakes_paused_runtime() { + let t0 = std::time::Instant::now(); + let result = time::timeout( + Duration::from_secs(15), + task::spawn_blocking(|| { + thread::sleep(Duration::from_millis(1)); + panic!("blocking task panicked"); + }), + ) + .await + .expect("timeout should not trigger"); + assert!(result.is_err(), "blocking task should have panicked"); + assert!( + t0.elapsed() < Duration::from_secs(10), + "completing a spawn_blocking should wake the scheduler if it's parked while time is paused" + ); +} diff --git a/vendor/tokio/tests/task_builder.rs b/vendor/tokio/tests/task_builder.rs index 1499abf19..78329ff26 100644 --- a/vendor/tokio/tests/task_builder.rs +++ b/vendor/tokio/tests/task_builder.rs @@ -11,6 +11,7 @@ mod tests { let result = Builder::new() .name("name") .spawn(async { "task executed" }) + .unwrap() .await; assert_eq!(result.unwrap(), "task executed"); @@ -21,6 +22,7 @@ mod tests { let result = Builder::new() .name("name") .spawn_blocking(|| "task executed") + .unwrap() .await; assert_eq!(result.unwrap(), "task executed"); @@ -34,6 +36,7 @@ mod tests { Builder::new() .name("name") .spawn_local(async move { unsend_data }) + .unwrap() .await }) .await; @@ -43,14 +46,20 @@ mod tests { #[test] async fn spawn_without_name() { - let result = Builder::new().spawn(async { "task executed" }).await; + let result = Builder::new() + .spawn(async { "task executed" }) + .unwrap() + .await; assert_eq!(result.unwrap(), "task executed"); } #[test] async fn spawn_blocking_without_name() { - let result = Builder::new().spawn_blocking(|| "task executed").await; + let result = Builder::new() + .spawn_blocking(|| "task executed") + .unwrap() + .await; assert_eq!(result.unwrap(), "task executed"); } @@ -59,7 +68,12 @@ mod tests { async fn spawn_local_without_name() { let unsend_data = Rc::new("task executed"); let result = LocalSet::new() - .run_until(async move { Builder::new().spawn_local(async move { unsend_data }).await }) + .run_until(async move { + Builder::new() + .spawn_local(async move { unsend_data }) + .unwrap() + .await + }) .await; assert_eq!(*result.unwrap(), "task executed"); diff --git a/vendor/tokio/tests/task_id.rs b/vendor/tokio/tests/task_id.rs new file mode 100644 index 000000000..d7b7c0cd8 --- /dev/null +++ b/vendor/tokio/tests/task_id.rs @@ -0,0 +1,303 @@ +#![warn(rust_2018_idioms)] +#![allow(clippy::declare_interior_mutable_const)] +#![cfg(all(feature = "full", tokio_unstable))] + +#[cfg(not(tokio_wasi))] +use std::error::Error; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; +#[cfg(not(tokio_wasi))] +use tokio::runtime::{Builder, Runtime}; +use tokio::sync::oneshot; +use tokio::task::{self, Id, LocalSet}; + +#[cfg(not(tokio_wasi))] +mod support { + pub mod panic; +} +#[cfg(not(tokio_wasi))] +use support::panic::test_panic; + +#[tokio::test(flavor = "current_thread")] +async fn task_id_spawn() { + tokio::spawn(async { println!("task id: {}", task::id()) }) + .await + .unwrap(); +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "current_thread")] +async fn task_id_spawn_blocking() { + task::spawn_blocking(|| println!("task id: {}", task::id())) + .await + .unwrap(); +} + +#[tokio::test(flavor = "current_thread")] +async fn task_id_collision_current_thread() { + let handle1 = tokio::spawn(async { task::id() }); + let handle2 = tokio::spawn(async { task::id() }); + + let (id1, id2) = tokio::join!(handle1, handle2); + assert_ne!(id1.unwrap(), id2.unwrap()); +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "multi_thread")] +async fn task_id_collision_multi_thread() { + let handle1 = tokio::spawn(async { task::id() }); + let handle2 = tokio::spawn(async { task::id() }); + + let (id1, id2) = tokio::join!(handle1, handle2); + assert_ne!(id1.unwrap(), id2.unwrap()); +} + +#[tokio::test(flavor = "current_thread")] +async fn task_ids_match_current_thread() { + let (tx, rx) = oneshot::channel(); + let handle = tokio::spawn(async { + let id = rx.await.unwrap(); + assert_eq!(id, task::id()); + }); + tx.send(handle.id()).unwrap(); + handle.await.unwrap(); +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "multi_thread")] +async fn task_ids_match_multi_thread() { + let (tx, rx) = oneshot::channel(); + let handle = tokio::spawn(async { + let id = rx.await.unwrap(); + assert_eq!(id, task::id()); + }); + tx.send(handle.id()).unwrap(); + handle.await.unwrap(); +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "multi_thread")] +async fn task_id_future_destructor_completion() { + struct MyFuture { + tx: Option<oneshot::Sender<Id>>, + } + + impl Future for MyFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + Poll::Ready(()) + } + } + + impl Drop for MyFuture { + fn drop(&mut self) { + let _ = self.tx.take().unwrap().send(task::id()); + } + } + + let (tx, rx) = oneshot::channel(); + let handle = tokio::spawn(MyFuture { tx: Some(tx) }); + let id = handle.id(); + handle.await.unwrap(); + assert_eq!(rx.await.unwrap(), id); +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "multi_thread")] +async fn task_id_future_destructor_abort() { + struct MyFuture { + tx: Option<oneshot::Sender<Id>>, + } + + impl Future for MyFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + Poll::Pending + } + } + impl Drop for MyFuture { + fn drop(&mut self) { + let _ = self.tx.take().unwrap().send(task::id()); + } + } + + let (tx, rx) = oneshot::channel(); + let handle = tokio::spawn(MyFuture { tx: Some(tx) }); + let id = handle.id(); + handle.abort(); + assert!(handle.await.unwrap_err().is_cancelled()); + assert_eq!(rx.await.unwrap(), id); +} + +#[tokio::test(flavor = "current_thread")] +async fn task_id_output_destructor_handle_dropped_before_completion() { + struct MyOutput { + tx: Option<oneshot::Sender<Id>>, + } + + impl Drop for MyOutput { + fn drop(&mut self) { + let _ = self.tx.take().unwrap().send(task::id()); + } + } + + struct MyFuture { + tx: Option<oneshot::Sender<Id>>, + } + + impl Future for MyFuture { + type Output = MyOutput; + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Ready(MyOutput { tx: self.tx.take() }) + } + } + + let (tx, mut rx) = oneshot::channel(); + let handle = tokio::spawn(MyFuture { tx: Some(tx) }); + let id = handle.id(); + drop(handle); + assert!(rx.try_recv().is_err()); + assert_eq!(rx.await.unwrap(), id); +} + +#[tokio::test(flavor = "current_thread")] +async fn task_id_output_destructor_handle_dropped_after_completion() { + struct MyOutput { + tx: Option<oneshot::Sender<Id>>, + } + + impl Drop for MyOutput { + fn drop(&mut self) { + let _ = self.tx.take().unwrap().send(task::id()); + } + } + + struct MyFuture { + tx_output: Option<oneshot::Sender<Id>>, + tx_future: Option<oneshot::Sender<()>>, + } + + impl Future for MyFuture { + type Output = MyOutput; + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + let _ = self.tx_future.take().unwrap().send(()); + Poll::Ready(MyOutput { + tx: self.tx_output.take(), + }) + } + } + + let (tx_output, mut rx_output) = oneshot::channel(); + let (tx_future, rx_future) = oneshot::channel(); + let handle = tokio::spawn(MyFuture { + tx_output: Some(tx_output), + tx_future: Some(tx_future), + }); + let id = handle.id(); + rx_future.await.unwrap(); + assert!(rx_output.try_recv().is_err()); + drop(handle); + assert_eq!(rx_output.await.unwrap(), id); +} + +#[test] +fn task_try_id_outside_task() { + assert_eq!(None, task::try_id()); +} + +#[cfg(not(tokio_wasi))] +#[test] +fn task_try_id_inside_block_on() { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + assert_eq!(None, task::try_id()); + }); +} + +#[tokio::test(flavor = "current_thread")] +async fn task_id_spawn_local() { + LocalSet::new() + .run_until(async { + task::spawn_local(async { println!("task id: {}", task::id()) }) + .await + .unwrap(); + }) + .await +} + +#[tokio::test(flavor = "current_thread")] +async fn task_id_nested_spawn_local() { + LocalSet::new() + .run_until(async { + task::spawn_local(async { + let parent_id = task::id(); + LocalSet::new() + .run_until(async { + task::spawn_local(async move { + assert_ne!(parent_id, task::id()); + }) + .await + .unwrap(); + }) + .await; + assert_eq!(parent_id, task::id()); + }) + .await + .unwrap(); + }) + .await; +} + +#[cfg(not(tokio_wasi))] +#[tokio::test(flavor = "multi_thread")] +async fn task_id_block_in_place_block_on_spawn() { + task::spawn(async { + let parent_id = task::id(); + + task::block_in_place(move || { + let rt = Builder::new_current_thread().build().unwrap(); + rt.block_on(rt.spawn(async move { + assert_ne!(parent_id, task::id()); + })) + .unwrap(); + }); + + assert_eq!(parent_id, task::id()); + }) + .await + .unwrap(); +} + +#[cfg(not(tokio_wasi))] +#[test] +fn task_id_outside_task_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = task::id(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[cfg(not(tokio_wasi))] +#[test] +fn task_id_inside_block_on_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + task::id(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} diff --git a/vendor/tokio/tests/task_join_set.rs b/vendor/tokio/tests/task_join_set.rs new file mode 100644 index 000000000..b1b6cf966 --- /dev/null +++ b/vendor/tokio/tests/task_join_set.rs @@ -0,0 +1,235 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full"))] + +use tokio::sync::oneshot; +use tokio::task::JoinSet; +use tokio::time::Duration; + +fn rt() -> tokio::runtime::Runtime { + tokio::runtime::Builder::new_current_thread() + .build() + .unwrap() +} + +#[tokio::test(start_paused = true)] +async fn test_with_sleep() { + let mut set = JoinSet::new(); + + for i in 0..10 { + set.spawn(async move { i }); + assert_eq!(set.len(), 1 + i); + } + set.detach_all(); + assert_eq!(set.len(), 0); + + assert!(matches!(set.join_next().await, None)); + + for i in 0..10 { + set.spawn(async move { + tokio::time::sleep(Duration::from_secs(i as u64)).await; + i + }); + assert_eq!(set.len(), 1 + i); + } + + let mut seen = [false; 10]; + while let Some(res) = set.join_next().await.transpose().unwrap() { + seen[res] = true; + } + + for was_seen in &seen { + assert!(was_seen); + } + assert!(matches!(set.join_next().await, None)); + + // Do it again. + for i in 0..10 { + set.spawn(async move { + tokio::time::sleep(Duration::from_secs(i as u64)).await; + i + }); + } + + let mut seen = [false; 10]; + while let Some(res) = set.join_next().await.transpose().unwrap() { + seen[res] = true; + } + + for was_seen in &seen { + assert!(was_seen); + } + assert!(matches!(set.join_next().await, None)); +} + +#[tokio::test] +async fn test_abort_on_drop() { + let mut set = JoinSet::new(); + + let mut recvs = Vec::new(); + + for _ in 0..16 { + let (send, recv) = oneshot::channel::<()>(); + recvs.push(recv); + + set.spawn(async { + // This task will never complete on its own. + futures::future::pending::<()>().await; + drop(send); + }); + } + + drop(set); + + for recv in recvs { + // The task is aborted soon and we will receive an error. + assert!(recv.await.is_err()); + } +} + +#[tokio::test] +async fn alternating() { + let mut set = JoinSet::new(); + + assert_eq!(set.len(), 0); + set.spawn(async {}); + assert_eq!(set.len(), 1); + set.spawn(async {}); + assert_eq!(set.len(), 2); + + for _ in 0..16 { + let () = set.join_next().await.unwrap().unwrap(); + assert_eq!(set.len(), 1); + set.spawn(async {}); + assert_eq!(set.len(), 2); + } +} + +#[tokio::test(start_paused = true)] +async fn abort_tasks() { + let mut set = JoinSet::new(); + let mut num_canceled = 0; + let mut num_completed = 0; + for i in 0..16 { + let abort = set.spawn(async move { + tokio::time::sleep(Duration::from_secs(i as u64)).await; + i + }); + + if i % 2 != 0 { + // abort odd-numbered tasks. + abort.abort(); + } + } + loop { + match set.join_next().await { + Some(Ok(res)) => { + num_completed += 1; + assert_eq!(res % 2, 0); + } + Some(Err(e)) => { + assert!(e.is_cancelled()); + num_canceled += 1; + } + None => break, + } + } + + assert_eq!(num_canceled, 8); + assert_eq!(num_completed, 8); +} + +#[test] +fn runtime_gone() { + let mut set = JoinSet::new(); + { + let rt = rt(); + set.spawn_on(async { 1 }, rt.handle()); + drop(rt); + } + + assert!(rt() + .block_on(set.join_next()) + .unwrap() + .unwrap_err() + .is_cancelled()); +} + +#[tokio::test(start_paused = true)] +async fn abort_all() { + let mut set: JoinSet<()> = JoinSet::new(); + + for _ in 0..5 { + set.spawn(futures::future::pending()); + } + for _ in 0..5 { + set.spawn(async { + tokio::time::sleep(Duration::from_secs(1)).await; + }); + } + + // The join set will now have 5 pending tasks and 5 ready tasks. + tokio::time::sleep(Duration::from_secs(2)).await; + + set.abort_all(); + assert_eq!(set.len(), 10); + + let mut count = 0; + while let Some(res) = set.join_next().await { + if let Err(err) = res { + assert!(err.is_cancelled()); + } + count += 1; + } + assert_eq!(count, 10); + assert_eq!(set.len(), 0); +} + +#[cfg(feature = "parking_lot")] +mod parking_lot { + use super::*; + + use futures::future::FutureExt; + + // This ensures that `join_next` works correctly when the coop budget is + // exhausted. + #[tokio::test(flavor = "current_thread")] + async fn join_set_coop() { + // Large enough to trigger coop. + const TASK_NUM: u32 = 1000; + + static SEM: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(0); + + let mut set = JoinSet::new(); + + for _ in 0..TASK_NUM { + set.spawn(async { + SEM.add_permits(1); + }); + } + + // Wait for all tasks to complete. + // + // Since this is a `current_thread` runtime, there's no race condition + // between the last permit being added and the task completing. + let _ = SEM.acquire_many(TASK_NUM).await.unwrap(); + + let mut count = 0; + let mut coop_count = 0; + loop { + match set.join_next().now_or_never() { + Some(Some(Ok(()))) => {} + Some(Some(Err(err))) => panic!("failed: {}", err), + None => { + coop_count += 1; + tokio::task::yield_now().await; + continue; + } + Some(None) => break, + } + + count += 1; + } + assert!(coop_count >= 1); + assert_eq!(count, TASK_NUM); + } +} diff --git a/vendor/tokio/tests/task_local.rs b/vendor/tokio/tests/task_local.rs index 998153242..949a40c2a 100644 --- a/vendor/tokio/tests/task_local.rs +++ b/vendor/tokio/tests/task_local.rs @@ -1,10 +1,17 @@ -tokio::task_local! { - static REQ_ID: u32; - pub static FOO: bool; -} +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support threads +#![allow(clippy::declare_interior_mutable_const)] +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::sync::oneshot; #[tokio::test(flavor = "multi_thread")] async fn local() { + tokio::task_local! { + static REQ_ID: u32; + pub static FOO: bool; + } + let j1 = tokio::spawn(REQ_ID.scope(1, async move { assert_eq!(REQ_ID.get(), 1); assert_eq!(REQ_ID.get(), 1); @@ -29,3 +36,84 @@ async fn local() { j2.await.unwrap(); j3.await.unwrap(); } + +#[tokio::test] +async fn task_local_available_on_abort() { + tokio::task_local! { + static KEY: u32; + } + + struct MyFuture { + tx_poll: Option<oneshot::Sender<()>>, + tx_drop: Option<oneshot::Sender<u32>>, + } + impl Future for MyFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + if let Some(tx_poll) = self.tx_poll.take() { + let _ = tx_poll.send(()); + } + Poll::Pending + } + } + impl Drop for MyFuture { + fn drop(&mut self) { + let _ = self.tx_drop.take().unwrap().send(KEY.get()); + } + } + + let (tx_drop, rx_drop) = oneshot::channel(); + let (tx_poll, rx_poll) = oneshot::channel(); + + let h = tokio::spawn(KEY.scope( + 42, + MyFuture { + tx_poll: Some(tx_poll), + tx_drop: Some(tx_drop), + }, + )); + + rx_poll.await.unwrap(); + h.abort(); + assert_eq!(rx_drop.await.unwrap(), 42); + + let err = h.await.unwrap_err(); + if !err.is_cancelled() { + if let Ok(panic) = err.try_into_panic() { + std::panic::resume_unwind(panic); + } else { + panic!(); + } + } +} + +#[tokio::test] +async fn task_local_available_on_completion_drop() { + tokio::task_local! { + static KEY: u32; + } + + struct MyFuture { + tx: Option<oneshot::Sender<u32>>, + } + impl Future for MyFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + Poll::Ready(()) + } + } + impl Drop for MyFuture { + fn drop(&mut self) { + let _ = self.tx.take().unwrap().send(KEY.get()); + } + } + + let (tx, rx) = oneshot::channel(); + + let h = tokio::spawn(KEY.scope(42, MyFuture { tx: Some(tx) })); + + assert_eq!(rx.await.unwrap(), 42); + h.await.unwrap(); +} diff --git a/vendor/tokio/tests/task_local_set.rs b/vendor/tokio/tests/task_local_set.rs index 58d510948..2da87f5ae 100644 --- a/vendor/tokio/tests/task_local_set.rs +++ b/vendor/tokio/tests/task_local_set.rs @@ -6,18 +6,23 @@ 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(tokio_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(tokio_wasi))] +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +#[cfg(not(tokio_wasi))] +use std::sync::atomic::Ordering::SeqCst; use std::time::Duration; #[tokio::test(flavor = "current_thread")] -async fn local_basic_scheduler() { +async fn local_current_thread_scheduler() { LocalSet::new() .run_until(async { task::spawn_local(async {}).await.unwrap(); @@ -25,6 +30,7 @@ async fn local_basic_scheduler() { .await; } +#[cfg(not(tokio_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(tokio_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(tokio_wasi))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn localset_future_timers() { static RAN1: AtomicBool = AtomicBool::new(false); @@ -67,11 +75,11 @@ async fn localset_future_timers() { let local = LocalSet::new(); local.spawn_local(async move { - time::sleep(Duration::from_millis(10)).await; + time::sleep(Duration::from_millis(5)).await; RAN1.store(true, Ordering::SeqCst); }); local.spawn_local(async move { - time::sleep(Duration::from_millis(20)).await; + time::sleep(Duration::from_millis(10)).await; RAN2.store(true, Ordering::SeqCst); }); local.await; @@ -104,6 +112,7 @@ async fn localset_future_drives_all_local_futs() { assert!(RAN3.load(Ordering::SeqCst)); } +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn local_threadpool_timer() { // This test ensures that runtime services like the timer are properly @@ -126,7 +135,23 @@ async fn local_threadpool_timer() { }) .await; } +#[test] +fn enter_guard_spawn() { + let local = LocalSet::new(); + let _guard = local.enter(); + // Run the local task set. + + let join = task::spawn_local(async { true }); + let rt = runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + local.block_on(&rt, async move { + assert!(join.await.unwrap()); + }); +} +#[cfg(not(tokio_wasi))] // Wasi doesn't support panic recovery #[test] // This will panic, since the thread that calls `block_on` cannot use // in-place blocking inside of `block_on`. @@ -153,6 +178,7 @@ fn local_threadpool_blocking_in_place() { }); } +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn local_threadpool_blocking_run() { thread_local! { @@ -181,6 +207,7 @@ async fn local_threadpool_blocking_run() { .await; } +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn all_spawns_are_local() { use futures::future; @@ -207,6 +234,7 @@ async fn all_spawns_are_local() { .await; } +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads #[tokio::test(flavor = "multi_thread")] async fn nested_spawn_is_local() { thread_local! { @@ -242,6 +270,7 @@ async fn nested_spawn_is_local() { .await; } +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads #[test] fn join_local_future_elsewhere() { thread_local! { @@ -255,14 +284,12 @@ fn join_local_future_elsewhere() { local.block_on(&rt, async move { let (tx, rx) = oneshot::channel(); let join = task::spawn_local(async move { - println!("hello world running..."); assert!( ON_RT_THREAD.with(|cell| cell.get()), "local task must run on local thread, no matter where it is awaited" ); rx.await.unwrap(); - println!("hello world task done"); "hello world" }); let join2 = task::spawn(async move { @@ -272,16 +299,34 @@ fn join_local_future_elsewhere() { ); tx.send(()).expect("task shouldn't have ended yet"); - println!("waking up hello world..."); join.await.expect("task should complete successfully"); - - println!("hello world task joined"); }); join2.await.unwrap() }); } +// Tests for <https://github.com/tokio-rs/tokio/issues/4973> +#[cfg(not(tokio_wasi))] // Wasi doesn't support threads +#[tokio::test(flavor = "multi_thread")] +async fn localset_in_thread_local() { + thread_local! { + static LOCAL_SET: LocalSet = LocalSet::new(); + } + + // holds runtime thread until end of main fn. + let (_tx, rx) = oneshot::channel::<()>(); + let handle = tokio::runtime::Handle::current(); + + std::thread::spawn(move || { + LOCAL_SET.with(|local_set| { + handle.block_on(local_set.run_until(async move { + let _ = rx.await; + })) + }); + }); +} + #[test] fn drop_cancels_tasks() { use std::rc::Rc; @@ -299,9 +344,7 @@ fn drop_cancels_tasks() { let _rc2 = rc2; started_tx.send(()).unwrap(); - loop { - time::sleep(Duration::from_secs(3600)).await; - } + futures::future::pending::<()>().await; }); local.block_on(&rt, async { @@ -347,9 +390,7 @@ fn with_timeout(timeout: Duration, f: impl FnOnce() + Send + 'static) { ), // Did the test thread panic? We'll find out for sure when we `join` // with it. - Err(RecvTimeoutError::Disconnected) => { - println!("done_rx dropped, did the test thread panic?"); - } + Err(RecvTimeoutError::Disconnected) => {} // Test completed successfully! Ok(()) => {} } @@ -357,6 +398,7 @@ fn with_timeout(timeout: Duration, f: impl FnOnce() + Send + 'static) { thread.join().expect("test thread should not panic!") } +#[cfg_attr(tokio_wasi, ignore = "`unwrap()` in `with_timeout()` panics on Wasi")] #[test] fn drop_cancels_remote_tasks() { // This test reproduces issue #1885. @@ -379,6 +421,10 @@ fn drop_cancels_remote_tasks() { }); } +#[cfg_attr( + tokio_wasi, + ignore = "FIXME: `task::spawn_local().await.unwrap()` panics on Wasi" +)] #[test] fn local_tasks_wake_join_all() { // This test reproduces issue #2460. @@ -400,13 +446,34 @@ fn local_tasks_wake_join_all() { }); } -#[tokio::test] -async fn local_tasks_are_polled_after_tick() { +#[cfg(not(tokio_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. + for _ in 0..4 { + let res = std::panic::catch_unwind(local_tasks_are_polled_after_tick_inner); + if res.is_ok() { + // success + return; + } + } + + // Test failed 4 times. Try one more time without catching panics. If it + // fails again, the test fails. + local_tasks_are_polled_after_tick_inner(); +} + +#[cfg(not(tokio_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 static RX1: AtomicUsize = AtomicUsize::new(0); static RX2: AtomicUsize = AtomicUsize::new(0); - static EXPECTED: usize = 500; + const EXPECTED: usize = 500; + + RX1.store(0, SeqCst); + RX2.store(0, SeqCst); let (tx, mut rx) = mpsc::unbounded_channel(); @@ -416,7 +483,7 @@ async fn local_tasks_are_polled_after_tick() { .run_until(async { let task2 = task::spawn(async move { // Wait a bit - time::sleep(Duration::from_millis(100)).await; + time::sleep(Duration::from_millis(10)).await; let mut oneshots = Vec::with_capacity(EXPECTED); @@ -427,18 +494,21 @@ async fn local_tasks_are_polled_after_tick() { tx.send(oneshot_rx).unwrap(); } - time::sleep(Duration::from_millis(100)).await; + time::sleep(Duration::from_millis(10)).await; for tx in oneshots.drain(..) { tx.send(()).unwrap(); } - time::sleep(Duration::from_millis(300)).await; - let rx1 = RX1.load(SeqCst); - let rx2 = RX2.load(SeqCst); - println!("EXPECT = {}; RX1 = {}; RX2 = {}", EXPECTED, rx1, rx2); - assert_eq!(EXPECTED, rx1); - assert_eq!(EXPECTED, rx2); + loop { + time::sleep(Duration::from_millis(20)).await; + let rx1 = RX1.load(SeqCst); + let rx2 = RX2.load(SeqCst); + + if rx1 == EXPECTED && rx2 == EXPECTED { + break; + } + } }); while let Some(oneshot) = rx.recv().await { @@ -500,7 +570,122 @@ async fn spawn_wakes_localset() { } } -fn rt() -> Runtime { +#[test] +fn store_local_set_in_thread_local_with_runtime() { + use tokio::runtime::Runtime; + + thread_local! { + static CURRENT: RtAndLocalSet = RtAndLocalSet::new(); + } + + struct RtAndLocalSet { + rt: Runtime, + local: LocalSet, + } + + impl RtAndLocalSet { + fn new() -> RtAndLocalSet { + RtAndLocalSet { + rt: tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(), + local: LocalSet::new(), + } + } + + async fn inner_method(&self) { + self.local + .run_until(async move { + tokio::task::spawn_local(async {}); + }) + .await + } + + fn method(&self) { + self.rt.block_on(self.inner_method()); + } + } + + CURRENT.with(|f| { + f.method(); + }); +} + +#[cfg(tokio_unstable)] +mod unstable { + use tokio::runtime::UnhandledPanic; + use tokio::task::LocalSet; + + #[tokio::test] + #[should_panic( + expected = "a spawned task panicked and the LocalSet is configured to shutdown on unhandled panic" + )] + async fn shutdown_on_panic() { + LocalSet::new() + .unhandled_panic(UnhandledPanic::ShutdownRuntime) + .run_until(async { + tokio::task::spawn_local(async { + panic!("boom"); + }); + + futures::future::pending::<()>().await; + }) + .await; + } + + // This test compares that, when the task driving `run_until` has already + // consumed budget, the `run_until` future has less budget than a "spawned" + // task. + // + // "Budget" is a fuzzy metric as the Tokio runtime is able to change values + // internally. This is why the test uses indirection to test this. + #[tokio::test] + async fn run_until_does_not_get_own_budget() { + // Consume some budget + tokio::task::consume_budget().await; + + LocalSet::new() + .run_until(async { + let spawned = tokio::spawn(async { + let mut spawned_n = 0; + + { + let mut spawned = tokio_test::task::spawn(async { + loop { + spawned_n += 1; + tokio::task::consume_budget().await; + } + }); + // Poll once + assert!(!spawned.poll().is_ready()); + } + + spawned_n + }); + + let mut run_until_n = 0; + { + let mut run_until = tokio_test::task::spawn(async { + loop { + run_until_n += 1; + tokio::task::consume_budget().await; + } + }); + // Poll once + assert!(!run_until.poll().is_ready()); + } + + let spawned_n = spawned.await.unwrap(); + assert_ne!(spawned_n, 0); + assert_ne!(run_until_n, 0); + assert!(spawned_n > run_until_n); + }) + .await + } +} + +fn rt() -> runtime::Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() .build() diff --git a/vendor/tokio/tests/task_panic.rs b/vendor/tokio/tests/task_panic.rs new file mode 100644 index 000000000..e4cedce27 --- /dev/null +++ b/vendor/tokio/tests/task_panic.rs @@ -0,0 +1,123 @@ +#![warn(rust_2018_idioms)] +#![allow(clippy::declare_interior_mutable_const)] +#![cfg(all(feature = "full", not(tokio_wasi)))] + +use futures::future; +use std::error::Error; +use tokio::runtime::Builder; +use tokio::task::{self, block_in_place}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn block_in_place_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = Builder::new_current_thread().enable_all().build().unwrap(); + rt.block_on(async { + block_in_place(|| {}); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn local_set_spawn_local_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _local = task::LocalSet::new(); + + let _ = task::spawn_local(async {}); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn local_set_block_on_panic_caller() -> Result<(), Box<dyn Error>> { + 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<dyn Error>> { + let panic_location_file = test_panic(|| { + tokio::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<dyn Error>> { + tokio::task_local! { + static NUMBER: u32; + } + + let panic_location_file = test_panic(|| { + NUMBER.sync_scope(1, || { + NUMBER.with(|_| { + 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<dyn Error>> { + 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<dyn Error>> { + tokio::task_local! { + static NUMBER: u32; + } + + let panic_location_file = test_panic(|| { + NUMBER.get(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} diff --git a/vendor/tokio/tests/task_yield_now.rs b/vendor/tokio/tests/task_yield_now.rs new file mode 100644 index 000000000..b16bca528 --- /dev/null +++ b/vendor/tokio/tests/task_yield_now.rs @@ -0,0 +1,16 @@ +#![cfg(all(feature = "full", tokio_unstable))] + +use tokio::task; +use tokio_test::task::spawn; + +// `yield_now` is tested within the runtime in `rt_common`. +#[test] +fn yield_now_outside_of_runtime() { + let mut task = spawn(async { + task::yield_now().await; + }); + + assert!(task.poll().is_pending()); + assert!(task.is_woken()); + assert!(task.poll().is_ready()); +} diff --git a/vendor/tokio/tests/tcp_accept.rs b/vendor/tokio/tests/tcp_accept.rs index 5ffb946f3..ba4a498d1 100644 --- a/vendor/tokio/tests/tcp_accept.rs +++ b/vendor/tokio/tests/tcp_accept.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::net::{TcpListener, TcpStream}; use tokio::sync::{mpsc, oneshot}; diff --git a/vendor/tokio/tests/tcp_connect.rs b/vendor/tokio/tests/tcp_connect.rs index cbe68fa27..f2384420e 100644 --- a/vendor/tokio/tests/tcp_connect.rs +++ b/vendor/tokio/tests/tcp_connect.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot; @@ -35,6 +35,7 @@ async fn connect_v4() { } #[tokio::test] +#[cfg(not(tokio_no_ipv6))] async fn connect_v6() { let srv = assert_ok!(TcpListener::bind("[::1]:0").await); let addr = assert_ok!(srv.local_addr()); diff --git a/vendor/tokio/tests/tcp_echo.rs b/vendor/tokio/tests/tcp_echo.rs index 5bb7ff0ac..f47d4ac18 100644 --- a/vendor/tokio/tests/tcp_echo.rs +++ b/vendor/tokio/tests/tcp_echo.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; diff --git a/vendor/tokio/tests/tcp_into_split.rs b/vendor/tokio/tests/tcp_into_split.rs index b4bb2eeb9..010984af8 100644 --- a/vendor/tokio/tests/tcp_into_split.rs +++ b/vendor/tokio/tests/tcp_into_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use std::io::{Error, ErrorKind, Result}; use std::io::{Read, Write}; @@ -116,7 +116,7 @@ async fn drop_write() -> Result<()> { // drop it while the read is in progress std::thread::spawn(move || { - thread::sleep(std::time::Duration::from_millis(50)); + thread::sleep(std::time::Duration::from_millis(10)); drop(write_half); }); diff --git a/vendor/tokio/tests/tcp_into_std.rs b/vendor/tokio/tests/tcp_into_std.rs index 4bf24c14d..320d9946e 100644 --- a/vendor/tokio/tests/tcp_into_std.rs +++ b/vendor/tokio/tests/tcp_into_std.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use std::io::Read; use std::io::Result; diff --git a/vendor/tokio/tests/tcp_peek.rs b/vendor/tokio/tests/tcp_peek.rs index aecc0ac19..a5fd6ba4f 100644 --- a/vendor/tokio/tests/tcp_peek.rs +++ b/vendor/tokio/tests/tcp_peek.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::io::AsyncReadExt; use tokio::net::TcpStream; @@ -7,7 +7,7 @@ use tokio::net::TcpStream; use tokio_test::assert_ok; use std::thread; -use std::{convert::TryInto, io::Write, net}; +use std::{io::Write, net}; #[tokio::test] async fn peek() { @@ -15,7 +15,7 @@ async fn peek() { let addr = listener.local_addr().unwrap(); let t = thread::spawn(move || assert_ok!(listener.accept()).0); - let left = net::TcpStream::connect(&addr).unwrap(); + let left = net::TcpStream::connect(addr).unwrap(); let mut right = t.join().unwrap(); let _ = right.write(&[1, 2, 3, 4]).unwrap(); diff --git a/vendor/tokio/tests/tcp_shutdown.rs b/vendor/tokio/tests/tcp_shutdown.rs index 536a16130..1d477f37c 100644 --- a/vendor/tokio/tests/tcp_shutdown.rs +++ b/vendor/tokio/tests/tcp_shutdown.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; diff --git a/vendor/tokio/tests/tcp_socket.rs b/vendor/tokio/tests/tcp_socket.rs index 9258864d4..66309d304 100644 --- a/vendor/tokio/tests/tcp_socket.rs +++ b/vendor/tokio/tests/tcp_socket.rs @@ -1,6 +1,7 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind +use std::time::Duration; use tokio::net::TcpSocket; use tokio_test::assert_ok; @@ -23,6 +24,7 @@ async fn basic_usage_v4() { } #[tokio::test] +#[cfg(not(tokio_no_ipv6))] async fn basic_usage_v6() { // Create server let addr = assert_ok!("[::1]:0".parse()); @@ -58,3 +60,16 @@ async fn bind_before_connect() { // Accept let _ = assert_ok!(srv.accept().await); } + +#[tokio::test] +async fn basic_linger() { + // Create server + let addr = assert_ok!("127.0.0.1:0".parse()); + let srv = assert_ok!(TcpSocket::new_v4()); + assert_ok!(srv.bind(addr)); + + assert!(srv.linger().unwrap().is_none()); + + srv.set_linger(Some(Duration::new(0, 0))).unwrap(); + assert_eq!(srv.linger().unwrap(), Some(Duration::new(0, 0))); +} diff --git a/vendor/tokio/tests/tcp_split.rs b/vendor/tokio/tests/tcp_split.rs index 7171dac46..335b21f29 100644 --- a/vendor/tokio/tests/tcp_split.rs +++ b/vendor/tokio/tests/tcp_split.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use std::io::Result; use std::io::{Read, Write}; @@ -36,7 +36,7 @@ async fn split() -> Result<()> { assert_eq!(peek_len1, read_len); assert_eq!(&read_buf[..read_len], MSG); - write_half.write(MSG).await?; + assert_eq!(write_half.write(MSG).await?, MSG.len()); handle.join().unwrap(); Ok(()) } diff --git a/vendor/tokio/tests/tcp_stream.rs b/vendor/tokio/tests/tcp_stream.rs index 0b5d12ae8..31fe3baa2 100644 --- a/vendor/tokio/tests/tcp_stream.rs +++ b/vendor/tokio/tests/tcp_stream.rs @@ -1,5 +1,5 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support bind use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest}; use tokio::net::{TcpListener, TcpStream}; @@ -254,30 +254,34 @@ async fn create_pair() -> (TcpStream, TcpStream) { (client, server) } -fn read_until_pending(stream: &mut TcpStream) { +fn read_until_pending(stream: &mut TcpStream) -> usize { let mut buf = vec![0u8; 1024 * 1024]; + let mut total = 0; loop { match stream.try_read(&mut buf) { - Ok(_) => (), + Ok(n) => total += n, Err(err) => { assert_eq!(err.kind(), io::ErrorKind::WouldBlock); break; } } } + total } -fn write_until_pending(stream: &mut TcpStream) { +fn write_until_pending(stream: &mut TcpStream) -> usize { let buf = vec![0u8; 1024 * 1024]; + let mut total = 0; loop { match stream.try_write(&buf) { - Ok(_) => (), + Ok(n) => total += n, Err(err) => { assert_eq!(err.kind(), io::ErrorKind::WouldBlock); break; } } } + total } #[tokio::test] @@ -357,3 +361,40 @@ async fn try_read_buf() { } } } + +// read_closed is a best effort event, so test only for no false positives. +#[tokio::test] +async fn read_closed() { + let (client, mut server) = create_pair().await; + + let mut ready_fut = task::spawn(client.ready(Interest::READABLE)); + assert_pending!(ready_fut.poll()); + + assert_ok!(server.write_all(b"ping").await); + + let ready_event = assert_ok!(ready_fut.await); + + assert!(!ready_event.is_read_closed()); +} + +// write_closed is a best effort event, so test only for no false positives. +#[tokio::test] +async fn write_closed() { + let (mut client, mut server) = create_pair().await; + + // Fill the write buffer. + let write_size = write_until_pending(&mut client); + let mut ready_fut = task::spawn(client.ready(Interest::WRITABLE)); + assert_pending!(ready_fut.poll()); + + // Drain the socket to make client writable. + let mut read_size = 0; + while read_size < write_size { + server.readable().await.unwrap(); + read_size += read_until_pending(&mut server); + } + + let ready_event = assert_ok!(ready_fut.await); + + assert!(!ready_event.is_write_closed()); +} diff --git a/vendor/tokio/tests/time_interval.rs b/vendor/tokio/tests/time_interval.rs index 5f7bf55f2..42285364a 100644 --- a/vendor/tokio/tests/time_interval.rs +++ b/vendor/tokio/tests/time_interval.rs @@ -1,10 +1,12 @@ #![warn(rust_2018_idioms)] #![cfg(feature = "full")] -use tokio::time::{self, Duration, Instant, MissedTickBehavior}; -use tokio_test::{assert_pending, assert_ready_eq, task}; +use std::pin::Pin; +use std::task::{Context, Poll}; -use std::task::Poll; +use futures::{Stream, StreamExt}; +use tokio::time::{self, Duration, Instant, Interval, MissedTickBehavior}; +use tokio_test::{assert_pending, assert_ready_eq, task}; // Takes the `Interval` task, `start` variable, and optional time deltas // For each time delta, it polls the `Interval` and asserts that the result is @@ -166,6 +168,42 @@ async fn skip() { check_interval_poll!(i, start, 1800); } +#[tokio::test(start_paused = true)] +async fn reset() { + let start = Instant::now(); + + // This is necessary because the timer is only so granular, and in order for + // all our ticks to resolve, the time needs to be 1ms ahead of what we + // expect, so that the runtime will see that it is time to resolve the timer + time::advance(ms(1)).await; + + let mut i = task::spawn(time::interval_at(start, ms(300))); + + check_interval_poll!(i, start, 0); + + time::advance(ms(100)).await; + check_interval_poll!(i, start); + + time::advance(ms(200)).await; + check_interval_poll!(i, start, 300); + + time::advance(ms(100)).await; + check_interval_poll!(i, start); + + i.reset(); + + time::advance(ms(250)).await; + check_interval_poll!(i, start); + + time::advance(ms(50)).await; + // We add one because when using `reset` method, `Interval` adds the + // `period` from `Instant::now()`, which will always be off by one + check_interval_poll!(i, start, 701); + + time::advance(ms(300)).await; + check_interval_poll!(i, start, 1001); +} + fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> { interval.enter(|cx, mut interval| interval.poll_tick(cx)) } @@ -173,3 +211,113 @@ fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> { fn ms(n: u64) -> Duration { Duration::from_millis(n) } + +/// Helper struct to test the [tokio::time::Interval::poll_tick()] method. +/// +/// `poll_tick()` should register the waker in the context only if it returns +/// `Poll::Pending`, not when returning `Poll::Ready`. This struct contains an +/// interval timer and counts up on every tick when used as stream. When the +/// counter is a multiple of four, it yields the current counter value. +/// Depending on the value for `wake_on_pending`, it will reschedule itself when +/// it returns `Poll::Pending` or not. When used with `wake_on_pending=false`, +/// we expect that the stream stalls because the timer will **not** reschedule +/// the next wake-up itself once it returned `Poll::Ready`. +struct IntervalStreamer { + counter: u32, + timer: Interval, + wake_on_pending: bool, +} + +impl Stream for IntervalStreamer { + type Item = u32; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + let this = Pin::into_inner(self); + + if this.counter > 12 { + return Poll::Ready(None); + } + + match this.timer.poll_tick(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => { + this.counter += 1; + if this.counter % 4 == 0 { + Poll::Ready(Some(this.counter)) + } else { + if this.wake_on_pending { + // Schedule this task for wake-up + cx.waker().wake_by_ref(); + } + Poll::Pending + } + } + } + } +} + +#[tokio::test(start_paused = true)] +async fn stream_with_interval_poll_tick_self_waking() { + let stream = IntervalStreamer { + counter: 0, + timer: tokio::time::interval(tokio::time::Duration::from_millis(10)), + wake_on_pending: true, + }; + + let (res_tx, mut res_rx) = tokio::sync::mpsc::channel(12); + + // Wrap task in timeout so that it will finish eventually even if the stream + // stalls. + tokio::spawn(tokio::time::timeout( + tokio::time::Duration::from_millis(150), + async move { + tokio::pin!(stream); + + while let Some(item) = stream.next().await { + res_tx.send(item).await.ok(); + } + }, + )); + + let mut items = Vec::with_capacity(3); + while let Some(result) = res_rx.recv().await { + items.push(result); + } + + // We expect the stream to yield normally and thus three items. + assert_eq!(items, vec![4, 8, 12]); +} + +#[tokio::test(start_paused = true)] +async fn stream_with_interval_poll_tick_no_waking() { + let stream = IntervalStreamer { + counter: 0, + timer: tokio::time::interval(tokio::time::Duration::from_millis(10)), + wake_on_pending: false, + }; + + let (res_tx, mut res_rx) = tokio::sync::mpsc::channel(12); + + // Wrap task in timeout so that it will finish eventually even if the stream + // stalls. + tokio::spawn(tokio::time::timeout( + tokio::time::Duration::from_millis(150), + async move { + tokio::pin!(stream); + + while let Some(item) = stream.next().await { + res_tx.send(item).await.ok(); + } + }, + )); + + let mut items = Vec::with_capacity(0); + while let Some(result) = res_rx.recv().await { + items.push(result); + } + + // We expect the stream to stall because it does not reschedule itself on + // `Poll::Pending` and neither does [tokio::time::Interval] reschedule the + // task when returning `Poll::Ready`. + assert_eq!(items, vec![]); +} diff --git a/vendor/tokio/tests/time_panic.rs b/vendor/tokio/tests/time_panic.rs new file mode 100644 index 000000000..aaff11bb4 --- /dev/null +++ b/vendor/tokio/tests/time_panic.rs @@ -0,0 +1,93 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi doesn't support panic recovery + +use futures::future; +use std::error::Error; +use std::time::Duration; +use tokio::runtime::{Builder, Runtime}; +use tokio::time::{self, interval, interval_at, timeout, Instant}; + +mod support { + pub mod panic; +} +use support::panic::test_panic; + +#[test] +fn pause_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + + rt.block_on(async { + time::pause(); + time::pause(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn resume_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let rt = current_thread(); + + rt.block_on(async { + time::resume(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn interval_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = interval(Duration::from_millis(0)); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn interval_at_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + let _ = interval_at(Instant::now(), Duration::from_millis(0)); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn timeout_panic_caller() -> Result<(), Box<dyn Error>> { + let panic_location_file = test_panic(|| { + // Runtime without `enable_time` so it has no current timer set. + let rt = Builder::new_current_thread().build().unwrap(); + rt.block_on(async { + let _ = timeout(Duration::from_millis(5), future::pending::<()>()); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +fn current_thread() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() +} diff --git a/vendor/tokio/tests/time_pause.rs b/vendor/tokio/tests/time_pause.rs index 02e050a2d..c772e3873 100644 --- a/vendor/tokio/tests/time_pause.rs +++ b/vendor/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(tokio_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(tokio_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(tokio_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/vendor/tokio/tests/time_rt.rs b/vendor/tokio/tests/time_rt.rs index 077534352..20f9e181b 100644 --- a/vendor/tokio/tests/time_rt.rs +++ b/vendor/tokio/tests/time_rt.rs @@ -5,6 +5,7 @@ use tokio::time::*; use std::sync::mpsc; +#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))] // Wasi doesn't support threads #[test] fn timer_with_threaded_runtime() { use tokio::runtime::Runtime; @@ -13,7 +14,7 @@ fn timer_with_threaded_runtime() { let (tx, rx) = mpsc::channel(); rt.spawn(async move { - let when = Instant::now() + Duration::from_millis(100); + let when = Instant::now() + Duration::from_millis(10); sleep_until(when).await; assert!(Instant::now() >= when); @@ -25,14 +26,14 @@ fn timer_with_threaded_runtime() { } #[test] -fn timer_with_basic_scheduler() { +fn timer_with_current_thread_scheduler() { use tokio::runtime::Builder; let rt = Builder::new_current_thread().enable_all().build().unwrap(); let (tx, rx) = mpsc::channel(); rt.block_on(async move { - let when = Instant::now() + Duration::from_millis(100); + let when = Instant::now() + Duration::from_millis(10); sleep_until(when).await; assert!(Instant::now() >= when); @@ -67,7 +68,7 @@ async fn starving() { } } - let when = Instant::now() + Duration::from_millis(20); + let when = Instant::now() + Duration::from_millis(10); let starve = Starve(Box::pin(sleep_until(when)), 0); starve.await; @@ -81,7 +82,7 @@ async fn timeout_value() { let (_tx, rx) = oneshot::channel::<()>(); let now = Instant::now(); - let dur = Duration::from_millis(20); + let dur = Duration::from_millis(10); let res = timeout(dur, rx).await; assert!(res.is_err()); diff --git a/vendor/tokio/tests/time_sleep.rs b/vendor/tokio/tests/time_sleep.rs index e3e27b0c9..94022e3c0 100644 --- a/vendor/tokio/tests/time_sleep.rs +++ b/vendor/tokio/tests/time_sleep.rs @@ -24,7 +24,7 @@ async fn immediate_sleep() { async fn is_elapsed() { time::pause(); - let sleep = time::sleep(Duration::from_millis(50)); + let sleep = time::sleep(Duration::from_millis(10)); tokio::pin!(sleep); @@ -168,6 +168,7 @@ async fn reset_sleep_to_past() { assert_ready!(sleep.poll()); } +#[cfg(not(tokio_wasi))] // Wasi doesn't support panic recovery #[test] #[should_panic] fn creating_sleep_outside_of_context() { @@ -188,10 +189,7 @@ async fn greater_than_max() { #[tokio::test] async fn short_sleeps() { - for i in 0..10000 { - if (i % 10) == 0 { - eprintln!("=== {}", i); - } + for _ in 0..10000 { tokio::time::sleep(std::time::Duration::from_millis(0)).await; } } @@ -236,22 +234,6 @@ async fn long_sleeps() { } #[tokio::test] -#[should_panic(expected = "Duration too far into the future")] -async fn very_long_sleeps() { - tokio::time::pause(); - - // Some platforms (eg macos) can't represent times this far in the future - if let Some(deadline) = tokio::time::Instant::now().checked_add(Duration::from_secs(1u64 << 62)) - { - tokio::time::sleep_until(deadline).await; - } else { - // make it pass anyway (we can't skip/ignore the test based on the - // result of checked_add) - panic!("Duration too far into the future (test ignored)") - } -} - -#[tokio::test] async fn reset_after_firing() { let timer = tokio::time::sleep(std::time::Duration::from_millis(1)); tokio::pin!(timer); @@ -286,6 +268,20 @@ async fn exactly_max() { } #[tokio::test] +async fn issue_5183() { + time::pause(); + + let big = std::time::Duration::from_secs(u64::MAX / 10); + // This is a workaround since awaiting sleep(big) will never finish. + #[rustfmt::skip] + tokio::select! { + biased; + _ = tokio::time::sleep(big) => {} + _ = tokio::time::sleep(std::time::Duration::from_nanos(1)) => {} + } +} + +#[tokio::test] async fn no_out_of_bounds_close_to_max() { time::pause(); time::sleep(ms(MAX_DURATION - 1)).await; @@ -333,18 +329,18 @@ async fn drop_from_wake() { tokio::time::pause(); - let mut lock = list.lock().unwrap(); + { + let mut lock = list.lock().unwrap(); - for _ in 0..100 { - let mut timer = Box::pin(tokio::time::sleep(Duration::from_millis(10))); + for _ in 0..100 { + let mut timer = Box::pin(tokio::time::sleep(Duration::from_millis(10))); - let _ = timer.as_mut().poll(&mut Context::from_waker(&arc_wake)); + let _ = timer.as_mut().poll(&mut Context::from_waker(&arc_wake)); - lock.push(timer); + lock.push(timer); + } } - drop(lock); - tokio::time::sleep(Duration::from_millis(11)).await; assert!( diff --git a/vendor/tokio/tests/time_timeout.rs b/vendor/tokio/tests/time_timeout.rs index dbd80eb8a..be6b8fb44 100644 --- a/vendor/tokio/tests/time_timeout.rs +++ b/vendor/tokio/tests/time_timeout.rs @@ -17,6 +17,7 @@ async fn simultaneous_deadline_future_completion() { assert_ready_ok!(fut.poll()); } +#[cfg_attr(tokio_wasi, ignore = "FIXME: `fut.poll()` panics on Wasi")] #[tokio::test] async fn completed_future_past_deadline() { // Wrap it with a deadline @@ -135,3 +136,16 @@ async fn deadline_future_elapses() { fn ms(n: u64) -> Duration { Duration::from_millis(n) } + +#[tokio::test] +async fn timeout_is_not_exhausted_by_future() { + let fut = timeout(ms(1), async { + let mut buffer = [0u8; 1]; + loop { + use tokio::io::AsyncReadExt; + let _ = tokio::io::empty().read(&mut buffer).await; + } + }); + + assert!(fut.await.is_err()); +} diff --git a/vendor/tokio/tests/udp.rs b/vendor/tokio/tests/udp.rs index 715d8ebe2..565323b37 100644 --- a/vendor/tokio/tests/udp.rs +++ b/vendor/tokio/tests/udp.rs @@ -1,10 +1,11 @@ #![warn(rust_2018_idioms)] -#![cfg(feature = "full")] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support bind or UDP use futures::future::poll_fn; use std::io; use std::sync::Arc; use tokio::{io::ReadBuf, net::UdpSocket}; +use tokio_test::assert_ok; const MSG: &[u8] = b"hello"; const MSG_LEN: usize = MSG.len(); @@ -38,7 +39,7 @@ async fn send_recv_poll() -> std::io::Result<()> { let mut recv_buf = [0u8; 32]; let mut read = ReadBuf::new(&mut recv_buf); - let _len = poll_fn(|cx| receiver.poll_recv(cx, &mut read)).await?; + poll_fn(|cx| receiver.poll_recv(cx, &mut read)).await?; assert_eq!(read.filled(), MSG); Ok(()) @@ -106,6 +107,45 @@ async fn send_to_peek_from() -> std::io::Result<()> { } #[tokio::test] +async fn send_to_try_peek_from() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + let receiver_addr = receiver.local_addr()?; + poll_fn(|cx| sender.poll_send_to(cx, MSG, receiver_addr)).await?; + + // peek + let mut recv_buf = [0u8; 32]; + + loop { + match receiver.try_peek_from(&mut recv_buf) { + Ok((n, addr)) => { + assert_eq!(&recv_buf[..n], MSG); + assert_eq!(addr, sender.local_addr()?); + break; + } + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + receiver.readable().await?; + } + Err(e) => return Err(e), + } + } + + // peek + let mut recv_buf = [0u8; 32]; + let (n, addr) = receiver.peek_from(&mut recv_buf).await?; + assert_eq!(&recv_buf[..n], MSG); + assert_eq!(addr, sender.local_addr()?); + + let mut recv_buf = [0u8; 32]; + let (n, addr) = receiver.recv_from(&mut recv_buf).await?; + assert_eq!(&recv_buf[..n], MSG); + assert_eq!(addr, sender.local_addr()?); + + Ok(()) +} + +#[tokio::test] async fn send_to_peek_from_poll() -> std::io::Result<()> { let sender = UdpSocket::bind("127.0.0.1:0").await?; let receiver = UdpSocket::bind("127.0.0.1:0").await?; @@ -134,6 +174,92 @@ async fn send_to_peek_from_poll() -> std::io::Result<()> { } #[tokio::test] +async fn peek_sender() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + let sender_addr = sender.local_addr()?; + let receiver_addr = receiver.local_addr()?; + + let msg = b"Hello, world!"; + sender.send_to(msg, receiver_addr).await?; + + let peeked_sender = receiver.peek_sender().await?; + assert_eq!(peeked_sender, sender_addr); + + // Assert that `peek_sender()` returns the right sender but + // doesn't remove from the receive queue. + let mut recv_buf = [0u8; 32]; + let (read, received_sender) = receiver.recv_from(&mut recv_buf).await?; + + assert_eq!(&recv_buf[..read], msg); + assert_eq!(received_sender, peeked_sender); + + Ok(()) +} + +#[tokio::test] +async fn poll_peek_sender() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + let sender_addr = sender.local_addr()?; + let receiver_addr = receiver.local_addr()?; + + let msg = b"Hello, world!"; + poll_fn(|cx| sender.poll_send_to(cx, msg, receiver_addr)).await?; + + let peeked_sender = poll_fn(|cx| receiver.poll_peek_sender(cx)).await?; + assert_eq!(peeked_sender, sender_addr); + + // Assert that `poll_peek_sender()` returns the right sender but + // doesn't remove from the receive queue. + let mut recv_buf = [0u8; 32]; + let mut read = ReadBuf::new(&mut recv_buf); + let received_sender = poll_fn(|cx| receiver.poll_recv_from(cx, &mut read)).await?; + + assert_eq!(read.filled(), msg); + assert_eq!(received_sender, peeked_sender); + + Ok(()) +} + +#[tokio::test] +async fn try_peek_sender() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + let sender_addr = sender.local_addr()?; + let receiver_addr = receiver.local_addr()?; + + let msg = b"Hello, world!"; + sender.send_to(msg, receiver_addr).await?; + + let peeked_sender = loop { + match receiver.try_peek_sender() { + Ok(peeked_sender) => break peeked_sender, + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + receiver.readable().await?; + } + Err(e) => return Err(e), + } + }; + + assert_eq!(peeked_sender, sender_addr); + + // Assert that `try_peek_sender()` returns the right sender but + // didn't remove from the receive queue. + let mut recv_buf = [0u8; 32]; + // We already peeked the sender so there must be data in the receive queue. + let (read, received_sender) = receiver.try_recv_from(&mut recv_buf).unwrap(); + + assert_eq!(&recv_buf[..read], msg); + assert_eq!(received_sender, peeked_sender); + + Ok(()) +} + +#[tokio::test] async fn split() -> std::io::Result<()> { let socket = UdpSocket::bind("127.0.0.1:0").await?; let s = Arc::new(socket); @@ -399,6 +525,23 @@ async fn try_recv_buf() { } #[tokio::test] +async fn recv_buf() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + sender.connect(receiver.local_addr()?).await?; + receiver.connect(sender.local_addr()?).await?; + + sender.send(MSG).await?; + let mut recv_buf = Vec::with_capacity(32); + let len = receiver.recv_buf(&mut recv_buf).await?; + + assert_eq!(len, MSG_LEN); + assert_eq!(&recv_buf[..len], MSG); + Ok(()) +} + +#[tokio::test] async fn try_recv_buf_from() { // Create listener let server = UdpSocket::bind("127.0.0.1:0").await.unwrap(); @@ -440,3 +583,63 @@ async fn try_recv_buf_from() { } } } + +#[tokio::test] +async fn recv_buf_from() -> std::io::Result<()> { + let sender = UdpSocket::bind("127.0.0.1:0").await?; + let receiver = UdpSocket::bind("127.0.0.1:0").await?; + + sender.connect(receiver.local_addr()?).await?; + + sender.send(MSG).await?; + let mut recv_buf = Vec::with_capacity(32); + let (len, caddr) = receiver.recv_buf_from(&mut recv_buf).await?; + + assert_eq!(len, MSG_LEN); + assert_eq!(&recv_buf[..len], MSG); + assert_eq!(caddr, sender.local_addr()?); + Ok(()) +} + +#[tokio::test] +async fn poll_ready() { + // Create listener + let server = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let saddr = server.local_addr().unwrap(); + + // Create socket pair + let client = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let caddr = client.local_addr().unwrap(); + + for _ in 0..5 { + loop { + assert_ok!(poll_fn(|cx| client.poll_send_ready(cx)).await); + + match client.try_send_to(b"hello world", saddr) { + Ok(n) => { + assert_eq!(n, 11); + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(e) => panic!("{:?}", e), + } + } + + loop { + assert_ok!(poll_fn(|cx| server.poll_recv_ready(cx)).await); + + let mut buf = Vec::with_capacity(512); + + match server.try_recv_buf_from(&mut buf) { + Ok((n, addr)) => { + assert_eq!(n, 11); + assert_eq!(addr, caddr); + assert_eq!(&buf[0..11], &b"hello world"[..]); + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(e) => panic!("{:?}", e), + } + } + } +} diff --git a/vendor/tokio/tests/uds_datagram.rs b/vendor/tokio/tests/uds_datagram.rs index 4d2846865..ad22a0b99 100644 --- a/vendor/tokio/tests/uds_datagram.rs +++ b/vendor/tokio/tests/uds_datagram.rs @@ -29,9 +29,7 @@ async fn echo() -> io::Result<()> { let server_socket = UnixDatagram::bind(server_path.clone())?; tokio::spawn(async move { - if let Err(e) = echo_server(server_socket).await { - eprintln!("Error in echo server: {}", e); - } + let _ = echo_server(server_socket).await; }); { @@ -55,9 +53,7 @@ async fn echo_from() -> io::Result<()> { let server_socket = UnixDatagram::bind(server_path.clone())?; tokio::spawn(async move { - if let Err(e) = echo_server(server_socket).await { - eprintln!("Error in echo server: {}", e); - } + let _ = echo_server(server_socket).await; }); { @@ -181,7 +177,7 @@ async fn send_recv_poll() -> std::io::Result<()> { let mut recv_buf = [0u8; 32]; let mut read = ReadBuf::new(&mut recv_buf); - let _len = poll_fn(|cx| receiver.poll_recv(cx, &mut read)).await?; + poll_fn(|cx| receiver.poll_recv(cx, &mut read)).await?; assert_eq!(read.filled(), msg); Ok(()) @@ -281,6 +277,28 @@ async fn try_recv_buf_from() -> std::io::Result<()> { Ok(()) } +#[tokio::test] +async fn recv_buf_from() -> std::io::Result<()> { + let tmp = tempfile::tempdir()?; + + // Bind each socket to a filesystem path + let tx_path = tmp.path().join("tx"); + let tx = UnixDatagram::bind(&tx_path)?; + let rx_path = tmp.path().join("rx"); + let rx = UnixDatagram::bind(&rx_path)?; + + let bytes = b"hello world"; + tx.send_to(bytes, &rx_path).await?; + + let mut buf = Vec::with_capacity(24); + let (size, addr) = rx.recv_buf_from(&mut buf).await?; + + let dgram = &buf[..size]; + assert_eq!(dgram, bytes); + assert_eq!(addr.as_pathname().unwrap(), &tx_path); + Ok(()) +} + // Even though we use sync non-blocking io we still need a reactor. #[tokio::test] async fn try_recv_buf_never_block() -> io::Result<()> { @@ -328,3 +346,68 @@ async fn try_recv_buf_never_block() -> io::Result<()> { Ok(()) } + +#[tokio::test] +async fn recv_buf() -> std::io::Result<()> { + // Create the pair of sockets + let (sock1, sock2) = UnixDatagram::pair()?; + + // Since the sockets are paired, the paired send/recv + // functions can be used + let bytes = b"hello world"; + sock1.send(bytes).await?; + + let mut buff = Vec::with_capacity(24); + let size = sock2.recv_buf(&mut buff).await?; + + let dgram = &buff[..size]; + assert_eq!(dgram, bytes); + Ok(()) +} + +#[tokio::test] +async fn poll_ready() -> io::Result<()> { + let dir = tempfile::tempdir().unwrap(); + let server_path = dir.path().join("server.sock"); + let client_path = dir.path().join("client.sock"); + + // Create listener + let server = UnixDatagram::bind(&server_path)?; + + // Create socket pair + let client = UnixDatagram::bind(&client_path)?; + + for _ in 0..5 { + loop { + poll_fn(|cx| client.poll_send_ready(cx)).await?; + + match client.try_send_to(b"hello world", &server_path) { + Ok(n) => { + assert_eq!(n, 11); + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(e) => panic!("{:?}", e), + } + } + + loop { + poll_fn(|cx| server.poll_recv_ready(cx)).await?; + + let mut buf = Vec::with_capacity(512); + + match server.try_recv_buf_from(&mut buf) { + Ok((n, addr)) => { + assert_eq!(n, 11); + assert_eq!(addr.as_pathname(), Some(client_path.as_ref())); + assert_eq!(&buf[0..11], &b"hello world"[..]); + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(e) => panic!("{:?}", e), + } + } + } + + Ok(()) +} diff --git a/vendor/tokio/tests/uds_stream.rs b/vendor/tokio/tests/uds_stream.rs index 5f1b4cffb..b8c4e6a8e 100644 --- a/vendor/tokio/tests/uds_stream.rs +++ b/vendor/tokio/tests/uds_stream.rs @@ -25,13 +25,13 @@ async fn accept_read_write() -> std::io::Result<()> { let connect = UnixStream::connect(&sock_path); let ((mut server, _), mut client) = try_join(accept, connect).await?; - // Write to the client. TODO: Switch to write_all. - let write_len = client.write(b"hello").await?; - assert_eq!(write_len, 5); + // Write to the client. + client.write_all(b"hello").await?; drop(client); - // Read from the server. TODO: Switch to read_to_end. - let mut buf = [0u8; 5]; - server.read_exact(&mut buf).await?; + + // Read from the server. + let mut buf = vec![]; + server.read_to_end(&mut buf).await?; assert_eq!(&buf, b"hello"); let len = server.read(&mut buf).await?; assert_eq!(len, 0); diff --git a/vendor/tokio/tests/unwindsafe.rs b/vendor/tokio/tests/unwindsafe.rs new file mode 100644 index 000000000..3e6382086 --- /dev/null +++ b/vendor/tokio/tests/unwindsafe.rs @@ -0,0 +1,42 @@ +#![warn(rust_2018_idioms)] +#![cfg(all(feature = "full", not(tokio_wasi)))] // Wasi does not support panic recovery + +use std::panic::{RefUnwindSafe, UnwindSafe}; + +#[test] +fn notify_is_unwind_safe() { + is_unwind_safe::<tokio::sync::Notify>(); +} + +#[test] +fn join_handle_is_unwind_safe() { + is_unwind_safe::<tokio::task::JoinHandle<()>>(); +} + +#[test] +fn net_types_are_unwind_safe() { + is_unwind_safe::<tokio::net::TcpListener>(); + is_unwind_safe::<tokio::net::TcpSocket>(); + is_unwind_safe::<tokio::net::TcpStream>(); + is_unwind_safe::<tokio::net::UdpSocket>(); +} + +#[test] +#[cfg(unix)] +fn unix_net_types_are_unwind_safe() { + is_unwind_safe::<tokio::net::UnixDatagram>(); + is_unwind_safe::<tokio::net::UnixListener>(); + is_unwind_safe::<tokio::net::UnixStream>(); +} + +#[test] +#[cfg(windows)] +fn windows_net_types_are_unwind_safe() { + use tokio::net::windows::named_pipe::NamedPipeClient; + use tokio::net::windows::named_pipe::NamedPipeServer; + + is_unwind_safe::<NamedPipeClient>(); + is_unwind_safe::<NamedPipeServer>(); +} + +fn is_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {} |