From 36d24cd0e1f5049be4caa2461a43c704eb61975e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 16 Dec 2019 13:57:27 +0100 Subject: [PATCH 001/272] New scheduler resilient to blocking --- Cargo.toml | 2 + src/lib.rs | 2 + src/net/mod.rs | 1 - src/net/tcp/listener.rs | 2 +- src/net/tcp/stream.rs | 2 +- src/net/udp/mod.rs | 4 +- src/os/unix/net/datagram.rs | 2 +- src/os/unix/net/listener.rs | 2 +- src/os/unix/net/stream.rs | 2 +- src/rt/mod.rs | 23 ++ src/{net/driver/mod.rs => rt/reactor.rs} | 109 ++---- src/rt/runtime.rs | 449 +++++++++++++++++++++++ src/task/block_on.rs | 32 +- src/task/builder.rs | 30 +- src/task/executor/mod.rs | 13 - src/task/executor/pool.rs | 179 --------- src/task/executor/sleepers.rs | 52 --- src/task/join_handle.rs | 3 - src/task/mod.rs | 5 +- src/task/spawn_blocking.rs | 90 +---- src/task/yield_now.rs | 4 +- src/utils.rs | 71 ++++ 22 files changed, 623 insertions(+), 456 deletions(-) create mode 100644 src/rt/mod.rs rename src/{net/driver/mod.rs => rt/reactor.rs} (77%) create mode 100644 src/rt/runtime.rs delete mode 100644 src/task/executor/mod.rs delete mode 100644 src/task/executor/pool.rs delete mode 100644 src/task/executor/sleepers.rs diff --git a/Cargo.toml b/Cargo.toml index 7c4613b8c..e5ae02d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ default = [ "async-task", "crossbeam-channel", "crossbeam-deque", + "crossbeam-queue", "futures-timer", "kv-log-macro", "log", @@ -56,6 +57,7 @@ async-task = { version = "1.0.0", optional = true } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } crossbeam-channel = { version = "0.4.0", optional = true } crossbeam-deque = { version = "0.7.2", optional = true } +crossbeam-queue = { version = "0.2.0", optional = true } crossbeam-utils = { version = "0.7.0", optional = true } futures-core = { version = "0.3.1", optional = true } futures-io = { version = "0.3.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index d0c87ff5c..070df8851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,6 +246,8 @@ cfg_std! { pub mod stream; pub mod sync; pub mod task; + + pub(crate) mod rt; } cfg_default! { diff --git a/src/net/mod.rs b/src/net/mod.rs index 29e430902..fe83d3b15 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -66,6 +66,5 @@ pub use tcp::{Incoming, TcpListener, TcpStream}; pub use udp::UdpSocket; mod addr; -pub(crate) mod driver; mod tcp; mod udp; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index fe06a96d6..b389518cb 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use crate::future; use crate::io; -use crate::net::driver::Watcher; +use crate::rt::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 413178333..71245a317 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use crate::future; use crate::io::{self, Read, Write}; -use crate::net::driver::Watcher; +use crate::rt::Watcher; use crate::net::ToSocketAddrs; use crate::task::{spawn_blocking, Context, Poll}; use crate::utils::Context as _; diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 418b4b60a..961288a07 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -3,8 +3,8 @@ use std::net::SocketAddr; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; -use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; +use crate::rt::Watcher; use crate::utils::Context as _; /// A UDP socket. @@ -102,7 +102,7 @@ impl UdpSocket { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::net::UdpSocket; + /// use async_std::net::UdpSocket; /// /// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// let addr = socket.local_addr()?; diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index fc426b7cd..5a2d6ec91 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -8,7 +8,7 @@ use mio_uds; use super::SocketAddr; use crate::future; use crate::io; -use crate::net::driver::Watcher; +use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::task::spawn_blocking; diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 675ef481f..9f6bdcbc5 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -10,7 +10,7 @@ use super::SocketAddr; use super::UnixStream; use crate::future; use crate::io; -use crate::net::driver::Watcher; +use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 647edc96f..a1c83f1b9 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -9,7 +9,7 @@ use mio_uds; use super::SocketAddr; use crate::io::{self, Read, Write}; -use crate::net::driver::Watcher; +use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::task::{spawn_blocking, Context, Poll}; diff --git a/src/rt/mod.rs b/src/rt/mod.rs new file mode 100644 index 000000000..2149d2420 --- /dev/null +++ b/src/rt/mod.rs @@ -0,0 +1,23 @@ +//! The runtime. + +use std::thread; + +use once_cell::sync::Lazy; + +use crate::utils::abort_on_panic; + +pub use reactor::{Reactor, Watcher}; +pub use runtime::Runtime; + +mod reactor; +mod runtime; + +/// The global runtime. +pub static RUNTIME: Lazy = Lazy::new(|| { + thread::Builder::new() + .name("async-std/runtime".to_string()) + .spawn(|| abort_on_panic(|| RUNTIME.run())) + .expect("cannot start a runtime thread"); + + Runtime::new() +}); diff --git a/src/net/driver/mod.rs b/src/rt/reactor.rs similarity index 77% rename from src/net/driver/mod.rs rename to src/rt/reactor.rs index 7f33e8594..c0046c971 100644 --- a/src/net/driver/mod.rs +++ b/src/rt/reactor.rs @@ -1,13 +1,13 @@ use std::fmt; use std::sync::{Arc, Mutex}; +use std::time::Duration; use mio::{self, Evented}; -use once_cell::sync::Lazy; use slab::Slab; use crate::io; +use crate::rt::RUNTIME; use crate::task::{Context, Poll, Waker}; -use crate::utils::abort_on_panic; /// Data associated with a registered I/O handle. #[derive(Debug)] @@ -18,15 +18,18 @@ struct Entry { /// Tasks that are blocked on reading from this I/O handle. readers: Mutex>, - /// Thasks that are blocked on writing to this I/O handle. + /// Tasks that are blocked on writing to this I/O handle. writers: Mutex>, } /// The state of a networking driver. -struct Reactor { +pub struct Reactor { /// A mio instance that polls for new events. poller: mio::Poll, + /// A list into which mio stores events. + events: Mutex, + /// A collection of registered I/O handles. entries: Mutex>>, @@ -39,12 +42,13 @@ struct Reactor { impl Reactor { /// Creates a new reactor for polling I/O events. - fn new() -> io::Result { + pub fn new() -> io::Result { let poller = mio::Poll::new()?; let notify_reg = mio::Registration::new2(); let mut reactor = Reactor { poller, + events: Mutex::new(mio::Events::with_capacity(1000)), entries: Mutex::new(Slab::new()), notify_reg, notify_token: mio::Token(0), @@ -92,50 +96,32 @@ impl Reactor { Ok(()) } - // fn notify(&self) { - // self.notify_reg - // .1 - // .set_readiness(mio::Ready::readable()) - // .unwrap(); - // } -} + /// Notifies the reactor so that polling stops blocking. + pub fn notify(&self) -> io::Result<()> { + self.notify_reg.1.set_readiness(mio::Ready::readable()) + } + + /// Waits on the poller for new events and wakes up tasks blocked on I/O handles. + /// + /// Returns `Ok(true)` if at least one new task was woken. + pub fn poll(&self, timeout: Option) -> io::Result { + let mut events = self.events.lock().unwrap(); -/// The state of the global networking driver. -static REACTOR: Lazy = Lazy::new(|| { - // Spawn a thread that waits on the poller for new events and wakes up tasks blocked on I/O - // handles. - std::thread::Builder::new() - .name("async-std/net".to_string()) - .spawn(move || { - // If the driver thread panics, there's not much we can do. It is not a - // recoverable error and there is no place to propagate it into so we just abort. - abort_on_panic(|| { - main_loop().expect("async networking thread has panicked"); - }) - }) - .expect("cannot start a thread driving blocking tasks"); - - Reactor::new().expect("cannot initialize reactor") -}); - -/// Waits on the poller for new events and wakes up tasks blocked on I/O handles. -fn main_loop() -> io::Result<()> { - let reactor = &REACTOR; - let mut events = mio::Events::with_capacity(1000); - - loop { // Block on the poller until at least one new event comes in. - reactor.poller.poll(&mut events, None)?; + self.poller.poll(&mut events, timeout)?; // Lock the entire entry table while we're processing new events. - let entries = reactor.entries.lock().unwrap(); + let entries = self.entries.lock().unwrap(); + + // The number of woken tasks. + let mut progress = false; for event in events.iter() { let token = event.token(); - if token == reactor.notify_token { + if token == self.notify_token { // If this is the notification token, we just need the notification state. - reactor.notify_reg.1.set_readiness(mio::Ready::empty())?; + self.notify_reg.1.set_readiness(mio::Ready::empty())?; } else { // Otherwise, look for the entry associated with this token. if let Some(entry) = entries.get(token.0) { @@ -143,21 +129,27 @@ fn main_loop() -> io::Result<()> { let readiness = event.readiness(); // Wake up reader tasks blocked on this I/O handle. - if !(readiness & reader_interests()).is_empty() { + let reader_interests = mio::Ready::all() - mio::Ready::writable(); + if !(readiness & reader_interests).is_empty() { for w in entry.readers.lock().unwrap().drain(..) { w.wake(); + progress = true; } } // Wake up writer tasks blocked on this I/O handle. - if !(readiness & writer_interests()).is_empty() { + let writer_interests = mio::Ready::all() - mio::Ready::readable(); + if !(readiness & writer_interests).is_empty() { for w in entry.writers.lock().unwrap().drain(..) { w.wake(); + progress = true; } } } } } + + Ok(progress) } } @@ -180,7 +172,8 @@ impl Watcher { /// lifetime of the returned I/O handle. pub fn new(source: T) -> Watcher { Watcher { - entry: REACTOR + entry: RUNTIME + .reactor() .register(&source) .expect("cannot register an I/O event source"), source: Some(source), @@ -264,7 +257,8 @@ impl Watcher { #[allow(dead_code)] pub fn into_inner(mut self) -> T { let source = self.source.take().unwrap(); - REACTOR + RUNTIME + .reactor() .deregister(&source, &self.entry) .expect("cannot deregister I/O event source"); source @@ -274,7 +268,8 @@ impl Watcher { impl Drop for Watcher { fn drop(&mut self) { if let Some(ref source) = self.source { - REACTOR + RUNTIME + .reactor() .deregister(source, &self.entry) .expect("cannot deregister I/O event source"); } @@ -289,27 +284,3 @@ impl fmt::Debug for Watcher { .finish() } } - -/// Returns a mask containing flags that interest tasks reading from I/O handles. -#[inline] -fn reader_interests() -> mio::Ready { - mio::Ready::all() - mio::Ready::writable() -} - -/// Returns a mask containing flags that interest tasks writing into I/O handles. -#[inline] -fn writer_interests() -> mio::Ready { - mio::Ready::writable() | hup() -} - -/// Returns a flag containing the hangup status. -#[inline] -fn hup() -> mio::Ready { - #[cfg(unix)] - let ready = mio::unix::UnixReady::hup().into(); - - #[cfg(not(unix))] - let ready = mio::Ready::empty(); - - ready -} diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs new file mode 100644 index 000000000..35ebe5055 --- /dev/null +++ b/src/rt/runtime.rs @@ -0,0 +1,449 @@ +use std::cell::Cell; +use std::io; +use std::iter; +use std::ptr; +use std::sync::atomic::{self, AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::Duration; + +use crossbeam_deque::{Injector, Steal, Stealer, Worker}; +use crossbeam_utils::thread::scope; +use once_cell::unsync::OnceCell; + +use crate::rt::Reactor; +use crate::task::Runnable; +use crate::utils::{abort_on_panic, random, Spinlock}; + +thread_local! { + /// A reference to the current machine, if the current thread runs tasks. + static MACHINE: OnceCell> = OnceCell::new(); + + /// This flag is set to true whenever `task::yield_now()` is invoked. + static YIELD_NOW: Cell = Cell::new(false); +} + +/// Scheduler state. +struct Scheduler { + /// Set to `true` every time before a machine blocks polling the reactor. + progress: bool, + + /// Set to `true` while a machine is polling the reactor. + polling: bool, + + /// Idle processors. + processors: Vec, + + /// Running machines. + machines: Vec>, +} + +/// An async runtime. +pub struct Runtime { + /// The reactor. + reactor: Reactor, + + /// The global queue of tasks. + injector: Injector, + + /// Handles to local queues for stealing work. + stealers: Vec>, + + /// The scheduler state. + sched: Mutex, +} + +impl Runtime { + /// Creates a new runtime. + pub fn new() -> Runtime { + let cpus = num_cpus::get().max(1); + let processors: Vec<_> = (0..cpus).map(|_| Processor::new()).collect(); + let stealers = processors.iter().map(|p| p.worker.stealer()).collect(); + + Runtime { + reactor: Reactor::new().unwrap(), + injector: Injector::new(), + stealers, + sched: Mutex::new(Scheduler { + processors, + machines: Vec::new(), + progress: false, + polling: false, + }), + } + } + + /// Returns a reference to the reactor. + pub fn reactor(&self) -> &Reactor { + &self.reactor + } + + /// Flushes the task slot so that tasks get run more fairly. + pub fn yield_now(&self) { + YIELD_NOW.with(|flag| flag.set(true)); + } + + /// Schedules a task. + pub fn schedule(&self, task: Runnable) { + MACHINE.with(|machine| { + // If the current thread is a worker thread, schedule it onto the current machine. + // Otherwise, push it into the global task queue. + match machine.get() { + None => { + self.injector.push(task); + self.notify(); + } + Some(m) => m.schedule(&self, task), + } + }); + } + + /// Runs the runtime on the current thread. + pub fn run(&self) { + scope(|s| { + let mut idle = 0; + let mut delay = 0; + + loop { + // Get a list of new machines to start, if any need to be started. + for m in self.make_machines() { + idle = 0; + + s.builder() + .name("async-std/machine".to_string()) + .spawn(move |_| { + abort_on_panic(|| { + let _ = MACHINE.with(|machine| machine.set(m.clone())); + m.run(self); + }) + }) + .expect("cannot start a machine thread"); + } + + // Sleep for a bit longer if the scheduler state hasn't changed in a while. + if idle > 10 { + delay = (delay * 2).min(10_000); + } else { + idle += 1; + delay = 1000; + } + + thread::sleep(Duration::from_micros(delay)); + } + }) + .unwrap(); + } + + /// Returns a list of machines that need to be started. + fn make_machines(&self) -> Vec> { + let mut sched = self.sched.lock().unwrap(); + let mut to_start = Vec::new(); + + // If there is a machine that is stuck on a task and not making any progress, steal its + // processor and set up a new machine to take over. + for m in &mut sched.machines { + if !m.progress.swap(false, Ordering::SeqCst) { + let opt_p = m.processor.try_lock().and_then(|mut p| p.take()); + + if let Some(p) = opt_p { + *m = Arc::new(Machine::new(p)); + to_start.push(m.clone()); + } + } + } + + // If no machine has been polling the reactor in a while, that means the runtime is + // overloaded with work and we need to start another machine. + if !sched.polling { + if !sched.progress { + if let Some(p) = sched.processors.pop() { + let m = Arc::new(Machine::new(p)); + to_start.push(m.clone()); + sched.machines.push(m); + } + } + + sched.progress = false; + } + + to_start + } + + /// Unparks a thread polling the reactor. + fn notify(&self) { + atomic::fence(Ordering::SeqCst); + self.reactor.notify().unwrap(); + } + + /// Attempts to poll the reactor without blocking on it. + /// + /// Returns `Ok(true)` if at least one new task was woken. + /// + /// This function might not poll the reactor at all so do not rely on it doing anything. Only + /// use for optimization. + fn quick_poll(&self) -> io::Result { + if let Ok(sched) = self.sched.try_lock() { + if !sched.polling { + return self.reactor.poll(Some(Duration::from_secs(0))); + } + } + Ok(false) + } +} + +/// A thread running a processor. +struct Machine { + /// Holds the processor until it gets stolen. + processor: Spinlock>, + + /// Gets set to `true` before running every task to indicate the machine is not stuck. + progress: AtomicBool, +} + +impl Machine { + /// Creates a new machine running a processor. + fn new(p: Processor) -> Machine { + Machine { + processor: Spinlock::new(Some(p)), + progress: AtomicBool::new(true), + } + } + + /// Schedules a task onto the machine. + fn schedule(&self, rt: &Runtime, task: Runnable) { + match self.processor.lock().as_mut() { + None => { + rt.injector.push(task); + rt.notify(); + } + Some(p) => p.schedule(rt, task), + } + } + + /// Finds the next runnable task. + fn find_task(&self, rt: &Runtime) -> Steal { + let mut retry = false; + + // First try finding a task in the local queue or in the global queue. + if let Some(p) = self.processor.lock().as_mut() { + if let Some(task) = p.pop_task() { + return Steal::Success(task); + } + + match p.steal_from_global(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), + } + } + + // Try polling the reactor, but don't block on it. + let progress = rt.quick_poll().unwrap(); + + // Try finding a task in the local queue, which might hold tasks woken by the reactor. If + // the local queue is still empty, try stealing from other processors. + if let Some(p) = self.processor.lock().as_mut() { + if progress { + if let Some(task) = p.pop_task() { + return Steal::Success(task); + } + } + + match p.steal_from_others(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), + } + } + + if retry { Steal::Retry } else { Steal::Empty } + } + + /// Runs the machine on the current thread. + fn run(&self, rt: &Runtime) { + /// Number of yields when no runnable task is found. + const YIELDS: u32 = 3; + /// Number of short sleeps when no runnable task in found. + const SLEEPS: u32 = 10; + /// Number of runs in a row before the global queue is inspected. + const RUNS: u32 = 64; + + // The number of times the thread found work in a row. + let mut runs = 0; + // The number of times the thread didn't find work in a row. + let mut fails = 0; + + loop { + // let the scheduler know this machine is making progress. + self.progress.store(true, Ordering::SeqCst); + + // Check if `task::yield_now()` was invoked and flush the slot if so. + YIELD_NOW.with(|flag| { + if flag.replace(false) { + if let Some(p) = self.processor.lock().as_mut() { + p.flush_slot(rt); + } + } + }); + + // After a number of runs in a row, do some work to ensure no task is left behind + // indefinitely. Poll the reactor, steal tasks from the global queue, and flush the + // task slot. + if runs >= RUNS { + runs = 0; + rt.quick_poll().unwrap(); + + if let Some(p) = self.processor.lock().as_mut() { + if let Steal::Success(task) = p.steal_from_global(rt) { + p.schedule(rt, task); + } + + p.flush_slot(rt); + } + } + + // Try to find a runnable task. + if let Steal::Success(task) = self.find_task(rt) { + task.run(); + runs += 1; + fails = 0; + continue; + } + + fails += 1; + + // Check if the processor was stolen. + if self.processor.lock().is_none() { + break; + } + + // Yield the current thread a few times. + if fails <= YIELDS { + thread::yield_now(); + continue; + } + + // Put the current thread to sleep a few times. + if fails <= YIELDS + SLEEPS { + let opt_p = self.processor.lock().take(); + thread::sleep(Duration::from_micros(10)); + *self.processor.lock() = opt_p; + continue; + } + + let mut sched = rt.sched.lock().unwrap(); + + // One final check for available tasks while the scheduler is locked. + if let Some(task) = iter::repeat_with(|| self.find_task(rt)) + .find(|s| !s.is_retry()) + .and_then(|s| s.success()) + { + self.schedule(rt, task); + continue; + } + + // If another thread is already blocked on the reactor, there is no point in keeping + // the current thread around since there is too little work to do. + if sched.polling { + break; + } + + // Take out the machine associated with the current thread. + let m = match sched + .machines + .iter() + .position(|elem| ptr::eq(&**elem, self)) + { + None => break, // The processor was stolen. + Some(pos) => sched.machines.swap_remove(pos), + }; + + // Unlock the schedule poll the reactor until new I/O events arrive. + sched.polling = true; + drop(sched); + rt.reactor.poll(None).unwrap(); + + // Lock the scheduler again and re-register the machine. + sched = rt.sched.lock().unwrap(); + sched.polling = false; + sched.machines.push(m); + sched.progress = true; + + runs = 0; + fails = 0; + } + + // When shutting down the thread, take the processor out if still available. + let opt_p = self.processor.lock().take(); + + // Return the processor to the scheduler and remove the machine. + if let Some(p) = opt_p { + let mut sched = rt.sched.lock().unwrap(); + sched.processors.push(p); + sched.machines.retain(|elem| !ptr::eq(&**elem, self)); + } + } +} + +struct Processor { + /// The local task queue. + worker: Worker, + + /// Contains the next task to run as an optimization that skips the queue. + slot: Option, +} + +impl Processor { + /// Creates a new processor. + fn new() -> Processor { + Processor { + worker: Worker::new_fifo(), + slot: None, + } + } + + /// Schedules a task to run on this processor. + fn schedule(&mut self, rt: &Runtime, task: Runnable) { + match self.slot.replace(task) { + None => {} + Some(task) => { + self.worker.push(task); + rt.notify(); + } + } + } + + /// Flushes a task from the slot into the local queue. + fn flush_slot(&mut self, rt: &Runtime) { + if let Some(task) = self.slot.take() { + self.worker.push(task); + rt.notify(); + } + } + + /// Pops a task from this processor. + fn pop_task(&mut self) -> Option { + self.slot.take().or_else(|| self.worker.pop()) + } + + /// Steals a task from the global queue. + fn steal_from_global(&mut self, rt: &Runtime) -> Steal { + rt.injector.steal_batch_and_pop(&self.worker) + } + + /// Steals a task from other processors. + fn steal_from_others(&mut self, rt: &Runtime) -> Steal { + // Pick a random starting point in the list of queues. + let len = rt.stealers.len(); + let start = random(len as u32) as usize; + + // Create an iterator over stealers that starts from the chosen point. + let (l, r) = rt.stealers.split_at(start); + let stealers = r.iter().chain(l.iter()); + + // Try stealing a batch of tasks from each queue. + stealers + .map(|s| s.steal_batch_and_pop(&self.worker)) + .collect() + } +} diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 80259c579..4bade5bd3 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -3,11 +3,9 @@ use std::future::Future; use std::mem::{self, ManuallyDrop}; use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; -use std::thread; use crossbeam_utils::sync::Parker; use kv_log_macro::trace; -use log::log_enabled; use crate::task::{Context, Poll, Task, Waker}; @@ -42,12 +40,10 @@ where let task = Task::new(None); // Log this `block_on` operation. - if log_enabled!(log::Level::Trace) { - trace!("block_on", { - task_id: task.id().0, - parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), - }); - } + trace!("block_on", { + task_id: task.id().0, + parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), + }); let future = async move { // Drop task-locals on exit. @@ -57,13 +53,9 @@ where // Log completion on exit. defer! { - if log_enabled!(log::Level::Trace) { - Task::get_current(|t| { - trace!("completed", { - task_id: t.id().0, - }); - }); - } + trace!("completed", { + task_id: Task::get_current(|t| t.id().0), + }); } future.await @@ -125,7 +117,6 @@ where let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &VTABLE))) }; let cx = &mut Context::from_waker(&waker); - let mut step = 0; loop { if let Poll::Ready(t) = future.as_mut().poll(cx) { // Save the parker for the next invocation of `block`. @@ -133,14 +124,7 @@ where return t; } - // Yield a few times or park the current thread. - if step < 3 { - thread::yield_now(); - step += 1; - } else { - arc_parker.park(); - step = 0; - } + arc_parker.park(); } }) } diff --git a/src/task/builder.rs b/src/task/builder.rs index afd4c2c1c..f1fef59e8 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,9 +1,9 @@ -use kv_log_macro::trace; -use log::log_enabled; use std::future::Future; +use kv_log_macro::trace; + use crate::io; -use crate::task::executor; +use crate::rt::RUNTIME; use crate::task::{JoinHandle, Task}; use crate::utils::abort_on_panic; @@ -37,12 +37,10 @@ impl Builder { let task = Task::new(self.name); // Log this `spawn` operation. - if log_enabled!(log::Level::Trace) { - trace!("spawn", { - task_id: task.id().0, - parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), - }); - } + trace!("spawn", { + task_id: task.id().0, + parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), + }); let future = async move { // Drop task-locals on exit. @@ -52,19 +50,15 @@ impl Builder { // Log completion on exit. defer! { - if log_enabled!(log::Level::Trace) { - Task::get_current(|t| { - trace!("completed", { - task_id: t.id().0, - }); - }); - } + trace!("completed", { + task_id: Task::get_current(|t| t.id().0), + }); } future.await }; - let schedule = move |t| executor::schedule(Runnable(t)); + let schedule = move |t| RUNTIME.schedule(Runnable(t)); let (task, handle) = async_task::spawn(future, schedule, task); task.schedule(); Ok(JoinHandle::new(handle)) @@ -72,7 +66,7 @@ impl Builder { } /// A runnable task. -pub(crate) struct Runnable(async_task::Task); +pub struct Runnable(async_task::Task); impl Runnable { /// Runs the task by polling its future once. diff --git a/src/task/executor/mod.rs b/src/task/executor/mod.rs deleted file mode 100644 index 2a6a696e1..000000000 --- a/src/task/executor/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Task executor. -//! -//! API bindings between `crate::task` and this module are very simple: -//! -//! * The only export is the `schedule` function. -//! * The only import is the `crate::task::Runnable` type. - -pub(crate) use pool::schedule; - -use sleepers::Sleepers; - -mod pool; -mod sleepers; diff --git a/src/task/executor/pool.rs b/src/task/executor/pool.rs deleted file mode 100644 index 5249b3d93..000000000 --- a/src/task/executor/pool.rs +++ /dev/null @@ -1,179 +0,0 @@ -use std::cell::Cell; -use std::iter; -use std::thread; -use std::time::Duration; - -use crossbeam_deque::{Injector, Stealer, Worker}; -use once_cell::sync::Lazy; -use once_cell::unsync::OnceCell; - -use crate::task::executor::Sleepers; -use crate::task::Runnable; -use crate::utils::{abort_on_panic, random}; - -/// The state of an executor. -struct Pool { - /// The global queue of tasks. - injector: Injector, - - /// Handles to local queues for stealing work from worker threads. - stealers: Vec>, - - /// Used for putting idle workers to sleep and notifying them when new tasks come in. - sleepers: Sleepers, -} - -/// Global executor that runs spawned tasks. -static POOL: Lazy = Lazy::new(|| { - let num_threads = num_cpus::get().max(1); - let mut stealers = Vec::new(); - - // Spawn worker threads. - for _ in 0..num_threads { - let worker = Worker::new_fifo(); - stealers.push(worker.stealer()); - - let proc = Processor { - worker, - slot: Cell::new(None), - slot_runs: Cell::new(0), - }; - - thread::Builder::new() - .name("async-std/executor".to_string()) - .spawn(|| { - let _ = PROCESSOR.with(|p| p.set(proc)); - abort_on_panic(main_loop); - }) - .expect("cannot start a thread driving tasks"); - } - - Pool { - injector: Injector::new(), - stealers, - sleepers: Sleepers::new(), - } -}); - -/// The state of a worker thread. -struct Processor { - /// The local task queue. - worker: Worker, - - /// Contains the next task to run as an optimization that skips queues. - slot: Cell>, - - /// How many times in a row tasks have been taked from the slot rather than the queue. - slot_runs: Cell, -} - -thread_local! { - /// Worker thread state. - static PROCESSOR: OnceCell = OnceCell::new(); -} - -/// Schedules a new runnable task for execution. -pub(crate) fn schedule(task: Runnable) { - PROCESSOR.with(|proc| { - // If the current thread is a worker thread, store it into its task slot or push it into - // its local task queue. Otherwise, push it into the global task queue. - match proc.get() { - Some(proc) => { - // Replace the task in the slot. - if let Some(task) = proc.slot.replace(Some(task)) { - // If the slot already contained a task, push it into the local task queue. - proc.worker.push(task); - POOL.sleepers.notify_one(); - } - } - None => { - POOL.injector.push(task); - POOL.sleepers.notify_one(); - } - } - }) -} - -/// Main loop running a worker thread. -fn main_loop() { - /// Number of yields when no runnable task is found. - const YIELDS: u32 = 3; - /// Number of short sleeps when no runnable task in found. - const SLEEPS: u32 = 1; - - // The number of times the thread didn't find work in a row. - let mut fails = 0; - - loop { - // Try to find a runnable task. - match find_runnable() { - Some(task) => { - fails = 0; - - // Run the found task. - task.run(); - } - None => { - fails += 1; - - // Yield the current thread or put it to sleep. - if fails <= YIELDS { - thread::yield_now(); - } else if fails <= YIELDS + SLEEPS { - thread::sleep(Duration::from_micros(10)); - } else { - POOL.sleepers.wait(); - fails = 0; - } - } - } - } -} - -/// Find the next runnable task. -fn find_runnable() -> Option { - /// Maximum number of times the slot can be used in a row. - const SLOT_LIMIT: u32 = 16; - - PROCESSOR.with(|proc| { - let proc = proc.get().unwrap(); - - // Try taking a task from the slot. - let runs = proc.slot_runs.get(); - if runs < SLOT_LIMIT { - if let Some(task) = proc.slot.take() { - proc.slot_runs.set(runs + 1); - return Some(task); - } - } - proc.slot_runs.set(0); - - // Pop a task from the local queue, if not empty. - proc.worker.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - iter::repeat_with(|| { - // Try stealing a batch of tasks from the global queue. - POOL.injector - .steal_batch_and_pop(&proc.worker) - // Or try stealing a batch of tasks from one of the other threads. - .or_else(|| { - // First, pick a random starting point in the list of local queues. - let len = POOL.stealers.len(); - let start = random(len as u32) as usize; - - // Try stealing a batch of tasks from each local queue starting from the - // chosen point. - let (l, r) = POOL.stealers.split_at(start); - let stealers = r.iter().chain(l.iter()); - stealers - .map(|s| s.steal_batch_and_pop(&proc.worker)) - .collect() - }) - }) - // Loop while no task was stolen and any steal operation needs to be retried. - .find(|s| !s.is_retry()) - // Extract the stolen task, if there is one. - .and_then(|s| s.success()) - }) - }) -} diff --git a/src/task/executor/sleepers.rs b/src/task/executor/sleepers.rs deleted file mode 100644 index 4e7012957..000000000 --- a/src/task/executor/sleepers.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Condvar, Mutex}; - -/// The place where worker threads go to sleep. -/// -/// Similar to how thread parking works, if a notification comes up while no threads are sleeping, -/// the next thread that attempts to go to sleep will pick up the notification immediately. -pub struct Sleepers { - /// How many threads are currently a sleep. - sleep: Mutex, - - /// A condvar for notifying sleeping threads. - wake: Condvar, - - /// Set to `true` if a notification came up while nobody was sleeping. - notified: AtomicBool, -} - -impl Sleepers { - /// Creates a new `Sleepers`. - pub fn new() -> Sleepers { - Sleepers { - sleep: Mutex::new(0), - wake: Condvar::new(), - notified: AtomicBool::new(false), - } - } - - /// Puts the current thread to sleep. - pub fn wait(&self) { - let mut sleep = self.sleep.lock().unwrap(); - - if !self.notified.swap(false, Ordering::SeqCst) { - *sleep += 1; - let _ = self.wake.wait(sleep).unwrap(); - } - } - - /// Notifies one thread. - pub fn notify_one(&self) { - if !self.notified.load(Ordering::SeqCst) { - let mut sleep = self.sleep.lock().unwrap(); - - if *sleep > 0 { - *sleep -= 1; - self.wake.notify_one(); - } else { - self.notified.store(true, Ordering::SeqCst); - } - } - } -} diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 9fefff2e6..d929d11fb 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -14,9 +14,6 @@ use crate::task::{Context, Poll, Task}; #[derive(Debug)] pub struct JoinHandle(async_task::JoinHandle); -unsafe impl Send for JoinHandle {} -unsafe impl Sync for JoinHandle {} - impl JoinHandle { /// Creates a new `JoinHandle`. pub(crate) fn new(inner: async_task::JoinHandle) -> JoinHandle { diff --git a/src/task/mod.rs b/src/task/mod.rs index 8e181d135..075e71dd8 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -140,13 +140,12 @@ cfg_default! { pub use spawn::spawn; pub use task_local::{AccessError, LocalKey}; - use builder::Runnable; - use task_local::LocalsMap; + pub(crate) use builder::Runnable; + pub(crate) use task_local::LocalsMap; mod block_on; mod builder; mod current; - mod executor; mod join_handle; mod sleep; mod spawn; diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 578afa4e3..e22c5cb46 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,12 +1,4 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; -use std::time::Duration; - -use crossbeam_channel::{unbounded, Receiver, Sender}; -use once_cell::sync::Lazy; - -use crate::task::{JoinHandle, Task}; -use crate::utils::abort_on_panic; +use crate::task::{spawn, JoinHandle}; /// Spawns a blocking task. /// @@ -31,7 +23,8 @@ use crate::utils::abort_on_panic; /// /// task::spawn_blocking(|| { /// println!("long-running task here"); -/// }).await; +/// }) +/// .await; /// # /// # }) /// ``` @@ -42,80 +35,5 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - let schedule = |task| POOL.sender.send(task).unwrap(); - let (task, handle) = async_task::spawn(async { f() }, schedule, Task::new(None)); - task.schedule(); - JoinHandle::new(handle) -} - -type Runnable = async_task::Task; - -/// The number of sleeping worker threads. -static SLEEPING: AtomicUsize = AtomicUsize::new(0); - -struct Pool { - sender: Sender, - receiver: Receiver, -} - -static POOL: Lazy = Lazy::new(|| { - // Start a single worker thread waiting for the first task. - start_thread(); - - let (sender, receiver) = unbounded(); - Pool { sender, receiver } -}); - -fn start_thread() { - SLEEPING.fetch_add(1, Ordering::SeqCst); - let timeout = Duration::from_secs(1); - - thread::Builder::new() - .name("async-std/blocking".to_string()) - .spawn(move || { - loop { - let mut task = match POOL.receiver.recv_timeout(timeout) { - Ok(task) => task, - Err(_) => { - // Check whether this is the last sleeping thread. - if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { - // If so, then restart the thread to make sure there is always at least - // one sleeping thread. - if SLEEPING.compare_and_swap(0, 1, Ordering::SeqCst) == 0 { - continue; - } - } - - // Stop the thread. - return; - } - }; - - // If there are no sleeping threads, then start one to make sure there is always at - // least one sleeping thread. - if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { - start_thread(); - } - - loop { - // Run the task. - abort_on_panic(|| task.run()); - - // Try taking another task if there are any available. - task = match POOL.receiver.try_recv() { - Ok(task) => task, - Err(_) => break, - }; - } - - // If there is at least one sleeping thread, stop this thread instead of putting it - // to sleep. - if SLEEPING.load(Ordering::SeqCst) > 0 { - return; - } - - SLEEPING.fetch_add(1, Ordering::SeqCst); - } - }) - .expect("cannot start a blocking thread"); + spawn(async { f() }) } diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 403069663..c7ddb175c 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -1,6 +1,7 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; +use crate::rt::RUNTIME; use crate::task::{Context, Poll}; /// Cooperatively gives up a timeslice to the task scheduler. @@ -43,6 +44,7 @@ impl Future for YieldNow { if !self.0 { self.0 = true; cx.waker().wake_by_ref(); + RUNTIME.yield_now(); Poll::Pending } else { Poll::Ready(()) diff --git a/src/utils.rs b/src/utils.rs index 7d253b49c..79c2fdf58 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,9 @@ +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, Ordering}; + +use crossbeam_utils::Backoff; + /// Calls a function and aborts if it panics. /// /// This is useful in unsafe code where we can't recover from panics. @@ -52,6 +58,71 @@ pub fn random(n: u32) -> u32 { }) } +/// A simple spinlock. +pub struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, +} + +unsafe impl Send for Spinlock {} +unsafe impl Sync for Spinlock {} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + pub const fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + pub fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } + + /// Attempts to lock the spinlock. + pub fn try_lock(&self) -> Option> { + if self.flag.swap(true, Ordering::Acquire) { + None + } else { + Some(SpinlockGuard { parent: self }) + } + } +} + +/// A guard holding a spinlock locked. +pub struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +unsafe impl Send for SpinlockGuard<'_, T> {} +unsafe impl Sync for SpinlockGuard<'_, T> {} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.flag.store(false, Ordering::Release); + } +} + +impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } +} + +impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } +} + /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; From ceba324bef9641d61239ed68a2f6671f59fa2831 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 16 Dec 2019 15:53:31 +0100 Subject: [PATCH 002/272] Fix feature flags --- src/lib.rs | 3 +- src/task/yield_now.rs | 6 +- src/utils.rs | 144 +++++++++++++++++++++--------------------- 3 files changed, 78 insertions(+), 75 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 070df8851..40ba15ab4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,14 +246,13 @@ cfg_std! { pub mod stream; pub mod sync; pub mod task; - - pub(crate) mod rt; } cfg_default! { pub mod fs; pub mod path; pub mod net; + pub(crate) mod rt; } cfg_unstable! { diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index c7ddb175c..bdb08eb62 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -1,7 +1,6 @@ use std::future::Future; use std::pin::Pin; -use crate::rt::RUNTIME; use crate::task::{Context, Poll}; /// Cooperatively gives up a timeslice to the task scheduler. @@ -44,7 +43,10 @@ impl Future for YieldNow { if !self.0 { self.0 = true; cx.waker().wake_by_ref(); - RUNTIME.yield_now(); + + #[cfg(feature = "default")] + crate::rt::RUNTIME.yield_now(); + Poll::Pending } else { Poll::Ready(()) diff --git a/src/utils.rs b/src/utils.rs index 79c2fdf58..c8984ba86 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,3 @@ -use std::cell::UnsafeCell; -use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, Ordering}; - -use crossbeam_utils::Backoff; - /// Calls a function and aborts if it panics. /// /// This is useful in unsafe code where we can't recover from panics. @@ -58,71 +52,6 @@ pub fn random(n: u32) -> u32 { }) } -/// A simple spinlock. -pub struct Spinlock { - flag: AtomicBool, - value: UnsafeCell, -} - -unsafe impl Send for Spinlock {} -unsafe impl Sync for Spinlock {} - -impl Spinlock { - /// Returns a new spinlock initialized with `value`. - pub const fn new(value: T) -> Spinlock { - Spinlock { - flag: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Locks the spinlock. - pub fn lock(&self) -> SpinlockGuard<'_, T> { - let backoff = Backoff::new(); - while self.flag.swap(true, Ordering::Acquire) { - backoff.snooze(); - } - SpinlockGuard { parent: self } - } - - /// Attempts to lock the spinlock. - pub fn try_lock(&self) -> Option> { - if self.flag.swap(true, Ordering::Acquire) { - None - } else { - Some(SpinlockGuard { parent: self }) - } - } -} - -/// A guard holding a spinlock locked. -pub struct SpinlockGuard<'a, T> { - parent: &'a Spinlock, -} - -unsafe impl Send for SpinlockGuard<'_, T> {} -unsafe impl Sync for SpinlockGuard<'_, T> {} - -impl<'a, T> Drop for SpinlockGuard<'a, T> { - fn drop(&mut self) { - self.parent.flag.store(false, Ordering::Release); - } -} - -impl<'a, T> Deref for SpinlockGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.parent.value.get() } - } -} - -impl<'a, T> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.parent.value.get() } - } -} - /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; @@ -338,3 +267,76 @@ macro_rules! extension_trait { extension_trait!($($tail)*); }; } + +cfg_default! { + use std::cell::UnsafeCell; + use std::ops::{Deref, DerefMut}; + use std::sync::atomic::{AtomicBool, Ordering}; + + use crossbeam_utils::Backoff; + + /// A simple spinlock. + pub struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, + } + + unsafe impl Send for Spinlock {} + unsafe impl Sync for Spinlock {} + + impl Spinlock { + /// Returns a new spinlock initialized with `value`. + pub const fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + pub fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } + + /// Attempts to lock the spinlock. + pub fn try_lock(&self) -> Option> { + if self.flag.swap(true, Ordering::Acquire) { + None + } else { + Some(SpinlockGuard { parent: self }) + } + } + } + + /// A guard holding a spinlock locked. + pub struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, + } + + unsafe impl Send for SpinlockGuard<'_, T> {} + unsafe impl Sync for SpinlockGuard<'_, T> {} + + impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.flag.store(false, Ordering::Release); + } + } + + impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } + } + + impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } + } +} From 303ac90b7c3b22dbc5d04395d485d893ddd58d6c Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 7 Feb 2020 22:09:42 +0300 Subject: [PATCH 003/272] Fixed `flat_map` --- src/stream/stream/flat_map.rs | 17 ++++++--- tests/stream.rs | 70 +++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index 6c828c920..8d5a12f33 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -51,14 +51,21 @@ where let mut this = self.project(); loop { if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { - if let item @ Some(_) = futures_core::ready!(inner.poll_next(cx)) { - return Poll::Ready(item); + let next_item = futures_core::ready!(inner.poll_next(cx)); + + if next_item.is_some() { + return Poll::Ready(next_item); + } else { + this.inner_stream.set(None); } } - match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { - None => return Poll::Ready(None), - Some(inner) => this.inner_stream.set(Some(inner.into_stream())), + let inner = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + if inner.is_some() { + this.inner_stream.set(inner.map(IntoStream::into_stream)); + } else { + return Poll::Ready(None); } } } diff --git a/tests/stream.rs b/tests/stream.rs index 42a6191fd..75c1b10c4 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -98,3 +98,73 @@ fn merge_works_with_unfused_streams() { }); assert_eq!(xs, vec![92, 92]); } + +#[test] +fn flat_map_doesnt_poll_completed_inner_stream() { + async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::task::*; + use std::convert::identity; + use std::marker::Unpin; + use std::pin::Pin; + + struct S(T); + + impl Stream for S { + type Item = T::Item; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + unsafe { Pin::new_unchecked(&mut self.0) }.poll_next(ctx) + } + } + + struct StrictOnce { + polled: bool, + }; + + impl Stream for StrictOnce { + type Item = (); + + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + if !self.polled { + self.polled = true; + Poll::Ready(None) + } else { + panic!("Polled after completion!"); + } + } + } + + struct Interchanger { + polled: bool, + }; + + impl Stream for Interchanger { + type Item = S + Unpin>>; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if self.polled { + let waker = ctx.waker().clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(10)); + waker.wake_by_ref(); + }); + self.polled = false; + Poll::Pending + } else { + self.polled = true; + Poll::Ready(Some(S(Box::new(StrictOnce { polled: false })))) + } + } + } + + assert_eq!( + Interchanger { polled: false } + .take(2) + .flat_map(identity) + .count() + .await, + 0 + ); + }); +} From c80915e216de0c8820a8d58efda04b98b07a38c3 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 7 Feb 2020 22:22:38 +0300 Subject: [PATCH 004/272] Dont spawn thread in tests --- tests/stream.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/stream.rs b/tests/stream.rs index 75c1b10c4..fec146655 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -144,12 +144,8 @@ fn flat_map_doesnt_poll_completed_inner_stream() { fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { if self.polled { - let waker = ctx.waker().clone(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_millis(10)); - waker.wake_by_ref(); - }); self.polled = false; + ctx.waker().wake_by_ref(); Poll::Pending } else { self.polled = true; From b68be72763931033dc2917838f7e0e84349e401e Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 7 Feb 2020 22:42:59 +0300 Subject: [PATCH 005/272] Use `assert` instead of `panic` --- tests/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stream.rs b/tests/stream.rs index fec146655..58a441cdf 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -130,7 +130,7 @@ fn flat_map_doesnt_poll_completed_inner_stream() { self.polled = true; Poll::Ready(None) } else { - panic!("Polled after completion!"); + assert!(false, "Polled after completion!"); } } } From 85c32ef9d2fc661d1f951a5297a65298bd1842ce Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 7 Feb 2020 22:45:15 +0300 Subject: [PATCH 006/272] Use `assert` without `if`-clause --- tests/stream.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/stream.rs b/tests/stream.rs index 58a441cdf..e80fc99d5 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -126,12 +126,9 @@ fn flat_map_doesnt_poll_completed_inner_stream() { type Item = (); fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { - if !self.polled { - self.polled = true; - Poll::Ready(None) - } else { - assert!(false, "Polled after completion!"); - } + assert!(!self.polled, "Polled after completion!"); + self.polled = true; + Poll::Ready(None) } } From 32068942a6130d12a7152706ae6e24637377339d Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sat, 8 Feb 2020 15:41:33 +0300 Subject: [PATCH 007/272] Fixed `flatten` --- src/stream/stream/flat_map.rs | 2 +- src/stream/stream/flatten.rs | 21 +++++--- tests/stream.rs | 96 +++++++++++++++++++---------------- 3 files changed, 68 insertions(+), 51 deletions(-) diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index 8d5a12f33..f9ceb86af 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -69,4 +69,4 @@ where } } } -} +} \ No newline at end of file diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 1d6fcae6a..d0e0d20df 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,5 +1,5 @@ -use std::fmt; -use std::pin::Pin; +use core::fmt; +use core::pin::Pin; use pin_project_lite::pin_project; @@ -52,14 +52,21 @@ where let mut this = self.project(); loop { if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { - if let item @ Some(_) = futures_core::ready!(inner.poll_next(cx)) { - return Poll::Ready(item); + let next_item = futures_core::ready!(inner.poll_next(cx)); + + if next_item.is_some() { + return Poll::Ready(next_item); + } else { + this.inner_stream.set(None); } } - match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { - None => return Poll::Ready(None), - Some(inner) => this.inner_stream.set(Some(inner.into_stream())), + let inner = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + if inner.is_some() { + this.inner_stream.set(inner.map(IntoStream::into_stream)); + } else { + return Poll::Ready(None); } } } diff --git a/tests/stream.rs b/tests/stream.rs index e80fc99d5..210ceae3c 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -1,3 +1,5 @@ +use std::convert::identity; +use std::marker::Unpin; use std::pin::Pin; use std::task::{Context, Poll}; @@ -99,58 +101,52 @@ fn merge_works_with_unfused_streams() { assert_eq!(xs, vec![92, 92]); } -#[test] -fn flat_map_doesnt_poll_completed_inner_stream() { - async_std::task::block_on(async { - use async_std::prelude::*; - use async_std::task::*; - use std::convert::identity; - use std::marker::Unpin; - use std::pin::Pin; +struct S(T); - struct S(T); +impl Stream for S { + type Item = T::Item; - impl Stream for S { - type Item = T::Item; + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + unsafe { Pin::new_unchecked(&mut self.0) }.poll_next(ctx) + } +} - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { - unsafe { Pin::new_unchecked(&mut self.0) }.poll_next(ctx) - } - } +struct StrictOnce { + polled: bool, +} - struct StrictOnce { - polled: bool, - }; +impl Stream for StrictOnce { + type Item = (); - impl Stream for StrictOnce { - type Item = (); + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + assert!(!self.polled, "Polled after completion!"); + self.polled = true; + Poll::Ready(None) + } +} - fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { - assert!(!self.polled, "Polled after completion!"); - self.polled = true; - Poll::Ready(None) - } - } +struct Interchanger { + polled: bool, +} - struct Interchanger { - polled: bool, - }; - - impl Stream for Interchanger { - type Item = S + Unpin>>; - - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { - if self.polled { - self.polled = false; - ctx.waker().wake_by_ref(); - Poll::Pending - } else { - self.polled = true; - Poll::Ready(Some(S(Box::new(StrictOnce { polled: false })))) - } - } +impl Stream for Interchanger { + type Item = S + Unpin>>; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if self.polled { + self.polled = false; + ctx.waker().wake_by_ref(); + Poll::Pending + } else { + self.polled = true; + Poll::Ready(Some(S(Box::new(StrictOnce { polled: false })))) } + } +} +#[test] +fn flat_map_doesnt_poll_completed_inner_stream() { + task::block_on(async { assert_eq!( Interchanger { polled: false } .take(2) @@ -161,3 +157,17 @@ fn flat_map_doesnt_poll_completed_inner_stream() { ); }); } + +#[test] +fn flatten_doesnt_poll_completed_inner_stream() { + task::block_on(async { + assert_eq!( + Interchanger { polled: false } + .take(2) + .flatten() + .count() + .await, + 0 + ); + }); +} From d7cab38b674109dae5804ee397a0cedd52bb467f Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sat, 8 Feb 2020 15:49:01 +0300 Subject: [PATCH 008/272] `core` => `std` --- src/stream/stream/flatten.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index d0e0d20df..13975f7bb 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,5 +1,5 @@ -use core::fmt; -use core::pin::Pin; +use std::fmt; +use std::pin::Pin; use pin_project_lite::pin_project; From 68063adddfc73ad9bd329610d4bd8cf258d11857 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sat, 8 Feb 2020 16:22:02 +0300 Subject: [PATCH 009/272] Add link to tests --- tests/stream.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/stream.rs b/tests/stream.rs index 210ceae3c..fdfa23cd8 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -144,6 +144,7 @@ impl Stream for Interchanger { } } +// https://github.com/async-rs/async-std/pull/701 #[test] fn flat_map_doesnt_poll_completed_inner_stream() { task::block_on(async { @@ -158,6 +159,7 @@ fn flat_map_doesnt_poll_completed_inner_stream() { }); } +// https://github.com/async-rs/async-std/pull/701 #[test] fn flatten_doesnt_poll_completed_inner_stream() { task::block_on(async { From aae835cc146c3eac301647ee170cc53dbeb913da Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Wed, 12 Feb 2020 01:38:20 +0100 Subject: [PATCH 010/272] channel/recv: improving function docs and code example At the moment it's not clear when and why recv returns Option, instead of just T. This changed comment makes it clear that None will only be returned once no data will ever be sent again (i.e. after all senders are gone). --- src/sync/channel.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 2647f6502..b42326b32 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -346,8 +346,9 @@ pub struct Receiver { impl Receiver { /// Receives a message from the channel. /// - /// If the channel is empty and still has senders, this method will wait until a message is - /// sent into the channel or until all senders get dropped. + /// If the channel is emtpy and still has senders, this method + /// will wait until a message is sent into it. Once all senders + /// have been dropped it will return `None`. /// /// # Examples /// @@ -362,10 +363,13 @@ impl Receiver { /// task::spawn(async move { /// s.send(1).await; /// s.send(2).await; + /// // Then we drop the sender /// }); /// /// assert_eq!(r.recv().await, Some(1)); /// assert_eq!(r.recv().await, Some(2)); + /// + /// // recv() returns `None` /// assert_eq!(r.recv().await, None); /// # /// # }) From 23b7c174f36f6632b0c29302f79094a23db325ec Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 3 Mar 2020 22:46:18 +0900 Subject: [PATCH 011/272] feat: Stabilize io::Std*Lock --- src/io/mod.rs | 12 +++--------- src/io/stderr.rs | 14 ++------------ src/io/stdin.rs | 14 ++------------ src/io/stdout.rs | 14 ++------------ 4 files changed, 9 insertions(+), 45 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 51c473d02..3734c8422 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -309,9 +309,9 @@ cfg_default! { #[doc(hidden)] pub use stdio::{_eprint, _print}; - pub use stderr::{stderr, Stderr}; - pub use stdin::{stdin, Stdin}; - pub use stdout::{stdout, Stdout}; + pub use stderr::{stderr, Stderr, StderrLock}; + pub use stdin::{stdin, Stdin, StdinLock}; + pub use stdout::{stdout, Stdout, StdoutLock}; pub use timeout::timeout; mod timeout; @@ -320,9 +320,3 @@ cfg_default! { mod stdio; mod stdout; } - -cfg_unstable_default! { - pub use stderr::StderrLock; - pub use stdin::StdinLock; - pub use stdout::StdoutLock; -} diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5ff8a029d..fc5f4a002 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,14 +1,12 @@ use std::pin::Pin; use std::sync::Mutex; use std::future::Future; +use std::io::Write as _; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Write as _; -} +use once_cell::sync::Lazy; /// Constructs a new handle to the standard error of the current process. /// @@ -65,13 +63,9 @@ pub struct Stderr(Mutex); /// /// [`Write`]: trait.Read.html /// [`Stderr::lock`]: struct.Stderr.html#method.lock -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct StderrLock<'a>(std::io::StderrLock<'a>); -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] unsafe impl Send for StderrLock<'_> {} /// The state of the asynchronous stderr. @@ -128,8 +122,6 @@ impl Stderr { /// # /// # Ok(()) }) } /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] pub async fn lock(&self) -> StderrLock<'static> { static STDERR: Lazy = Lazy::new(std::io::stderr); @@ -240,8 +232,6 @@ cfg_windows! { } } -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] impl io::Write for StderrLock<'_> { fn poll_write( mut self: Pin<&mut Self>, diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 369ccae4c..b299d09fc 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,16 +1,14 @@ use std::future::Future; use std::pin::Pin; use std::sync::Mutex; +use std::io::Read as _; use crate::future; use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::utils::Context as _; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Read as _; -} +use once_cell::sync::Lazy; /// Constructs a new handle to the standard input of the current process. /// @@ -67,13 +65,9 @@ pub struct Stdin(Mutex); /// /// [`Read`]: trait.Read.html /// [`Stdin::lock`]: struct.Stdin.html#method.lock -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] #[derive(Debug)] pub struct StdinLock<'a>(std::io::StdinLock<'a>); -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] unsafe impl Send for StdinLock<'_> {} /// The state of the asynchronous stdin. @@ -187,8 +181,6 @@ impl Stdin { /// # /// # Ok(()) }) } /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] pub async fn lock(&self) -> StdinLock<'static> { static STDIN: Lazy = Lazy::new(std::io::stdin); @@ -266,8 +258,6 @@ cfg_windows! { } } -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] impl Read for StdinLock<'_> { fn poll_read( mut self: Pin<&mut Self>, diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 1711c090e..d98ef1359 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,14 +1,12 @@ use std::pin::Pin; use std::sync::Mutex; use std::future::Future; +use std::io::Write as _; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Write as _; -} +use once_cell::sync::Lazy; /// Constructs a new handle to the standard output of the current process. /// @@ -65,13 +63,9 @@ pub struct Stdout(Mutex); /// /// [`Write`]: trait.Read.html /// [`Stdout::lock`]: struct.Stdout.html#method.lock -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] unsafe impl Send for StdoutLock<'_> {} /// The state of the asynchronous stdout. @@ -128,8 +122,6 @@ impl Stdout { /// # /// # Ok(()) }) } /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] pub async fn lock(&self) -> StdoutLock<'static> { static STDOUT: Lazy = Lazy::new(std::io::stdout); @@ -240,8 +232,6 @@ cfg_windows! { } } -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] impl Write for StdoutLock<'_> { fn poll_write( mut self: Pin<&mut Self>, From be60dd9fe7081ad89ca592a783e3329d6626ecf7 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 3 Mar 2020 22:49:59 +0900 Subject: [PATCH 012/272] fix: Remove unnecessary re-export and macros --- src/future/into_future.rs | 3 +- src/io/mod.rs | 4 - src/lib.rs | 3 - src/macros.rs | 168 -------------------------------------- 4 files changed, 2 insertions(+), 176 deletions(-) diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 8e5e5e046..473ed4592 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -5,9 +5,10 @@ use std::future::Future; /// # Examples /// /// ``` +/// use std::pin::Pin; +/// /// use async_std::future::{Future, IntoFuture}; /// use async_std::io; -/// use async_std::pin::Pin; /// /// struct Client; /// diff --git a/src/io/mod.rs b/src/io/mod.rs index 3734c8422..c7c970c55 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -305,10 +305,6 @@ cfg_std! { } cfg_default! { - // For use in the print macros. - #[doc(hidden)] - pub use stdio::{_eprint, _print}; - pub use stderr::{stderr, Stderr, StderrLock}; pub use stdin::{stdin, Stdin, StdinLock}; pub use stdout::{stdout, Stdout, StdoutLock}; diff --git a/src/lib.rs b/src/lib.rs index d49879275..8cd0d3006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,9 +273,6 @@ cfg_default! { } cfg_unstable! { - pub mod pin; - pub mod process; - mod unit; mod vec; mod result; diff --git a/src/macros.rs b/src/macros.rs index 638a2348b..22cd00d73 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,171 +1,3 @@ -/// Prints to the standard output. -/// -/// Equivalent to the [`println!`] macro except that a newline is not printed at -/// the end of the message. -/// -/// Note that stdout is frequently line-buffered by default so it may be -/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted -/// immediately. -/// -/// Use `print!` only for the primary output of your program. Use -/// [`eprint!`] instead to print error and progress messages. -/// -/// [`println!`]: macro.println.html -/// [flush]: io/trait.Write.html#tymethod.flush -/// [`eprint!`]: macro.eprint.html -/// -/// # Panics -/// -/// Panics if writing to `io::stdout()` fails. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::io; -/// use async_std::prelude::*; -/// use async_std::print; -/// -/// print!("this ").await; -/// print!("will ").await; -/// print!("be ").await; -/// print!("on ").await; -/// print!("the ").await; -/// print!("same ").await; -/// print!("line ").await; -/// -/// io::stdout().flush().await.unwrap(); -/// -/// print!("this string has a newline, why not choose println! instead?\n").await; -/// -/// io::stdout().flush().await.unwrap(); -/// # -/// # }) -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) -} - -/// Prints to the standard output, with a newline. -/// -/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone -/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). -/// -/// Use the [`format!`] syntax to write data to the standard output. -/// See [`std::fmt`] for more information. -/// -/// Use `println!` only for the primary output of your program. Use -/// [`eprintln!`] instead to print error and progress messages. -/// -/// [`format!`]: macro.format.html -/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html -/// [`eprintln!`]: macro.eprintln.html -/// # Panics -/// -/// Panics if writing to `io::stdout` fails. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::println; -/// -/// println!().await; // prints just a newline -/// println!("hello there!").await; -/// println!("format {} arguments", "some").await; -/// # -/// # }) -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => (async { - $crate::io::_print(format_args!($($arg)*)).await; - $crate::io::_print(format_args!("\n")).await; - }) -} - -/// Prints to the standard error. -/// -/// Equivalent to the [`print!`] macro, except that output goes to -/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for -/// example usage. -/// -/// Use `eprint!` only for error and progress messages. Use `print!` -/// instead for the primary output of your program. -/// -/// [`io::stderr`]: io/struct.Stderr.html -/// [`print!`]: macro.print.html -/// -/// # Panics -/// -/// Panics if writing to `io::stderr` fails. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::eprint; -/// -/// eprint!("Error: Could not complete task").await; -/// # -/// # }) -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[macro_export] -macro_rules! eprint { - ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))) -} - -/// Prints to the standard error, with a newline. -/// -/// Equivalent to the [`println!`] macro, except that output goes to -/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for -/// example usage. -/// -/// Use `eprintln!` only for error and progress messages. Use `println!` -/// instead for the primary output of your program. -/// -/// [`io::stderr`]: io/struct.Stderr.html -/// [`println!`]: macro.println.html -/// -/// # Panics -/// -/// Panics if writing to `io::stderr` fails. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::eprintln; -/// -/// eprintln!("Error: Could not complete task").await; -/// # -/// # }) -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[macro_export] -macro_rules! eprintln { - () => (async { $crate::eprint!("\n").await; }); - ($($arg:tt)*) => ( - async { - $crate::io::_eprint(format_args!($($arg)*)).await; - $crate::io::_eprint(format_args!("\n")).await; - } - ); -} - /// Declares task-local values. /// /// The macro wraps any number of static declarations and makes them task-local. Attributes and From 75223905bd1a61a064ccf30eb0f1f2f403910a8f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 3 Mar 2020 23:12:09 +0900 Subject: [PATCH 013/272] fix: Stabilize stream most method --- src/stream/double_ended_stream/mod.rs | 2 - src/stream/exact_size_stream.rs | 2 - src/stream/extend.rs | 2 - src/stream/fused_stream.rs | 2 - src/stream/interval.rs | 4 -- src/stream/mod.rs | 36 +++++++++--------- src/stream/once.rs | 4 +- src/stream/product.rs | 12 +++--- src/stream/stream/count.rs | 2 - src/stream/stream/merge.rs | 2 - src/stream/stream/mod.rs | 54 ++++++++++----------------- src/stream/stream/timeout.rs | 2 - src/stream/stream/unzip.rs | 2 - src/stream/successors.rs | 4 -- src/stream/sum.rs | 2 - 15 files changed, 43 insertions(+), 89 deletions(-) diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index a177865b6..ed10fd216 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -22,8 +22,6 @@ use try_rfold::TryRFoldFuture; /// `Item`s from the back, as well as the front. /// /// [`Stream`]: trait.Stream.html -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait DoubleEndedStream: Stream { #[doc = r#" Attempts to receive the next item from the back of the stream. diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 8b6ba97da..28792c615 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -76,8 +76,6 @@ pub use crate::stream::Stream; /// # }); /// ``` #[allow(clippy::len_without_is_empty)] // ExactSizeIterator::is_empty is unstable -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait ExactSizeStream: Stream { /// Returns the exact number of times the stream will iterate. /// diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 702cbcac6..9f5400730 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -27,8 +27,6 @@ use crate::stream::IntoStream; /// # /// # }) /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. fn extend<'a, T: IntoStream + 'a>( diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs index e14ab5b1f..5d02f1d7a 100644 --- a/src/stream/fused_stream.rs +++ b/src/stream/fused_stream.rs @@ -14,8 +14,6 @@ use crate::stream::Stream; /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`Stream::fuse`]: trait.Stream.html#method.fuse /// [`Fuse`]: struct.Fuse.html -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait FusedStream: Stream {} impl FusedStream for &mut S {} diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 7a0c1740b..8dd6b5e29 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -41,8 +41,6 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn interval(dur: Duration) -> Interval { Interval { delay: Delay::new(dur), @@ -56,8 +54,6 @@ pub fn interval(dur: Duration) -> Interval { /// documentation for more. /// /// [`interval`]: fn.interval.html -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Interval { delay: Delay, diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 0bfd4e865..8984b2283 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -300,46 +300,46 @@ //! [`take`]: trait.Stream.html#method.take //! [`min`]: trait.Stream.html#method.min +pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; +pub use exact_size_stream::ExactSizeStream; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; +pub use fused_stream::FusedStream; +pub use interval::{interval, Interval}; pub use once::{once, Once}; +pub use pending::{pending, Pending}; +pub use product::Product; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; +pub use stream::Merge; pub use stream::*; +pub use successors::{successors, Successors}; +pub use sum::Sum; pub(crate) mod stream; +mod double_ended_stream; mod empty; +mod exact_size_stream; mod from_fn; mod from_iter; +mod fused_stream; +mod interval; mod once; +mod pending; +mod product; mod repeat; mod repeat_with; +mod successors; +mod sum; cfg_unstable! { - mod double_ended_stream; - mod exact_size_stream; - mod extend; mod from_stream; - mod fused_stream; - mod interval; mod into_stream; - mod pending; - mod product; - mod successors; - mod sum; + mod extend; - pub use double_ended_stream::DoubleEndedStream; - pub use exact_size_stream::ExactSizeStream; pub use extend::{extend, Extend}; pub use from_stream::FromStream; - pub use fused_stream::FusedStream; - pub use interval::{interval, Interval}; pub use into_stream::IntoStream; - pub use pending::{pending, Pending}; - pub use product::Product; - pub use stream::Merge; - pub use successors::{successors, Successors}; - pub use sum::Sum; } diff --git a/src/stream/once.rs b/src/stream/once.rs index b86f181d9..c21a1e664 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -5,7 +5,6 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[cfg(feature = "unstable")] use crate::stream::DoubleEndedStream; /// Creates a stream that yields a single item. @@ -50,8 +49,7 @@ impl Stream for Once { } } -#[cfg(feature = "unstable")] -impl DoubleEndedStream for Once { +impl DoubleEndedStream for Once { fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(self.project().value.take()) } diff --git a/src/stream/product.rs b/src/stream/product.rs index 15497e87c..44aef4e51 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,5 +1,5 @@ -use core::pin::Pin; use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; @@ -13,8 +13,6 @@ use crate::stream::Stream; /// [`product`]: trait.Product.html#tymethod.product /// [`FromStream`]: trait.FromStream.html /// [`Stream::product`]: trait.Stream.html#method.product -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. @@ -23,9 +21,9 @@ pub trait Product: Sized { S: Stream + 'a; } -use core::ops::Mul; -use core::num::Wrapping; use crate::stream::stream::StreamExt; +use core::num::Wrapping; +use core::ops::Mul; macro_rules! integer_product { (@impls $one: expr, $($a:ty)*) => ($( @@ -75,5 +73,5 @@ macro_rules! float_product { ); } -integer_product!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } -float_product!{ f32 f64 } +integer_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_product! { f32 f64 } diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 63e044977..ae6c78cc7 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -9,8 +9,6 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct CountFuture { #[pin] stream: S, diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 232097292..d1eea9d16 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -16,8 +16,6 @@ pin_project! { /// /// [`merge`]: trait.Stream.html#method.merge /// [`Stream`]: trait.Stream.html - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { #[pin] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d0cc718e4..da852694f 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -27,7 +27,9 @@ mod chain; mod cloned; mod cmp; mod copied; +mod count; mod cycle; +mod delay; mod enumerate; mod eq; mod filter; @@ -47,6 +49,7 @@ mod map; mod max; mod max_by; mod max_by_key; +mod merge; mod min; mod min_by; mod min_by_key; @@ -61,13 +64,17 @@ mod skip_while; mod step_by; mod take; mod take_while; +mod throttle; +mod timeout; mod try_fold; mod try_for_each; +mod unzip; mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; +use count::CountFuture; use cycle::Cycle; use enumerate::Enumerate; use eq::EqFuture; @@ -94,53 +101,48 @@ use partial_cmp::PartialCmpFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; +use unzip::UnzipFuture; pub use chain::Chain; pub use cloned::Cloned; pub use copied::Copied; +pub use delay::Delay; pub use filter::Filter; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; +pub use merge::Merge; pub use scan::Scan; pub use skip::Skip; pub use skip_while::SkipWhile; pub use step_by::StepBy; pub use take::Take; pub use take_while::TakeWhile; +pub use throttle::Throttle; +pub use timeout::{Timeout, TimeoutError}; pub use zip::Zip; use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; +use core::time::Duration; -cfg_unstable! { - use core::future::Future; - use core::pin::Pin; - use core::time::Duration; +use crate::stream::{Product, Sum}; +cfg_unstable! { + use crate::stream::FromStream; use crate::stream::into_stream::IntoStream; - use crate::stream::{FromStream, Product, Sum}; use crate::stream::Extend; - use count::CountFuture; use partition::PartitionFuture; - use unzip::UnzipFuture; - pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; - pub use timeout::{TimeoutError, Timeout}; - pub use throttle::Throttle; - pub use delay::Delay; - mod count; - mod merge; + mod flatten; mod flat_map; mod partition; - mod timeout; - mod throttle; - mod delay; - mod unzip; } extension_trait! { @@ -355,8 +357,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn throttle(self, d: Duration) -> Throttle where Self: Sized, @@ -598,8 +598,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn delay(self, dur: std::time::Duration) -> Delay where Self: Sized, @@ -1511,8 +1509,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn by_ref(&mut self) -> &mut Self { self } @@ -1656,8 +1652,6 @@ extension_trait! { # Ok(()) }) } ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn timeout(self, dur: Duration) -> Timeout where Self: Stream + Sized, @@ -1822,8 +1816,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn unzip(self) -> impl Future [UnzipFuture] where FromA: Default + Extend, @@ -1921,8 +1913,6 @@ extension_trait! { # }); ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn merge(self, other: U) -> Merge where Self: Sized, @@ -2068,8 +2058,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn count(self) -> impl Future [CountFuture] where Self: Sized, @@ -2330,8 +2318,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn sum<'a, S>( self, ) -> impl Future + 'a [Pin + 'a>>] @@ -2376,8 +2362,6 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn product<'a, P>( self, ) -> impl Future + 'a [Pin + 'a>>] diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index ce658c83a..411be7e6c 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -47,8 +47,6 @@ impl Stream for Timeout { } /// An error returned when a stream times out. -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { _private: (), diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index 7771509a5..94cbc0a0c 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -8,8 +8,6 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Clone, Debug)] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct UnzipFuture { #[pin] stream: S, diff --git a/src/stream/successors.rs b/src/stream/successors.rs index a9ce40ffe..fb450c66c 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -27,8 +27,6 @@ use pin_project_lite::pin_project; /// # /// # }) } /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Option, @@ -43,8 +41,6 @@ pin_project! { /// This stream is constructed by [`successors`] function /// /// [`successors`]: fn.succssors.html - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Successors where diff --git a/src/stream/sum.rs b/src/stream/sum.rs index 3b3144e5e..eee41bd42 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -13,8 +13,6 @@ use crate::stream::Stream; /// [`sum`]: trait.Sum.html#tymethod.sum /// [`FromStream`]: trait.FromStream.html /// [`Stream::sum`]: trait.Stream.html#method.sum -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. From 9a62df143f94b58c5e5b7707877e982c9b724d9a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 3 Mar 2020 23:14:25 +0900 Subject: [PATCH 014/272] add whitespace --- src/future/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/future/mod.rs b/src/future/mod.rs index 9b75533d3..56cd71dd8 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -63,12 +63,14 @@ cfg_std! { cfg_default! { pub use timeout::{timeout, TimeoutError}; + mod timeout; } cfg_unstable! { pub use into_future::IntoFuture; pub(crate) use maybe_done::MaybeDone; + mod into_future; mod maybe_done; } From f31878655ed6d93a846ea7ec8dbed58a314e6fe8 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 4 Mar 2020 08:30:45 +0900 Subject: [PATCH 015/272] fix: Stabilize stream method --- Cargo.toml | 2 +- src/prelude.rs | 12 +++---- src/stream/double_ended_stream/mod.rs | 4 +-- src/stream/mod.rs | 37 +++++++++++---------- src/stream/once.rs | 2 ++ src/stream/product.rs | 1 + src/stream/stream/mod.rs | 46 +++++++++++++++++---------- src/stream/sum.rs | 1 + src/utils.rs | 2 +- 9 files changed, 63 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d4f602e9..00766b42a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ default = [ "pin-project-lite", ] docs = ["attributes", "unstable", "default"] -unstable = ["std", "broadcaster", "futures-timer"] +unstable = ["std", "broadcaster"] attributes = ["async-attributes"] std = [ "alloc", diff --git a/src/prelude.rs b/src/prelude.rs index a2a14a182..a5227451e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -38,16 +38,14 @@ cfg_std! { pub use crate::io::prelude::SeekExt as _; #[doc(no_inline)] pub use crate::io::prelude::WriteExt as _; -} - -cfg_default! { - #[doc(no_inline)] - pub use crate::task_local; -} -cfg_unstable! { #[doc(no_inline)] pub use crate::stream::DoubleEndedStream; #[doc(no_inline)] pub use crate::stream::ExactSizeStream; } + +cfg_default! { + #[doc(no_inline)] + pub use crate::task_local; +} diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index ed10fd216..1563e1bc1 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -1,7 +1,7 @@ use crate::stream::Stream; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; mod next_back; mod nth_back; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8984b2283..f1c5fdfd3 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -300,39 +300,42 @@ //! [`take`]: trait.Stream.html#method.take //! [`min`]: trait.Stream.html#method.min -pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; -pub use exact_size_stream::ExactSizeStream; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; -pub use fused_stream::FusedStream; -pub use interval::{interval, Interval}; pub use once::{once, Once}; -pub use pending::{pending, Pending}; -pub use product::Product; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; -pub use stream::Merge; pub use stream::*; -pub use successors::{successors, Successors}; -pub use sum::Sum; pub(crate) mod stream; -mod double_ended_stream; mod empty; -mod exact_size_stream; mod from_fn; mod from_iter; -mod fused_stream; -mod interval; mod once; -mod pending; -mod product; mod repeat; mod repeat_with; -mod successors; -mod sum; + +cfg_std! { + pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; + pub use fused_stream::FusedStream; + pub use interval::{interval, Interval}; + pub use pending::{pending, Pending}; + pub use product::Product; + pub use successors::{successors, Successors}; + pub use sum::Sum; + + mod double_ended_stream; + mod exact_size_stream; + mod fused_stream; + mod interval; + mod pending; + mod product; + mod successors; + mod sum; +} cfg_unstable! { mod from_stream; diff --git a/src/stream/once.rs b/src/stream/once.rs index c21a1e664..9daeac5d4 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -5,6 +5,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; +#[cfg(feature = "std")] use crate::stream::DoubleEndedStream; /// Creates a stream that yields a single item. @@ -49,6 +50,7 @@ impl Stream for Once { } } +#[cfg(feature = "std")] impl DoubleEndedStream for Once { fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(self.project().value.take()) diff --git a/src/stream/product.rs b/src/stream/product.rs index 44aef4e51..9794846fb 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,3 +1,4 @@ +use alloc::boxed::Box; use core::future::Future; use core::pin::Pin; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index da852694f..5cdf530c6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -27,9 +27,7 @@ mod chain; mod cloned; mod cmp; mod copied; -mod count; mod cycle; -mod delay; mod enumerate; mod eq; mod filter; @@ -49,7 +47,6 @@ mod map; mod max; mod max_by; mod max_by_key; -mod merge; mod min; mod min_by; mod min_by_key; @@ -64,17 +61,13 @@ mod skip_while; mod step_by; mod take; mod take_while; -mod throttle; -mod timeout; mod try_fold; mod try_for_each; -mod unzip; mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; -use count::CountFuture; use cycle::Cycle; use enumerate::Enumerate; use eq::EqFuture; @@ -101,33 +94,46 @@ use partial_cmp::PartialCmpFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; -use unzip::UnzipFuture; pub use chain::Chain; pub use cloned::Cloned; pub use copied::Copied; -pub use delay::Delay; pub use filter::Filter; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; -pub use merge::Merge; pub use scan::Scan; pub use skip::Skip; pub use skip_while::SkipWhile; pub use step_by::StepBy; pub use take::Take; pub use take_while::TakeWhile; -pub use throttle::Throttle; -pub use timeout::{Timeout, TimeoutError}; pub use zip::Zip; use core::cmp::Ordering; -use core::future::Future; -use core::pin::Pin; -use core::time::Duration; -use crate::stream::{Product, Sum}; +cfg_std! { + use core::time::Duration; + use crate::stream::{Product, Sum}; + use alloc::boxed::Box; + use core::future::Future; + use core::pin::Pin; + + use unzip::UnzipFuture; + use count::CountFuture; + + pub use throttle::Throttle; + pub use merge::Merge; + pub use delay::Delay; + pub use timeout::{Timeout, TimeoutError}; + + mod timeout; + mod throttle; + mod merge; + mod delay; + mod unzip; + mod count; +} cfg_unstable! { use crate::stream::FromStream; @@ -357,6 +363,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn throttle(self, d: Duration) -> Throttle where Self: Sized, @@ -598,6 +605,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn delay(self, dur: std::time::Duration) -> Delay where Self: Sized, @@ -1652,6 +1660,7 @@ extension_trait! { # Ok(()) }) } ``` "#] + #[cfg(feature = "std")] fn timeout(self, dur: Duration) -> Timeout where Self: Stream + Sized, @@ -1816,6 +1825,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn unzip(self) -> impl Future [UnzipFuture] where FromA: Default + Extend, @@ -1913,6 +1923,7 @@ extension_trait! { # }); ``` "#] + #[cfg(feature = "std")] fn merge(self, other: U) -> Merge where Self: Sized, @@ -2058,6 +2069,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn count(self) -> impl Future [CountFuture] where Self: Sized, @@ -2318,6 +2330,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn sum<'a, S>( self, ) -> impl Future + 'a [Pin + 'a>>] @@ -2362,6 +2375,7 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "std")] fn product<'a, P>( self, ) -> impl Future + 'a [Pin + 'a>>] diff --git a/src/stream/sum.rs b/src/stream/sum.rs index eee41bd42..dc41a0f5e 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,3 +1,4 @@ +use alloc::boxed::Box; use core::future::Future; use core::pin::Pin; diff --git a/src/utils.rs b/src/utils.rs index f18b74d19..ab2b3dbb6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,7 +21,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { } /// Generates a random number in `0..n`. -#[cfg(any(feature = "unstable", feature = "default"))] +#[cfg(any(feature = "std", feature = "default"))] pub fn random(n: u32) -> u32 { use std::cell::Cell; use std::num::Wrapping; From 1e18839f1f4eaf1cde34bc893cee26adeb1ae5d9 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 4 Mar 2020 08:38:31 +0900 Subject: [PATCH 016/272] fix warning --- Cargo.toml | 2 +- src/utils.rs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00766b42a..c1479c2e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ default = [ "async-task", "crossbeam-channel", "crossbeam-deque", - "futures-timer", "kv-log-macro", "log", "mio", @@ -42,6 +41,7 @@ std = [ "crossbeam-utils", "futures-core/std", "futures-io", + "futures-timer", "memchr", "once_cell", "pin-utils", diff --git a/src/utils.rs b/src/utils.rs index ab2b3dbb6..a930a84d2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -257,11 +257,6 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; - // Optimization: expand `$head` eagerly before starting a new method definition. - (@ext ($($head:tt)*) #[doc = $d:literal] $($tail:tt)*) => { - $($head)* extension_trait!(@ext (#[doc = $d]) $($tail)*); - }; - // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); From b95bd6c1fe6403db3becb776bf0613577980c97c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 5 Mar 2020 09:22:00 +0900 Subject: [PATCH 017/272] fix: Remove unnecessary io modules --- src/io/copy.rs | 10 +- src/io/mod.rs | 74 ++----------- src/io/stderr.rs | 251 ------------------------------------------ src/io/stdin.rs | 269 ---------------------------------------------- src/io/stdio.rs | 21 ---- src/io/stdout.rs | 251 ------------------------------------------ src/io/timeout.rs | 4 +- 7 files changed, 18 insertions(+), 862 deletions(-) delete mode 100644 src/io/stderr.rs delete mode 100644 src/io/stdin.rs delete mode 100644 src/io/stdio.rs delete mode 100644 src/io/stdout.rs diff --git a/src/io/copy.rs b/src/io/copy.rs index f05ed0e17..f946c8bd2 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -32,13 +32,14 @@ use crate::utils::Context as _; /// /// # Examples /// -/// ``` +/// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; +/// use async_std::fs::File; /// /// let mut reader: &[u8] = b"hello"; -/// let mut writer = io::stdout(); +/// let mut writer = File::open("foo.txt").await?; /// /// io::copy(&mut reader, &mut writer).await?; /// # @@ -119,13 +120,14 @@ where /// /// # Examples /// -/// ``` +/// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; +/// use async_std::fs::File; /// /// let mut reader: &[u8] = b"hello"; -/// let mut writer = io::stdout(); +/// let mut writer = File::open("foo.txt").await?; /// /// io::copy(&mut reader, &mut writer).await?; /// # diff --git a/src/io/mod.rs b/src/io/mod.rs index c7c970c55..65c204c9c 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -122,56 +122,6 @@ //! # Ok(()) }) } //! ``` //! -//! ## Standard input and output -//! -//! A very common source of input is standard input: -//! -//! ```no_run -//! use async_std::io; -//! -//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -//! # -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input).await?; -//! -//! println!("You typed: {}", input.trim()); -//! # -//! # Ok(()) }) } -//! ``` -//! -//! Note that you cannot use the [`?` operator] in functions that do not return -//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] -//! or `match` on the return value to catch any possible errors: -//! -//! ```no_run -//! use async_std::io; -//! -//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -//! # -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input).await.unwrap(); -//! # -//! # Ok(()) }) } -//! ``` -//! -//! And a very common source of output is standard output: -//! -//! ```no_run -//! use async_std::io; -//! use async_std::io::prelude::*; -//! -//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -//! # -//! io::stdout().write(&[42]).await?; -//! # -//! # Ok(()) }) } -//! ``` -//! -//! Of course, using [`io::stdout`] directly is less common than something like -//! [`println!`]. -//! //! ## Iterator types //! //! A large number of the structures provided by `std::io` are for various @@ -204,10 +154,14 @@ //! //! ```no_run //! use async_std::io; +//! use async_std::fs::File; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # -//! io::copy(&mut io::stdin(), &mut io::stdout()).await?; +//! let mut reader: &[u8] = b"hello"; +//! let mut writer = File::open("foo.txt").await?; +//! +//! io::copy(&mut reader, &mut writer).await?; //! # //! # Ok(()) }) } //! ``` @@ -224,13 +178,14 @@ //! ``` //! #![allow(dead_code)] //! use async_std::io; +//! use std::time::Duration; //! //! async fn read_input() -> io::Result<()> { -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input).await?; +//! let f = io::timeout(Duration::from_secs(5), async { +//! Ok(()) +//! }); //! -//! println!("You typed: {}", input.trim()); +//! assert_eq!(f.await?, ()); //! //! Ok(()) //! } @@ -260,8 +215,6 @@ //! [`BufReader`]: struct.BufReader.html //! [`BufWriter`]: struct.BufWriter.html //! [`Write::write`]: trait.Write.html#tymethod.write -//! [`io::stdout`]: fn.stdout.html -//! [`println!`]: ../macro.println.html //! [`Lines`]: struct.Lines.html //! [`io::Result`]: type.Result.html //! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html @@ -305,14 +258,7 @@ cfg_std! { } cfg_default! { - pub use stderr::{stderr, Stderr, StderrLock}; - pub use stdin::{stdin, Stdin, StdinLock}; - pub use stdout::{stdout, Stdout, StdoutLock}; pub use timeout::timeout; mod timeout; - mod stderr; - mod stdin; - mod stdio; - mod stdout; } diff --git a/src/io/stderr.rs b/src/io/stderr.rs deleted file mode 100644 index fc5f4a002..000000000 --- a/src/io/stderr.rs +++ /dev/null @@ -1,251 +0,0 @@ -use std::pin::Pin; -use std::sync::Mutex; -use std::future::Future; -use std::io::Write as _; - -use crate::io::{self, Write}; -use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; - -use once_cell::sync::Lazy; - -/// Constructs a new handle to the standard error of the current process. -/// -/// This function is an async version of [`std::io::stderr`]. -/// -/// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// # -/// use async_std::io; -/// use async_std::prelude::*; -/// -/// let mut stderr = io::stderr(); -/// stderr.write_all(b"Hello, world!").await?; -/// # -/// # Ok(()) }) } -/// ``` -pub fn stderr() -> Stderr { - Stderr(Mutex::new(State::Idle(Some(Inner { - stderr: std::io::stderr(), - buf: Vec::new(), - last_op: None, - })))) -} - -/// A handle to the standard error of the current process. -/// -/// This writer is created by the [`stderr`] function. See its documentation for -/// more. -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// [`stderr`]: fn.stderr.html -#[derive(Debug)] -pub struct Stderr(Mutex); - -/// A locked reference to the Stderr handle. -/// -/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`] -/// method. -/// -/// [`Write`]: trait.Read.html -/// [`Stderr::lock`]: struct.Stderr.html#method.lock -#[derive(Debug)] -pub struct StderrLock<'a>(std::io::StderrLock<'a>); - -unsafe impl Send for StderrLock<'_> {} - -/// The state of the asynchronous stderr. -/// -/// The stderr can be either idle or busy performing an asynchronous operation. -#[derive(Debug)] -enum State { - /// The stderr is idle. - Idle(Option), - - /// The stderr is blocked on an asynchronous operation. - /// - /// Awaiting this operation will result in the new state of the stderr. - Busy(JoinHandle), -} - -/// Inner representation of the asynchronous stderr. -#[derive(Debug)] -struct Inner { - /// The blocking stderr handle. - stderr: std::io::Stderr, - - /// The write buffer. - buf: Vec, - - /// The result of the last asynchronous operation on the stderr. - last_op: Option, -} - -/// Possible results of an asynchronous operation on the stderr. -#[derive(Debug)] -enum Operation { - Write(io::Result), - Flush(io::Result<()>), -} - -impl Stderr { - /// Locks this handle to the standard error stream, returning a writable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let stderr = io::stderr(); - /// let mut handle = stderr.lock().await; - /// - /// handle.write_all(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - pub async fn lock(&self) -> StderrLock<'static> { - static STDERR: Lazy = Lazy::new(std::io::stderr); - - spawn_blocking(move || StderrLock(STDERR.lock())).await - } -} - -impl Write for Stderr { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::Write(res)) = inner.last_op.take() { - let n = res?; - - // If more data was written than is available in the buffer, let's retry - // the write operation. - if n <= buf.len() { - return Poll::Ready(Ok(n)); - } - } else { - let mut inner = opt.take().unwrap(); - - // Set the length of the inner buffer to the length of the provided buffer. - if inner.buf.len() < buf.len() { - inner.buf.reserve(buf.len() - inner.buf.len()); - } - unsafe { - inner.buf.set_len(buf.len()); - } - - // Copy the data to write into the inner buffer. - inner.buf[..buf.len()].copy_from_slice(buf); - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - let res = std::io::Write::write(&mut inner.stderr, &inner.buf); - inner.last_op = Some(Operation::Write(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stderr is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::Flush(res)) = inner.last_op.take() { - return Poll::Ready(res); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - let res = std::io::Write::flush(&mut inner.stderr); - inner.last_op = Some(Operation::Flush(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stderr is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} - -cfg_unix! { - use crate::os::unix::io::{AsRawFd, RawFd}; - - impl AsRawFd for Stderr { - fn as_raw_fd(&self) -> RawFd { - std::io::stderr().as_raw_fd() - } - } -} - -cfg_windows! { - use crate::os::windows::io::{AsRawHandle, RawHandle}; - - impl AsRawHandle for Stderr { - fn as_raw_handle(&self) -> RawHandle { - std::io::stderr().as_raw_handle() - } - } -} - -impl io::Write for StderrLock<'_> { - fn poll_write( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready(self.0.write(buf)) - } - - fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.0.flush()) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} diff --git a/src/io/stdin.rs b/src/io/stdin.rs deleted file mode 100644 index b299d09fc..000000000 --- a/src/io/stdin.rs +++ /dev/null @@ -1,269 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::sync::Mutex; -use std::io::Read as _; - -use crate::future; -use crate::io::{self, Read}; -use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; -use crate::utils::Context as _; - -use once_cell::sync::Lazy; - -/// Constructs a new handle to the standard input of the current process. -/// -/// This function is an async version of [`std::io::stdin`]. -/// -/// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// # -/// use async_std::io; -/// -/// let stdin = io::stdin(); -/// let mut line = String::new(); -/// stdin.read_line(&mut line).await?; -/// # -/// # Ok(()) }) } -/// ``` -pub fn stdin() -> Stdin { - Stdin(Mutex::new(State::Idle(Some(Inner { - stdin: std::io::stdin(), - line: String::new(), - buf: Vec::new(), - last_op: None, - })))) -} - -/// A handle to the standard input of the current process. -/// -/// This reader is created by the [`stdin`] function. See its documentation for -/// more. -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// [`stdin`]: fn.stdin.html -#[derive(Debug)] -pub struct Stdin(Mutex); - -/// A locked reference to the Stdin handle. -/// -/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method. -/// -/// [`Read`]: trait.Read.html -/// [`Stdin::lock`]: struct.Stdin.html#method.lock -#[derive(Debug)] -pub struct StdinLock<'a>(std::io::StdinLock<'a>); - -unsafe impl Send for StdinLock<'_> {} - -/// The state of the asynchronous stdin. -/// -/// The stdin can be either idle or busy performing an asynchronous operation. -#[derive(Debug)] -enum State { - /// The stdin is idle. - Idle(Option), - - /// The stdin is blocked on an asynchronous operation. - /// - /// Awaiting this operation will result in the new state of the stdin. - Busy(JoinHandle), -} - -/// Inner representation of the asynchronous stdin. -#[derive(Debug)] -struct Inner { - /// The blocking stdin handle. - stdin: std::io::Stdin, - - /// The line buffer. - line: String, - - /// The write buffer. - buf: Vec, - - /// The result of the last asynchronous operation on the stdin. - last_op: Option, -} - -/// Possible results of an asynchronous operation on the stdin. -#[derive(Debug)] -enum Operation { - ReadLine(io::Result), - Read(io::Result), -} - -impl Stdin { - /// Reads a line of input into the specified buffer. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// - /// let stdin = io::stdin(); - /// let mut line = String::new(); - /// stdin.read_line(&mut line).await?; - /// # - /// # Ok(()) }) } - /// ``` - pub async fn read_line(&self, buf: &mut String) -> io::Result { - future::poll_fn(|cx| { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::ReadLine(res)) = inner.last_op.take() { - let n = res?; - - // Copy the read data into the buffer and return. - buf.push_str(&inner.line); - return Poll::Ready(Ok(n)); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - inner.line.clear(); - let res = inner.stdin.read_line(&mut inner.line); - inner.last_op = Some(Operation::ReadLine(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stdin is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - }) - .await - .context(|| String::from("could not read line on stdin")) - } - - /// Locks this handle to the standard input stream, returning a readable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let mut buffer = String::new(); - /// - /// let stdin = io::stdin(); - /// let mut handle = stdin.lock().await; - /// - /// handle.read_to_string(&mut buffer).await?; - /// # - /// # Ok(()) }) } - /// ``` - pub async fn lock(&self) -> StdinLock<'static> { - static STDIN: Lazy = Lazy::new(std::io::stdin); - - spawn_blocking(move || StdinLock(STDIN.lock())).await - } -} - -impl Read for Stdin { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::Read(res)) = inner.last_op.take() { - let n = res?; - - // If more data was read than fits into the buffer, let's retry the read - // operation. - if n <= buf.len() { - // Copy the read data into the buffer and return. - buf[..n].copy_from_slice(&inner.buf[..n]); - return Poll::Ready(Ok(n)); - } - } else { - let mut inner = opt.take().unwrap(); - - // Set the length of the inner buffer to the length of the provided buffer. - if inner.buf.len() < buf.len() { - inner.buf.reserve(buf.len() - inner.buf.len()); - } - unsafe { - inner.buf.set_len(buf.len()); - } - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); - inner.last_op = Some(Operation::Read(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stdin is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - } -} - -cfg_unix! { - use crate::os::unix::io::{AsRawFd, RawFd}; - - impl AsRawFd for Stdin { - fn as_raw_fd(&self) -> RawFd { - std::io::stdin().as_raw_fd() - } - } -} - -cfg_windows! { - use crate::os::windows::io::{AsRawHandle, RawHandle}; - - impl AsRawHandle for Stdin { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdin().as_raw_handle() - } - } -} - -impl Read for StdinLock<'_> { - fn poll_read( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - Poll::Ready(self.0.read(buf)) - } -} diff --git a/src/io/stdio.rs b/src/io/stdio.rs deleted file mode 100644 index 0e11e1a99..000000000 --- a/src/io/stdio.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Internal types for stdio. -//! -//! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`. - -use crate::io::{stderr, stdout}; -use crate::prelude::*; -use std::fmt; - -#[doc(hidden)] -pub async fn _print(args: fmt::Arguments<'_>) { - if let Err(e) = stdout().write_fmt(args).await { - panic!("failed printing to stdout: {}", e); - } -} - -#[doc(hidden)] -pub async fn _eprint(args: fmt::Arguments<'_>) { - if let Err(e) = stderr().write_fmt(args).await { - panic!("failed printing to stderr: {}", e); - } -} diff --git a/src/io/stdout.rs b/src/io/stdout.rs deleted file mode 100644 index d98ef1359..000000000 --- a/src/io/stdout.rs +++ /dev/null @@ -1,251 +0,0 @@ -use std::pin::Pin; -use std::sync::Mutex; -use std::future::Future; -use std::io::Write as _; - -use crate::io::{self, Write}; -use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; - -use once_cell::sync::Lazy; - -/// Constructs a new handle to the standard output of the current process. -/// -/// This function is an async version of [`std::io::stdout`]. -/// -/// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// # -/// use async_std::io; -/// use async_std::prelude::*; -/// -/// let mut stdout = io::stdout(); -/// stdout.write_all(b"Hello, world!").await?; -/// # -/// # Ok(()) }) } -/// ``` -pub fn stdout() -> Stdout { - Stdout(Mutex::new(State::Idle(Some(Inner { - stdout: std::io::stdout(), - buf: Vec::new(), - last_op: None, - })))) -} - -/// A handle to the standard output of the current process. -/// -/// This writer is created by the [`stdout`] function. See its documentation -/// for more. -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// [`stdout`]: fn.stdout.html -#[derive(Debug)] -pub struct Stdout(Mutex); - -/// A locked reference to the Stderr handle. -/// -/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`] -/// method. -/// -/// [`Write`]: trait.Read.html -/// [`Stdout::lock`]: struct.Stdout.html#method.lock -#[derive(Debug)] -pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); - -unsafe impl Send for StdoutLock<'_> {} - -/// The state of the asynchronous stdout. -/// -/// The stdout can be either idle or busy performing an asynchronous operation. -#[derive(Debug)] -enum State { - /// The stdout is idle. - Idle(Option), - - /// The stdout is blocked on an asynchronous operation. - /// - /// Awaiting this operation will result in the new state of the stdout. - Busy(JoinHandle), -} - -/// Inner representation of the asynchronous stdout. -#[derive(Debug)] -struct Inner { - /// The blocking stdout handle. - stdout: std::io::Stdout, - - /// The write buffer. - buf: Vec, - - /// The result of the last asynchronous operation on the stdout. - last_op: Option, -} - -/// Possible results of an asynchronous operation on the stdout. -#[derive(Debug)] -enum Operation { - Write(io::Result), - Flush(io::Result<()>), -} - -impl Stdout { - /// Locks this handle to the standard error stream, returning a writable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let stdout = io::stdout(); - /// let mut handle = stdout.lock().await; - /// - /// handle.write_all(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - pub async fn lock(&self) -> StdoutLock<'static> { - static STDOUT: Lazy = Lazy::new(std::io::stdout); - - spawn_blocking(move || StdoutLock(STDOUT.lock())).await - } -} - -impl Write for Stdout { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::Write(res)) = inner.last_op.take() { - let n = res?; - - // If more data was written than is available in the buffer, let's retry - // the write operation. - if n <= buf.len() { - return Poll::Ready(Ok(n)); - } - } else { - let mut inner = opt.take().unwrap(); - - // Set the length of the inner buffer to the length of the provided buffer. - if inner.buf.len() < buf.len() { - inner.buf.reserve(buf.len() - inner.buf.len()); - } - unsafe { - inner.buf.set_len(buf.len()); - } - - // Copy the data to write into the inner buffer. - inner.buf[..buf.len()].copy_from_slice(buf); - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - let res = std::io::Write::write(&mut inner.stdout, &inner.buf); - inner.last_op = Some(Operation::Write(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stdout is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - let inner = opt.as_mut().unwrap(); - - // Check if the operation has completed. - if let Some(Operation::Flush(res)) = inner.last_op.take() { - return Poll::Ready(res); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(spawn_blocking(move || { - let res = std::io::Write::flush(&mut inner.stdout); - inner.last_op = Some(Operation::Flush(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the stdout is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} - -cfg_unix! { - use crate::os::unix::io::{AsRawFd, RawFd}; - - impl AsRawFd for Stdout { - fn as_raw_fd(&self) -> RawFd { - std::io::stdout().as_raw_fd() - } - } -} - -cfg_windows! { - use crate::os::windows::io::{AsRawHandle, RawHandle}; - - impl AsRawHandle for Stdout { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdout().as_raw_handle() - } - } -} - -impl Write for StdoutLock<'_> { - fn poll_write( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready(self.0.write(buf)) - } - - fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.0.flush()) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 6e22dbf26..362743972 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -23,9 +23,9 @@ use crate::io; /// use async_std::io; /// /// io::timeout(Duration::from_secs(5), async { -/// let stdin = io::stdin(); +/// let stdin = std::io::stdin(); /// let mut line = String::new(); -/// let n = stdin.read_line(&mut line).await?; +/// let n = stdin.read_line(&mut line)?; /// Ok(()) /// }) /// .await?; From ec4b09ecd07de02dca1a2e3763477b61dfd927f0 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 5 Mar 2020 10:38:19 +0900 Subject: [PATCH 018/272] fix test code --- examples/a-chat/client.rs | 9 ++++++--- examples/print-file.rs | 7 ++++--- examples/stdin-echo.rs | 12 ++++++------ examples/stdin-timeout.rs | 4 ++-- tests/io_timeout.rs | 4 ++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/a-chat/client.rs b/examples/a-chat/client.rs index 48634ba03..c8d5a6c6e 100644 --- a/examples/a-chat/client.rs +++ b/examples/a-chat/client.rs @@ -1,10 +1,12 @@ use futures::select; use futures::FutureExt; +use std::io::{self, BufReader as StdBufReader, BufRead}; use async_std::{ - io::{stdin, BufReader}, + io::{BufReader}, net::{TcpStream, ToSocketAddrs}, prelude::*, + stream, task, }; @@ -20,8 +22,9 @@ async fn try_main(addr: impl ToSocketAddrs) -> Result<()> { let reader = BufReader::new(reader); let mut lines_from_server = futures::StreamExt::fuse(reader.lines()); - let stdin = BufReader::new(stdin()); - let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines()); + let stdin = StdBufReader::new(io::stdin()); + let mut lines_from_stdin = stream::from_iter(stdin.lines()); + loop { select! { line = lines_from_server.next().fuse() => match line { diff --git a/examples/print-file.rs b/examples/print-file.rs index e2cdde794..794390b28 100644 --- a/examples/print-file.rs +++ b/examples/print-file.rs @@ -1,6 +1,7 @@ //! Prints a file given as an argument to stdout. use std::env::args; +use std::io::Write; use async_std::fs::File; use async_std::io; @@ -14,7 +15,7 @@ fn main() -> io::Result<()> { task::block_on(async { let mut file = File::open(&path).await?; - let mut stdout = io::stdout(); + let mut stdout = std::io::stdout(); let mut buf = vec![0u8; LEN]; loop { @@ -23,12 +24,12 @@ fn main() -> io::Result<()> { // If this is the end of file, clean up and return. if n == 0 { - stdout.flush().await?; + stdout.flush()?; return Ok(()); } // Write the buffer into stdout. - stdout.write_all(&buf[..n]).await?; + stdout.write_all(&buf[..n])?; } }) } diff --git a/examples/stdin-echo.rs b/examples/stdin-echo.rs index 9514219e8..c9b571cc3 100644 --- a/examples/stdin-echo.rs +++ b/examples/stdin-echo.rs @@ -1,18 +1,18 @@ //! Echoes lines read on stdin to stdout. +use std::io::Write; use async_std::io; -use async_std::prelude::*; use async_std::task; fn main() -> io::Result<()> { task::block_on(async { - let stdin = io::stdin(); - let mut stdout = io::stdout(); + let stdin = std::io::stdin(); + let mut stdout = std::io::stdout(); let mut line = String::new(); loop { // Read a line from stdin. - let n = stdin.read_line(&mut line).await?; + let n = stdin.read_line(&mut line)?; // If this is the end of stdin, return. if n == 0 { @@ -20,8 +20,8 @@ fn main() -> io::Result<()> { } // Write the line to stdout. - stdout.write_all(line.as_bytes()).await?; - stdout.flush().await?; + stdout.write_all(line.as_bytes())?; + stdout.flush()?; line.clear(); } }) diff --git a/examples/stdin-timeout.rs b/examples/stdin-timeout.rs index f13c38748..2bcab5e9f 100644 --- a/examples/stdin-timeout.rs +++ b/examples/stdin-timeout.rs @@ -8,11 +8,11 @@ use async_std::task; fn main() -> io::Result<()> { // This async scope times out after 5 seconds. task::block_on(io::timeout(Duration::from_secs(5), async { - let stdin = io::stdin(); + let stdin = std::io::stdin(); // Read a line from the standard input and display it. let mut line = String::new(); - stdin.read_line(&mut line).await?; + stdin.read_line(&mut line)?; dbg!(line); Ok(()) diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index 85a17ab75..46d9d391a 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -8,9 +8,9 @@ use async_std::task; fn io_timeout_timedout() { task::block_on(async { io::timeout(Duration::from_secs(1), async { - let stdin = io::stdin(); + let stdin = std::io::stdin(); let mut line = String::new(); - let _n = stdin.read_line(&mut line).await?; + let _n = stdin.read_line(&mut line)?; Ok(()) }) .await From e3bf89fc0520d529c015971fc8a4326f5acea32d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 5 Mar 2020 18:49:58 +0900 Subject: [PATCH 019/272] $cargo fmt --- examples/a-chat/client.rs | 7 +++---- examples/stdin-echo.rs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/a-chat/client.rs b/examples/a-chat/client.rs index c8d5a6c6e..196429747 100644 --- a/examples/a-chat/client.rs +++ b/examples/a-chat/client.rs @@ -1,13 +1,12 @@ use futures::select; use futures::FutureExt; -use std::io::{self, BufReader as StdBufReader, BufRead}; +use std::io::{self, BufRead, BufReader as StdBufReader}; use async_std::{ - io::{BufReader}, + io::BufReader, net::{TcpStream, ToSocketAddrs}, prelude::*, - stream, - task, + stream, task, }; type Result = std::result::Result>; diff --git a/examples/stdin-echo.rs b/examples/stdin-echo.rs index c9b571cc3..cf0674ade 100644 --- a/examples/stdin-echo.rs +++ b/examples/stdin-echo.rs @@ -1,8 +1,8 @@ //! Echoes lines read on stdin to stdout. -use std::io::Write; use async_std::io; use async_std::task; +use std::io::Write; fn main() -> io::Result<()> { task::block_on(async { From f33d7f40ab35406230262c188bd1a55b62befa61 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 6 Mar 2020 09:20:08 +0900 Subject: [PATCH 020/272] fix test --- tests/io_timeout.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index 46d9d391a..aa464f43d 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -7,10 +7,9 @@ use async_std::task; #[should_panic(expected = "timed out")] fn io_timeout_timedout() { task::block_on(async { - io::timeout(Duration::from_secs(1), async { - let stdin = std::io::stdin(); - let mut line = String::new(); - let _n = stdin.read_line(&mut line)?; + io::timeout(Duration::from_millis(100), async { + task::sleep(Duration::from_secs(1)).await; + Ok(()) }) .await From 0b0531057d023c71dcf20b475d264c244aeda581 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 8 Mar 2020 20:46:26 +0900 Subject: [PATCH 021/272] feat: update dependence crates --- Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1479c2e8..c67ccadd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,31 +54,31 @@ alloc = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-task = { version = "1.2.1", optional = true } +async-task = { version = "1.3.1", optional = true } broadcaster = { version = "1.0.0", optional = true } -crossbeam-channel = { version = "0.4.0", optional = true } -crossbeam-deque = { version = "0.7.2", optional = true } -crossbeam-utils = { version = "0.7.0", optional = true } -futures-core = { version = "0.3.1", optional = true, default-features = false } -futures-io = { version = "0.3.1", optional = true } +crossbeam-channel = { version = "0.4.2", optional = true } +crossbeam-deque = { version = "0.7.3", optional = true } +crossbeam-utils = { version = "0.7.2", optional = true } +futures-core = { version = "0.3.4", optional = true, default-features = false } +futures-io = { version = "0.3.4", optional = true } futures-timer = { version = "2.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } -memchr = { version = "2.3.0", optional = true } +memchr = { version = "2.3.3", optional = true } mio = { version = "0.6.19", optional = true } mio-uds = { version = "0.6.7", optional = true } -num_cpus = { version = "1.11.1", optional = true } -once_cell = { version = "1.2.0", optional = true } -pin-project-lite = { version = "0.1.2", optional = true } +num_cpus = { version = "1.12.0", optional = true } +once_cell = { version = "1.3.1", optional = true } +pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } [dev-dependencies] femme = "1.3.0" rand = "0.7.3" -surf = "1.0.3" +surf = "2.0.0-alpha.0" tempdir = "0.3.7" -futures = "0.3.1" +futures = "0.3.4" [[test]] name = "stream" From f69887a50de8a3e2c4469bd2440224ca88bc8365 Mon Sep 17 00:00:00 2001 From: nasa Date: Mon, 9 Mar 2020 09:09:17 +0900 Subject: [PATCH 022/272] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c67ccadd7..0bba8ec6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ slab = { version = "0.4.2", optional = true } [dev-dependencies] femme = "1.3.0" rand = "0.7.3" -surf = "2.0.0-alpha.0" +surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.4" From cc19592f80dd62d7a44dd37fb6796f3b48e5dbfa Mon Sep 17 00:00:00 2001 From: nasa Date: Thu, 12 Mar 2020 18:34:09 +0900 Subject: [PATCH 023/272] Revert "Stabilize most stream method and remove unnecessary macros" --- Cargo.toml | 4 +- examples/a-chat/client.rs | 10 +- examples/print-file.rs | 7 +- examples/stdin-echo.rs | 12 +- examples/stdin-timeout.rs | 4 +- src/future/into_future.rs | 3 +- src/future/mod.rs | 2 - src/io/copy.rs | 10 +- src/io/mod.rs | 84 +++++++- src/io/stderr.rs | 261 ++++++++++++++++++++++++ src/io/stdin.rs | 279 ++++++++++++++++++++++++++ src/io/stdio.rs | 21 ++ src/io/stdout.rs | 261 ++++++++++++++++++++++++ src/io/timeout.rs | 4 +- src/lib.rs | 3 + src/macros.rs | 168 ++++++++++++++++ src/prelude.rs | 12 +- src/stream/double_ended_stream/mod.rs | 6 +- src/stream/exact_size_stream.rs | 2 + src/stream/extend.rs | 2 + src/stream/fused_stream.rs | 2 + src/stream/interval.rs | 4 + src/stream/mod.rs | 29 ++- src/stream/once.rs | 6 +- src/stream/product.rs | 13 +- src/stream/stream/count.rs | 2 + src/stream/stream/merge.rs | 2 + src/stream/stream/mod.rs | 64 +++--- src/stream/stream/timeout.rs | 2 + src/stream/stream/unzip.rs | 2 + src/stream/successors.rs | 4 + src/stream/sum.rs | 3 +- src/utils.rs | 7 +- tests/io_timeout.rs | 7 +- 34 files changed, 1192 insertions(+), 110 deletions(-) create mode 100644 src/io/stderr.rs create mode 100644 src/io/stdin.rs create mode 100644 src/io/stdio.rs create mode 100644 src/io/stdout.rs diff --git a/Cargo.toml b/Cargo.toml index c1479c2e8..2d4f602e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ default = [ "async-task", "crossbeam-channel", "crossbeam-deque", + "futures-timer", "kv-log-macro", "log", "mio", @@ -34,14 +35,13 @@ default = [ "pin-project-lite", ] docs = ["attributes", "unstable", "default"] -unstable = ["std", "broadcaster"] +unstable = ["std", "broadcaster", "futures-timer"] attributes = ["async-attributes"] std = [ "alloc", "crossbeam-utils", "futures-core/std", "futures-io", - "futures-timer", "memchr", "once_cell", "pin-utils", diff --git a/examples/a-chat/client.rs b/examples/a-chat/client.rs index 196429747..48634ba03 100644 --- a/examples/a-chat/client.rs +++ b/examples/a-chat/client.rs @@ -1,12 +1,11 @@ use futures::select; use futures::FutureExt; -use std::io::{self, BufRead, BufReader as StdBufReader}; use async_std::{ - io::BufReader, + io::{stdin, BufReader}, net::{TcpStream, ToSocketAddrs}, prelude::*, - stream, task, + task, }; type Result = std::result::Result>; @@ -21,9 +20,8 @@ async fn try_main(addr: impl ToSocketAddrs) -> Result<()> { let reader = BufReader::new(reader); let mut lines_from_server = futures::StreamExt::fuse(reader.lines()); - let stdin = StdBufReader::new(io::stdin()); - let mut lines_from_stdin = stream::from_iter(stdin.lines()); - + let stdin = BufReader::new(stdin()); + let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines()); loop { select! { line = lines_from_server.next().fuse() => match line { diff --git a/examples/print-file.rs b/examples/print-file.rs index 794390b28..e2cdde794 100644 --- a/examples/print-file.rs +++ b/examples/print-file.rs @@ -1,7 +1,6 @@ //! Prints a file given as an argument to stdout. use std::env::args; -use std::io::Write; use async_std::fs::File; use async_std::io; @@ -15,7 +14,7 @@ fn main() -> io::Result<()> { task::block_on(async { let mut file = File::open(&path).await?; - let mut stdout = std::io::stdout(); + let mut stdout = io::stdout(); let mut buf = vec![0u8; LEN]; loop { @@ -24,12 +23,12 @@ fn main() -> io::Result<()> { // If this is the end of file, clean up and return. if n == 0 { - stdout.flush()?; + stdout.flush().await?; return Ok(()); } // Write the buffer into stdout. - stdout.write_all(&buf[..n])?; + stdout.write_all(&buf[..n]).await?; } }) } diff --git a/examples/stdin-echo.rs b/examples/stdin-echo.rs index cf0674ade..9514219e8 100644 --- a/examples/stdin-echo.rs +++ b/examples/stdin-echo.rs @@ -1,18 +1,18 @@ //! Echoes lines read on stdin to stdout. use async_std::io; +use async_std::prelude::*; use async_std::task; -use std::io::Write; fn main() -> io::Result<()> { task::block_on(async { - let stdin = std::io::stdin(); - let mut stdout = std::io::stdout(); + let stdin = io::stdin(); + let mut stdout = io::stdout(); let mut line = String::new(); loop { // Read a line from stdin. - let n = stdin.read_line(&mut line)?; + let n = stdin.read_line(&mut line).await?; // If this is the end of stdin, return. if n == 0 { @@ -20,8 +20,8 @@ fn main() -> io::Result<()> { } // Write the line to stdout. - stdout.write_all(line.as_bytes())?; - stdout.flush()?; + stdout.write_all(line.as_bytes()).await?; + stdout.flush().await?; line.clear(); } }) diff --git a/examples/stdin-timeout.rs b/examples/stdin-timeout.rs index 2bcab5e9f..f13c38748 100644 --- a/examples/stdin-timeout.rs +++ b/examples/stdin-timeout.rs @@ -8,11 +8,11 @@ use async_std::task; fn main() -> io::Result<()> { // This async scope times out after 5 seconds. task::block_on(io::timeout(Duration::from_secs(5), async { - let stdin = std::io::stdin(); + let stdin = io::stdin(); // Read a line from the standard input and display it. let mut line = String::new(); - stdin.read_line(&mut line)?; + stdin.read_line(&mut line).await?; dbg!(line); Ok(()) diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 473ed4592..8e5e5e046 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -5,10 +5,9 @@ use std::future::Future; /// # Examples /// /// ``` -/// use std::pin::Pin; -/// /// use async_std::future::{Future, IntoFuture}; /// use async_std::io; +/// use async_std::pin::Pin; /// /// struct Client; /// diff --git a/src/future/mod.rs b/src/future/mod.rs index 56cd71dd8..9b75533d3 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -63,14 +63,12 @@ cfg_std! { cfg_default! { pub use timeout::{timeout, TimeoutError}; - mod timeout; } cfg_unstable! { pub use into_future::IntoFuture; pub(crate) use maybe_done::MaybeDone; - mod into_future; mod maybe_done; } diff --git a/src/io/copy.rs b/src/io/copy.rs index f946c8bd2..f05ed0e17 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -32,14 +32,13 @@ use crate::utils::Context as _; /// /// # Examples /// -/// ```no_run +/// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; -/// use async_std::fs::File; /// /// let mut reader: &[u8] = b"hello"; -/// let mut writer = File::open("foo.txt").await?; +/// let mut writer = io::stdout(); /// /// io::copy(&mut reader, &mut writer).await?; /// # @@ -120,14 +119,13 @@ where /// /// # Examples /// -/// ```no_run +/// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; -/// use async_std::fs::File; /// /// let mut reader: &[u8] = b"hello"; -/// let mut writer = File::open("foo.txt").await?; +/// let mut writer = io::stdout(); /// /// io::copy(&mut reader, &mut writer).await?; /// # diff --git a/src/io/mod.rs b/src/io/mod.rs index 65c204c9c..51c473d02 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -122,6 +122,56 @@ //! # Ok(()) }) } //! ``` //! +//! ## Standard input and output +//! +//! A very common source of input is standard input: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; +//! +//! println!("You typed: {}", input.trim()); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Note that you cannot use the [`?` operator] in functions that do not return +//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] +//! or `match` on the return value to catch any possible errors: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await.unwrap(); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! And a very common source of output is standard output: +//! +//! ```no_run +//! use async_std::io; +//! use async_std::io::prelude::*; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! io::stdout().write(&[42]).await?; +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Of course, using [`io::stdout`] directly is less common than something like +//! [`println!`]. +//! //! ## Iterator types //! //! A large number of the structures provided by `std::io` are for various @@ -154,14 +204,10 @@ //! //! ```no_run //! use async_std::io; -//! use async_std::fs::File; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # -//! let mut reader: &[u8] = b"hello"; -//! let mut writer = File::open("foo.txt").await?; -//! -//! io::copy(&mut reader, &mut writer).await?; +//! io::copy(&mut io::stdin(), &mut io::stdout()).await?; //! # //! # Ok(()) }) } //! ``` @@ -178,14 +224,13 @@ //! ``` //! #![allow(dead_code)] //! use async_std::io; -//! use std::time::Duration; //! //! async fn read_input() -> io::Result<()> { -//! let f = io::timeout(Duration::from_secs(5), async { -//! Ok(()) -//! }); +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; //! -//! assert_eq!(f.await?, ()); +//! println!("You typed: {}", input.trim()); //! //! Ok(()) //! } @@ -215,6 +260,8 @@ //! [`BufReader`]: struct.BufReader.html //! [`BufWriter`]: struct.BufWriter.html //! [`Write::write`]: trait.Write.html#tymethod.write +//! [`io::stdout`]: fn.stdout.html +//! [`println!`]: ../macro.println.html //! [`Lines`]: struct.Lines.html //! [`io::Result`]: type.Result.html //! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html @@ -258,7 +305,24 @@ cfg_std! { } cfg_default! { + // For use in the print macros. + #[doc(hidden)] + pub use stdio::{_eprint, _print}; + + pub use stderr::{stderr, Stderr}; + pub use stdin::{stdin, Stdin}; + pub use stdout::{stdout, Stdout}; pub use timeout::timeout; mod timeout; + mod stderr; + mod stdin; + mod stdio; + mod stdout; +} + +cfg_unstable_default! { + pub use stderr::StderrLock; + pub use stdin::StdinLock; + pub use stdout::StdoutLock; } diff --git a/src/io/stderr.rs b/src/io/stderr.rs new file mode 100644 index 000000000..5ff8a029d --- /dev/null +++ b/src/io/stderr.rs @@ -0,0 +1,261 @@ +use std::pin::Pin; +use std::sync::Mutex; +use std::future::Future; + +use crate::io::{self, Write}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; + +cfg_unstable! { + use once_cell::sync::Lazy; + use std::io::Write as _; +} + +/// Constructs a new handle to the standard error of the current process. +/// +/// This function is an async version of [`std::io::stderr`]. +/// +/// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// use async_std::prelude::*; +/// +/// let mut stderr = io::stderr(); +/// stderr.write_all(b"Hello, world!").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub fn stderr() -> Stderr { + Stderr(Mutex::new(State::Idle(Some(Inner { + stderr: std::io::stderr(), + buf: Vec::new(), + last_op: None, + })))) +} + +/// A handle to the standard error of the current process. +/// +/// This writer is created by the [`stderr`] function. See its documentation for +/// more. +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// [`stderr`]: fn.stderr.html +#[derive(Debug)] +pub struct Stderr(Mutex); + +/// A locked reference to the Stderr handle. +/// +/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`] +/// method. +/// +/// [`Write`]: trait.Read.html +/// [`Stderr::lock`]: struct.Stderr.html#method.lock +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(Debug)] +pub struct StderrLock<'a>(std::io::StderrLock<'a>); + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +unsafe impl Send for StderrLock<'_> {} + +/// The state of the asynchronous stderr. +/// +/// The stderr can be either idle or busy performing an asynchronous operation. +#[derive(Debug)] +enum State { + /// The stderr is idle. + Idle(Option), + + /// The stderr is blocked on an asynchronous operation. + /// + /// Awaiting this operation will result in the new state of the stderr. + Busy(JoinHandle), +} + +/// Inner representation of the asynchronous stderr. +#[derive(Debug)] +struct Inner { + /// The blocking stderr handle. + stderr: std::io::Stderr, + + /// The write buffer. + buf: Vec, + + /// The result of the last asynchronous operation on the stderr. + last_op: Option, +} + +/// Possible results of an asynchronous operation on the stderr. +#[derive(Debug)] +enum Operation { + Write(io::Result), + Flush(io::Result<()>), +} + +impl Stderr { + /// Locks this handle to the standard error stream, returning a writable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use async_std::prelude::*; + /// + /// let stderr = io::stderr(); + /// let mut handle = stderr.lock().await; + /// + /// handle.write_all(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StderrLock<'static> { + static STDERR: Lazy = Lazy::new(std::io::stderr); + + spawn_blocking(move || StderrLock(STDERR.lock())).await + } +} + +impl Write for Stderr { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::Write(res)) = inner.last_op.take() { + let n = res?; + + // If more data was written than is available in the buffer, let's retry + // the write operation. + if n <= buf.len() { + return Poll::Ready(Ok(n)); + } + } else { + let mut inner = opt.take().unwrap(); + + // Set the length of the inner buffer to the length of the provided buffer. + if inner.buf.len() < buf.len() { + inner.buf.reserve(buf.len() - inner.buf.len()); + } + unsafe { + inner.buf.set_len(buf.len()); + } + + // Copy the data to write into the inner buffer. + inner.buf[..buf.len()].copy_from_slice(buf); + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + let res = std::io::Write::write(&mut inner.stderr, &inner.buf); + inner.last_op = Some(Operation::Write(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stderr is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::Flush(res)) = inner.last_op.take() { + return Poll::Ready(res); + } else { + let mut inner = opt.take().unwrap(); + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + let res = std::io::Write::flush(&mut inner.stderr); + inner.last_op = Some(Operation::Flush(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stderr is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; + + impl AsRawFd for Stderr { + fn as_raw_fd(&self) -> RawFd { + std::io::stderr().as_raw_fd() + } + } +} + +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stderr { + fn as_raw_handle(&self) -> RawHandle { + std::io::stderr().as_raw_handle() + } + } +} + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +impl io::Write for StderrLock<'_> { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.0.write(buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.flush()) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} diff --git a/src/io/stdin.rs b/src/io/stdin.rs new file mode 100644 index 000000000..369ccae4c --- /dev/null +++ b/src/io/stdin.rs @@ -0,0 +1,279 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::Mutex; + +use crate::future; +use crate::io::{self, Read}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; + +cfg_unstable! { + use once_cell::sync::Lazy; + use std::io::Read as _; +} + +/// Constructs a new handle to the standard input of the current process. +/// +/// This function is an async version of [`std::io::stdin`]. +/// +/// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// +/// let stdin = io::stdin(); +/// let mut line = String::new(); +/// stdin.read_line(&mut line).await?; +/// # +/// # Ok(()) }) } +/// ``` +pub fn stdin() -> Stdin { + Stdin(Mutex::new(State::Idle(Some(Inner { + stdin: std::io::stdin(), + line: String::new(), + buf: Vec::new(), + last_op: None, + })))) +} + +/// A handle to the standard input of the current process. +/// +/// This reader is created by the [`stdin`] function. See its documentation for +/// more. +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// [`stdin`]: fn.stdin.html +#[derive(Debug)] +pub struct Stdin(Mutex); + +/// A locked reference to the Stdin handle. +/// +/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method. +/// +/// [`Read`]: trait.Read.html +/// [`Stdin::lock`]: struct.Stdin.html#method.lock +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] +#[derive(Debug)] +pub struct StdinLock<'a>(std::io::StdinLock<'a>); + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +unsafe impl Send for StdinLock<'_> {} + +/// The state of the asynchronous stdin. +/// +/// The stdin can be either idle or busy performing an asynchronous operation. +#[derive(Debug)] +enum State { + /// The stdin is idle. + Idle(Option), + + /// The stdin is blocked on an asynchronous operation. + /// + /// Awaiting this operation will result in the new state of the stdin. + Busy(JoinHandle), +} + +/// Inner representation of the asynchronous stdin. +#[derive(Debug)] +struct Inner { + /// The blocking stdin handle. + stdin: std::io::Stdin, + + /// The line buffer. + line: String, + + /// The write buffer. + buf: Vec, + + /// The result of the last asynchronous operation on the stdin. + last_op: Option, +} + +/// Possible results of an asynchronous operation on the stdin. +#[derive(Debug)] +enum Operation { + ReadLine(io::Result), + Read(io::Result), +} + +impl Stdin { + /// Reads a line of input into the specified buffer. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// + /// let stdin = io::stdin(); + /// let mut line = String::new(); + /// stdin.read_line(&mut line).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub async fn read_line(&self, buf: &mut String) -> io::Result { + future::poll_fn(|cx| { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::ReadLine(res)) = inner.last_op.take() { + let n = res?; + + // Copy the read data into the buffer and return. + buf.push_str(&inner.line); + return Poll::Ready(Ok(n)); + } else { + let mut inner = opt.take().unwrap(); + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + inner.line.clear(); + let res = inner.stdin.read_line(&mut inner.line); + inner.last_op = Some(Operation::ReadLine(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stdin is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + }) + .await + .context(|| String::from("could not read line on stdin")) + } + + /// Locks this handle to the standard input stream, returning a readable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use async_std::prelude::*; + /// + /// let mut buffer = String::new(); + /// + /// let stdin = io::stdin(); + /// let mut handle = stdin.lock().await; + /// + /// handle.read_to_string(&mut buffer).await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StdinLock<'static> { + static STDIN: Lazy = Lazy::new(std::io::stdin); + + spawn_blocking(move || StdinLock(STDIN.lock())).await + } +} + +impl Read for Stdin { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::Read(res)) = inner.last_op.take() { + let n = res?; + + // If more data was read than fits into the buffer, let's retry the read + // operation. + if n <= buf.len() { + // Copy the read data into the buffer and return. + buf[..n].copy_from_slice(&inner.buf[..n]); + return Poll::Ready(Ok(n)); + } + } else { + let mut inner = opt.take().unwrap(); + + // Set the length of the inner buffer to the length of the provided buffer. + if inner.buf.len() < buf.len() { + inner.buf.reserve(buf.len() - inner.buf.len()); + } + unsafe { + inner.buf.set_len(buf.len()); + } + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); + inner.last_op = Some(Operation::Read(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stdin is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + } +} + +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; + + impl AsRawFd for Stdin { + fn as_raw_fd(&self) -> RawFd { + std::io::stdin().as_raw_fd() + } + } +} + +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdin { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdin().as_raw_handle() + } + } +} + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +impl Read for StdinLock<'_> { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Poll::Ready(self.0.read(buf)) + } +} diff --git a/src/io/stdio.rs b/src/io/stdio.rs new file mode 100644 index 000000000..0e11e1a99 --- /dev/null +++ b/src/io/stdio.rs @@ -0,0 +1,21 @@ +//! Internal types for stdio. +//! +//! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`. + +use crate::io::{stderr, stdout}; +use crate::prelude::*; +use std::fmt; + +#[doc(hidden)] +pub async fn _print(args: fmt::Arguments<'_>) { + if let Err(e) = stdout().write_fmt(args).await { + panic!("failed printing to stdout: {}", e); + } +} + +#[doc(hidden)] +pub async fn _eprint(args: fmt::Arguments<'_>) { + if let Err(e) = stderr().write_fmt(args).await { + panic!("failed printing to stderr: {}", e); + } +} diff --git a/src/io/stdout.rs b/src/io/stdout.rs new file mode 100644 index 000000000..1711c090e --- /dev/null +++ b/src/io/stdout.rs @@ -0,0 +1,261 @@ +use std::pin::Pin; +use std::sync::Mutex; +use std::future::Future; + +use crate::io::{self, Write}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; + +cfg_unstable! { + use once_cell::sync::Lazy; + use std::io::Write as _; +} + +/// Constructs a new handle to the standard output of the current process. +/// +/// This function is an async version of [`std::io::stdout`]. +/// +/// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// use async_std::prelude::*; +/// +/// let mut stdout = io::stdout(); +/// stdout.write_all(b"Hello, world!").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub fn stdout() -> Stdout { + Stdout(Mutex::new(State::Idle(Some(Inner { + stdout: std::io::stdout(), + buf: Vec::new(), + last_op: None, + })))) +} + +/// A handle to the standard output of the current process. +/// +/// This writer is created by the [`stdout`] function. See its documentation +/// for more. +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// [`stdout`]: fn.stdout.html +#[derive(Debug)] +pub struct Stdout(Mutex); + +/// A locked reference to the Stderr handle. +/// +/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`] +/// method. +/// +/// [`Write`]: trait.Read.html +/// [`Stdout::lock`]: struct.Stdout.html#method.lock +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(Debug)] +pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +unsafe impl Send for StdoutLock<'_> {} + +/// The state of the asynchronous stdout. +/// +/// The stdout can be either idle or busy performing an asynchronous operation. +#[derive(Debug)] +enum State { + /// The stdout is idle. + Idle(Option), + + /// The stdout is blocked on an asynchronous operation. + /// + /// Awaiting this operation will result in the new state of the stdout. + Busy(JoinHandle), +} + +/// Inner representation of the asynchronous stdout. +#[derive(Debug)] +struct Inner { + /// The blocking stdout handle. + stdout: std::io::Stdout, + + /// The write buffer. + buf: Vec, + + /// The result of the last asynchronous operation on the stdout. + last_op: Option, +} + +/// Possible results of an asynchronous operation on the stdout. +#[derive(Debug)] +enum Operation { + Write(io::Result), + Flush(io::Result<()>), +} + +impl Stdout { + /// Locks this handle to the standard error stream, returning a writable guard. + /// + /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use async_std::prelude::*; + /// + /// let stdout = io::stdout(); + /// let mut handle = stdout.lock().await; + /// + /// handle.write_all(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + pub async fn lock(&self) -> StdoutLock<'static> { + static STDOUT: Lazy = Lazy::new(std::io::stdout); + + spawn_blocking(move || StdoutLock(STDOUT.lock())).await + } +} + +impl Write for Stdout { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::Write(res)) = inner.last_op.take() { + let n = res?; + + // If more data was written than is available in the buffer, let's retry + // the write operation. + if n <= buf.len() { + return Poll::Ready(Ok(n)); + } + } else { + let mut inner = opt.take().unwrap(); + + // Set the length of the inner buffer to the length of the provided buffer. + if inner.buf.len() < buf.len() { + inner.buf.reserve(buf.len() - inner.buf.len()); + } + unsafe { + inner.buf.set_len(buf.len()); + } + + // Copy the data to write into the inner buffer. + inner.buf[..buf.len()].copy_from_slice(buf); + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + let res = std::io::Write::write(&mut inner.stdout, &inner.buf); + inner.last_op = Some(Operation::Write(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stdout is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let state = &mut *self.0.lock().unwrap(); + + loop { + match state { + State::Idle(opt) => { + let inner = opt.as_mut().unwrap(); + + // Check if the operation has completed. + if let Some(Operation::Flush(res)) = inner.last_op.take() { + return Poll::Ready(res); + } else { + let mut inner = opt.take().unwrap(); + + // Start the operation asynchronously. + *state = State::Busy(spawn_blocking(move || { + let res = std::io::Write::flush(&mut inner.stdout); + inner.last_op = Some(Operation::Flush(res)); + State::Idle(Some(inner)) + })); + } + } + // Poll the asynchronous operation the stdout is currently blocked on. + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), + } + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; + + impl AsRawFd for Stdout { + fn as_raw_fd(&self) -> RawFd { + std::io::stdout().as_raw_fd() + } + } +} + +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdout { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdout().as_raw_handle() + } + } +} + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +impl Write for StdoutLock<'_> { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.0.write(buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.flush()) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 362743972..6e22dbf26 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -23,9 +23,9 @@ use crate::io; /// use async_std::io; /// /// io::timeout(Duration::from_secs(5), async { -/// let stdin = std::io::stdin(); +/// let stdin = io::stdin(); /// let mut line = String::new(); -/// let n = stdin.read_line(&mut line)?; +/// let n = stdin.read_line(&mut line).await?; /// Ok(()) /// }) /// .await?; diff --git a/src/lib.rs b/src/lib.rs index 8cd0d3006..d49879275 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,6 +273,9 @@ cfg_default! { } cfg_unstable! { + pub mod pin; + pub mod process; + mod unit; mod vec; mod result; diff --git a/src/macros.rs b/src/macros.rs index 22cd00d73..638a2348b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,171 @@ +/// Prints to the standard output. +/// +/// Equivalent to the [`println!`] macro except that a newline is not printed at +/// the end of the message. +/// +/// Note that stdout is frequently line-buffered by default so it may be +/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted +/// immediately. +/// +/// Use `print!` only for the primary output of your program. Use +/// [`eprint!`] instead to print error and progress messages. +/// +/// [`println!`]: macro.println.html +/// [flush]: io/trait.Write.html#tymethod.flush +/// [`eprint!`]: macro.eprint.html +/// +/// # Panics +/// +/// Panics if writing to `io::stdout()` fails. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::io; +/// use async_std::prelude::*; +/// use async_std::print; +/// +/// print!("this ").await; +/// print!("will ").await; +/// print!("be ").await; +/// print!("on ").await; +/// print!("the ").await; +/// print!("same ").await; +/// print!("line ").await; +/// +/// io::stdout().flush().await.unwrap(); +/// +/// print!("this string has a newline, why not choose println! instead?\n").await; +/// +/// io::stdout().flush().await.unwrap(); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) +} + +/// Prints to the standard output, with a newline. +/// +/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone +/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). +/// +/// Use the [`format!`] syntax to write data to the standard output. +/// See [`std::fmt`] for more information. +/// +/// Use `println!` only for the primary output of your program. Use +/// [`eprintln!`] instead to print error and progress messages. +/// +/// [`format!`]: macro.format.html +/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html +/// [`eprintln!`]: macro.eprintln.html +/// # Panics +/// +/// Panics if writing to `io::stdout` fails. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::println; +/// +/// println!().await; // prints just a newline +/// println!("hello there!").await; +/// println!("format {} arguments", "some").await; +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => (async { + $crate::io::_print(format_args!($($arg)*)).await; + $crate::io::_print(format_args!("\n")).await; + }) +} + +/// Prints to the standard error. +/// +/// Equivalent to the [`print!`] macro, except that output goes to +/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for +/// example usage. +/// +/// Use `eprint!` only for error and progress messages. Use `print!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: io/struct.Stderr.html +/// [`print!`]: macro.print.html +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::eprint; +/// +/// eprint!("Error: Could not complete task").await; +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[macro_export] +macro_rules! eprint { + ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))) +} + +/// Prints to the standard error, with a newline. +/// +/// Equivalent to the [`println!`] macro, except that output goes to +/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for +/// example usage. +/// +/// Use `eprintln!` only for error and progress messages. Use `println!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: io/struct.Stderr.html +/// [`println!`]: macro.println.html +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::eprintln; +/// +/// eprintln!("Error: Could not complete task").await; +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[macro_export] +macro_rules! eprintln { + () => (async { $crate::eprint!("\n").await; }); + ($($arg:tt)*) => ( + async { + $crate::io::_eprint(format_args!($($arg)*)).await; + $crate::io::_eprint(format_args!("\n")).await; + } + ); +} + /// Declares task-local values. /// /// The macro wraps any number of static declarations and makes them task-local. Attributes and diff --git a/src/prelude.rs b/src/prelude.rs index a5227451e..a2a14a182 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -38,14 +38,16 @@ cfg_std! { pub use crate::io::prelude::SeekExt as _; #[doc(no_inline)] pub use crate::io::prelude::WriteExt as _; - - #[doc(no_inline)] - pub use crate::stream::DoubleEndedStream; - #[doc(no_inline)] - pub use crate::stream::ExactSizeStream; } cfg_default! { #[doc(no_inline)] pub use crate::task_local; } + +cfg_unstable! { + #[doc(no_inline)] + pub use crate::stream::DoubleEndedStream; + #[doc(no_inline)] + pub use crate::stream::ExactSizeStream; +} diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index 1563e1bc1..a177865b6 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -1,7 +1,7 @@ use crate::stream::Stream; -use core::pin::Pin; -use core::task::{Context, Poll}; +use std::pin::Pin; +use std::task::{Context, Poll}; mod next_back; mod nth_back; @@ -22,6 +22,8 @@ use try_rfold::TryRFoldFuture; /// `Item`s from the back, as well as the front. /// /// [`Stream`]: trait.Stream.html +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait DoubleEndedStream: Stream { #[doc = r#" Attempts to receive the next item from the back of the stream. diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 28792c615..8b6ba97da 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -76,6 +76,8 @@ pub use crate::stream::Stream; /// # }); /// ``` #[allow(clippy::len_without_is_empty)] // ExactSizeIterator::is_empty is unstable +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait ExactSizeStream: Stream { /// Returns the exact number of times the stream will iterate. /// diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 9f5400730..702cbcac6 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -27,6 +27,8 @@ use crate::stream::IntoStream; /// # /// # }) /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. fn extend<'a, T: IntoStream + 'a>( diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs index 5d02f1d7a..e14ab5b1f 100644 --- a/src/stream/fused_stream.rs +++ b/src/stream/fused_stream.rs @@ -14,6 +14,8 @@ use crate::stream::Stream; /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`Stream::fuse`]: trait.Stream.html#method.fuse /// [`Fuse`]: struct.Fuse.html +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait FusedStream: Stream {} impl FusedStream for &mut S {} diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 8dd6b5e29..7a0c1740b 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -41,6 +41,8 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn interval(dur: Duration) -> Interval { Interval { delay: Delay::new(dur), @@ -54,6 +56,8 @@ pub fn interval(dur: Duration) -> Interval { /// documentation for more. /// /// [`interval`]: fn.interval.html +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Interval { delay: Delay, diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f1c5fdfd3..0bfd4e865 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -317,32 +317,29 @@ mod once; mod repeat; mod repeat_with; -cfg_std! { - pub use double_ended_stream::DoubleEndedStream; - pub use exact_size_stream::ExactSizeStream; - pub use fused_stream::FusedStream; - pub use interval::{interval, Interval}; - pub use pending::{pending, Pending}; - pub use product::Product; - pub use successors::{successors, Successors}; - pub use sum::Sum; - +cfg_unstable! { mod double_ended_stream; mod exact_size_stream; + mod extend; + mod from_stream; mod fused_stream; mod interval; + mod into_stream; mod pending; mod product; mod successors; mod sum; -} - -cfg_unstable! { - mod from_stream; - mod into_stream; - mod extend; + pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; pub use extend::{extend, Extend}; pub use from_stream::FromStream; + pub use fused_stream::FusedStream; + pub use interval::{interval, Interval}; pub use into_stream::IntoStream; + pub use pending::{pending, Pending}; + pub use product::Product; + pub use stream::Merge; + pub use successors::{successors, Successors}; + pub use sum::Sum; } diff --git a/src/stream/once.rs b/src/stream/once.rs index 9daeac5d4..b86f181d9 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -5,7 +5,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[cfg(feature = "std")] +#[cfg(feature = "unstable")] use crate::stream::DoubleEndedStream; /// Creates a stream that yields a single item. @@ -50,8 +50,8 @@ impl Stream for Once { } } -#[cfg(feature = "std")] -impl DoubleEndedStream for Once { +#[cfg(feature = "unstable")] +impl DoubleEndedStream for Once { fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(self.project().value.take()) } diff --git a/src/stream/product.rs b/src/stream/product.rs index 9794846fb..15497e87c 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,6 +1,5 @@ -use alloc::boxed::Box; -use core::future::Future; use core::pin::Pin; +use core::future::Future; use crate::stream::Stream; @@ -14,6 +13,8 @@ use crate::stream::Stream; /// [`product`]: trait.Product.html#tymethod.product /// [`FromStream`]: trait.FromStream.html /// [`Stream::product`]: trait.Stream.html#method.product +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. @@ -22,9 +23,9 @@ pub trait Product: Sized { S: Stream + 'a; } -use crate::stream::stream::StreamExt; -use core::num::Wrapping; use core::ops::Mul; +use core::num::Wrapping; +use crate::stream::stream::StreamExt; macro_rules! integer_product { (@impls $one: expr, $($a:ty)*) => ($( @@ -74,5 +75,5 @@ macro_rules! float_product { ); } -integer_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } -float_product! { f32 f64 } +integer_product!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_product!{ f32 f64 } diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index ae6c78cc7..63e044977 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -9,6 +9,8 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct CountFuture { #[pin] stream: S, diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index d1eea9d16..232097292 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -16,6 +16,8 @@ pin_project! { /// /// [`merge`]: trait.Stream.html#method.merge /// [`Stream`]: trait.Stream.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { #[pin] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5cdf530c6..d0cc718e4 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -112,43 +112,35 @@ pub use zip::Zip; use core::cmp::Ordering; -cfg_std! { - use core::time::Duration; - use crate::stream::{Product, Sum}; - use alloc::boxed::Box; +cfg_unstable! { use core::future::Future; use core::pin::Pin; + use core::time::Duration; - use unzip::UnzipFuture; - use count::CountFuture; - - pub use throttle::Throttle; - pub use merge::Merge; - pub use delay::Delay; - pub use timeout::{Timeout, TimeoutError}; - - mod timeout; - mod throttle; - mod merge; - mod delay; - mod unzip; - mod count; -} - -cfg_unstable! { - use crate::stream::FromStream; use crate::stream::into_stream::IntoStream; + use crate::stream::{FromStream, Product, Sum}; use crate::stream::Extend; + use count::CountFuture; use partition::PartitionFuture; + use unzip::UnzipFuture; + pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; + pub use timeout::{TimeoutError, Timeout}; + pub use throttle::Throttle; + pub use delay::Delay; - + mod count; + mod merge; mod flatten; mod flat_map; mod partition; + mod timeout; + mod throttle; + mod delay; + mod unzip; } extension_trait! { @@ -363,7 +355,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn throttle(self, d: Duration) -> Throttle where Self: Sized, @@ -605,7 +598,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn delay(self, dur: std::time::Duration) -> Delay where Self: Sized, @@ -1517,6 +1511,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn by_ref(&mut self) -> &mut Self { self } @@ -1660,7 +1656,8 @@ extension_trait! { # Ok(()) }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn timeout(self, dur: Duration) -> Timeout where Self: Stream + Sized, @@ -1825,7 +1822,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn unzip(self) -> impl Future [UnzipFuture] where FromA: Default + Extend, @@ -1923,7 +1921,8 @@ extension_trait! { # }); ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn merge(self, other: U) -> Merge where Self: Sized, @@ -2069,7 +2068,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn count(self) -> impl Future [CountFuture] where Self: Sized, @@ -2330,7 +2330,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn sum<'a, S>( self, ) -> impl Future + 'a [Pin + 'a>>] @@ -2375,7 +2376,8 @@ extension_trait! { # }) } ``` "#] - #[cfg(feature = "std")] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn product<'a, P>( self, ) -> impl Future + 'a [Pin + 'a>>] diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 411be7e6c..ce658c83a 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -47,6 +47,8 @@ impl Stream for Timeout { } /// An error returned when a stream times out. +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { _private: (), diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index 94cbc0a0c..7771509a5 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -8,6 +8,8 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Clone, Debug)] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct UnzipFuture { #[pin] stream: S, diff --git a/src/stream/successors.rs b/src/stream/successors.rs index fb450c66c..a9ce40ffe 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -27,6 +27,8 @@ use pin_project_lite::pin_project; /// # /// # }) } /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Option, @@ -41,6 +43,8 @@ pin_project! { /// This stream is constructed by [`successors`] function /// /// [`successors`]: fn.succssors.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Successors where diff --git a/src/stream/sum.rs b/src/stream/sum.rs index dc41a0f5e..3b3144e5e 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,4 +1,3 @@ -use alloc::boxed::Box; use core::future::Future; use core::pin::Pin; @@ -14,6 +13,8 @@ use crate::stream::Stream; /// [`sum`]: trait.Sum.html#tymethod.sum /// [`FromStream`]: trait.FromStream.html /// [`Stream::sum`]: trait.Stream.html#method.sum +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. diff --git a/src/utils.rs b/src/utils.rs index a930a84d2..f18b74d19 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,7 +21,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { } /// Generates a random number in `0..n`. -#[cfg(any(feature = "std", feature = "default"))] +#[cfg(any(feature = "unstable", feature = "default"))] pub fn random(n: u32) -> u32 { use std::cell::Cell; use std::num::Wrapping; @@ -257,6 +257,11 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; + // Optimization: expand `$head` eagerly before starting a new method definition. + (@ext ($($head:tt)*) #[doc = $d:literal] $($tail:tt)*) => { + $($head)* extension_trait!(@ext (#[doc = $d]) $($tail)*); + }; + // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index aa464f43d..85a17ab75 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -7,9 +7,10 @@ use async_std::task; #[should_panic(expected = "timed out")] fn io_timeout_timedout() { task::block_on(async { - io::timeout(Duration::from_millis(100), async { - task::sleep(Duration::from_secs(1)).await; - + io::timeout(Duration::from_secs(1), async { + let stdin = io::stdin(); + let mut line = String::new(); + let _n = stdin.read_line(&mut line).await?; Ok(()) }) .await From 8931d1464e6bdfbb8d6cd0ec7e066de9daaa2423 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 14 Mar 2020 22:46:22 +0900 Subject: [PATCH 024/272] fix ci --- src/utils.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index f18b74d19..4bdbd925b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -257,11 +257,6 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; - // Optimization: expand `$head` eagerly before starting a new method definition. - (@ext ($($head:tt)*) #[doc = $d:literal] $($tail:tt)*) => { - $($head)* extension_trait!(@ext (#[doc = $d]) $($tail)*); - }; - // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); From bb11c676a1e4884d564c99fb333586a6ca38ef67 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 15 Mar 2020 23:46:36 +0100 Subject: [PATCH 025/272] doctests pass --- src/sync/channel.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index b42326b32..a957bec92 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -41,7 +41,7 @@ use crate::sync::WakerSet; /// let (s, r) = channel(1); /// /// // This call returns immediately because there is enough space in the channel. -/// s.send(1).await; +/// s.send(1usize).await; /// /// task::spawn(async move { /// // This call will have to wait because the channel is full. @@ -323,7 +323,7 @@ impl fmt::Debug for Sender { /// let (s, r) = channel(100); /// /// task::spawn(async move { -/// s.send(1).await; +/// s.send(1usize).await; /// task::sleep(Duration::from_secs(1)).await; /// s.send(2).await; /// }); @@ -346,7 +346,7 @@ pub struct Receiver { impl Receiver { /// Receives a message from the channel. /// - /// If the channel is emtpy and still has senders, this method + /// If the channel is empty and still has senders, this method /// will wait until a message is sent into it. Once all senders /// have been dropped it will return `None`. /// @@ -361,7 +361,7 @@ impl Receiver { /// let (s, r) = channel(1); /// /// task::spawn(async move { - /// s.send(1).await; + /// s.send(1usize).await; /// s.send(2).await; /// // Then we drop the sender /// }); From 49dd02b4debe96029c4375b32df29059920001c3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 15 Mar 2020 23:51:19 +0100 Subject: [PATCH 026/272] Make the split struct public --- src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 51c473d02..dd97567b6 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -275,7 +275,7 @@ cfg_std! { #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; - pub use buf_read::{BufRead, Lines}; + pub use buf_read::{BufRead, Lines, Split}; pub use buf_reader::BufReader; pub use buf_writer::{BufWriter, IntoInnerError}; pub use copy::copy; From 32dce319d33f4b5279e8b421134fbecd0c5b2120 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 25 Nov 2019 19:35:50 +0100 Subject: [PATCH 027/272] expose try_recv and try_send on channels Signed-off-by: Yoshua Wuyts --- src/sync/channel.rs | 88 ++++++++++++++++++++++++++++++++++++++++++--- src/sync/mod.rs | 2 +- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index a957bec92..ff9f99612 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -1,5 +1,6 @@ use std::cell::UnsafeCell; -use std::fmt; +use std::error::Error; +use std::fmt::{Debug, Display, self}; use std::future::Future; use std::isize; use std::marker::PhantomData; @@ -192,6 +193,27 @@ impl Sender { .await } + /// Attempts to send a message into the channel. + /// + /// If the channel is full, this method will return an error. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// assert!(s.try_send(1).is_ok()); + /// assert!(s.try_send(2).is_err()); + /// # + /// # }) + /// ``` + pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { + self.channel.try_send(msg) + } + /// Returns the channel capacity. /// /// # Examples @@ -409,6 +431,30 @@ impl Receiver { .await } + /// Attempts to receive a message from the channel. + /// + /// If the channel is empty, this method will return an error. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// s.send(1u8).await; + /// + /// assert!(r.try_recv().is_ok()); + /// assert!(r.try_recv().is_err()); + /// # + /// # }) + /// ``` + pub fn try_recv(&self) -> Result { + self.channel.try_recv() + } + /// Returns the channel capacity. /// /// # Examples @@ -936,8 +982,8 @@ impl Drop for Channel { } } -/// An error returned from the `try_send()` method. -enum TrySendError { +/// An error returned from the `try_send` method. +pub enum TrySendError { /// The channel is full but not disconnected. Full(T), @@ -945,11 +991,43 @@ enum TrySendError { Disconnected(T), } -/// An error returned from the `try_recv()` method. -enum TryRecvError { +impl Error for TrySendError {} + +impl Debug for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Full(_) => Debug::fmt("Full", f), + Self::Disconnected(_) => Debug::fmt("Disconnected", f), + } + } +} + +impl Display for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Full(_) => Display::fmt("The channel is full.", f), + Self::Disconnected(_) => Display::fmt("The channel is full and disconnected.", f), + } + } +} + +/// An error returned from the `try_recv` method. +#[derive(Debug)] +pub enum TryRecvError { /// The channel is empty but not disconnected. Empty, /// The channel is empty and disconnected. Disconnected, } + +impl Error for TryRecvError {} + +impl Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Empty => Display::fmt("The channel is empty.", f), + Self::Disconnected => Display::fmt("The channel is empty and disconnected.", f), + } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 088c520b0..1d6a93f5c 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -184,7 +184,7 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; - pub use channel::{channel, Sender, Receiver}; + pub use channel::{channel, Sender, Receiver, TryRecvError, TrySendError}; mod barrier; mod channel; From 7b7b959a6efa4732d7284a7b41cc3220e94b5694 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 26 Nov 2019 10:58:53 +0100 Subject: [PATCH 028/272] mark channel errs as unstable Signed-off-by: Yoshua Wuyts --- src/sync/channel.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index ff9f99612..a1f21831e 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -983,6 +983,8 @@ impl Drop for Channel { } /// An error returned from the `try_send` method. +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub enum TrySendError { /// The channel is full but not disconnected. Full(T), @@ -1012,6 +1014,8 @@ impl Display for TrySendError { } /// An error returned from the `try_recv` method. +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub enum TryRecvError { /// The channel is empty but not disconnected. From 7885c245c54ebc7943b406624462fde8be1fc41c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Dec 2019 10:36:43 +0100 Subject: [PATCH 029/272] recverror Signed-off-by: Yoshua Wuyts --- src/sync/channel.rs | 39 ++++++++++++++++++++++++++------------- src/sync/mod.rs | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index a1f21831e..ace18e75e 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -1,6 +1,6 @@ use std::cell::UnsafeCell; use std::error::Error; -use std::fmt::{Debug, Display, self}; +use std::fmt::{self, Debug, Display}; use std::future::Future; use std::isize; use std::marker::PhantomData; @@ -388,22 +388,20 @@ impl Receiver { /// // Then we drop the sender /// }); /// - /// assert_eq!(r.recv().await, Some(1)); - /// assert_eq!(r.recv().await, Some(2)); - /// - /// // recv() returns `None` - /// assert_eq!(r.recv().await, None); + /// assert_eq!(r.recv().await, Ok(1)); + /// assert_eq!(r.recv().await, Ok(2)); + /// assert!(r.recv().await.is_err()); /// # /// # }) /// ``` - pub async fn recv(&self) -> Option { + pub async fn recv(&self) -> Result { struct RecvFuture<'a, T> { channel: &'a Channel, opt_key: Option, } impl Future for RecvFuture<'_, T> { - type Output = Option; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { poll_recv( @@ -569,12 +567,13 @@ impl Stream for Receiver { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = &mut *self; - poll_recv( + let res = futures_core::ready!(poll_recv( &this.channel, &this.channel.stream_wakers, &mut this.opt_key, cx, - ) + )); + Poll::Ready(res.ok()) } } @@ -593,7 +592,7 @@ fn poll_recv( wakers: &WakerSet, opt_key: &mut Option, cx: &mut Context<'_>, -) -> Poll> { +) -> Poll> { loop { // If the current task is in the set, remove it. if let Some(key) = opt_key.take() { @@ -602,8 +601,8 @@ fn poll_recv( // Try receiving a message. match channel.try_recv() { - Ok(msg) => return Poll::Ready(Some(msg)), - Err(TryRecvError::Disconnected) => return Poll::Ready(None), + Ok(msg) => return Poll::Ready(Ok(msg)), + Err(TryRecvError::Disconnected) => return Poll::Ready(Err(RecvError {})), Err(TryRecvError::Empty) => { // Insert this receive operation. *opt_key = Some(wakers.insert(cx)); @@ -1035,3 +1034,17 @@ impl Display for TryRecvError { } } } + +/// An error returned from the `recv` method. +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(Debug)] +pub struct RecvError; + +impl Error for RecvError {} + +impl Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt("The channel is empty.", f) + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 1d6a93f5c..c2211656a 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -184,7 +184,7 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; - pub use channel::{channel, Sender, Receiver, TryRecvError, TrySendError}; + pub use channel::{channel, Sender, Receiver, RecvError, TryRecvError, TrySendError}; mod barrier; mod channel; From 19fd7a4084b937c1d3de04b044f73ec9e52d77e1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Dec 2019 11:24:52 +0100 Subject: [PATCH 030/272] fix channel tests Signed-off-by: Yoshua Wuyts --- tests/channel.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/channel.rs b/tests/channel.rs index 34bd888fc..f30290600 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -18,13 +18,13 @@ fn smoke() { let (s, r) = channel(1); s.send(7).await; - assert_eq!(r.recv().await, Some(7)); + assert_eq!(r.recv().await.unwrap(), 7); s.send(8).await; - assert_eq!(r.recv().await, Some(8)); + assert_eq!(r.recv().await.unwrap(), 8); drop(s); - assert_eq!(r.recv().await, None); + assert!(r.recv().await.is_err()); }); task::block_on(async { @@ -74,7 +74,7 @@ fn len_empty_full() { assert_eq!(r.is_empty(), false); assert_eq!(r.is_full(), true); - r.recv().await; + let _ = r.recv().await; assert_eq!(s.len(), 1); assert_eq!(s.is_empty(), false); @@ -91,12 +91,12 @@ fn recv() { let (s, r) = channel(100); task::spawn(async move { - assert_eq!(r.recv().await, Some(7)); + assert_eq!(r.recv().await.unwrap(), 7); task::sleep(ms(1000)).await; - assert_eq!(r.recv().await, Some(8)); + assert_eq!(r.recv().await.unwrap(), 8); task::sleep(ms(1000)).await; - assert_eq!(r.recv().await, Some(9)); - assert_eq!(r.recv().await, None); + assert_eq!(r.recv().await.unwrap(), 9); + assert!(r.recv().await.is_err()); }); task::sleep(ms(1500)).await; @@ -122,9 +122,9 @@ fn send() { }); task::sleep(ms(1500)).await; - assert_eq!(r.recv().await, Some(7)); - assert_eq!(r.recv().await, Some(8)); - assert_eq!(r.recv().await, Some(9)); + assert_eq!(r.recv().await.unwrap(), 7); + assert_eq!(r.recv().await.unwrap(), 8); + assert_eq!(r.recv().await.unwrap(), 9); }) } @@ -139,10 +139,10 @@ fn recv_after_disconnect() { drop(s); - assert_eq!(r.recv().await, Some(1)); - assert_eq!(r.recv().await, Some(2)); - assert_eq!(r.recv().await, Some(3)); - assert_eq!(r.recv().await, None); + assert_eq!(r.recv().await.unwrap(), 1); + assert_eq!(r.recv().await.unwrap(), 2); + assert_eq!(r.recv().await.unwrap(), 3); + assert!(r.recv().await.is_err()); }) } @@ -164,7 +164,7 @@ fn len() { } for i in 0..50 { - r.recv().await; + let _ = r.recv().await; assert_eq!(r.len(), 50 - i - 1); } } @@ -188,7 +188,7 @@ fn len() { let r = r.clone(); async move { for i in 0..COUNT { - assert_eq!(r.recv().await, Some(i)); + assert_eq!(r.recv().await.unwrap(), i); let len = r.len(); assert!(len <= CAP); } @@ -214,7 +214,7 @@ fn disconnect_wakes_receiver() { let (s, r) = channel::<()>(1); let child = task::spawn(async move { - assert_eq!(r.recv().await, None); + assert!(r.recv().await.is_err()); }); task::sleep(ms(1000)).await; @@ -233,9 +233,9 @@ fn spsc() { let child = task::spawn(async move { for i in 0..COUNT { - assert_eq!(r.recv().await, Some(i)); + assert_eq!(r.recv().await.unwrap(), i); } - assert_eq!(r.recv().await, None); + assert!(r.recv().await.is_err()); }); for i in 0..COUNT { From b7c7efc797a0a5d4bce5e1d80bc95bb189d160f1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Mar 2020 00:05:39 +0100 Subject: [PATCH 031/272] Update try_channel doctests --- src/sync/channel.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index ace18e75e..8ab1cc12a 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -32,6 +32,7 @@ use crate::sync::WakerSet; /// # Examples /// /// ``` +/// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # /// use std::time::Duration; @@ -51,10 +52,11 @@ use crate::sync::WakerSet; /// }); /// /// task::sleep(Duration::from_secs(1)).await; -/// assert_eq!(r.recv().await, Some(1)); -/// assert_eq!(r.recv().await, Some(2)); +/// assert_eq!(r.recv().await?, 1); +/// assert_eq!(r.recv().await?, 2); +/// # Ok(()) /// # -/// # }) +/// # }) } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -113,6 +115,7 @@ impl Sender { /// # Examples /// /// ``` + /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -125,11 +128,12 @@ impl Sender { /// s.send(2).await; /// }); /// - /// assert_eq!(r.recv().await, Some(1)); - /// assert_eq!(r.recv().await, Some(2)); - /// assert_eq!(r.recv().await, None); + /// assert_eq!(r.recv().await?, 1); + /// assert_eq!(r.recv().await?, 2); + /// assert!(r.recv().await.is_err()); /// # - /// # }) + /// # Ok(()) + /// # }) } /// ``` pub async fn send(&self, msg: T) { struct SendFuture<'a, T> { @@ -335,6 +339,7 @@ impl fmt::Debug for Sender { /// # Examples /// /// ``` +/// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # /// use std::time::Duration; @@ -350,10 +355,11 @@ impl fmt::Debug for Sender { /// s.send(2).await; /// }); /// -/// assert_eq!(r.recv().await, Some(1)); // Received immediately. -/// assert_eq!(r.recv().await, Some(2)); // Received after 1 second. +/// assert_eq!(r.recv().await?, 1); // Received immediately. +/// assert_eq!(r.recv().await?, 2); // Received after 1 second. /// # -/// # }) +/// # Ok(()) +/// # }) } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -375,6 +381,7 @@ impl Receiver { /// # Examples /// /// ``` + /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -388,11 +395,12 @@ impl Receiver { /// // Then we drop the sender /// }); /// - /// assert_eq!(r.recv().await, Ok(1)); - /// assert_eq!(r.recv().await, Ok(2)); + /// assert_eq!(r.recv().await?, 1); + /// assert_eq!(r.recv().await?, 2); /// assert!(r.recv().await.is_err()); /// # - /// # }) + /// # Ok(()) + /// # }) } /// ``` pub async fn recv(&self) -> Result { struct RecvFuture<'a, T> { From 98cbf7f8ebbab626d6cb8e46d2048b54687c554d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 17 Mar 2020 20:39:30 +0900 Subject: [PATCH 032/272] Restore task::spawn_blocking --- src/task/spawn_blocking.rs | 87 +++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index e22c5cb46..27143f769 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,4 +1,12 @@ -use crate::task::{spawn, JoinHandle}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread; +use std::time::Duration; + +use crossbeam_channel::{unbounded, Receiver, Sender}; +use once_cell::sync::Lazy; + +use crate::task::{JoinHandle, Task}; +use crate::utils::abort_on_panic; /// Spawns a blocking task. /// @@ -35,5 +43,80 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - spawn(async { f() }) + let schedule = |task| POOL.sender.send(task).unwrap(); + let (task, handle) = async_task::spawn(async { f() }, schedule, Task::new(None)); + task.schedule(); + JoinHandle::new(handle) +} + +type Runnable = async_task::Task; + +struct Pool { + sender: Sender, + receiver: Receiver, +} + +/// The number of sleeping worker threads. +static SLEEPING: AtomicUsize = AtomicUsize::new(0); + +static POOL: Lazy = Lazy::new(|| { + // Start a single worker thread waiting for the first task. + start_thread(); + + let (sender, receiver) = unbounded(); + Pool { sender, receiver } +}); + +fn start_thread() { + SLEEPING.fetch_add(1, Ordering::SeqCst); + let timeout = Duration::from_secs(1); + + thread::Builder::new() + .name("async-std/blocking".to_string()) + .spawn(move || { + loop { + let mut task = match POOL.receiver.recv_timeout(timeout) { + Ok(task) => task, + Err(_) => { + // Check whether this is the last sleeping thread. + if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { + // If so, then restart the thread to make sure there is always at least + // one sleeping thread. + if SLEEPING.compare_and_swap(0, 1, Ordering::SeqCst) == 0 { + continue; + } + } + + // Stop the thread. + return; + } + }; + + // If there are no sleeping threads, then start one to make sure there is always at + // least one sleeping thread. + if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { + start_thread(); + } + + loop { + // Run the task. + abort_on_panic(|| task.run()); + + // Try taking another task if there are any available. + task = match POOL.receiver.try_recv() { + Ok(task) => task, + Err(_) => break, + }; + } + + // If there is at least one sleeping thread, stop this thread instead of putting it + // to sleep. + if SLEEPING.load(Ordering::SeqCst) > 0 { + return; + } + + SLEEPING.fetch_add(1, Ordering::SeqCst); + } + }) + .expect("cannot start a blocking thread"); } From 6c8237276b89646bac099874626af14accedc823 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 18 Mar 2020 23:02:59 +0900 Subject: [PATCH 033/272] fix doc test --- src/lib.rs | 9 ++++++--- src/stream/stream/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d49879275..e3cfb515d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,8 @@ //! //! Call an async function from the main function: //! -//! ``` +#![cfg_attr(feature = "attributes", doc = "```")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] //! async fn say_hello() { //! println!("Hello, world!"); //! } @@ -151,7 +152,8 @@ //! //! Await two futures concurrently, and return a tuple of their output: //! -//! ``` +#![cfg_attr(feature = "attributes", doc = "```")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] //! use async_std::prelude::*; //! //! #[async_std::main] @@ -164,7 +166,8 @@ //! //! Create a UDP server that echoes back each received message to the sender: //! -//! ```no_run +#![cfg_attr(feature = "attributes", doc = "```no_run")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] //! use async_std::net::UdpSocket; //! //! #[async_std::main] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d0cc718e4..883ec0428 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1011,7 +1011,7 @@ extension_trait! { # Examples - ```ignore + ``` # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; @@ -1044,7 +1044,7 @@ extension_trait! { # Examples - ```ignore + ``` # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; From c0f18600cff225811313064b88375ee87d3b612a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 18 Mar 2020 23:03:17 +0900 Subject: [PATCH 034/272] run ignored test --- src/stream/stream/max.rs | 17 +++++------------ src/stream/stream/min.rs | 17 +++++------------ src/stream/stream/mod.rs | 12 ++++++------ 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs index 8a4d59447..03fe63595 100644 --- a/src/stream/stream/max.rs +++ b/src/stream/stream/max.rs @@ -1,7 +1,6 @@ use core::cmp::{Ord, Ordering}; -use core::marker::PhantomData; -use core::pin::Pin; use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; @@ -11,29 +10,23 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - pub struct MaxFuture { + pub struct MaxFuture { #[pin] stream: S, - _compare: PhantomData, max: Option, } } -impl MaxFuture { +impl MaxFuture { pub(super) fn new(stream: S) -> Self { - Self { - stream, - _compare: PhantomData, - max: None, - } + Self { stream, max: None } } } -impl Future for MaxFuture +impl Future for MaxFuture where S: Stream, S::Item: Ord, - F: FnMut(&S::Item, &S::Item) -> Ordering, { type Output = Option; diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs index 4fe2a6772..8430b943a 100644 --- a/src/stream/stream/min.rs +++ b/src/stream/stream/min.rs @@ -1,7 +1,6 @@ use core::cmp::{Ord, Ordering}; -use core::marker::PhantomData; -use core::pin::Pin; use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; @@ -11,29 +10,23 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - pub struct MinFuture { + pub struct MinFuture { #[pin] stream: S, - _compare: PhantomData, min: Option, } } -impl MinFuture { +impl MinFuture { pub(super) fn new(stream: S) -> Self { - Self { - stream, - _compare: PhantomData, - min: None, - } + Self { stream, min: None } } } -impl Future for MinFuture +impl Future for MinFuture where S: Stream, S::Item: Ord, - F: FnMut(&S::Item, &S::Item) -> Ordering, { type Output = Option; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 883ec0428..4be0eb5f9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1028,12 +1028,12 @@ extension_trait! { # }) } ``` "#] - fn max( + fn max( self, - ) -> impl Future> [MaxFuture] + ) -> impl Future> [MaxFuture] where Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, + Self::Item: Ord, { MaxFuture::new(self) } @@ -1061,12 +1061,12 @@ extension_trait! { # }) } ``` "#] - fn min( + fn min( self, - ) -> impl Future> [MinFuture] + ) -> impl Future> [MinFuture] where Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, + Self::Item: Ord, { MinFuture::new(self) } From 2ab075d02796f0577316b4d22412030340a652e3 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 19 Mar 2020 11:50:19 +0900 Subject: [PATCH 035/272] refactor --- src/os/unix/net/datagram.rs | 2 -- src/os/unix/net/listener.rs | 2 -- src/os/unix/net/stream.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index fc426b7cd..dbf421b7f 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -3,8 +3,6 @@ use std::fmt; use std::net::Shutdown; -use mio_uds; - use super::SocketAddr; use crate::future; use crate::io; diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 675ef481f..01ecf3f81 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -4,8 +4,6 @@ use std::fmt; use std::pin::Pin; use std::future::Future; -use mio_uds; - use super::SocketAddr; use super::UnixStream; use crate::future; diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 647edc96f..f74cb603e 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -5,8 +5,6 @@ use std::io::{Read as _, Write as _}; use std::net::Shutdown; use std::pin::Pin; -use mio_uds; - use super::SocketAddr; use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; From b1ec1ea9300de215f2b46d2adcd232602abd214e Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 18 Mar 2020 23:59:12 +0900 Subject: [PATCH 036/272] Move Spinlock to sync module --- src/rt/runtime.rs | 3 +- src/sync/mod.rs | 2 + src/sync/spin_lock.rs | 96 +++++++++++++++++++++++++++++++++++++++++++ src/utils.rs | 73 -------------------------------- 4 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 src/sync/spin_lock.rs diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 35ebe5055..a7b75520e 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -12,8 +12,9 @@ use crossbeam_utils::thread::scope; use once_cell::unsync::OnceCell; use crate::rt::Reactor; +use crate::sync::Spinlock; use crate::task::Runnable; -use crate::utils::{abort_on_panic, random, Spinlock}; +use crate::utils::{abort_on_panic, random}; thread_local! { /// A reference to the current machine, if the current thread runs tasks. diff --git a/src/sync/mod.rs b/src/sync/mod.rs index c2211656a..caaf7f77e 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -178,9 +178,11 @@ pub use std::sync::{Arc, Weak}; pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub(crate) use spin_lock::Spinlock; mod mutex; mod rwlock; +mod spin_lock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs new file mode 100644 index 000000000..39dab78d6 --- /dev/null +++ b/src/sync/spin_lock.rs @@ -0,0 +1,96 @@ +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, Ordering}; + +use crossbeam_utils::Backoff; + +/// A simple spinlock. +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::sync::{Arc, Spinlock}; +/// use async_std::task; +/// +/// let m = Arc::new(Spinlock::new(0)); +/// let mut tasks = vec![]; +/// +/// for _ in 0..10 { +/// let m = m.clone(); +/// tasks.push(task::spawn(async move { +/// *m.lock() += 1; +/// })); +/// } +/// +/// for t in tasks { +/// t.await; +/// } +/// assert_eq!(*m.lock(), 10); +/// # +/// # }) +/// ``` +#[derive(Debug)] +pub struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, +} + +unsafe impl Send for Spinlock {} +unsafe impl Sync for Spinlock {} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + pub const fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + pub fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } + + /// Attempts to lock the spinlock. + pub fn try_lock(&self) -> Option> { + if self.flag.swap(true, Ordering::Acquire) { + None + } else { + Some(SpinlockGuard { parent: self }) + } + } +} + +/// A guard holding a spinlock locked. +#[derive(Debug)] +pub struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +unsafe impl Send for SpinlockGuard<'_, T> {} +unsafe impl Sync for SpinlockGuard<'_, T> {} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.flag.store(false, Ordering::Release); + } +} + +impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } +} + +impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } +} diff --git a/src/utils.rs b/src/utils.rs index 271a8579a..4bdbd925b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -293,76 +293,3 @@ macro_rules! extension_trait { extension_trait!($($tail)*); }; } - -cfg_default! { - use std::cell::UnsafeCell; - use std::ops::{Deref, DerefMut}; - use std::sync::atomic::{AtomicBool, Ordering}; - - use crossbeam_utils::Backoff; - - /// A simple spinlock. - pub struct Spinlock { - flag: AtomicBool, - value: UnsafeCell, - } - - unsafe impl Send for Spinlock {} - unsafe impl Sync for Spinlock {} - - impl Spinlock { - /// Returns a new spinlock initialized with `value`. - pub const fn new(value: T) -> Spinlock { - Spinlock { - flag: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Locks the spinlock. - pub fn lock(&self) -> SpinlockGuard<'_, T> { - let backoff = Backoff::new(); - while self.flag.swap(true, Ordering::Acquire) { - backoff.snooze(); - } - SpinlockGuard { parent: self } - } - - /// Attempts to lock the spinlock. - pub fn try_lock(&self) -> Option> { - if self.flag.swap(true, Ordering::Acquire) { - None - } else { - Some(SpinlockGuard { parent: self }) - } - } - } - - /// A guard holding a spinlock locked. - pub struct SpinlockGuard<'a, T> { - parent: &'a Spinlock, - } - - unsafe impl Send for SpinlockGuard<'_, T> {} - unsafe impl Sync for SpinlockGuard<'_, T> {} - - impl<'a, T> Drop for SpinlockGuard<'a, T> { - fn drop(&mut self) { - self.parent.flag.store(false, Ordering::Release); - } - } - - impl<'a, T> Deref for SpinlockGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.parent.value.get() } - } - } - - impl<'a, T> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.parent.value.get() } - } - } -} From 2b44c1be2eb82c30f5b44ba253c886caaef02979 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 19 Mar 2020 18:41:00 +0900 Subject: [PATCH 037/272] refactor: swap to swap_and_compare --- src/sync/spin_lock.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs index 39dab78d6..55343f11d 100644 --- a/src/sync/spin_lock.rs +++ b/src/sync/spin_lock.rs @@ -31,7 +31,7 @@ use crossbeam_utils::Backoff; /// ``` #[derive(Debug)] pub struct Spinlock { - flag: AtomicBool, + locked: AtomicBool, value: UnsafeCell, } @@ -42,7 +42,7 @@ impl Spinlock { /// Returns a new spinlock initialized with `value`. pub const fn new(value: T) -> Spinlock { Spinlock { - flag: AtomicBool::new(false), + locked: AtomicBool::new(false), value: UnsafeCell::new(value), } } @@ -50,7 +50,7 @@ impl Spinlock { /// Locks the spinlock. pub fn lock(&self) -> SpinlockGuard<'_, T> { let backoff = Backoff::new(); - while self.flag.swap(true, Ordering::Acquire) { + while self.locked.compare_and_swap(false, true, Ordering::Acquire) { backoff.snooze(); } SpinlockGuard { parent: self } @@ -58,7 +58,7 @@ impl Spinlock { /// Attempts to lock the spinlock. pub fn try_lock(&self) -> Option> { - if self.flag.swap(true, Ordering::Acquire) { + if self.locked.swap(true, Ordering::Acquire) { None } else { Some(SpinlockGuard { parent: self }) @@ -77,7 +77,7 @@ unsafe impl Sync for SpinlockGuard<'_, T> {} impl<'a, T> Drop for SpinlockGuard<'a, T> { fn drop(&mut self) { - self.parent.flag.store(false, Ordering::Release); + self.parent.locked.store(false, Ordering::Release); } } From d7ee29a03f4ffb89f722e859d10343f6db5d7d7e Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 19 Mar 2020 19:16:12 +0900 Subject: [PATCH 038/272] fix test code --- src/sync/spin_lock.rs | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs index 55343f11d..48ad15d2f 100644 --- a/src/sync/spin_lock.rs +++ b/src/sync/spin_lock.rs @@ -5,30 +5,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use crossbeam_utils::Backoff; /// A simple spinlock. -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::{Arc, Spinlock}; -/// use async_std::task; -/// -/// let m = Arc::new(Spinlock::new(0)); -/// let mut tasks = vec![]; -/// -/// for _ in 0..10 { -/// let m = m.clone(); -/// tasks.push(task::spawn(async move { -/// *m.lock() += 1; -/// })); -/// } -/// -/// for t in tasks { -/// t.await; -/// } -/// assert_eq!(*m.lock(), 10); -/// # -/// # }) -/// ``` #[derive(Debug)] pub struct Spinlock { locked: AtomicBool, @@ -94,3 +70,27 @@ impl<'a, T> DerefMut for SpinlockGuard<'a, T> { unsafe { &mut *self.parent.value.get() } } } + +#[test] +fn spinlock() { + crate::task::block_on(async { + use crate::sync::{Arc, Spinlock}; + use crate::task; + + let m = Arc::new(Spinlock::new(0)); + let mut tasks = vec![]; + + for _ in 0..10 { + let m = m.clone(); + tasks.push(task::spawn(async move { + *m.lock() += 1; + })); + } + + for t in tasks { + t.await; + } + assert_eq!(*m.lock(), 10); + + }) +} From 24c5dbf949e60252f4010714a81174d958d03d48 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 20 Mar 2020 23:10:45 +0900 Subject: [PATCH 039/272] Remove scheduler state --- src/rt/runtime.rs | 252 ++++++++++-------------------------------- src/sync/mod.rs | 2 - src/sync/spin_lock.rs | 96 ---------------- 3 files changed, 58 insertions(+), 292 deletions(-) delete mode 100644 src/sync/spin_lock.rs diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index a7b75520e..3a08bb636 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -1,9 +1,8 @@ use std::cell::Cell; use std::io; use std::iter; -use std::ptr; -use std::sync::atomic::{self, AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::{self, Ordering}; +use std::sync::Arc; use std::thread; use std::time::Duration; @@ -12,7 +11,6 @@ use crossbeam_utils::thread::scope; use once_cell::unsync::OnceCell; use crate::rt::Reactor; -use crate::sync::Spinlock; use crate::task::Runnable; use crate::utils::{abort_on_panic, random}; @@ -24,21 +22,6 @@ thread_local! { static YIELD_NOW: Cell = Cell::new(false); } -/// Scheduler state. -struct Scheduler { - /// Set to `true` every time before a machine blocks polling the reactor. - progress: bool, - - /// Set to `true` while a machine is polling the reactor. - polling: bool, - - /// Idle processors. - processors: Vec, - - /// Running machines. - machines: Vec>, -} - /// An async runtime. pub struct Runtime { /// The reactor. @@ -50,8 +33,7 @@ pub struct Runtime { /// Handles to local queues for stealing work. stealers: Vec>, - /// The scheduler state. - sched: Mutex, + machines: Vec>, } impl Runtime { @@ -59,18 +41,22 @@ impl Runtime { pub fn new() -> Runtime { let cpus = num_cpus::get().max(1); let processors: Vec<_> = (0..cpus).map(|_| Processor::new()).collect(); - let stealers = processors.iter().map(|p| p.worker.stealer()).collect(); + + let machines: Vec<_> = processors + .into_iter() + .map(|p| Arc::new(Machine::new(p))) + .collect(); + + let stealers = machines + .iter() + .map(|m| m.processor.worker.stealer()) + .collect(); Runtime { reactor: Reactor::new().unwrap(), injector: Injector::new(), stealers, - sched: Mutex::new(Scheduler { - processors, - machines: Vec::new(), - progress: false, - polling: false, - }), + machines, } } @@ -102,74 +88,21 @@ impl Runtime { /// Runs the runtime on the current thread. pub fn run(&self) { scope(|s| { - let mut idle = 0; - let mut delay = 0; - - loop { - // Get a list of new machines to start, if any need to be started. - for m in self.make_machines() { - idle = 0; - - s.builder() - .name("async-std/machine".to_string()) - .spawn(move |_| { - abort_on_panic(|| { - let _ = MACHINE.with(|machine| machine.set(m.clone())); - m.run(self); - }) + for m in &self.machines { + s.builder() + .name("async-std/machine".to_string()) + .spawn(move |_| { + abort_on_panic(|| { + let _ = MACHINE.with(|machine| machine.set(m.clone())); + m.run(self); }) - .expect("cannot start a machine thread"); - } - - // Sleep for a bit longer if the scheduler state hasn't changed in a while. - if idle > 10 { - delay = (delay * 2).min(10_000); - } else { - idle += 1; - delay = 1000; - } - - thread::sleep(Duration::from_micros(delay)); + }) + .expect("cannot start a machine thread"); } }) .unwrap(); } - /// Returns a list of machines that need to be started. - fn make_machines(&self) -> Vec> { - let mut sched = self.sched.lock().unwrap(); - let mut to_start = Vec::new(); - - // If there is a machine that is stuck on a task and not making any progress, steal its - // processor and set up a new machine to take over. - for m in &mut sched.machines { - if !m.progress.swap(false, Ordering::SeqCst) { - let opt_p = m.processor.try_lock().and_then(|mut p| p.take()); - - if let Some(p) = opt_p { - *m = Arc::new(Machine::new(p)); - to_start.push(m.clone()); - } - } - } - - // If no machine has been polling the reactor in a while, that means the runtime is - // overloaded with work and we need to start another machine. - if !sched.polling { - if !sched.progress { - if let Some(p) = sched.processors.pop() { - let m = Arc::new(Machine::new(p)); - to_start.push(m.clone()); - sched.machines.push(m); - } - } - - sched.progress = false; - } - - to_start - } - /// Unparks a thread polling the reactor. fn notify(&self) { atomic::fence(Ordering::SeqCst); @@ -183,42 +116,28 @@ impl Runtime { /// This function might not poll the reactor at all so do not rely on it doing anything. Only /// use for optimization. fn quick_poll(&self) -> io::Result { - if let Ok(sched) = self.sched.try_lock() { - if !sched.polling { - return self.reactor.poll(Some(Duration::from_secs(0))); - } - } - Ok(false) + return self.reactor.poll(Some(Duration::from_secs(0))); } } /// A thread running a processor. struct Machine { /// Holds the processor until it gets stolen. - processor: Spinlock>, - - /// Gets set to `true` before running every task to indicate the machine is not stuck. - progress: AtomicBool, + processor: Processor, } +unsafe impl Send for Machine {} +unsafe impl Sync for Machine {} + impl Machine { /// Creates a new machine running a processor. fn new(p: Processor) -> Machine { - Machine { - processor: Spinlock::new(Some(p)), - progress: AtomicBool::new(true), - } + Machine { processor: p } } /// Schedules a task onto the machine. fn schedule(&self, rt: &Runtime, task: Runnable) { - match self.processor.lock().as_mut() { - None => { - rt.injector.push(task); - rt.notify(); - } - Some(p) => p.schedule(rt, task), - } + self.processor.schedule(rt, task); } /// Finds the next runnable task. @@ -226,16 +145,14 @@ impl Machine { let mut retry = false; // First try finding a task in the local queue or in the global queue. - if let Some(p) = self.processor.lock().as_mut() { - if let Some(task) = p.pop_task() { - return Steal::Success(task); - } + if let Some(task) = self.processor.pop_task() { + return Steal::Success(task); + } - match p.steal_from_global(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), - } + match self.processor.steal_from_global(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), } // Try polling the reactor, but don't block on it. @@ -243,18 +160,16 @@ impl Machine { // Try finding a task in the local queue, which might hold tasks woken by the reactor. If // the local queue is still empty, try stealing from other processors. - if let Some(p) = self.processor.lock().as_mut() { - if progress { - if let Some(task) = p.pop_task() { - return Steal::Success(task); - } + if progress { + if let Some(task) = self.processor.pop_task() { + return Steal::Success(task); } + } - match p.steal_from_others(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), - } + match self.processor.steal_from_others(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), } if retry { Steal::Retry } else { Steal::Empty } @@ -275,15 +190,10 @@ impl Machine { let mut fails = 0; loop { - // let the scheduler know this machine is making progress. - self.progress.store(true, Ordering::SeqCst); - // Check if `task::yield_now()` was invoked and flush the slot if so. YIELD_NOW.with(|flag| { if flag.replace(false) { - if let Some(p) = self.processor.lock().as_mut() { - p.flush_slot(rt); - } + self.processor.flush_slot(rt); } }); @@ -294,13 +204,11 @@ impl Machine { runs = 0; rt.quick_poll().unwrap(); - if let Some(p) = self.processor.lock().as_mut() { - if let Steal::Success(task) = p.steal_from_global(rt) { - p.schedule(rt, task); - } - - p.flush_slot(rt); + if let Steal::Success(task) = self.processor.steal_from_global(rt) { + self.processor.schedule(rt, task); } + + self.processor.flush_slot(rt); } // Try to find a runnable task. @@ -313,11 +221,6 @@ impl Machine { fails += 1; - // Check if the processor was stolen. - if self.processor.lock().is_none() { - break; - } - // Yield the current thread a few times. if fails <= YIELDS { thread::yield_now(); @@ -326,14 +229,10 @@ impl Machine { // Put the current thread to sleep a few times. if fails <= YIELDS + SLEEPS { - let opt_p = self.processor.lock().take(); thread::sleep(Duration::from_micros(10)); - *self.processor.lock() = opt_p; continue; } - let mut sched = rt.sched.lock().unwrap(); - // One final check for available tasks while the scheduler is locked. if let Some(task) = iter::repeat_with(|| self.find_task(rt)) .find(|s| !s.is_retry()) @@ -343,46 +242,11 @@ impl Machine { continue; } - // If another thread is already blocked on the reactor, there is no point in keeping - // the current thread around since there is too little work to do. - if sched.polling { - break; - } - - // Take out the machine associated with the current thread. - let m = match sched - .machines - .iter() - .position(|elem| ptr::eq(&**elem, self)) - { - None => break, // The processor was stolen. - Some(pos) => sched.machines.swap_remove(pos), - }; - - // Unlock the schedule poll the reactor until new I/O events arrive. - sched.polling = true; - drop(sched); rt.reactor.poll(None).unwrap(); - // Lock the scheduler again and re-register the machine. - sched = rt.sched.lock().unwrap(); - sched.polling = false; - sched.machines.push(m); - sched.progress = true; - runs = 0; fails = 0; } - - // When shutting down the thread, take the processor out if still available. - let opt_p = self.processor.lock().take(); - - // Return the processor to the scheduler and remove the machine. - if let Some(p) = opt_p { - let mut sched = rt.sched.lock().unwrap(); - sched.processors.push(p); - sched.machines.retain(|elem| !ptr::eq(&**elem, self)); - } } } @@ -391,7 +255,7 @@ struct Processor { worker: Worker, /// Contains the next task to run as an optimization that skips the queue. - slot: Option, + slot: Cell>, } impl Processor { @@ -399,13 +263,13 @@ impl Processor { fn new() -> Processor { Processor { worker: Worker::new_fifo(), - slot: None, + slot: Cell::new(None), } } /// Schedules a task to run on this processor. - fn schedule(&mut self, rt: &Runtime, task: Runnable) { - match self.slot.replace(task) { + fn schedule(&self, rt: &Runtime, task: Runnable) { + match self.slot.replace(Some(task)) { None => {} Some(task) => { self.worker.push(task); @@ -415,7 +279,7 @@ impl Processor { } /// Flushes a task from the slot into the local queue. - fn flush_slot(&mut self, rt: &Runtime) { + fn flush_slot(&self, rt: &Runtime) { if let Some(task) = self.slot.take() { self.worker.push(task); rt.notify(); @@ -423,17 +287,17 @@ impl Processor { } /// Pops a task from this processor. - fn pop_task(&mut self) -> Option { + fn pop_task(&self) -> Option { self.slot.take().or_else(|| self.worker.pop()) } /// Steals a task from the global queue. - fn steal_from_global(&mut self, rt: &Runtime) -> Steal { + fn steal_from_global(&self, rt: &Runtime) -> Steal { rt.injector.steal_batch_and_pop(&self.worker) } /// Steals a task from other processors. - fn steal_from_others(&mut self, rt: &Runtime) -> Steal { + fn steal_from_others(&self, rt: &Runtime) -> Steal { // Pick a random starting point in the list of queues. let len = rt.stealers.len(); let start = random(len as u32) as usize; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index caaf7f77e..c2211656a 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -178,11 +178,9 @@ pub use std::sync::{Arc, Weak}; pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -pub(crate) use spin_lock::Spinlock; mod mutex; mod rwlock; -mod spin_lock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs deleted file mode 100644 index 48ad15d2f..000000000 --- a/src/sync/spin_lock.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::cell::UnsafeCell; -use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, Ordering}; - -use crossbeam_utils::Backoff; - -/// A simple spinlock. -#[derive(Debug)] -pub struct Spinlock { - locked: AtomicBool, - value: UnsafeCell, -} - -unsafe impl Send for Spinlock {} -unsafe impl Sync for Spinlock {} - -impl Spinlock { - /// Returns a new spinlock initialized with `value`. - pub const fn new(value: T) -> Spinlock { - Spinlock { - locked: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Locks the spinlock. - pub fn lock(&self) -> SpinlockGuard<'_, T> { - let backoff = Backoff::new(); - while self.locked.compare_and_swap(false, true, Ordering::Acquire) { - backoff.snooze(); - } - SpinlockGuard { parent: self } - } - - /// Attempts to lock the spinlock. - pub fn try_lock(&self) -> Option> { - if self.locked.swap(true, Ordering::Acquire) { - None - } else { - Some(SpinlockGuard { parent: self }) - } - } -} - -/// A guard holding a spinlock locked. -#[derive(Debug)] -pub struct SpinlockGuard<'a, T> { - parent: &'a Spinlock, -} - -unsafe impl Send for SpinlockGuard<'_, T> {} -unsafe impl Sync for SpinlockGuard<'_, T> {} - -impl<'a, T> Drop for SpinlockGuard<'a, T> { - fn drop(&mut self) { - self.parent.locked.store(false, Ordering::Release); - } -} - -impl<'a, T> Deref for SpinlockGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.parent.value.get() } - } -} - -impl<'a, T> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.parent.value.get() } - } -} - -#[test] -fn spinlock() { - crate::task::block_on(async { - use crate::sync::{Arc, Spinlock}; - use crate::task; - - let m = Arc::new(Spinlock::new(0)); - let mut tasks = vec![]; - - for _ in 0..10 { - let m = m.clone(); - tasks.push(task::spawn(async move { - *m.lock() += 1; - })); - } - - for t in tasks { - t.await; - } - assert_eq!(*m.lock(), 10); - - }) -} From f9607768461242b129454256ec4528c680c9a946 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 21 Mar 2020 13:37:37 +0900 Subject: [PATCH 040/272] fix --- src/rt/runtime.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 3a08bb636..8f84f5419 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use std::io; use std::iter; use std::sync::atomic::{self, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; @@ -22,6 +22,11 @@ thread_local! { static YIELD_NOW: Cell = Cell::new(false); } +struct Scheduler { + /// Set to `true` while a machine is polling the reactor. + polling: bool, +} + /// An async runtime. pub struct Runtime { /// The reactor. @@ -33,7 +38,11 @@ pub struct Runtime { /// Handles to local queues for stealing work. stealers: Vec>, + /// Machines to start machines: Vec>, + + /// The scheduler state. + sched: Mutex, } impl Runtime { @@ -57,6 +66,7 @@ impl Runtime { injector: Injector::new(), stealers, machines, + sched: Mutex::new(Scheduler { polling: false }), } } @@ -116,7 +126,25 @@ impl Runtime { /// This function might not poll the reactor at all so do not rely on it doing anything. Only /// use for optimization. fn quick_poll(&self) -> io::Result { - return self.reactor.poll(Some(Duration::from_secs(0))); + if let Ok(sched) = self.sched.try_lock() { + if !sched.polling { + return self.reactor.poll(Some(Duration::from_secs(0))); + } + } + Ok(false) + } + + fn poll(&self) -> io::Result { + let mut sched = self.sched.lock().unwrap(); + sched.polling = true; + drop(sched); + + let result = self.reactor.poll(None); + + let mut sched = self.sched.lock().unwrap(); + sched.polling = false; + + result } } @@ -242,7 +270,7 @@ impl Machine { continue; } - rt.reactor.poll(None).unwrap(); + rt.poll().unwrap(); runs = 0; fails = 0; From 6d3ca5a06f6b9a15cb03b9059540867f18c5266f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 21 Mar 2020 14:19:38 +0900 Subject: [PATCH 041/272] remove poll function --- src/rt/runtime.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 8f84f5419..3e810d0d5 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -133,19 +133,6 @@ impl Runtime { } Ok(false) } - - fn poll(&self) -> io::Result { - let mut sched = self.sched.lock().unwrap(); - sched.polling = true; - drop(sched); - - let result = self.reactor.poll(None); - - let mut sched = self.sched.lock().unwrap(); - sched.polling = false; - - result - } } /// A thread running a processor. @@ -270,7 +257,20 @@ impl Machine { continue; } - rt.poll().unwrap(); + let mut sched = rt.sched.lock().unwrap(); + + if sched.polling { + thread::sleep(Duration::from_micros(10)); + continue; + } + + sched.polling = true; + drop(sched); + + rt.reactor.poll(None).unwrap(); + + let mut sched = rt.sched.lock().unwrap(); + sched.polling = false; runs = 0; fails = 0; From 57c648cf01b6a0b577a32a29eb87ba1a01d4e838 Mon Sep 17 00:00:00 2001 From: sunli Date: Sat, 21 Mar 2020 15:49:15 +0800 Subject: [PATCH 042/272] Add async-graphql to the ecosystems inside the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4ebf5924a..69ed7eadd 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,8 @@ documentation] on how to enable them. * [Xactor](https://crates.io/crates/xactor) — Xactor is a rust actors framework based on async-std. + * [async-graphql](https://crates.io/crates/async-graphql) — The GraphQL server library implemented by rust, fully support async/await. + ## License From cfaec2aa9506dc555870b0bcc501f65c51b88ac2 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 22 Mar 2020 19:19:17 +0900 Subject: [PATCH 043/272] re add spin_lock --- src/sync/mod.rs | 2 + src/sync/spin_lock.rs | 89 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/sync/spin_lock.rs diff --git a/src/sync/mod.rs b/src/sync/mod.rs index c2211656a..b9162f148 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -192,3 +192,5 @@ cfg_unstable! { pub(crate) mod waker_set; pub(crate) use waker_set::WakerSet; +pub(crate) mod spin_lock; +pub(crate) use spin_lock::Spinlock; diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs new file mode 100644 index 000000000..854b7e024 --- /dev/null +++ b/src/sync/spin_lock.rs @@ -0,0 +1,89 @@ +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, Ordering}; + +use crossbeam_utils::Backoff; + +/// A simple spinlock. +#[derive(Debug)] +pub struct Spinlock { + locked: AtomicBool, + value: UnsafeCell, +} + +unsafe impl Send for Spinlock {} +unsafe impl Sync for Spinlock {} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + pub const fn new(value: T) -> Spinlock { + Spinlock { + locked: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + pub fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.locked.compare_and_swap(false, true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } +} + +/// A guard holding a spinlock locked. +#[derive(Debug)] +pub struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +unsafe impl Send for SpinlockGuard<'_, T> {} +unsafe impl Sync for SpinlockGuard<'_, T> {} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.locked.store(false, Ordering::Release); + } +} + +impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } +} + +impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } +} + +#[test] +fn spinlock() { + use std::sync::Arc; + + use crate::sync::{Spinlock}; + use crate::task; + + task::block_on(async { + + let m = Arc::new(Spinlock::new(0)); + let mut tasks = vec![]; + + for _ in 0..10 { + let m = m.clone(); + tasks.push(task::spawn(async move { + *m.lock() += 1; + })); + } + + for t in tasks { + t.await; + } + assert_eq!(*m.lock(), 10); + }) +} From 322911142cdd3ed9315f73da90b41ea8bb26d0b5 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 22 Mar 2020 19:20:01 +0900 Subject: [PATCH 044/272] lock processor and remove unsafe Send, Sync --- src/rt/runtime.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 3e810d0d5..a832b9f6b 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -11,6 +11,7 @@ use crossbeam_utils::thread::scope; use once_cell::unsync::OnceCell; use crate::rt::Reactor; +use crate::sync::Spinlock; use crate::task::Runnable; use crate::utils::{abort_on_panic, random}; @@ -58,7 +59,7 @@ impl Runtime { let stealers = machines .iter() - .map(|m| m.processor.worker.stealer()) + .map(|m| m.processor.lock().worker.stealer()) .collect(); Runtime { @@ -138,21 +139,20 @@ impl Runtime { /// A thread running a processor. struct Machine { /// Holds the processor until it gets stolen. - processor: Processor, + processor: Spinlock, } -unsafe impl Send for Machine {} -unsafe impl Sync for Machine {} - impl Machine { /// Creates a new machine running a processor. fn new(p: Processor) -> Machine { - Machine { processor: p } + Machine { + processor: Spinlock::new(p), + } } /// Schedules a task onto the machine. fn schedule(&self, rt: &Runtime, task: Runnable) { - self.processor.schedule(rt, task); + self.processor.lock().schedule(rt, task); } /// Finds the next runnable task. @@ -160,11 +160,11 @@ impl Machine { let mut retry = false; // First try finding a task in the local queue or in the global queue. - if let Some(task) = self.processor.pop_task() { + if let Some(task) = self.processor.lock().pop_task() { return Steal::Success(task); } - match self.processor.steal_from_global(rt) { + match self.processor.lock().steal_from_global(rt) { Steal::Empty => {} Steal::Retry => retry = true, Steal::Success(task) => return Steal::Success(task), @@ -176,12 +176,12 @@ impl Machine { // Try finding a task in the local queue, which might hold tasks woken by the reactor. If // the local queue is still empty, try stealing from other processors. if progress { - if let Some(task) = self.processor.pop_task() { + if let Some(task) = self.processor.lock().pop_task() { return Steal::Success(task); } } - match self.processor.steal_from_others(rt) { + match self.processor.lock().steal_from_others(rt) { Steal::Empty => {} Steal::Retry => retry = true, Steal::Success(task) => return Steal::Success(task), @@ -208,7 +208,7 @@ impl Machine { // Check if `task::yield_now()` was invoked and flush the slot if so. YIELD_NOW.with(|flag| { if flag.replace(false) { - self.processor.flush_slot(rt); + self.processor.lock().flush_slot(rt); } }); @@ -219,11 +219,12 @@ impl Machine { runs = 0; rt.quick_poll().unwrap(); - if let Steal::Success(task) = self.processor.steal_from_global(rt) { - self.processor.schedule(rt, task); + let p = self.processor.lock(); + if let Steal::Success(task) = p.steal_from_global(rt) { + p.schedule(rt, task); } - self.processor.flush_slot(rt); + p.flush_slot(rt); } // Try to find a runnable task. From 11ee2a89856dd84e08ced0371d64e8c8cb7af97b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 22 Mar 2020 19:25:40 +0900 Subject: [PATCH 045/272] fix --- src/sync/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index b9162f148..82759fb6b 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -192,5 +192,8 @@ cfg_unstable! { pub(crate) mod waker_set; pub(crate) use waker_set::WakerSet; -pub(crate) mod spin_lock; -pub(crate) use spin_lock::Spinlock; + +cfg_default! { + pub(crate) mod spin_lock; + pub(crate) use spin_lock::Spinlock; +} From b88138b5d7c0f9cf7e2f6f0602c3602e7bb22fdf Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 27 Mar 2020 17:03:16 +0900 Subject: [PATCH 046/272] kick ci From 68fa054517bcbe2d5f2e16753ab5d0887d38f6cd Mon Sep 17 00:00:00 2001 From: Devashish Dixit Date: Mon, 30 Mar 2020 17:44:56 +0800 Subject: [PATCH 047/272] Update futures-timer to 3.0.2 --- Cargo.toml | 2 +- src/stream/interval.rs | 123 +--------------------------------- src/stream/stream/throttle.rs | 4 +- 3 files changed, 6 insertions(+), 123 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 37374f7b6..d4a5ad70c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ crossbeam-deque = { version = "0.7.3", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } -futures-timer = { version = "2.0.2", optional = true } +futures-timer = { version = "3.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 7a0c1740b..be94b06cb 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -1,6 +1,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; +use std::time::Duration; use crate::future::Future; use crate::stream::Stream; @@ -71,125 +71,8 @@ impl Stream for Interval { if Pin::new(&mut self.delay).poll(cx).is_pending() { return Poll::Pending; } - let when = Instant::now(); - let next = next_interval(when, Instant::now(), self.interval); - self.delay.reset(next); + let interval = self.interval; + self.delay.reset(interval); Poll::Ready(Some(())) } } - -/// Converts Duration object to raw nanoseconds if possible -/// -/// This is useful to divide intervals. -/// -/// While technically for large duration it's impossible to represent any -/// duration as nanoseconds, the largest duration we can represent is about -/// 427_000 years. Large enough for any interval we would use or calculate in -/// async-std. -fn duration_to_nanos(dur: Duration) -> Option { - dur.as_secs() - .checked_mul(1_000_000_000) - .and_then(|v| v.checked_add(u64::from(dur.subsec_nanos()))) -} - -fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant { - let new = prev + interval; - if new > now { - return new; - } - - let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired"); - let interval_ns = - duration_to_nanos(interval).expect("interval is less that 427 thousand years"); - let mult = spent_ns / interval_ns + 1; - assert!( - mult < (1 << 32), - "can't skip more than 4 billion intervals of {:?} \ - (trying to skip {})", - interval, - mult - ); - prev + interval * (mult as u32) -} - -#[cfg(test)] -mod test { - use super::next_interval; - use std::cmp::Ordering; - use std::time::{Duration, Instant}; - - struct Timeline(Instant); - - impl Timeline { - fn new() -> Timeline { - Timeline(Instant::now()) - } - fn at(&self, millis: u64) -> Instant { - self.0 + Duration::from_millis(millis) - } - fn at_ns(&self, sec: u64, nanos: u32) -> Instant { - self.0 + Duration::new(sec, nanos) - } - } - - fn dur(millis: u64) -> Duration { - Duration::from_millis(millis) - } - - // The math around Instant/Duration isn't 100% precise due to rounding - // errors, see #249 for more info - fn almost_eq(a: Instant, b: Instant) -> bool { - match a.cmp(&b) { - Ordering::Equal => true, - Ordering::Greater => a - b < Duration::from_millis(1), - Ordering::Less => b - a < Duration::from_millis(1), - } - } - - #[test] - fn norm_next() { - let tm = Timeline::new(); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(2), dur(10)), - tm.at(11) - )); - assert!(almost_eq( - next_interval(tm.at(7777), tm.at(7788), dur(100)), - tm.at(7877) - )); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(1000), dur(2100)), - tm.at(2101) - )); - } - - #[test] - fn fast_forward() { - let tm = Timeline::new(); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(1000), dur(10)), - tm.at(1001) - )); - assert!(almost_eq( - next_interval(tm.at(7777), tm.at(8888), dur(100)), - tm.at(8977) - )); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(10000), dur(2100)), - tm.at(10501) - )); - } - - /// TODO: this test actually should be successful, but since we can't - /// multiply Duration on anything larger than u32 easily we decided - /// to allow it to fail for now - #[test] - #[should_panic(expected = "can't skip more than 4 billion intervals")] - fn large_skip() { - let tm = Timeline::new(); - assert_eq!( - next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)), - tm.at_ns(25, 1) - ); - } -} diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index ce8c13b37..554ca306e 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -1,6 +1,6 @@ use std::future::Future; use std::pin::Pin; -use std::time::{Duration, Instant}; +use std::time::Duration; use futures_timer::Delay; use pin_project_lite::pin_project; @@ -59,7 +59,7 @@ impl Stream for Throttle { Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { *this.blocked = true; - this.delay.reset(Instant::now() + *this.duration); + this.delay.reset(*this.duration); Poll::Ready(Some(v)) } } From 088aa5662cb3d69bbfefcc134bd7251ef94086ab Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 3 Apr 2020 13:38:07 +0900 Subject: [PATCH 048/272] refactor: Remove wrapping cell --- src/rt/runtime.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index a832b9f6b..62b85f841 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -219,7 +219,7 @@ impl Machine { runs = 0; rt.quick_poll().unwrap(); - let p = self.processor.lock(); + let mut p = self.processor.lock(); if let Steal::Success(task) = p.steal_from_global(rt) { p.schedule(rt, task); } @@ -284,7 +284,7 @@ struct Processor { worker: Worker, /// Contains the next task to run as an optimization that skips the queue. - slot: Cell>, + slot: Option, } impl Processor { @@ -292,13 +292,13 @@ impl Processor { fn new() -> Processor { Processor { worker: Worker::new_fifo(), - slot: Cell::new(None), + slot: None, } } /// Schedules a task to run on this processor. - fn schedule(&self, rt: &Runtime, task: Runnable) { - match self.slot.replace(Some(task)) { + fn schedule(&mut self, rt: &Runtime, task: Runnable) { + match self.slot.replace(task) { None => {} Some(task) => { self.worker.push(task); @@ -308,7 +308,7 @@ impl Processor { } /// Flushes a task from the slot into the local queue. - fn flush_slot(&self, rt: &Runtime) { + fn flush_slot(&mut self, rt: &Runtime) { if let Some(task) = self.slot.take() { self.worker.push(task); rt.notify(); @@ -316,7 +316,7 @@ impl Processor { } /// Pops a task from this processor. - fn pop_task(&self) -> Option { + fn pop_task(&mut self) -> Option { self.slot.take().or_else(|| self.worker.pop()) } From 0c9a66c1f64b1706f5e31e46fffb4644bfaf1eee Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 9 Apr 2020 17:02:27 +0200 Subject: [PATCH 049/272] fix scheduler loop This now matches more closely the logic as implemented in #631, and fixes the performance regression as far as I have observed. Closes #746 --- src/rt/runtime.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 62b85f841..5a9d3025f 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -249,6 +249,8 @@ impl Machine { continue; } + let mut sched = rt.sched.lock().unwrap(); + // One final check for available tasks while the scheduler is locked. if let Some(task) = iter::repeat_with(|| self.find_task(rt)) .find(|s| !s.is_retry()) @@ -258,19 +260,19 @@ impl Machine { continue; } - let mut sched = rt.sched.lock().unwrap(); - + // If another thread is already blocked on the reactor, there is no point in keeping + // the current thread around since there is too little work to do. if sched.polling { - thread::sleep(Duration::from_micros(10)); - continue; + break; } + // Unlock the schedule poll the reactor until new I/O events arrive. sched.polling = true; drop(sched); - rt.reactor.poll(None).unwrap(); - let mut sched = rt.sched.lock().unwrap(); + // Lock the scheduler again and re-register the machine. + sched = rt.sched.lock().unwrap(); sched.polling = false; runs = 0; From a4e07e345c496e112169b21bc92ca5c903c818bd Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 10 Apr 2020 02:22:03 +0200 Subject: [PATCH 050/272] fix(rt): bring back dynamic machines Even if we do not make use of the progress blocking, we do need to make use of the dynamic restarting of machines as far as I understand. Keeps the perf, while removing the regression from #747 --- src/rt/runtime.rs | 166 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 48 deletions(-) diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs index 5a9d3025f..a0d88b983 100644 --- a/src/rt/runtime.rs +++ b/src/rt/runtime.rs @@ -1,6 +1,7 @@ use std::cell::Cell; use std::io; use std::iter; +use std::ptr; use std::sync::atomic::{self, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; @@ -26,6 +27,12 @@ thread_local! { struct Scheduler { /// Set to `true` while a machine is polling the reactor. polling: bool, + + /// Idle processors. + processors: Vec, + + /// Running machines. + machines: Vec>, } /// An async runtime. @@ -39,9 +46,6 @@ pub struct Runtime { /// Handles to local queues for stealing work. stealers: Vec>, - /// Machines to start - machines: Vec>, - /// The scheduler state. sched: Mutex, } @@ -51,23 +55,17 @@ impl Runtime { pub fn new() -> Runtime { let cpus = num_cpus::get().max(1); let processors: Vec<_> = (0..cpus).map(|_| Processor::new()).collect(); - - let machines: Vec<_> = processors - .into_iter() - .map(|p| Arc::new(Machine::new(p))) - .collect(); - - let stealers = machines - .iter() - .map(|m| m.processor.lock().worker.stealer()) - .collect(); + let stealers = processors.iter().map(|p| p.worker.stealer()).collect(); Runtime { reactor: Reactor::new().unwrap(), injector: Injector::new(), stealers, - machines, - sched: Mutex::new(Scheduler { polling: false }), + sched: Mutex::new(Scheduler { + processors, + machines: Vec::new(), + polling: false, + }), } } @@ -99,21 +97,57 @@ impl Runtime { /// Runs the runtime on the current thread. pub fn run(&self) { scope(|s| { - for m in &self.machines { - s.builder() - .name("async-std/machine".to_string()) - .spawn(move |_| { - abort_on_panic(|| { - let _ = MACHINE.with(|machine| machine.set(m.clone())); - m.run(self); + let mut idle = 0; + let mut delay = 0; + + loop { + // Get a list of new machines to start, if any need to be started. + for m in self.make_machines() { + idle = 0; + + s.builder() + .name("async-std/machine".to_string()) + .spawn(move |_| { + abort_on_panic(|| { + let _ = MACHINE.with(|machine| machine.set(m.clone())); + m.run(self); + }) }) - }) - .expect("cannot start a machine thread"); + .expect("cannot start a machine thread"); + } + + // Sleep for a bit longer if the scheduler state hasn't changed in a while. + if idle > 10 { + delay = (delay * 2).min(10_000); + } else { + idle += 1; + delay = 1000; + } + + thread::sleep(Duration::from_micros(delay)); } }) .unwrap(); } + /// Returns a list of machines that need to be started. + fn make_machines(&self) -> Vec> { + let mut sched = self.sched.lock().unwrap(); + let mut to_start = Vec::new(); + + // If no machine has been polling the reactor in a while, that means the runtime is + // overloaded with work and we need to start another machine. + if !sched.polling { + if let Some(p) = sched.processors.pop() { + let m = Arc::new(Machine::new(p)); + to_start.push(m.clone()); + sched.machines.push(m); + } + } + + to_start + } + /// Unparks a thread polling the reactor. fn notify(&self) { atomic::fence(Ordering::SeqCst); @@ -139,20 +173,26 @@ impl Runtime { /// A thread running a processor. struct Machine { /// Holds the processor until it gets stolen. - processor: Spinlock, + processor: Spinlock>, } impl Machine { /// Creates a new machine running a processor. fn new(p: Processor) -> Machine { Machine { - processor: Spinlock::new(p), + processor: Spinlock::new(Some(p)), } } /// Schedules a task onto the machine. fn schedule(&self, rt: &Runtime, task: Runnable) { - self.processor.lock().schedule(rt, task); + match self.processor.lock().as_mut() { + None => { + rt.injector.push(task); + rt.notify(); + } + Some(p) => p.schedule(rt, task), + } } /// Finds the next runnable task. @@ -160,14 +200,16 @@ impl Machine { let mut retry = false; // First try finding a task in the local queue or in the global queue. - if let Some(task) = self.processor.lock().pop_task() { - return Steal::Success(task); - } + if let Some(p) = self.processor.lock().as_mut() { + if let Some(task) = p.pop_task() { + return Steal::Success(task); + } - match self.processor.lock().steal_from_global(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), + match p.steal_from_global(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), + } } // Try polling the reactor, but don't block on it. @@ -175,16 +217,18 @@ impl Machine { // Try finding a task in the local queue, which might hold tasks woken by the reactor. If // the local queue is still empty, try stealing from other processors. - if progress { - if let Some(task) = self.processor.lock().pop_task() { - return Steal::Success(task); + if let Some(p) = self.processor.lock().as_mut() { + if progress { + if let Some(task) = p.pop_task() { + return Steal::Success(task); + } } - } - match self.processor.lock().steal_from_others(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), + match p.steal_from_others(rt) { + Steal::Empty => {} + Steal::Retry => retry = true, + Steal::Success(task) => return Steal::Success(task), + } } if retry { Steal::Retry } else { Steal::Empty } @@ -208,7 +252,9 @@ impl Machine { // Check if `task::yield_now()` was invoked and flush the slot if so. YIELD_NOW.with(|flag| { if flag.replace(false) { - self.processor.lock().flush_slot(rt); + if let Some(p) = self.processor.lock().as_mut() { + p.flush_slot(rt); + } } }); @@ -219,12 +265,13 @@ impl Machine { runs = 0; rt.quick_poll().unwrap(); - let mut p = self.processor.lock(); - if let Steal::Success(task) = p.steal_from_global(rt) { - p.schedule(rt, task); - } + if let Some(p) = self.processor.lock().as_mut() { + if let Steal::Success(task) = p.steal_from_global(rt) { + p.schedule(rt, task); + } - p.flush_slot(rt); + p.flush_slot(rt); + } } // Try to find a runnable task. @@ -245,7 +292,9 @@ impl Machine { // Put the current thread to sleep a few times. if fails <= YIELDS + SLEEPS { + let opt_p = self.processor.lock().take(); thread::sleep(Duration::from_micros(10)); + *self.processor.lock() = opt_p; continue; } @@ -266,6 +315,16 @@ impl Machine { break; } + // Take out the machine associated with the current thread. + let m = match sched + .machines + .iter() + .position(|elem| ptr::eq(&**elem, self)) + { + None => break, // The processor was stolen. + Some(pos) => sched.machines.swap_remove(pos), + }; + // Unlock the schedule poll the reactor until new I/O events arrive. sched.polling = true; drop(sched); @@ -274,10 +333,21 @@ impl Machine { // Lock the scheduler again and re-register the machine. sched = rt.sched.lock().unwrap(); sched.polling = false; + sched.machines.push(m); runs = 0; fails = 0; } + + // When shutting down the thread, take the processor out if still available. + let opt_p = self.processor.lock().take(); + + // Return the processor to the scheduler and remove the machine. + if let Some(p) = opt_p { + let mut sched = rt.sched.lock().unwrap(); + sched.processors.push(p); + sched.machines.retain(|elem| !ptr::eq(&**elem, self)); + } } } From db438abb8fc4c168035ea51ef287d4d72971a41b Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Sun, 12 Apr 2020 05:35:18 -0600 Subject: [PATCH 051/272] Implement async_std::sync::Condvar (#369) * Implement async_std::sync::Condvar Part of #217 * More rigourous detection of notification for condvar * Use state of Waker instead of AtomicUsize to keep track of if task was notified. * Add test for notify_all * Implement wait_timeout_until And add warnings about spurious wakeups to wait and wait_timeout * Use WakerSet for Condvar This should also address concerns about spurious wakeups. * Add test for wait_timeout with no lock held * Add comments describing AwaitNotify struct And remove an unnneded comment in a Debug implementation --- src/sync/condvar.rs | 417 ++++++++++++++++++++++++++++++++++++++++++ src/sync/mod.rs | 2 + src/sync/mutex.rs | 5 + src/sync/waker_set.rs | 22 +++ tests/condvar.rs | 91 +++++++++ 5 files changed, 537 insertions(+) create mode 100644 src/sync/condvar.rs create mode 100644 tests/condvar.rs diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs new file mode 100644 index 000000000..67507f384 --- /dev/null +++ b/src/sync/condvar.rs @@ -0,0 +1,417 @@ +use std::fmt; +use std::pin::Pin; +use std::time::Duration; + +use super::mutex::{guard_lock, MutexGuard}; +use crate::future::{timeout, Future}; +use crate::sync::WakerSet; +use crate::task::{Context, Poll}; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct WaitTimeoutResult(bool); + +/// A type indicating whether a timed wait on a condition variable returned due to a time out or +/// not +impl WaitTimeoutResult { + /// Returns `true` if the wait was known to have timed out. + pub fn timed_out(self) -> bool { + self.0 + } +} + +/// A Condition Variable +/// +/// This type is an async version of [`std::sync::Mutex`]. +/// +/// [`std::sync::Condvar`]: https://doc.rust-lang.org/std/sync/struct.Condvar.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::sync::Arc; +/// +/// use async_std::sync::{Mutex, Condvar}; +/// use async_std::task; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = pair.clone(); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start. +/// task::spawn(async move { +/// let (lock, cvar) = &*pair2; +/// let mut started = lock.lock().await; +/// *started = true; +/// // We notify the condvar that the value has changed. +/// cvar.notify_one(); +/// }); +/// +/// // Wait for the thread to start up. +/// let (lock, cvar) = &*pair; +/// let mut started = lock.lock().await; +/// while !*started { +/// started = cvar.wait(started).await; +/// } +/// +/// # }) +/// ``` +pub struct Condvar { + wakers: WakerSet, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Default for Condvar { + fn default() -> Self { + Condvar::new() + } +} + +impl Condvar { + /// Creates a new condition variable + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::Condvar; + /// + /// let cvar = Condvar::new(); + /// ``` + pub fn new() -> Self { + Condvar { + wakers: WakerSet::new(), + } + } + + /// Blocks the current task until this condition variable receives a notification. + /// + /// Unlike the std equivalent, this does not check that a single mutex is used at runtime. + /// However, as a best practice avoid using with multiple mutexes. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + let mutex = guard_lock(&guard); + + self.await_notify(guard).await; + + mutex.lock().await + } + + fn await_notify<'a, T>(&self, guard: MutexGuard<'a, T>) -> AwaitNotify<'_, 'a, T> { + AwaitNotify { + cond: self, + guard: Some(guard), + key: None, + } + } + + /// Blocks the current taks until this condition variable receives a notification and the + /// required condition is met. Spurious wakeups are ignored and this function will only + /// return once the condition has been met. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// let _guard = cvar.wait_until(lock.lock().await, |started| { *started }).await; + /// # + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_until<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + mut condition: F, + ) -> MutexGuard<'a, T> + where + F: FnMut(&mut T) -> bool, + { + while !condition(&mut *guard) { + guard = self.wait(guard).await; + } + guard + } + + /// Waits on this condition variable for a notification, timing out after a specified duration. + /// + /// For these reasons `Condvar::wait_timeout_until` is recommended in most cases. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// loop { + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).await; + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// # + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_timeout<'a, T>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { + let mutex = guard_lock(&guard); + match timeout(dur, self.wait(guard)).await { + Ok(guard) => (guard, WaitTimeoutResult(false)), + Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), + } + } + + /// Waits on this condition variable for a notification, timing out after a specified duration. + /// Spurious wakes will not cause this function to return. + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let result = cvar.wait_timeout_until( + /// lock.lock().await, + /// Duration::from_millis(100), + /// |&mut started| started, + /// ).await; + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to true. + /// } + /// // access the locked mutex via result.0 + /// # }); + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_timeout_until<'a, T, F>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + condition: F, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) + where + F: FnMut(&mut T) -> bool, + { + let mutex = guard_lock(&guard); + match timeout(dur, self.wait_until(guard, condition)).await { + Ok(guard) => (guard, WaitTimeoutResult(false)), + Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), + } + } + + /// Wakes up one blocked task on this condvar. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # }) } + /// ``` + pub fn notify_one(&self) { + self.wakers.notify_one(); + } + + /// Wakes up all blocked tasks on this condvar. + /// + /// # Examples + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_all(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # + /// # }) } + /// ``` + pub fn notify_all(&self) { + self.wakers.notify_all(); + } +} + +impl fmt::Debug for Condvar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Condvar { .. }") + } +} + +/// A future that waits for another task to notify the condition variable. +/// +/// This is an internal future that `wait` and `wait_until` await on. +struct AwaitNotify<'a, 'b, T> { + /// The condition variable that we are waiting on + cond: &'a Condvar, + /// The lock used with `cond`. + /// This will be released the first time the future is polled, + /// after registering the context to be notified. + guard: Option>, + /// A key into the conditions variable's `WakerSet`. + /// This is set to the index of the `Waker` for the context each time + /// the future is polled and not completed. + key: Option, +} + +impl<'a, 'b, T> Future for AwaitNotify<'a, 'b, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.guard.take() { + Some(_) => { + self.key = Some(self.cond.wakers.insert(cx)); + // the guard is dropped when we return, which frees the lock + Poll::Pending + } + None => { + if let Some(key) = self.key { + if self.cond.wakers.remove_if_notified(key, cx) { + self.key = None; + Poll::Ready(()) + } else { + Poll::Pending + } + } else { + // This should only happen if it is polled twice after receiving a notification + Poll::Ready(()) + } + } + } + } +} + +impl<'a, 'b, T> Drop for AwaitNotify<'a, 'b, T> { + fn drop(&mut self) { + if let Some(key) = self.key { + self.cond.wakers.cancel(key); + } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 82759fb6b..1531f8c57 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -185,8 +185,10 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; pub use channel::{channel, Sender, Receiver, RecvError, TryRecvError, TrySendError}; + pub use condvar::Condvar; mod barrier; + mod condvar; mod channel; } diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index c62b5616a..ae953fd82 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -287,3 +287,8 @@ impl DerefMut for MutexGuard<'_, T> { unsafe { &mut *self.0.value.get() } } } + +#[cfg(feature = "unstable")] +pub fn guard_lock<'a, T>(guard: &MutexGuard<'a, T>) -> &'a Mutex { + guard.0 +} diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 7e897af15..881304bac 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -80,6 +80,28 @@ impl WakerSet { } } + /// If the waker for this key is still waiting for a notification, then update + /// the waker for the entry, and return false. If the waker has been notified, + /// treat the entry as completed and return true. + #[cfg(feature = "unstable")] + pub fn remove_if_notified(&self, key: usize, cx: &Context<'_>) -> bool { + let mut inner = self.lock(); + + match &mut inner.entries[key] { + None => { + inner.entries.remove(key); + true + } + Some(w) => { + // We were never woken, so update instead + if !w.will_wake(cx.waker()) { + *w = cx.waker().clone(); + } + false + } + } + } + /// Removes the waker of a cancelled operation. /// /// Returns `true` if another blocked operation from the set was notified. diff --git a/tests/condvar.rs b/tests/condvar.rs new file mode 100644 index 000000000..c4d680fc9 --- /dev/null +++ b/tests/condvar.rs @@ -0,0 +1,91 @@ +#![cfg(feature = "unstable")] +use std::sync::Arc; +use std::time::Duration; + +use async_std::sync::{Condvar, Mutex}; +use async_std::task::{self, JoinHandle}; + +#[test] +fn wait_timeout_with_lock() { + task::block_on(async { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + task::spawn(async move { + let (m, c) = &*pair2; + let _g = m.lock().await; + task::sleep(Duration::from_millis(20)).await; + c.notify_one(); + }); + + let (m, c) = &*pair; + let (_, wait_result) = c + .wait_timeout(m.lock().await, Duration::from_millis(10)) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +fn wait_timeout_without_lock() { + task::block_on(async { + let m = Mutex::new(false); + let c = Condvar::new(); + + let (_, wait_result) = c + .wait_timeout(m.lock().await, Duration::from_millis(10)) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +fn wait_timeout_until_timed_out() { + task::block_on(async { + let m = Mutex::new(false); + let c = Condvar::new(); + + let (_, wait_result) = c + .wait_timeout_until(m.lock().await, Duration::from_millis(10), |&mut started| { + started + }) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +fn notify_all() { + task::block_on(async { + let mut tasks: Vec> = Vec::new(); + let pair = Arc::new((Mutex::new(0u32), Condvar::new())); + + for _ in 0..10 { + let pair = pair.clone(); + tasks.push(task::spawn(async move { + let (m, c) = &*pair; + let mut count = m.lock().await; + while *count == 0 { + count = c.wait(count).await; + } + *count += 1; + })); + } + + // Give some time for tasks to start up + task::sleep(Duration::from_millis(5)).await; + + let (m, c) = &*pair; + { + let mut count = m.lock().await; + *count += 1; + c.notify_all(); + } + + for t in tasks { + t.await; + } + let count = m.lock().await; + assert_eq!(11, *count); + }) +} From e707ea96e077ae66861ab8bd00e95eb3f78b829c Mon Sep 17 00:00:00 2001 From: Fangdun Cai Date: Mon, 27 Apr 2020 00:18:21 +0800 Subject: [PATCH 052/272] docs(readme): add ci status badge --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 4ebf5924a..7550cd687 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@
+ + + CI Status + Date: Mon, 27 Apr 2020 01:23:09 +0900 Subject: [PATCH 053/272] ci: speed up github actions --- .github/workflows/ci.yml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcda99060..e9fcdcc6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,24 @@ jobs: toolchain: ${{ matrix.rust }} override: true + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }} + + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.toml') }} + + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }} + - name: check uses: actions-rs/cargo@v1 with: @@ -66,12 +84,6 @@ jobs: command: test args: --all --features "unstable attributes" - - name: documentation test - uses: actions-rs/cargo@v1 - with: - command: test - args: --doc --features "unstable attributes" - build__with_no_std: name: Build with no-std runs-on: ubuntu-latest @@ -117,15 +129,3 @@ jobs: - name: Docs run: cargo doc --features docs - - # clippy_check: - # name: Clippy check - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v1 - # - name: Install rust - # run: rustup update beta && rustup default beta - # - name: Install clippy - # run: rustup component add clippy - # - name: clippy - # run: cargo clippy --all --features unstable From 100c3423c186e7053fca11df078bc0aa9903c293 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 27 Apr 2020 13:49:53 +0800 Subject: [PATCH 054/272] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thank you.😁 Co-Authored-By: Friedel Ziegelmayer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69ed7eadd..cdbbaa633 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ documentation] on how to enable them. * [Xactor](https://crates.io/crates/xactor) — Xactor is a rust actors framework based on async-std. - * [async-graphql](https://crates.io/crates/async-graphql) — The GraphQL server library implemented by rust, fully support async/await. + * [async-graphql](https://crates.io/crates/async-graphql) — A GraphQL server library implemented in rust, with full support for async/await. ## License From 690ab165875cd8ed2dedb67473099d6e724034e2 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 24 Apr 2020 16:16:41 +0200 Subject: [PATCH 055/272] add dependency --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index d49eb957b..45662bd8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ default = [ "mio-uds", "num_cpus", "pin-project-lite", + "smol", ] docs = ["attributes", "unstable", "default"] unstable = ["std", "broadcaster", "futures-timer"] @@ -74,6 +75,7 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } +smol = { path = "../smol", optional = true } [dev-dependencies] femme = "1.3.0" From 1308fbdf55cfc585e6403fcecf8657a17751519f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 24 Apr 2020 18:43:31 +0200 Subject: [PATCH 056/272] switch to smol instead of an internal runtime --- Cargo.toml | 2 + src/net/tcp/listener.rs | 67 +++--- src/net/tcp/stream.rs | 89 ++++---- src/net/udp/mod.rs | 116 +++------- src/os/unix/net/datagram.rs | 47 ++-- src/os/unix/net/listener.rs | 54 ++--- src/os/unix/net/stream.rs | 54 ++--- src/rt/mod.rs | 24 +-- src/rt/reactor.rs | 354 ------------------------------ src/rt/runtime.rs | 415 ------------------------------------ src/sync/mod.rs | 10 +- src/sync/spin_lock.rs | 89 -------- src/task/block_on.rs | 83 +------- src/task/builder.rs | 29 +-- src/task/join_handle.rs | 13 +- src/task/mod.rs | 1 - src/task/spawn_blocking.rs | 88 +------- src/task/yield_now.rs | 4 - src/utils.rs | 34 --- tests/mutex.rs | 17 +- 20 files changed, 210 insertions(+), 1380 deletions(-) delete mode 100644 src/rt/reactor.rs delete mode 100644 src/rt/runtime.rs delete mode 100644 src/sync/spin_lock.rs diff --git a/Cargo.toml b/Cargo.toml index 45662bd8f..3bc9084ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ std = [ "once_cell", "pin-utils", "slab", + "piper", ] alloc = [ "futures-core/alloc", @@ -76,6 +77,7 @@ pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } smol = { path = "../smol", optional = true } +piper = { git = "https://github.com/stjepang/piper.git", branch = "master", optional = true } [dev-dependencies] femme = "1.3.0" diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 9e15d40f6..290da0d1d 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,13 +1,13 @@ use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; -use std::sync::Arc; -use crate::future; +use smol::Async; + use crate::io; -use crate::rt::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; +use crate::sync::Arc; use crate::task::{Context, Poll}; /// A TCP socket server, listening for connections. @@ -49,7 +49,7 @@ use crate::task::{Context, Poll}; /// ``` #[derive(Debug)] pub struct TcpListener { - watcher: Watcher, + watcher: Async, } impl TcpListener { @@ -79,11 +79,9 @@ impl TcpListener { let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - match mio::net::TcpListener::bind(&addr) { - Ok(mio_listener) => { - return Ok(TcpListener { - watcher: Watcher::new(mio_listener), - }); + match Async::::bind(&addr) { + Ok(listener) => { + return Ok(TcpListener { watcher: listener }); } Err(err) => last_err = Some(err), } @@ -114,13 +112,9 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let (io, addr) = - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std())) - .await?; - - let mio_stream = mio::net::TcpStream::from_stream(io)?; + let (stream, addr) = self.watcher.accept().await?; let stream = TcpStream { - watcher: Arc::new(Watcher::new(mio_stream)), + watcher: Arc::new(stream), }; Ok((stream, addr)) } @@ -206,9 +200,8 @@ impl<'a> Stream for Incoming<'a> { impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. fn from(listener: std::net::TcpListener) -> TcpListener { - let mio_listener = mio::net::TcpListener::from_std(listener).unwrap(); TcpListener { - watcher: Watcher::new(mio_listener), + watcher: Async::new(listener).expect("TcpListener is known to be good"), } } } @@ -230,29 +223,29 @@ cfg_unix! { impl IntoRawFd for TcpListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_raw_fd() } } } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for TcpListener { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpListener { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { - // net::TcpListener::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + + impl AsRawSocket for TcpListener { + fn as_raw_socket(&self) -> RawSocket { + self.watcher.as_raw_socket() + } + } + + impl FromRawSocket for TcpListener { + unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { + net::TcpListener::from_raw_socket(handle).try_into().unwrap() + } + } + + impl IntoRawSocket for TcpListener { + fn into_raw_socket(self) -> RawSocket { + self.watcher.into_raw_socket() + } + } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1f50e8f1e..1d2d0ce14 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,12 +1,12 @@ -use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; +use std::io::{IoSlice, IoSliceMut}; use std::net::SocketAddr; use std::pin::Pin; -use std::sync::Arc; -use crate::future; +use smol::Async; + use crate::io::{self, Read, Write}; -use crate::rt::Watcher; use crate::net::ToSocketAddrs; +use crate::sync::Arc; use crate::task::{Context, Poll}; /// A TCP stream between a local and a remote socket. @@ -47,7 +47,7 @@ use crate::task::{Context, Poll}; /// ``` #[derive(Debug, Clone)] pub struct TcpStream { - pub(super) watcher: Arc>, + pub(super) watcher: Arc>, } impl TcpStream { @@ -75,28 +75,16 @@ impl TcpStream { let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - // mio's TcpStream::connect is non-blocking and may just be in progress - // when it returns with `Ok`. We therefore wait for write readiness to - // be sure the connection has either been established or there was an - // error which we check for afterwards. - let watcher = match mio::net::TcpStream::connect(&addr) { - Ok(s) => Watcher::new(s), + match Async::::connect(&addr).await { + Ok(stream) => { + return Ok(TcpStream { + watcher: Arc::new(stream), + }); + } Err(e) => { last_err = Some(e); continue; } - }; - - future::poll_fn(|cx| watcher.poll_write_ready(cx)).await; - - match watcher.get_ref().take_error() { - Ok(None) => { - return Ok(TcpStream { - watcher: Arc::new(watcher), - }); - } - Ok(Some(e)) => last_err = Some(e), - Err(e) => last_err = Some(e), } } @@ -214,7 +202,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await + self.watcher.peek(buf).await } /// Gets the value of the `TCP_NODELAY` option on this socket. @@ -317,7 +305,7 @@ impl Read for &TcpStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) + Pin::new(&mut &*self.watcher).poll_read(cx, buf) } } @@ -353,26 +341,23 @@ impl Write for &TcpStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher - .poll_write_with(cx, |mut inner| inner.write(buf)) + Pin::new(&mut &*self.watcher).poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.flush()) + Pin::new(&mut &*self.watcher).poll_flush(cx) } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - self.shutdown(std::net::Shutdown::Write)?; - Poll::Ready(Ok(())) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut &*self.watcher).poll_close(cx) } } impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. fn from(stream: std::net::TcpStream) -> TcpStream { - let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); TcpStream { - watcher: Arc::new(Watcher::new(mio_stream)), + watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")), } } } @@ -403,23 +388,23 @@ cfg_unix! { } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for TcpStream { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpStream { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { - // net::TcpStream::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + + impl AsRawSocket for TcpStream { + fn as_raw_socket(&self) -> RawSocket { + self.raw_socket + } + } + + impl FromRawSocket for TcpStream { + unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { + net::TcpStream::from_raw_socket(handle).try_into().unwrap() + } + } + + impl IntoRawSocket for TcpListener { + fn into_raw_socket(self) -> RawSocket { + self.raw_socket + } + } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 774478d3b..3bc9ad777 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -2,9 +2,9 @@ use std::io; use std::net::SocketAddr; use std::net::{Ipv4Addr, Ipv6Addr}; -use crate::future; +use smol::Async; + use crate::net::ToSocketAddrs; -use crate::rt::Watcher; use crate::utils::Context as _; /// A UDP socket. @@ -45,7 +45,7 @@ use crate::utils::Context as _; /// ``` #[derive(Debug)] pub struct UdpSocket { - watcher: Watcher, + watcher: Async, } impl UdpSocket { @@ -69,16 +69,12 @@ impl UdpSocket { /// ``` pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; - let addrs = addrs - .to_socket_addrs() - .await?; + let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - match mio::net::UdpSocket::bind(&addr) { - Ok(mio_socket) => { - return Ok(UdpSocket { - watcher: Watcher::new(mio_socket), - }); + match Async::::bind(&addr) { + Ok(socket) => { + return Ok(UdpSocket { watcher: socket }); } Err(err) => last_err = Some(err), } @@ -153,12 +149,10 @@ impl UdpSocket { } }; - future::poll_fn(|cx| { - self.watcher - .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) - }) - .await - .context(|| format!("could not send packet to {}", addr)) + self.watcher + .send_to(buf, addr) + .await + .context(|| format!("could not send packet to {}", addr)) } /// Receives data from the socket. @@ -181,22 +175,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - future::poll_fn(|cx| { - self.watcher - .poll_read_with(cx, |inner| inner.recv_from(buf)) - }) - .await - .context(|| { - use std::fmt::Write; - - let mut error = String::from("could not receive data on "); - if let Ok(addr) = self.local_addr() { - let _ = write!(&mut error, "{}", addr); - } else { - error.push_str("socket"); - } - error - }) + self.watcher.recv_from(buf).await } /// Connects the UDP socket to a remote address. @@ -267,19 +246,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))) - .await - .context(|| { - use std::fmt::Write; - - let mut error = String::from("could not send data on "); - if let Ok(addr) = self.local_addr() { - let _ = write!(&mut error, "{}", addr); - } else { - error.push_str("socket"); - } - error - }) + self.watcher.send(buf).await } /// Receives data from the socket. @@ -303,19 +270,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))) - .await - .context(|| { - use std::fmt::Write; - - let mut error = String::from("could not receive data on "); - if let Ok(addr) = self.local_addr() { - let _ = write!(&mut error, "{}", addr); - } else { - error.push_str("socket"); - } - error - }) + self.watcher.recv(buf).await } /// Gets the value of the `SO_BROADCAST` option for this socket. @@ -498,9 +453,8 @@ impl UdpSocket { impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. fn from(socket: std::net::UdpSocket) -> UdpSocket { - let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap(); UdpSocket { - watcher: Watcher::new(mio_socket), + watcher: Async::new(socket).expect("UdpSocket is known to be good"), } } } @@ -522,29 +476,29 @@ cfg_unix! { impl IntoRawFd for UdpSocket { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_raw_fd() } } } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for UdpSocket { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for UdpSocket { - // unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { - // net::UdpSocket::from_raw_socket(handle).into() - // } - // } - // - // impl IntoRawSocket for UdpSocket { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + + impl AsRawSocket for UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + self.watcher.as_raw_socket() + } + } + + impl FromRawSocket for UdpSocket { + unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { + net::UdpSocket::from_raw_socket(handle).into() + } + } + + impl IntoRawSocket for UdpSocket { + fn into_raw_socket(self) -> RawSocket { + self.watcher.into_raw_socket() + } + } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 5a2d6ec91..6a98736c7 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -2,16 +2,14 @@ use std::fmt; use std::net::Shutdown; +use std::os::unix::net::UnixDatagram as StdUnixDatagram; -use mio_uds; +use smol::Async; use super::SocketAddr; -use crate::future; use crate::io; -use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::spawn_blocking; /// A Unix datagram socket. /// @@ -42,13 +40,13 @@ use crate::task::spawn_blocking; /// # Ok(()) }) } /// ``` pub struct UnixDatagram { - watcher: Watcher, + watcher: Async, } impl UnixDatagram { - fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { + fn new(socket: StdUnixDatagram) -> UnixDatagram { UnixDatagram { - watcher: Watcher::new(socket), + watcher: Async::new(socket).expect("UnixDatagram is known to be good"), } } @@ -67,8 +65,8 @@ impl UnixDatagram { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let socket = spawn_blocking(move || mio_uds::UnixDatagram::bind(path)).await?; - Ok(UnixDatagram::new(socket)) + let socket = Async::::bind(path)?; + Ok(UnixDatagram { watcher: socket }) } /// Creates a Unix datagram which is not bound to any address. @@ -85,7 +83,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn unbound() -> io::Result { - let socket = mio_uds::UnixDatagram::unbound()?; + let socket = StdUnixDatagram::unbound()?; Ok(UnixDatagram::new(socket)) } @@ -105,7 +103,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { - let (a, b) = mio_uds::UnixDatagram::pair()?; + let (a, b) = StdUnixDatagram::pair()?; let a = UnixDatagram::new(a); let b = UnixDatagram::new(b); Ok((a, b)) @@ -197,11 +195,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - future::poll_fn(|cx| { - self.watcher - .poll_read_with(cx, |inner| inner.recv_from(buf)) - }) - .await + self.watcher.recv_from(buf).await } /// Receives data from the socket. @@ -222,7 +216,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await + self.watcher.recv(buf).await } /// Sends data on the socket to the specified address. @@ -242,11 +236,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { - future::poll_fn(|cx| { - self.watcher - .poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref())) - }) - .await + self.watcher.send_to(buf, path.as_ref()).await } /// Sends data on the socket to the socket's peer. @@ -267,7 +257,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await + self.watcher.send(buf).await } /// Shut down the read, write, or both halves of this connection. @@ -312,19 +302,18 @@ impl fmt::Debug for UnixDatagram { } } -impl From for UnixDatagram { +impl From for UnixDatagram { /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. - fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { - let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap(); + fn from(datagram: StdUnixDatagram) -> UnixDatagram { UnixDatagram { - watcher: Watcher::new(mio_datagram), + watcher: Async::new(datagram).expect("UnixDatagram is known to be good"), } } } impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } @@ -337,6 +326,6 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_raw_fd() } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 9f6bdcbc5..4099bd6f5 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -1,20 +1,19 @@ //! Unix-specific networking extensions. use std::fmt; -use std::pin::Pin; use std::future::Future; +use std::os::unix::net::UnixListener as StdUnixListener; +use std::pin::Pin; -use mio_uds; +use smol::Async; use super::SocketAddr; use super::UnixStream; -use crate::future; use crate::io; -use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; -use crate::task::{spawn_blocking, Context, Poll}; +use crate::task::{Context, Poll}; /// A Unix domain socket server, listening for connections. /// @@ -50,7 +49,7 @@ use crate::task::{spawn_blocking, Context, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixListener { - watcher: Watcher, + watcher: Async, } impl UnixListener { @@ -69,11 +68,9 @@ impl UnixListener { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let listener = spawn_blocking(move || mio_uds::UnixListener::bind(path)).await?; + let listener = Async::::bind(path)?; - Ok(UnixListener { - watcher: Watcher::new(listener), - }) + Ok(UnixListener { watcher: listener }) } /// Accepts a new incoming connection to this listener. @@ -93,29 +90,9 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { - future::poll_fn(|cx| { - let res = futures_core::ready!(self.watcher.poll_read_with(cx, |inner| { - match inner.accept_std() { - // Converting to `WouldBlock` so that the watcher will - // add the waker of this task to a list of readers. - Ok(None) => Err(io::ErrorKind::WouldBlock.into()), - res => res, - } - })); - - match res? { - Some((io, addr)) => { - let mio_stream = mio_uds::UnixStream::from_stream(io)?; - let stream = UnixStream { - watcher: Watcher::new(mio_stream), - }; - Poll::Ready(Ok((stream, addr))) - } - // This should never happen since `None` is converted to `WouldBlock` - None => unreachable!(), - } - }) - .await + let (stream, addr) = self.watcher.accept().await?; + + Ok((UnixStream { watcher: stream }, addr)) } /// Returns a stream of incoming connections. @@ -206,19 +183,18 @@ impl Stream for Incoming<'_> { } } -impl From for UnixListener { +impl From for UnixListener { /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. - fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { - let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap(); + fn from(listener: StdUnixListener) -> UnixListener { UnixListener { - watcher: Watcher::new(mio_listener), + watcher: Async::new(listener).expect("UnixListener is known to be good"), } } } impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } @@ -231,6 +207,6 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_raw_fd() } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index a1c83f1b9..7320c85be 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -1,18 +1,17 @@ //! Unix-specific networking extensions. use std::fmt; -use std::io::{Read as _, Write as _}; use std::net::Shutdown; +use std::os::unix::net::UnixStream as StdUnixStream; use std::pin::Pin; -use mio_uds; +use smol::Async; use super::SocketAddr; use crate::io::{self, Read, Write}; -use crate::rt::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::{spawn_blocking, Context, Poll}; +use crate::task::{Context, Poll}; /// A Unix stream socket. /// @@ -38,7 +37,7 @@ use crate::task::{spawn_blocking, Context, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixStream { - pub(super) watcher: Watcher, + pub(super) watcher: Async, } impl UnixStream { @@ -57,15 +56,9 @@ impl UnixStream { /// ``` pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); + let stream = Async::::connect(path).await?; - spawn_blocking(move || { - let std_stream = std::os::unix::net::UnixStream::connect(path)?; - let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?; - Ok(UnixStream { - watcher: Watcher::new(mio_stream), - }) - }) - .await + Ok(UnixStream { watcher: stream }) } /// Creates an unnamed pair of connected sockets. @@ -84,13 +77,9 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - let (a, b) = mio_uds::UnixStream::pair()?; - let a = UnixStream { - watcher: Watcher::new(a), - }; - let b = UnixStream { - watcher: Watcher::new(b), - }; + let (a, b) = Async::::pair()?; + let a = UnixStream { watcher: a }; + let b = UnixStream { watcher: b }; Ok((a, b)) } @@ -169,7 +158,7 @@ impl Read for &UnixStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) + Pin::new(&mut &self.watcher).poll_read(cx, buf) } } @@ -197,16 +186,15 @@ impl Write for &UnixStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher - .poll_write_with(cx, |mut inner| inner.write(buf)) + Pin::new(&mut &self.watcher).poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.flush()) + Pin::new(&mut &self.watcher).poll_flush(cx) } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut &self.watcher).poll_close(cx) } } @@ -227,19 +215,17 @@ impl fmt::Debug for UnixStream { } } -impl From for UnixStream { +impl From for UnixStream { /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. - fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { - let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); - UnixStream { - watcher: Watcher::new(mio_stream), - } + fn from(stream: StdUnixStream) -> UnixStream { + let stream = Async::new(stream).expect("UnixStream is known to be good"); + UnixStream { watcher: stream } } } impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } @@ -252,6 +238,6 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_raw_fd() } } diff --git a/src/rt/mod.rs b/src/rt/mod.rs index 2149d2420..d5d0d6105 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -4,20 +4,20 @@ use std::thread; use once_cell::sync::Lazy; -use crate::utils::abort_on_panic; +use crate::future; -pub use reactor::{Reactor, Watcher}; -pub use runtime::Runtime; - -mod reactor; -mod runtime; +/// Dummy runtime struct. +pub struct Runtime {} /// The global runtime. pub static RUNTIME: Lazy = Lazy::new(|| { - thread::Builder::new() - .name("async-std/runtime".to_string()) - .spawn(|| abort_on_panic(|| RUNTIME.run())) - .expect("cannot start a runtime thread"); - - Runtime::new() + // Create an executor thread pool. + let num_threads = num_cpus::get().max(1); + for _ in 0..num_threads { + thread::Builder::new() + .name("async-std/runtime".to_string()) + .spawn(|| smol::run(future::pending::<()>())) + .expect("cannot start a runtime thread"); + } + Runtime {} }); diff --git a/src/rt/reactor.rs b/src/rt/reactor.rs deleted file mode 100644 index 2a35b72c5..000000000 --- a/src/rt/reactor.rs +++ /dev/null @@ -1,354 +0,0 @@ -use std::fmt; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -use mio::{self, Evented}; -use slab::Slab; - -use crate::io; -use crate::rt::RUNTIME; -use crate::task::{Context, Poll, Waker}; - -/// Data associated with a registered I/O handle. -#[derive(Debug)] -struct Entry { - /// A unique identifier. - token: mio::Token, - - /// Tasks that are blocked on reading from this I/O handle. - readers: Mutex, - - /// Tasks that are blocked on writing to this I/O handle. - writers: Mutex, -} - -/// The state of a networking driver. -pub struct Reactor { - /// A mio instance that polls for new events. - poller: mio::Poll, - - /// A list into which mio stores events. - events: Mutex, - - /// A collection of registered I/O handles. - entries: Mutex>>, - - /// Dummy I/O handle that is only used to wake up the polling thread. - notify_reg: (mio::Registration, mio::SetReadiness), - - /// An identifier for the notification handle. - notify_token: mio::Token, -} - -/// The set of `Waker`s interested in read readiness. -#[derive(Debug)] -struct Readers { - /// Flag indicating read readiness. - /// (cf. `Watcher::poll_read_ready`) - ready: bool, - /// The `Waker`s blocked on reading. - wakers: Vec, -} - -/// The set of `Waker`s interested in write readiness. -#[derive(Debug)] -struct Writers { - /// Flag indicating write readiness. - /// (cf. `Watcher::poll_write_ready`) - ready: bool, - /// The `Waker`s blocked on writing. - wakers: Vec, -} - -impl Reactor { - /// Creates a new reactor for polling I/O events. - pub fn new() -> io::Result { - let poller = mio::Poll::new()?; - let notify_reg = mio::Registration::new2(); - - let mut reactor = Reactor { - poller, - events: Mutex::new(mio::Events::with_capacity(1000)), - entries: Mutex::new(Slab::new()), - notify_reg, - notify_token: mio::Token(0), - }; - - // Register a dummy I/O handle for waking up the polling thread. - let entry = reactor.register(&reactor.notify_reg.0)?; - reactor.notify_token = entry.token; - - Ok(reactor) - } - - /// Registers an I/O event source and returns its associated entry. - fn register(&self, source: &dyn Evented) -> io::Result> { - let mut entries = self.entries.lock().unwrap(); - - // Reserve a vacant spot in the slab and use its key as the token value. - let vacant = entries.vacant_entry(); - let token = mio::Token(vacant.key()); - - // Allocate an entry and insert it into the slab. - let entry = Arc::new(Entry { - token, - readers: Mutex::new(Readers { - ready: false, - wakers: Vec::new(), - }), - writers: Mutex::new(Writers { - ready: false, - wakers: Vec::new(), - }), - }); - vacant.insert(entry.clone()); - - // Register the I/O event source in the poller. - let interest = mio::Ready::all(); - let opts = mio::PollOpt::edge(); - self.poller.register(source, token, interest, opts)?; - - Ok(entry) - } - - /// Deregisters an I/O event source associated with an entry. - fn deregister(&self, source: &dyn Evented, entry: &Entry) -> io::Result<()> { - // Deregister the I/O object from the mio instance. - self.poller.deregister(source)?; - - // Remove the entry associated with the I/O object. - self.entries.lock().unwrap().remove(entry.token.0); - - Ok(()) - } - - /// Notifies the reactor so that polling stops blocking. - pub fn notify(&self) -> io::Result<()> { - self.notify_reg.1.set_readiness(mio::Ready::readable()) - } - - /// Waits on the poller for new events and wakes up tasks blocked on I/O handles. - /// - /// Returns `Ok(true)` if at least one new task was woken. - pub fn poll(&self, timeout: Option) -> io::Result { - let mut events = self.events.lock().unwrap(); - - // Block on the poller until at least one new event comes in. - self.poller.poll(&mut events, timeout)?; - - // Lock the entire entry table while we're processing new events. - let entries = self.entries.lock().unwrap(); - - // The number of woken tasks. - let mut progress = false; - - for event in events.iter() { - let token = event.token(); - - if token == self.notify_token { - // If this is the notification token, we just need the notification state. - self.notify_reg.1.set_readiness(mio::Ready::empty())?; - } else { - // Otherwise, look for the entry associated with this token. - if let Some(entry) = entries.get(token.0) { - // Set the readiness flags from this I/O event. - let readiness = event.readiness(); - - // Wake up reader tasks blocked on this I/O handle. - let reader_interests = mio::Ready::all() - mio::Ready::writable(); - if !(readiness & reader_interests).is_empty() { - let mut readers = entry.readers.lock().unwrap(); - readers.ready = true; - for w in readers.wakers.drain(..) { - w.wake(); - progress = true; - } - } - - // Wake up writer tasks blocked on this I/O handle. - let writer_interests = mio::Ready::all() - mio::Ready::readable(); - if !(readiness & writer_interests).is_empty() { - let mut writers = entry.writers.lock().unwrap(); - writers.ready = true; - for w in writers.wakers.drain(..) { - w.wake(); - progress = true; - } - } - } - } - } - - Ok(progress) - } -} - -/// An I/O handle powered by the networking driver. -/// -/// This handle wraps an I/O event source and exposes a "futurized" interface on top of it, -/// implementing traits `AsyncRead` and `AsyncWrite`. -pub struct Watcher { - /// Data associated with the I/O handle. - entry: Arc, - - /// The I/O event source. - source: Option, -} - -impl Watcher { - /// Creates a new I/O handle. - /// - /// The provided I/O event source will be kept registered inside the reactor's poller for the - /// lifetime of the returned I/O handle. - pub fn new(source: T) -> Watcher { - Watcher { - entry: RUNTIME - .reactor() - .register(&source) - .expect("cannot register an I/O event source"), - source: Some(source), - } - } - - /// Returns a reference to the inner I/O event source. - pub fn get_ref(&self) -> &T { - self.source.as_ref().unwrap() - } - - /// Polls the inner I/O source for a non-blocking read operation. - /// - /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task - /// will be registered for wakeup when the I/O source becomes readable. - pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll> - where - F: FnMut(&'a T) -> io::Result, - { - // If the operation isn't blocked, return its result. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Lock the waker list. - let mut readers = self.entry.readers.lock().unwrap(); - - // Try running the operation again. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Register the task if it isn't registered already. - - if readers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { - readers.wakers.push(cx.waker().clone()); - } - - Poll::Pending - } - - /// Polls the inner I/O source for a non-blocking write operation. - /// - /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task - /// will be registered for wakeup when the I/O source becomes writable. - pub fn poll_write_with<'a, F, R>( - &'a self, - cx: &mut Context<'_>, - mut f: F, - ) -> Poll> - where - F: FnMut(&'a T) -> io::Result, - { - // If the operation isn't blocked, return its result. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Lock the waker list. - let mut writers = self.entry.writers.lock().unwrap(); - - // Try running the operation again. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Register the task if it isn't registered already. - if writers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { - writers.wakers.push(cx.waker().clone()); - } - - Poll::Pending - } - - /// Polls the inner I/O source until a non-blocking read can be performed. - /// - /// If non-blocking reads are currently not possible, the `Waker` - /// will be saved and notified when it can read non-blocking - /// again. - #[allow(dead_code)] - pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<()> { - // Lock the waker list. - let mut readers = self.entry.readers.lock().unwrap(); - if readers.ready { - return Poll::Ready(()); - } - // Register the task if it isn't registered already. - if readers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { - readers.wakers.push(cx.waker().clone()); - } - Poll::Pending - } - - /// Polls the inner I/O source until a non-blocking write can be performed. - /// - /// If non-blocking writes are currently not possible, the `Waker` - /// will be saved and notified when it can write non-blocking - /// again. - pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<()> { - // Lock the waker list. - let mut writers = self.entry.writers.lock().unwrap(); - if writers.ready { - return Poll::Ready(()); - } - // Register the task if it isn't registered already. - if writers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { - writers.wakers.push(cx.waker().clone()); - } - Poll::Pending - } - - /// Deregisters and returns the inner I/O source. - /// - /// This method is typically used to convert `Watcher`s to raw file descriptors/handles. - #[allow(dead_code)] - pub fn into_inner(mut self) -> T { - let source = self.source.take().unwrap(); - RUNTIME - .reactor() - .deregister(&source, &self.entry) - .expect("cannot deregister I/O event source"); - source - } -} - -impl Drop for Watcher { - fn drop(&mut self) { - if let Some(ref source) = self.source { - RUNTIME - .reactor() - .deregister(source, &self.entry) - .expect("cannot deregister I/O event source"); - } - } -} - -impl fmt::Debug for Watcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Watcher") - .field("entry", &self.entry) - .field("source", &self.source) - .finish() - } -} diff --git a/src/rt/runtime.rs b/src/rt/runtime.rs deleted file mode 100644 index a0d88b983..000000000 --- a/src/rt/runtime.rs +++ /dev/null @@ -1,415 +0,0 @@ -use std::cell::Cell; -use std::io; -use std::iter; -use std::ptr; -use std::sync::atomic::{self, Ordering}; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::Duration; - -use crossbeam_deque::{Injector, Steal, Stealer, Worker}; -use crossbeam_utils::thread::scope; -use once_cell::unsync::OnceCell; - -use crate::rt::Reactor; -use crate::sync::Spinlock; -use crate::task::Runnable; -use crate::utils::{abort_on_panic, random}; - -thread_local! { - /// A reference to the current machine, if the current thread runs tasks. - static MACHINE: OnceCell> = OnceCell::new(); - - /// This flag is set to true whenever `task::yield_now()` is invoked. - static YIELD_NOW: Cell = Cell::new(false); -} - -struct Scheduler { - /// Set to `true` while a machine is polling the reactor. - polling: bool, - - /// Idle processors. - processors: Vec, - - /// Running machines. - machines: Vec>, -} - -/// An async runtime. -pub struct Runtime { - /// The reactor. - reactor: Reactor, - - /// The global queue of tasks. - injector: Injector, - - /// Handles to local queues for stealing work. - stealers: Vec>, - - /// The scheduler state. - sched: Mutex, -} - -impl Runtime { - /// Creates a new runtime. - pub fn new() -> Runtime { - let cpus = num_cpus::get().max(1); - let processors: Vec<_> = (0..cpus).map(|_| Processor::new()).collect(); - let stealers = processors.iter().map(|p| p.worker.stealer()).collect(); - - Runtime { - reactor: Reactor::new().unwrap(), - injector: Injector::new(), - stealers, - sched: Mutex::new(Scheduler { - processors, - machines: Vec::new(), - polling: false, - }), - } - } - - /// Returns a reference to the reactor. - pub fn reactor(&self) -> &Reactor { - &self.reactor - } - - /// Flushes the task slot so that tasks get run more fairly. - pub fn yield_now(&self) { - YIELD_NOW.with(|flag| flag.set(true)); - } - - /// Schedules a task. - pub fn schedule(&self, task: Runnable) { - MACHINE.with(|machine| { - // If the current thread is a worker thread, schedule it onto the current machine. - // Otherwise, push it into the global task queue. - match machine.get() { - None => { - self.injector.push(task); - self.notify(); - } - Some(m) => m.schedule(&self, task), - } - }); - } - - /// Runs the runtime on the current thread. - pub fn run(&self) { - scope(|s| { - let mut idle = 0; - let mut delay = 0; - - loop { - // Get a list of new machines to start, if any need to be started. - for m in self.make_machines() { - idle = 0; - - s.builder() - .name("async-std/machine".to_string()) - .spawn(move |_| { - abort_on_panic(|| { - let _ = MACHINE.with(|machine| machine.set(m.clone())); - m.run(self); - }) - }) - .expect("cannot start a machine thread"); - } - - // Sleep for a bit longer if the scheduler state hasn't changed in a while. - if idle > 10 { - delay = (delay * 2).min(10_000); - } else { - idle += 1; - delay = 1000; - } - - thread::sleep(Duration::from_micros(delay)); - } - }) - .unwrap(); - } - - /// Returns a list of machines that need to be started. - fn make_machines(&self) -> Vec> { - let mut sched = self.sched.lock().unwrap(); - let mut to_start = Vec::new(); - - // If no machine has been polling the reactor in a while, that means the runtime is - // overloaded with work and we need to start another machine. - if !sched.polling { - if let Some(p) = sched.processors.pop() { - let m = Arc::new(Machine::new(p)); - to_start.push(m.clone()); - sched.machines.push(m); - } - } - - to_start - } - - /// Unparks a thread polling the reactor. - fn notify(&self) { - atomic::fence(Ordering::SeqCst); - self.reactor.notify().unwrap(); - } - - /// Attempts to poll the reactor without blocking on it. - /// - /// Returns `Ok(true)` if at least one new task was woken. - /// - /// This function might not poll the reactor at all so do not rely on it doing anything. Only - /// use for optimization. - fn quick_poll(&self) -> io::Result { - if let Ok(sched) = self.sched.try_lock() { - if !sched.polling { - return self.reactor.poll(Some(Duration::from_secs(0))); - } - } - Ok(false) - } -} - -/// A thread running a processor. -struct Machine { - /// Holds the processor until it gets stolen. - processor: Spinlock>, -} - -impl Machine { - /// Creates a new machine running a processor. - fn new(p: Processor) -> Machine { - Machine { - processor: Spinlock::new(Some(p)), - } - } - - /// Schedules a task onto the machine. - fn schedule(&self, rt: &Runtime, task: Runnable) { - match self.processor.lock().as_mut() { - None => { - rt.injector.push(task); - rt.notify(); - } - Some(p) => p.schedule(rt, task), - } - } - - /// Finds the next runnable task. - fn find_task(&self, rt: &Runtime) -> Steal { - let mut retry = false; - - // First try finding a task in the local queue or in the global queue. - if let Some(p) = self.processor.lock().as_mut() { - if let Some(task) = p.pop_task() { - return Steal::Success(task); - } - - match p.steal_from_global(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), - } - } - - // Try polling the reactor, but don't block on it. - let progress = rt.quick_poll().unwrap(); - - // Try finding a task in the local queue, which might hold tasks woken by the reactor. If - // the local queue is still empty, try stealing from other processors. - if let Some(p) = self.processor.lock().as_mut() { - if progress { - if let Some(task) = p.pop_task() { - return Steal::Success(task); - } - } - - match p.steal_from_others(rt) { - Steal::Empty => {} - Steal::Retry => retry = true, - Steal::Success(task) => return Steal::Success(task), - } - } - - if retry { Steal::Retry } else { Steal::Empty } - } - - /// Runs the machine on the current thread. - fn run(&self, rt: &Runtime) { - /// Number of yields when no runnable task is found. - const YIELDS: u32 = 3; - /// Number of short sleeps when no runnable task in found. - const SLEEPS: u32 = 10; - /// Number of runs in a row before the global queue is inspected. - const RUNS: u32 = 64; - - // The number of times the thread found work in a row. - let mut runs = 0; - // The number of times the thread didn't find work in a row. - let mut fails = 0; - - loop { - // Check if `task::yield_now()` was invoked and flush the slot if so. - YIELD_NOW.with(|flag| { - if flag.replace(false) { - if let Some(p) = self.processor.lock().as_mut() { - p.flush_slot(rt); - } - } - }); - - // After a number of runs in a row, do some work to ensure no task is left behind - // indefinitely. Poll the reactor, steal tasks from the global queue, and flush the - // task slot. - if runs >= RUNS { - runs = 0; - rt.quick_poll().unwrap(); - - if let Some(p) = self.processor.lock().as_mut() { - if let Steal::Success(task) = p.steal_from_global(rt) { - p.schedule(rt, task); - } - - p.flush_slot(rt); - } - } - - // Try to find a runnable task. - if let Steal::Success(task) = self.find_task(rt) { - task.run(); - runs += 1; - fails = 0; - continue; - } - - fails += 1; - - // Yield the current thread a few times. - if fails <= YIELDS { - thread::yield_now(); - continue; - } - - // Put the current thread to sleep a few times. - if fails <= YIELDS + SLEEPS { - let opt_p = self.processor.lock().take(); - thread::sleep(Duration::from_micros(10)); - *self.processor.lock() = opt_p; - continue; - } - - let mut sched = rt.sched.lock().unwrap(); - - // One final check for available tasks while the scheduler is locked. - if let Some(task) = iter::repeat_with(|| self.find_task(rt)) - .find(|s| !s.is_retry()) - .and_then(|s| s.success()) - { - self.schedule(rt, task); - continue; - } - - // If another thread is already blocked on the reactor, there is no point in keeping - // the current thread around since there is too little work to do. - if sched.polling { - break; - } - - // Take out the machine associated with the current thread. - let m = match sched - .machines - .iter() - .position(|elem| ptr::eq(&**elem, self)) - { - None => break, // The processor was stolen. - Some(pos) => sched.machines.swap_remove(pos), - }; - - // Unlock the schedule poll the reactor until new I/O events arrive. - sched.polling = true; - drop(sched); - rt.reactor.poll(None).unwrap(); - - // Lock the scheduler again and re-register the machine. - sched = rt.sched.lock().unwrap(); - sched.polling = false; - sched.machines.push(m); - - runs = 0; - fails = 0; - } - - // When shutting down the thread, take the processor out if still available. - let opt_p = self.processor.lock().take(); - - // Return the processor to the scheduler and remove the machine. - if let Some(p) = opt_p { - let mut sched = rt.sched.lock().unwrap(); - sched.processors.push(p); - sched.machines.retain(|elem| !ptr::eq(&**elem, self)); - } - } -} - -struct Processor { - /// The local task queue. - worker: Worker, - - /// Contains the next task to run as an optimization that skips the queue. - slot: Option, -} - -impl Processor { - /// Creates a new processor. - fn new() -> Processor { - Processor { - worker: Worker::new_fifo(), - slot: None, - } - } - - /// Schedules a task to run on this processor. - fn schedule(&mut self, rt: &Runtime, task: Runnable) { - match self.slot.replace(task) { - None => {} - Some(task) => { - self.worker.push(task); - rt.notify(); - } - } - } - - /// Flushes a task from the slot into the local queue. - fn flush_slot(&mut self, rt: &Runtime) { - if let Some(task) = self.slot.take() { - self.worker.push(task); - rt.notify(); - } - } - - /// Pops a task from this processor. - fn pop_task(&mut self) -> Option { - self.slot.take().or_else(|| self.worker.pop()) - } - - /// Steals a task from the global queue. - fn steal_from_global(&self, rt: &Runtime) -> Steal { - rt.injector.steal_batch_and_pop(&self.worker) - } - - /// Steals a task from other processors. - fn steal_from_others(&self, rt: &Runtime) -> Steal { - // Pick a random starting point in the list of queues. - let len = rt.stealers.len(); - let start = random(len as u32) as usize; - - // Create an iterator over stealers that starts from the chosen point. - let (l, r) = rt.stealers.split_at(start); - let stealers = r.iter().chain(l.iter()); - - // Try stealing a batch of tasks from each queue. - stealers - .map(|s| s.steal_batch_and_pop(&self.worker)) - .collect() - } -} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 1531f8c57..217709b9b 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -174,7 +174,10 @@ #![allow(clippy::needless_doctest_main)] #[doc(inline)] -pub use std::sync::{Arc, Weak}; +pub use std::sync::Weak; + +#[doc(inline)] +pub use piper::Arc; pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -194,8 +197,3 @@ cfg_unstable! { pub(crate) mod waker_set; pub(crate) use waker_set::WakerSet; - -cfg_default! { - pub(crate) mod spin_lock; - pub(crate) use spin_lock::Spinlock; -} diff --git a/src/sync/spin_lock.rs b/src/sync/spin_lock.rs deleted file mode 100644 index 854b7e024..000000000 --- a/src/sync/spin_lock.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::cell::UnsafeCell; -use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, Ordering}; - -use crossbeam_utils::Backoff; - -/// A simple spinlock. -#[derive(Debug)] -pub struct Spinlock { - locked: AtomicBool, - value: UnsafeCell, -} - -unsafe impl Send for Spinlock {} -unsafe impl Sync for Spinlock {} - -impl Spinlock { - /// Returns a new spinlock initialized with `value`. - pub const fn new(value: T) -> Spinlock { - Spinlock { - locked: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Locks the spinlock. - pub fn lock(&self) -> SpinlockGuard<'_, T> { - let backoff = Backoff::new(); - while self.locked.compare_and_swap(false, true, Ordering::Acquire) { - backoff.snooze(); - } - SpinlockGuard { parent: self } - } -} - -/// A guard holding a spinlock locked. -#[derive(Debug)] -pub struct SpinlockGuard<'a, T> { - parent: &'a Spinlock, -} - -unsafe impl Send for SpinlockGuard<'_, T> {} -unsafe impl Sync for SpinlockGuard<'_, T> {} - -impl<'a, T> Drop for SpinlockGuard<'a, T> { - fn drop(&mut self) { - self.parent.locked.store(false, Ordering::Release); - } -} - -impl<'a, T> Deref for SpinlockGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.parent.value.get() } - } -} - -impl<'a, T> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.parent.value.get() } - } -} - -#[test] -fn spinlock() { - use std::sync::Arc; - - use crate::sync::{Spinlock}; - use crate::task; - - task::block_on(async { - - let m = Arc::new(Spinlock::new(0)); - let mut tasks = vec![]; - - for _ in 0..10 { - let m = m.clone(); - tasks.push(task::spawn(async move { - *m.lock() += 1; - })); - } - - for t in tasks { - t.await; - } - assert_eq!(*m.lock(), 10); - }) -} diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 4bade5bd3..41e0ca01d 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,13 +1,8 @@ -use std::cell::Cell; use std::future::Future; -use std::mem::{self, ManuallyDrop}; -use std::sync::Arc; -use std::task::{RawWaker, RawWakerVTable}; -use crossbeam_utils::sync::Parker; use kv_log_macro::trace; -use crate::task::{Context, Poll, Task, Waker}; +use crate::task::Task; /// Spawns a task and blocks the current thread on its result. /// @@ -45,11 +40,11 @@ where parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); - let future = async move { + let wrapped_future = async move { // Drop task-locals on exit. - defer! { - Task::get_current(|t| unsafe { t.drop_locals() }); - } + // defer! { + // Task::get_current(|t| unsafe { t.drop_locals() }); + // } // Log completion on exit. defer! { @@ -61,70 +56,8 @@ where future.await }; - // Run the future as a task. - unsafe { Task::set_current(&task, || run(future)) } -} - -/// Blocks the current thread on a future's result. -fn run(future: F) -> T -where - F: Future, -{ - thread_local! { - // May hold a pre-allocated parker that can be reused for efficiency. - // - // Note that each invocation of `block` needs its own parker. In particular, if `block` - // recursively calls itself, we must make sure that each recursive call uses a distinct - // parker instance. - static CACHE: Cell>> = Cell::new(None); - } - - // Virtual table for wakers based on `Arc`. - static VTABLE: RawWakerVTable = { - unsafe fn clone_raw(ptr: *const ()) -> RawWaker { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); - #[allow(clippy::redundant_clone)] - mem::forget(arc.clone()); - RawWaker::new(ptr, &VTABLE) - } - - unsafe fn wake_raw(ptr: *const ()) { - let arc = Arc::from_raw(ptr as *const Parker); - arc.unparker().unpark(); - } - - unsafe fn wake_by_ref_raw(ptr: *const ()) { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); - arc.unparker().unpark(); - } - - unsafe fn drop_raw(ptr: *const ()) { - drop(Arc::from_raw(ptr as *const Parker)) - } - - RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) - }; - - // Pin the future on the stack. - pin_utils::pin_mut!(future); - - CACHE.with(|cache| { - // Reuse a cached parker or create a new one for this invocation of `block`. - let arc_parker: Arc = cache.take().unwrap_or_else(|| Arc::new(Parker::new())); - let ptr = (&*arc_parker as *const Parker) as *const (); - - // Create a waker and task context. - let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &VTABLE))) }; - let cx = &mut Context::from_waker(&waker); + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - loop { - if let Poll::Ready(t) = future.as_mut().poll(cx) { - // Save the parker for the next invocation of `block`. - cache.set(Some(arc_parker)); - return t; - } - - arc_parker.park(); - } - }) + // Run the future as a task. + unsafe { Task::set_current(&task, || smol::block_on(wrapped_future)) } } diff --git a/src/task/builder.rs b/src/task/builder.rs index f1fef59e8..898308c7f 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -3,9 +3,7 @@ use std::future::Future; use kv_log_macro::trace; use crate::io; -use crate::rt::RUNTIME; use crate::task::{JoinHandle, Task}; -use crate::utils::abort_on_panic; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -42,11 +40,11 @@ impl Builder { parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); - let future = async move { + let wrapped_future = async move { // Drop task-locals on exit. - defer! { - Task::get_current(|t| unsafe { t.drop_locals() }); - } + // defer! { + // Task::get_current(|t| unsafe { t.drop_locals() }); + // } // Log completion on exit. defer! { @@ -54,25 +52,12 @@ impl Builder { task_id: Task::get_current(|t| t.id().0), }); } - future.await }; - let schedule = move |t| RUNTIME.schedule(Runnable(t)); - let (task, handle) = async_task::spawn(future, schedule, task); - task.schedule(); - Ok(JoinHandle::new(handle)) - } -} - -/// A runnable task. -pub struct Runnable(async_task::Task); + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); -impl Runnable { - /// Runs the task by polling its future once. - pub fn run(self) { - unsafe { - Task::set_current(self.0.tag(), || abort_on_panic(|| self.0.run())); - } + let task = smol::Task::spawn(wrapped_future); + Ok(JoinHandle::new(task)) } } diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index d929d11fb..72fb2e0cb 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -12,11 +12,11 @@ use crate::task::{Context, Poll, Task}; /// /// [spawned]: fn.spawn.html #[derive(Debug)] -pub struct JoinHandle(async_task::JoinHandle); +pub struct JoinHandle(smol::Task); impl JoinHandle { /// Creates a new `JoinHandle`. - pub(crate) fn new(inner: async_task::JoinHandle) -> JoinHandle { + pub(crate) fn new(inner: smol::Task) -> JoinHandle { JoinHandle(inner) } @@ -36,7 +36,7 @@ impl JoinHandle { /// # /// # }) pub fn task(&self) -> &Task { - self.0.tag() + todo!() } } @@ -44,10 +44,7 @@ impl Future for JoinHandle { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match Pin::new(&mut self.0).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => panic!("cannot await the result of a panicked task"), - Poll::Ready(Some(val)) => Poll::Ready(val), - } + dbg!("poll joinhandle"); + Pin::new(&mut self.0).poll(cx) } } diff --git a/src/task/mod.rs b/src/task/mod.rs index 56224a363..61917cd06 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -141,7 +141,6 @@ cfg_default! { pub use spawn::spawn; pub use task_local::{AccessError, LocalKey}; - pub(crate) use builder::Runnable; pub(crate) use task_local::LocalsMap; mod block_on; diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 27143f769..d7b4fd0b1 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,12 +1,4 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; -use std::time::Duration; - -use crossbeam_channel::{unbounded, Receiver, Sender}; -use once_cell::sync::Lazy; - -use crate::task::{JoinHandle, Task}; -use crate::utils::abort_on_panic; +use crate::task::JoinHandle; /// Spawns a blocking task. /// @@ -43,80 +35,8 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - let schedule = |task| POOL.sender.send(task).unwrap(); - let (task, handle) = async_task::spawn(async { f() }, schedule, Task::new(None)); - task.schedule(); - JoinHandle::new(handle) -} - -type Runnable = async_task::Task; - -struct Pool { - sender: Sender, - receiver: Receiver, -} - -/// The number of sleeping worker threads. -static SLEEPING: AtomicUsize = AtomicUsize::new(0); - -static POOL: Lazy = Lazy::new(|| { - // Start a single worker thread waiting for the first task. - start_thread(); + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let (sender, receiver) = unbounded(); - Pool { sender, receiver } -}); - -fn start_thread() { - SLEEPING.fetch_add(1, Ordering::SeqCst); - let timeout = Duration::from_secs(1); - - thread::Builder::new() - .name("async-std/blocking".to_string()) - .spawn(move || { - loop { - let mut task = match POOL.receiver.recv_timeout(timeout) { - Ok(task) => task, - Err(_) => { - // Check whether this is the last sleeping thread. - if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { - // If so, then restart the thread to make sure there is always at least - // one sleeping thread. - if SLEEPING.compare_and_swap(0, 1, Ordering::SeqCst) == 0 { - continue; - } - } - - // Stop the thread. - return; - } - }; - - // If there are no sleeping threads, then start one to make sure there is always at - // least one sleeping thread. - if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 { - start_thread(); - } - - loop { - // Run the task. - abort_on_panic(|| task.run()); - - // Try taking another task if there are any available. - task = match POOL.receiver.try_recv() { - Ok(task) => task, - Err(_) => break, - }; - } - - // If there is at least one sleeping thread, stop this thread instead of putting it - // to sleep. - if SLEEPING.load(Ordering::SeqCst) > 0 { - return; - } - - SLEEPING.fetch_add(1, Ordering::SeqCst); - } - }) - .expect("cannot start a blocking thread"); + let handle = smol::Task::blocking(async move { f() }); + JoinHandle::new(handle) } diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index bdb08eb62..2b1fd0b92 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -43,10 +43,6 @@ impl Future for YieldNow { if !self.0 { self.0 = true; cx.waker().wake_by_ref(); - - #[cfg(feature = "default")] - crate::rt::RUNTIME.yield_now(); - Poll::Pending } else { Poll::Ready(()) diff --git a/src/utils.rs b/src/utils.rs index 4bdbd925b..13ee16de7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,40 +20,6 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } -/// Generates a random number in `0..n`. -#[cfg(any(feature = "unstable", feature = "default"))] -pub fn random(n: u32) -> u32 { - use std::cell::Cell; - use std::num::Wrapping; - - thread_local! { - static RNG: Cell> = { - // Take the address of a local value as seed. - let mut x = 0i32; - let r = &mut x; - let addr = r as *mut i32 as usize; - Cell::new(Wrapping(addr as u32)) - } - } - - RNG.with(|rng| { - // This is the 32-bit variant of Xorshift. - // - // Source: https://en.wikipedia.org/wiki/Xorshift - let mut x = rng.get(); - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - rng.set(x); - - // This is a fast alternative to `x % n`. - // - // Author: Daniel Lemire - // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ - ((u64::from(x.0)).wrapping_mul(u64::from(n)) >> 32) as u32 - }) -} - /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; diff --git a/tests/mutex.rs b/tests/mutex.rs index fd1c07b38..ebdd75201 100644 --- a/tests/mutex.rs +++ b/tests/mutex.rs @@ -40,24 +40,33 @@ fn contention() { let tx = Arc::new(tx); let mutex = Arc::new(Mutex::new(0)); - let num_tasks = 10000; + let num_tasks = 10; //000; + let mut handles = Vec::new(); for _ in 0..num_tasks { let tx = tx.clone(); let mutex = mutex.clone(); - task::spawn(async move { + dbg!("spawn"); + handles.push(task::spawn(async move { let mut lock = mutex.lock().await; *lock += 1; tx.unbounded_send(()).unwrap(); drop(lock); - }); + })); } - for _ in 0..num_tasks { + for i in 0..num_tasks { + dbg!(i); rx.next().await.unwrap(); } + for handle in handles.into_iter() { + handle.await; + } + + dbg!("wait"); + let lock = mutex.lock().await; assert_eq!(num_tasks, *lock); }); From fc9ee0dfdd377952eb3b93ccb81ee52bb11d25af Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 24 Apr 2020 20:08:31 +0200 Subject: [PATCH 057/272] keep std::sync::Arc --- Cargo.toml | 2 -- src/sync/mod.rs | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3bc9084ae..45662bd8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ std = [ "once_cell", "pin-utils", "slab", - "piper", ] alloc = [ "futures-core/alloc", @@ -77,7 +76,6 @@ pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } smol = { path = "../smol", optional = true } -piper = { git = "https://github.com/stjepang/piper.git", branch = "master", optional = true } [dev-dependencies] femme = "1.3.0" diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 217709b9b..bccc6ec87 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -174,10 +174,7 @@ #![allow(clippy::needless_doctest_main)] #[doc(inline)] -pub use std::sync::Weak; - -#[doc(inline)] -pub use piper::Arc; +pub use std::sync::{Arc, Weak}; pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; From e082634b5e4ce0e89c6107ae61be3ab5b0e95445 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 24 Apr 2020 22:53:39 +0200 Subject: [PATCH 058/272] fix spawning --- Cargo.toml | 2 +- src/task/block_on.rs | 6 +++--- src/task/builder.rs | 12 +++++++----- src/task/join_handle.rs | 29 +++++++++++++++++++++++------ src/task/spawn_blocking.rs | 6 +++--- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 45662bd8f..b850b545a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ alloc = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-task = { version = "1.3.1", optional = true } +async-task = { version = "3.0.0", optional = true } broadcaster = { version = "1.0.0", optional = true } crossbeam-channel = { version = "0.4.2", optional = true } crossbeam-deque = { version = "0.7.3", optional = true } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 41e0ca01d..65d654194 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -42,9 +42,9 @@ where let wrapped_future = async move { // Drop task-locals on exit. - // defer! { - // Task::get_current(|t| unsafe { t.drop_locals() }); - // } + defer! { + Task::get_current(|t| unsafe { t.drop_locals() }); + } // Log completion on exit. defer! { diff --git a/src/task/builder.rs b/src/task/builder.rs index 898308c7f..de3e6ac92 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -42,9 +42,9 @@ impl Builder { let wrapped_future = async move { // Drop task-locals on exit. - // defer! { - // Task::get_current(|t| unsafe { t.drop_locals() }); - // } + defer! { + Task::get_current(|t| unsafe { t.drop_locals() }); + } // Log completion on exit. defer! { @@ -57,7 +57,9 @@ impl Builder { once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let task = smol::Task::spawn(wrapped_future); - Ok(JoinHandle::new(task)) + // FIXME: figure out how to set the current task. + + let smol_task = smol::Task::spawn(wrapped_future).detach(); + Ok(JoinHandle::new(smol_task, task)) } } diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 72fb2e0cb..3a632711b 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -12,12 +12,18 @@ use crate::task::{Context, Poll, Task}; /// /// [spawned]: fn.spawn.html #[derive(Debug)] -pub struct JoinHandle(smol::Task); +pub struct JoinHandle { + handle: Option>, + task: Task, +} impl JoinHandle { /// Creates a new `JoinHandle`. - pub(crate) fn new(inner: smol::Task) -> JoinHandle { - JoinHandle(inner) + pub(crate) fn new(inner: async_task::JoinHandle, task: Task) -> JoinHandle { + JoinHandle { + handle: Some(inner), + task, + } } /// Returns a handle to the underlying task. @@ -36,7 +42,14 @@ impl JoinHandle { /// # /// # }) pub fn task(&self) -> &Task { - todo!() + &self.task + } + + /// Cancel this task. + pub async fn cancel(mut self) -> Option { + let handle = self.handle.take().unwrap(); + handle.cancel(); + handle.await } } @@ -44,7 +57,11 @@ impl Future for JoinHandle { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - dbg!("poll joinhandle"); - Pin::new(&mut self.0).poll(cx) + match Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(output) => { + Poll::Ready(output.expect("cannot await the result of a panicked task")) + } + } } } diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index d7b4fd0b1..054f3fdb7 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,4 +1,4 @@ -use crate::task::JoinHandle; +use crate::task::{JoinHandle, Task}; /// Spawns a blocking task. /// @@ -37,6 +37,6 @@ where { once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let handle = smol::Task::blocking(async move { f() }); - JoinHandle::new(handle) + let handle = smol::Task::blocking(async move { f() }).detach(); + JoinHandle::new(handle, Task::new(None)) } From 75ab7219df9db0db8425992e238dc0c17de0d6be Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 24 Apr 2020 23:31:13 +0200 Subject: [PATCH 059/272] bring back random --- src/utils.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 13ee16de7..33e66044c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,6 +20,40 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } +/// Generates a random number in `0..n`. +#[cfg(feature = "unstable")] +pub fn random(n: u32) -> u32 { + use std::cell::Cell; + use std::num::Wrapping; + + thread_local! { + static RNG: Cell> = { + // Take the address of a local value as seed. + let mut x = 0i32; + let r = &mut x; + let addr = r as *mut i32 as usize; + Cell::new(Wrapping(addr as u32)) + } + } + + RNG.with(|rng| { + // This is the 32-bit variant of Xorshift. + // + // Source: https://en.wikipedia.org/wiki/Xorshift + let mut x = rng.get(); + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + rng.set(x); + + // This is a fast alternative to `x % n`. + // + // Author: Daniel Lemire + // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + ((u64::from(x.0)).wrapping_mul(u64::from(n)) >> 32) as u32 + }) +} + /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; From b96afc41dc8f69cad8f9b5a61454b439612e4b4a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 01:40:54 +0200 Subject: [PATCH 060/272] implement task locals --- src/task/block_on.rs | 34 +------ src/task/builder.rs | 91 ++++++++++++++----- src/task/current.rs | 4 +- src/task/mod.rs | 2 + src/task/task.rs | 152 +++----------------------------- src/task/task_local.rs | 4 +- src/task/task_locals_wrapper.rs | 84 ++++++++++++++++++ 7 files changed, 172 insertions(+), 199 deletions(-) create mode 100644 src/task/task_locals_wrapper.rs diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 65d654194..92a118796 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,8 +1,6 @@ use std::future::Future; -use kv_log_macro::trace; - -use crate::task::Task; +use crate::task::Builder; /// Spawns a task and blocks the current thread on its result. /// @@ -31,33 +29,5 @@ pub fn block_on(future: F) -> T where F: Future, { - // Create a new task handle. - let task = Task::new(None); - - // Log this `block_on` operation. - trace!("block_on", { - task_id: task.id().0, - parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), - }); - - let wrapped_future = async move { - // Drop task-locals on exit. - defer! { - Task::get_current(|t| unsafe { t.drop_locals() }); - } - - // Log completion on exit. - defer! { - trace!("completed", { - task_id: Task::get_current(|t| t.id().0), - }); - } - - future.await - }; - - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - - // Run the future as a task. - unsafe { Task::set_current(&task, || smol::block_on(wrapped_future)) } + Builder::new().blocking(future) } diff --git a/src/task/builder.rs b/src/task/builder.rs index de3e6ac92..4936d4b4d 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,9 +1,12 @@ use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; use kv_log_macro::trace; use crate::io; -use crate::task::{JoinHandle, Task}; +use crate::task::{JoinHandle, Task, TaskLocalsWrapper}; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -25,41 +28,83 @@ impl Builder { self } + fn build(self, future: F) -> SupportTaskLocals + where + F: Future, + { + let name = self.name.map(Arc::new); + + // Create a new task handle. + let task = Task::new(name); + + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + + let tag = TaskLocalsWrapper::new(task.clone()); + + // FIXME: do not require all futures to be boxed. + SupportTaskLocals { + tag, + future: Box::pin(future), + } + } + /// Spawns a task with the configured settings. pub fn spawn(self, future: F) -> io::Result> where F: Future + Send + 'static, T: Send + 'static, { - // Create a new task handle. - let task = Task::new(self.name); + let wrapped = self.build(future); // Log this `spawn` operation. trace!("spawn", { - task_id: task.id().0, - parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), }); - let wrapped_future = async move { - // Drop task-locals on exit. - defer! { - Task::get_current(|t| unsafe { t.drop_locals() }); - } - - // Log completion on exit. - defer! { - trace!("completed", { - task_id: Task::get_current(|t| t.id().0), - }); - } - future.await - }; + let task = wrapped.tag.task().clone(); + let smol_task = smol::Task::spawn(wrapped).detach(); - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + Ok(JoinHandle::new(smol_task, task)) + } - // FIXME: figure out how to set the current task. + /// Spawns a task with the configured settings, blocking on its execution. + pub fn blocking(self, future: F) -> T + where + F: Future, + { + let wrapped = self.build(future); - let smol_task = smol::Task::spawn(wrapped_future).detach(); - Ok(JoinHandle::new(smol_task, task)) + // Log this `block_on` operation. + trace!("block_on", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + // Run the future as a task. + unsafe { TaskLocalsWrapper::set_current(&wrapped.tag, || smol::block_on(wrapped)) } + } +} + +/// Wrapper to add support for task locals. +struct SupportTaskLocals { + tag: TaskLocalsWrapper, + future: Pin>, +} + +impl Drop for SupportTaskLocals { + fn drop(&mut self) { + // Log completion on exit. + trace!("completed", { + task_id: self.tag.id().0, + }); + } +} + +impl Future for SupportTaskLocals { + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { TaskLocalsWrapper::set_current(&self.tag, || Pin::new(&mut self.future).poll(cx)) } } } diff --git a/src/task/current.rs b/src/task/current.rs index 0dc36991c..e4624e15f 100644 --- a/src/task/current.rs +++ b/src/task/current.rs @@ -1,4 +1,4 @@ -use crate::task::Task; +use crate::task::{Task, TaskLocalsWrapper}; /// Returns a handle to the current task. /// @@ -23,6 +23,6 @@ use crate::task::Task; /// # }) /// ``` pub fn current() -> Task { - Task::get_current(|t| t.clone()) + TaskLocalsWrapper::get_current(|t| t.task().clone()) .expect("`task::current()` called outside the context of a task") } diff --git a/src/task/mod.rs b/src/task/mod.rs index 61917cd06..d4fccea3b 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -142,6 +142,7 @@ cfg_default! { pub use task_local::{AccessError, LocalKey}; pub(crate) use task_local::LocalsMap; + pub(crate) use task_locals_wrapper::TaskLocalsWrapper; mod block_on; mod builder; @@ -153,6 +154,7 @@ cfg_default! { mod task; mod task_id; mod task_local; + mod task_locals_wrapper; #[cfg(any(feature = "unstable", test))] pub use spawn_blocking::spawn_blocking; diff --git a/src/task/task.rs b/src/task/task.rs index bcec2e0e4..eba99c752 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -1,74 +1,32 @@ -use std::cell::Cell; use std::fmt; -use std::mem::ManuallyDrop; -use std::ptr; -use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::Arc; -use crate::task::{LocalsMap, TaskId}; -use crate::utils::abort_on_panic; +use crate::task::TaskId; -thread_local! { - /// A pointer to the currently running task. - static CURRENT: Cell<*const Task> = Cell::new(ptr::null_mut()); -} - -/// The inner representation of a task handle. -struct Inner { +/// A handle to a task. +#[derive(Clone)] +pub struct Task { /// The task ID. id: TaskId, /// The optional task name. - name: Option>, - - /// The map holding task-local values. - locals: LocalsMap, -} - -impl Inner { - #[inline] - fn new(name: Option) -> Inner { - Inner { - id: TaskId::generate(), - name: name.map(String::into_boxed_str), - locals: LocalsMap::new(), - } - } + name: Option>, } -/// A handle to a task. -pub struct Task { - /// The inner representation. - /// - /// This pointer is lazily initialized on first use. In most cases, the inner representation is - /// never touched and therefore we don't allocate it unless it's really needed. - inner: AtomicPtr, -} - -unsafe impl Send for Task {} -unsafe impl Sync for Task {} - impl Task { /// Creates a new task handle. - /// - /// If the task is unnamed, the inner representation of the task will be lazily allocated on - /// demand. #[inline] - pub(crate) fn new(name: Option) -> Task { - let inner = match name { - None => AtomicPtr::default(), - Some(name) => { - let raw = Arc::into_raw(Arc::new(Inner::new(Some(name)))); - AtomicPtr::new(raw as *mut Inner) - } - }; - Task { inner } + pub(crate) fn new(name: Option>) -> Task { + Task { + id: TaskId::generate(), + name, + } } /// Gets the task's unique identifier. #[inline] pub fn id(&self) -> TaskId { - self.inner().id + self.id } /// Returns the name of this task. @@ -77,93 +35,7 @@ impl Task { /// /// [`Builder::name`]: struct.Builder.html#method.name pub fn name(&self) -> Option<&str> { - self.inner().name.as_ref().map(|s| &**s) - } - - /// Returns the map holding task-local values. - pub(crate) fn locals(&self) -> &LocalsMap { - &self.inner().locals - } - - /// Drops all task-local values. - /// - /// This method is only safe to call at the end of the task. - #[inline] - pub(crate) unsafe fn drop_locals(&self) { - let raw = self.inner.load(Ordering::Acquire); - if let Some(inner) = raw.as_mut() { - // Abort the process if dropping task-locals panics. - abort_on_panic(|| { - inner.locals.clear(); - }); - } - } - - /// Returns the inner representation, initializing it on first use. - fn inner(&self) -> &Inner { - loop { - let raw = self.inner.load(Ordering::Acquire); - if !raw.is_null() { - return unsafe { &*raw }; - } - - let new = Arc::into_raw(Arc::new(Inner::new(None))) as *mut Inner; - if self.inner.compare_and_swap(raw, new, Ordering::AcqRel) != raw { - unsafe { - drop(Arc::from_raw(new)); - } - } - } - } - - /// Set a reference to the current task. - pub(crate) unsafe fn set_current(task: *const Task, f: F) -> R - where - F: FnOnce() -> R, - { - CURRENT.with(|current| { - let old_task = current.replace(task); - defer! { - current.set(old_task); - } - f() - }) - } - - /// Gets a reference to the current task. - pub(crate) fn get_current(f: F) -> Option - where - F: FnOnce(&Task) -> R, - { - let res = CURRENT.try_with(|current| unsafe { current.get().as_ref().map(f) }); - match res { - Ok(Some(val)) => Some(val), - Ok(None) | Err(_) => None, - } - } -} - -impl Drop for Task { - fn drop(&mut self) { - // Deallocate the inner representation if it was initialized. - let raw = *self.inner.get_mut(); - if !raw.is_null() { - unsafe { - drop(Arc::from_raw(raw)); - } - } - } -} - -impl Clone for Task { - fn clone(&self) -> Task { - // We need to make sure the inner representation is initialized now so that this instance - // and the clone have raw pointers that point to the same `Arc`. - let arc = unsafe { ManuallyDrop::new(Arc::from_raw(self.inner())) }; - let raw = Arc::into_raw(Arc::clone(&arc)); - Task { - inner: AtomicPtr::new(raw as *mut Inner), - } + self.name.as_ref().map(|s| s.as_str()) } } diff --git a/src/task/task_local.rs b/src/task/task_local.rs index 72e53d72a..4e2ba8387 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -3,7 +3,7 @@ use std::error::Error; use std::fmt; use std::sync::atomic::{AtomicU32, Ordering}; -use crate::task::Task; +use crate::task::TaskLocalsWrapper; /// The key for accessing a task-local value. /// @@ -98,7 +98,7 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - Task::get_current(|task| unsafe { + TaskLocalsWrapper::get_current(|task| unsafe { // Prepare the numeric key, initialization function, and the map of task-locals. let key = self.key(); let init = || Box::new((self.__init)()) as Box; diff --git a/src/task/task_locals_wrapper.rs b/src/task/task_locals_wrapper.rs new file mode 100644 index 000000000..2a7ddb7af --- /dev/null +++ b/src/task/task_locals_wrapper.rs @@ -0,0 +1,84 @@ +use std::cell::Cell; +use std::ptr; + +use crate::task::{LocalsMap, Task, TaskId}; +use crate::utils::abort_on_panic; + +thread_local! { + /// A pointer to the currently running task. + static CURRENT: Cell<*const TaskLocalsWrapper> = Cell::new(ptr::null_mut()); +} + +/// A wrapper to store task local data. +pub(crate) struct TaskLocalsWrapper { + /// The actual task details. + task: Task, + + /// The map holding task-local values. + locals: LocalsMap, +} + +impl TaskLocalsWrapper { + /// Creates a new task handle. + /// + /// If the task is unnamed, the inner representation of the task will be lazily allocated on + /// demand. + #[inline] + pub(crate) fn new(task: Task) -> Self { + Self { + task, + locals: LocalsMap::new(), + } + } + + /// Gets the task's unique identifier. + #[inline] + pub fn id(&self) -> TaskId { + self.task.id() + } + + /// Returns a reference to the inner `Task`. + pub(crate) fn task(&self) -> &Task { + &self.task + } + + /// Returns the map holding task-local values. + pub(crate) fn locals(&self) -> &LocalsMap { + &self.locals + } + + /// Set a reference to the current task. + pub(crate) unsafe fn set_current(task: *const TaskLocalsWrapper, f: F) -> R + where + F: FnOnce() -> R, + { + CURRENT.with(|current| { + let old_task = current.replace(task); + defer! { + current.set(old_task); + } + f() + }) + } + + /// Gets a reference to the current task. + pub(crate) fn get_current(f: F) -> Option + where + F: FnOnce(&TaskLocalsWrapper) -> R, + { + let res = CURRENT.try_with(|current| unsafe { current.get().as_ref().map(f) }); + match res { + Ok(Some(val)) => Some(val), + Ok(None) | Err(_) => None, + } + } +} + +impl Drop for TaskLocalsWrapper { + fn drop(&mut self) { + // Abort the process if dropping task-locals panics. + abort_on_panic(|| { + unsafe { self.locals.clear() }; + }); + } +} From f5fa0d7e4e2a40bc07d4c916c9524c7970b61071 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 16:02:32 +0200 Subject: [PATCH 061/272] avoid boxing futures --- src/task/builder.rs | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/task/builder.rs b/src/task/builder.rs index 4936d4b4d..aa9f0c028 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; use kv_log_macro::trace; +use pin_project_lite::pin_project; use crate::io; use crate::task::{JoinHandle, Task, TaskLocalsWrapper}; @@ -42,10 +43,7 @@ impl Builder { let tag = TaskLocalsWrapper::new(task.clone()); // FIXME: do not require all futures to be boxed. - SupportTaskLocals { - tag, - future: Box::pin(future), - } + SupportTaskLocals { tag, future } } /// Spawns a task with the configured settings. @@ -56,12 +54,6 @@ impl Builder { { let wrapped = self.build(future); - // Log this `spawn` operation. - trace!("spawn", { - task_id: wrapped.tag.id().0, - parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), - }); - let task = wrapped.tag.task().clone(); let smol_task = smol::Task::spawn(wrapped).detach(); @@ -86,25 +78,24 @@ impl Builder { } } -/// Wrapper to add support for task locals. -struct SupportTaskLocals { - tag: TaskLocalsWrapper, - future: Pin>, -} - -impl Drop for SupportTaskLocals { - fn drop(&mut self) { - // Log completion on exit. - trace!("completed", { - task_id: self.tag.id().0, - }); +pin_project! { + /// Wrapper to add support for task locals. + struct SupportTaskLocals { + tag: TaskLocalsWrapper, + #[pin] + future: F, } } impl Future for SupportTaskLocals { type Output = F::Output; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { TaskLocalsWrapper::set_current(&self.tag, || Pin::new(&mut self.future).poll(cx)) } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { + TaskLocalsWrapper::set_current(&self.tag, || { + let this = self.project(); + this.future.poll(cx) + }) + } } } From ab9d6554aaf5c95b070a377ffc3f1a0b4b034986 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 17:07:10 +0200 Subject: [PATCH 062/272] switch to smol::Timer --- Cargo.toml | 4 +--- src/future/future/delay.rs | 6 +++--- src/future/timeout.rs | 17 ++++++++--------- src/io/timeout.rs | 8 ++++---- src/os/unix/net/datagram.rs | 4 ++-- src/stream/interval.rs | 10 +++++----- src/stream/stream/delay.rs | 5 +++-- src/stream/stream/throttle.rs | 8 ++++---- src/stream/stream/timeout.rs | 6 +++--- 9 files changed, 33 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b850b545a..404e5fb1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ default = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-queue", - "futures-timer", "kv-log-macro", "log", "mio", @@ -37,7 +36,7 @@ default = [ "smol", ] docs = ["attributes", "unstable", "default"] -unstable = ["std", "broadcaster", "futures-timer"] +unstable = ["std", "broadcaster"] attributes = ["async-attributes"] std = [ "alloc", @@ -64,7 +63,6 @@ crossbeam-queue = { version = "0.2.0", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } -futures-timer = { version = "3.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index 641084ff3..e19447020 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -2,8 +2,8 @@ use std::future::Future; use std::pin::Pin; use std::time::Duration; -use futures_timer::Delay; use pin_project_lite::pin_project; +use smol::Timer; use crate::task::{Context, Poll}; @@ -14,13 +14,13 @@ pin_project! { #[pin] future: F, #[pin] - delay: Delay, + delay: Timer, } } impl DelayFuture { pub fn new(future: F, dur: Duration) -> DelayFuture { - let delay = Delay::new(dur); + let delay = Timer::after(dur); DelayFuture { future, delay } } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index 05aaa4509..ec547f894 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -1,11 +1,11 @@ use std::error::Error; use std::fmt; +use std::future::Future; use std::pin::Pin; use std::time::Duration; -use std::future::Future; -use futures_timer::Delay; use pin_project_lite::pin_project; +use smol::Timer; use crate::task::{Context, Poll}; @@ -33,11 +33,7 @@ pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, { - let f = TimeoutFuture { - future: f, - delay: Delay::new(dur), - }; - f.await + TimeoutFuture::new(f, dur).await } pin_project! { @@ -46,14 +42,17 @@ pin_project! { #[pin] future: F, #[pin] - delay: Delay, + delay: Timer, } } impl TimeoutFuture { #[allow(dead_code)] pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture { - TimeoutFuture { future: future, delay: Delay::new(dur) } + TimeoutFuture { + future, + delay: Timer::after(dur), + } } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 6e22dbf26..c19d25dda 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -1,10 +1,10 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use std::future::Future; -use futures_timer::Delay; use pin_project_lite::pin_project; +use smol::Timer; use crate::io; @@ -37,7 +37,7 @@ where F: Future>, { Timeout { - timeout: Delay::new(dur), + timeout: Timer::after(dur), future: f, } .await @@ -53,7 +53,7 @@ pin_project! { #[pin] future: F, #[pin] - timeout: Delay, + timeout: Timer, } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 6a98736c7..c73c9ce12 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -319,8 +319,8 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - let datagram = std::os::unix::net::UnixDatagram::from_raw_fd(fd); - datagram.into() + let datagram = Async::::from_raw_fd(fd); + UnixDatagram { watcher: datagram } } } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index be94b06cb..0161240b2 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -1,10 +1,10 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use crate::future::Future; use crate::stream::Stream; -use futures_timer::Delay; +use smol::Timer; /// Creates a new stream that yields at a set interval. /// @@ -45,7 +45,7 @@ use futures_timer::Delay; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn interval(dur: Duration) -> Interval { Interval { - delay: Delay::new(dur), + delay: Timer::after(dur), interval: dur, } } @@ -60,7 +60,7 @@ pub fn interval(dur: Duration) -> Interval { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Interval { - delay: Delay, + delay: Timer, interval: Duration, } @@ -72,7 +72,7 @@ impl Stream for Interval { return Poll::Pending; } let interval = self.interval; - self.delay.reset(interval); + std::mem::replace(&mut self.delay, Timer::after(interval)); Poll::Ready(Some(())) } } diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index ff4c93738..754bef809 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -3,6 +3,7 @@ use core::pin::Pin; use core::time::Duration; use pin_project_lite::pin_project; +use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -14,7 +15,7 @@ pin_project! { #[pin] stream: S, #[pin] - delay: futures_timer::Delay, + delay: Timer, delay_done: bool, } } @@ -23,7 +24,7 @@ impl Delay { pub(super) fn new(stream: S, dur: Duration) -> Self { Delay { stream, - delay: futures_timer::Delay::new(dur), + delay: Timer::after(dur), delay_done: false, } } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 554ca306e..15a0f3199 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -2,8 +2,8 @@ use std::future::Future; use std::pin::Pin; use std::time::Duration; -use futures_timer::Delay; use pin_project_lite::pin_project; +use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -25,7 +25,7 @@ pin_project! { #[pin] blocked: bool, #[pin] - delay: Delay, + delay: Timer, } } @@ -35,7 +35,7 @@ impl Throttle { stream, duration, blocked: false, - delay: Delay::new(Duration::default()), + delay: Timer::after(Duration::default()), } } } @@ -59,7 +59,7 @@ impl Stream for Throttle { Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { *this.blocked = true; - this.delay.reset(*this.duration); + std::mem::replace(&mut *this.delay, Timer::after(*this.duration)); Poll::Ready(Some(v)) } } diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index ce658c83a..f49aed31d 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -4,8 +4,8 @@ use std::future::Future; use std::pin::Pin; use std::time::Duration; -use futures_timer::Delay; use pin_project_lite::pin_project; +use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -17,13 +17,13 @@ pin_project! { #[pin] stream: S, #[pin] - delay: Delay, + delay: Timer, } } impl Timeout { pub(crate) fn new(stream: S, dur: Duration) -> Self { - let delay = Delay::new(dur); + let delay = Timer::after(dur); Self { stream, delay } } From fd6ae40817e42031d19ff53ef74eaf9da5727c01 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 17:36:20 +0200 Subject: [PATCH 063/272] add timeout stress test --- tests/timeout.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/timeout.rs diff --git a/tests/timeout.rs b/tests/timeout.rs new file mode 100644 index 000000000..c9694f837 --- /dev/null +++ b/tests/timeout.rs @@ -0,0 +1,22 @@ +use std::time::Duration; + +use async_std::future::timeout; +use async_std::task; + +#[test] +fn timeout_future_many() { + task::block_on(async { + let futures = (0..100) + .map(|i| { + timeout(Duration::from_millis(i * 10), async move { + task::sleep(Duration::from_millis(i)).await; + Ok::<(), async_std::future::TimeoutError>(()) + }) + }) + .collect::>(); + + for future in futures { + future.await.unwrap().unwrap(); + } + }); +} From 10c8b9a6d893608828e304b386dfa7b86b65b158 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 17:38:41 +0200 Subject: [PATCH 064/272] silence must use --- src/stream/interval.rs | 2 +- src/stream/stream/throttle.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 0161240b2..fe249fb28 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -72,7 +72,7 @@ impl Stream for Interval { return Poll::Pending; } let interval = self.interval; - std::mem::replace(&mut self.delay, Timer::after(interval)); + let _ = std::mem::replace(&mut self.delay, Timer::after(interval)); Poll::Ready(Some(())) } } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 15a0f3199..4d4cc878d 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -59,7 +59,7 @@ impl Stream for Throttle { Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { *this.blocked = true; - std::mem::replace(&mut *this.delay, Timer::after(*this.duration)); + let _ = std::mem::replace(&mut *this.delay, Timer::after(*this.duration)); Poll::Ready(Some(v)) } } From 0a7a52aed50ff288e81ae861e97928a8ac280436 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 19:05:05 +0200 Subject: [PATCH 065/272] update to work on smol/master --- src/os/unix/net/datagram.rs | 3 ++- src/task/builder.rs | 2 +- src/task/spawn_blocking.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index c73c9ce12..6a30b0279 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -319,7 +319,8 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - let datagram = Async::::from_raw_fd(fd); + let raw = StdUnixDatagram::from_raw_fd(fd); + let datagram = Async::::new(raw).expect("invalid file descriptor"); UnixDatagram { watcher: datagram } } } diff --git a/src/task/builder.rs b/src/task/builder.rs index aa9f0c028..51a5898c8 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -55,7 +55,7 @@ impl Builder { let wrapped = self.build(future); let task = wrapped.tag.task().clone(); - let smol_task = smol::Task::spawn(wrapped).detach(); + let smol_task = smol::Task::spawn(wrapped).into(); Ok(JoinHandle::new(smol_task, task)) } diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 054f3fdb7..e9ed0c5a0 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -37,6 +37,6 @@ where { once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let handle = smol::Task::blocking(async move { f() }).detach(); + let handle = smol::Task::blocking(async move { f() }).into(); JoinHandle::new(handle, Task::new(None)) } From 228cc59b3bcd341b5a8cfc11cca3a8cd18866f98 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 23:00:00 +0200 Subject: [PATCH 066/272] feat: add spawn_local --- src/task/builder.rs | 14 ++++++++++++++ src/task/mod.rs | 2 ++ src/task/spawn_local.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/task/spawn_local.rs diff --git a/src/task/builder.rs b/src/task/builder.rs index 51a5898c8..f1bf791aa 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -60,6 +60,20 @@ impl Builder { Ok(JoinHandle::new(smol_task, task)) } + /// Spawns a task locally with the configured settings. + pub fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + let wrapped = self.build(future); + + let task = wrapped.tag.task().clone(); + let smol_task = smol::Task::local(wrapped).into(); + + Ok(JoinHandle::new(smol_task, task)) + } + /// Spawns a task with the configured settings, blocking on its execution. pub fn blocking(self, future: F) -> T where diff --git a/src/task/mod.rs b/src/task/mod.rs index d4fccea3b..f5bc8641f 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -139,6 +139,7 @@ cfg_default! { pub use join_handle::JoinHandle; pub use sleep::sleep; pub use spawn::spawn; + pub use spawn_local::spawn_local; pub use task_local::{AccessError, LocalKey}; pub(crate) use task_local::LocalsMap; @@ -151,6 +152,7 @@ cfg_default! { mod sleep; mod spawn; mod spawn_blocking; + mod spawn_local; mod task; mod task_id; mod task_local; diff --git a/src/task/spawn_local.rs b/src/task/spawn_local.rs new file mode 100644 index 000000000..5ed7226d3 --- /dev/null +++ b/src/task/spawn_local.rs @@ -0,0 +1,32 @@ +use std::future::Future; + +use crate::task::{Builder, JoinHandle}; + +/// Spawns a task onto the thread-local executor. +/// +/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task. +/// +/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// let handle = task::spawn_local(async { +/// 1 + 2 +/// }); +/// +/// assert_eq!(handle.await, 3); +/// # +/// # }) +/// ``` +pub fn spawn_local(future: F) -> JoinHandle +where + F: Future + 'static, + T: 'static, +{ + Builder::new().local(future).expect("cannot spawn task") +} From 3161a4e449d4d4ec0e536dff74da93d923dd177f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 25 Apr 2020 23:09:40 +0200 Subject: [PATCH 067/272] add some missing windows imports --- src/net/tcp/listener.rs | 2 +- src/net/tcp/stream.rs | 9 ++++++--- src/net/udp/mod.rs | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 290da0d1d..f31f1357f 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -229,7 +229,7 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, AsRawSocket, RawSocket, FromRawSocket}; impl AsRawSocket for TcpListener { fn as_raw_socket(&self) -> RawSocket { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1d2d0ce14..0dc43f5c9 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -388,7 +388,7 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket}; impl AsRawSocket for TcpStream { fn as_raw_socket(&self) -> RawSocket { @@ -402,9 +402,12 @@ cfg_windows! { } } - impl IntoRawSocket for TcpListener { + impl IntoRawSocket for crate::net::tcp::TcpListener { fn into_raw_socket(self) -> RawSocket { - self.raw_socket + // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file + // descriptor because it's possible that there are other clones of this `TcpStream` + // using it at the same time. We should probably document that behavior. + self.as_raw_socket() } } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 3bc9ad777..53add8e7f 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -482,7 +482,7 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket}; impl AsRawSocket for UdpSocket { fn as_raw_socket(&self) -> RawSocket { From 2cd2ba3530fc75b8cff0b6ad542fec6dbd176031 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 26 Apr 2020 15:29:45 +0200 Subject: [PATCH 068/272] remove unused dependencies --- Cargo.toml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 404e5fb1c..dffdc5cd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,13 +24,8 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] default = [ "std", "async-task", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-queue", "kv-log-macro", "log", - "mio", - "mio-uds", "num_cpus", "pin-project-lite", "smol", @@ -57,17 +52,12 @@ alloc = [ async-attributes = { version = "1.1.1", optional = true } async-task = { version = "3.0.0", optional = true } broadcaster = { version = "1.0.0", optional = true } -crossbeam-channel = { version = "0.4.2", optional = true } -crossbeam-deque = { version = "0.7.3", optional = true } -crossbeam-queue = { version = "0.2.0", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } -mio = { version = "0.6.19", optional = true } -mio-uds = { version = "0.6.7", optional = true } num_cpus = { version = "1.12.0", optional = true } once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true } From e4df1405c1a04b9e4a65f878b2bb1a86f855e986 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 26 Apr 2020 18:00:00 +0200 Subject: [PATCH 069/272] feat: add basic wasm support --- .github/workflows/ci.yml | 8 ++++++ Cargo.toml | 11 ++++++++ src/future/future/delay.rs | 2 +- src/future/timeout.rs | 2 +- src/io/mod.rs | 11 ++++++++ src/io/read/mod.rs | 11 ++++---- src/io/read/take.rs | 2 +- src/io/timeout.rs | 2 +- src/lib.rs | 3 +++ src/net/mod.rs | 6 +++++ src/path/path.rs | 12 +++++++-- src/stream/interval.rs | 2 +- src/stream/stream/delay.rs | 2 +- src/stream/stream/throttle.rs | 2 +- src/stream/stream/timeout.rs | 2 +- src/sync/barrier.rs | 2 +- src/task/block_on.rs | 11 ++++++++ src/task/builder.rs | 29 ++++++++++++++++++--- src/task/join_handle.rs | 18 ++++++++++++-- src/task/mod.rs | 5 ++++ src/utils.rs | 31 +++++++++++++++++++++++ tests/addr.rs | 2 ++ tests/block_on.rs | 2 ++ tests/buf_writer.rs | 24 +++++++++++------- tests/channel.rs | 47 ++++++++++++++++++++++++----------- tests/condvar.rs | 16 ++++++++++-- tests/io_timeout.rs | 1 + tests/mutex.rs | 21 ++++++++++++---- tests/rwlock.rs | 25 +++++++++++++++---- tests/stream.rs | 20 +++++++++++---- tests/task_local.rs | 11 +++++++- tests/tcp.rs | 2 ++ tests/timeout.rs | 4 +++ tests/udp.rs | 2 ++ tests/uds.rs | 2 +- tests/verbose_errors.rs | 2 ++ wasm-test.sh | 10 ++++++++ 37 files changed, 301 insertions(+), 64 deletions(-) create mode 100755 wasm-test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9fcdcc6f..8f519e533 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,14 @@ jobs: with: command: check args: --features unstable --all --bins --examples --tests + + - name: check wasm + uses: actions-rs/cargo@v1 + with: + command: check + target: wasm32-unknown-unknown + override: true + args: --features unstable --all --bins --tests - name: check bench uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index dffdc5cd9..e74a2ed06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,14 +63,25 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } + +[target.'cfg(not(target_os = "unknown"))'.dependencies] smol = { path = "../smol", optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-timer = "0.2.4" +wasm-bindgen-futures = "0.4.10" +futures-channel = "0.3.4" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3.10" + [dev-dependencies] femme = "1.3.0" rand = "0.7.3" surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.4" +rand_xorshift = "0.2.0" [[test]] name = "stream" diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index e19447020..b6c30bcc3 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -3,9 +3,9 @@ use std::pin::Pin; use std::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::task::{Context, Poll}; +use crate::utils::Timer; pin_project! { #[doc(hidden)] diff --git a/src/future/timeout.rs b/src/future/timeout.rs index ec547f894..4a9d93c7f 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -5,9 +5,9 @@ use std::pin::Pin; use std::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::task::{Context, Poll}; +use crate::utils::Timer; /// Awaits a future or times out after a duration of time. /// diff --git a/src/io/mod.rs b/src/io/mod.rs index dd97567b6..f5dd9e2c0 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -307,22 +307,33 @@ cfg_std! { cfg_default! { // For use in the print macros. #[doc(hidden)] + #[cfg(not(target_os = "unknown"))] pub use stdio::{_eprint, _print}; + #[cfg(not(target_os = "unknown"))] pub use stderr::{stderr, Stderr}; + #[cfg(not(target_os = "unknown"))] pub use stdin::{stdin, Stdin}; + #[cfg(not(target_os = "unknown"))] pub use stdout::{stdout, Stdout}; pub use timeout::timeout; mod timeout; + #[cfg(not(target_os = "unknown"))] mod stderr; + #[cfg(not(target_os = "unknown"))] mod stdin; + #[cfg(not(target_os = "unknown"))] mod stdio; + #[cfg(not(target_os = "unknown"))] mod stdout; } cfg_unstable_default! { + #[cfg(not(target_os = "unknown"))] pub use stderr::StderrLock; + #[cfg(not(target_os = "unknown"))] pub use stdin::StdinLock; + #[cfg(not(target_os = "unknown"))] pub use stdout::StdoutLock; } diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 8aade1894..0d429209d 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -17,9 +17,9 @@ use std::mem; use crate::io::IoSliceMut; -pub use take::Take; pub use bytes::Bytes; pub use chain::Chain; +pub use take::Take; extension_trait! { use std::pin::Pin; @@ -483,7 +483,7 @@ mod tests { use crate::prelude::*; #[test] - fn test_read_by_ref() -> io::Result<()> { + fn test_read_by_ref() { crate::task::block_on(async { let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]); let mut buffer = Vec::new(); @@ -493,14 +493,13 @@ mod tests { let reference = f.by_ref(); // read at most 5 bytes - assert_eq!(reference.take(5).read_to_end(&mut buffer).await?, 5); + assert_eq!(reference.take(5).read_to_end(&mut buffer).await.unwrap(), 5); assert_eq!(&buffer, &[0, 1, 2, 3, 4]) } // drop our &mut reference so we can use f again // original file still usable, read the rest - assert_eq!(f.read_to_end(&mut other_buffer).await?, 4); + assert_eq!(f.read_to_end(&mut other_buffer).await.unwrap(), 4); assert_eq!(&other_buffer, &[5, 6, 7, 8]); - Ok(()) - }) + }); } } diff --git a/src/io/read/take.rs b/src/io/read/take.rs index 09b02c2fa..ba9a9e318 100644 --- a/src/io/read/take.rs +++ b/src/io/read/take.rs @@ -218,7 +218,7 @@ impl BufRead for Take { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "unknown")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/timeout.rs b/src/io/timeout.rs index c19d25dda..ce33fea1d 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -4,9 +4,9 @@ use std::task::{Context, Poll}; use std::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::io; +use crate::utils::Timer; /// Awaits an I/O future or times out after a duration of time. /// diff --git a/src/lib.rs b/src/lib.rs index 7e0e98d3b..408a7ab16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,14 +267,17 @@ cfg_std! { } cfg_default! { + #[cfg(not(target_os = "unknown"))] pub mod fs; pub mod path; pub mod net; + #[cfg(not(target_os = "unknown"))] pub(crate) mod rt; } cfg_unstable! { pub mod pin; + #[cfg(not(target_os = "unknown"))] pub mod process; mod unit; diff --git a/src/net/mod.rs b/src/net/mod.rs index fe83d3b15..181407357 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -61,10 +61,16 @@ pub use std::net::Shutdown; pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +#[cfg(not(target_os = "unknown"))] pub use addr::ToSocketAddrs; +#[cfg(not(target_os = "unknown"))] pub use tcp::{Incoming, TcpListener, TcpStream}; +#[cfg(not(target_os = "unknown"))] pub use udp::UdpSocket; +#[cfg(not(target_os = "unknown"))] mod addr; +#[cfg(not(target_os = "unknown"))] mod tcp; +#[cfg(not(target_os = "unknown"))] mod udp; diff --git a/src/path/path.rs b/src/path/path.rs index dfe9426a4..185bfaff0 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -4,9 +4,9 @@ use std::ffi::{OsStr, OsString}; use std::rc::Rc; use std::sync::Arc; -use crate::fs; -use crate::io; use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; +#[cfg(not(target_os = "unknown"))] +use crate::{fs, io}; /// A slice of a path. /// @@ -584,6 +584,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn metadata(&self) -> io::Result { fs::metadata(self).await } @@ -607,6 +608,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn symlink_metadata(&self) -> io::Result { fs::symlink_metadata(self).await } @@ -632,6 +634,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn canonicalize(&self) -> io::Result { fs::canonicalize(self).await } @@ -654,6 +657,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn read_link(&self) -> io::Result { fs::read_link(self).await } @@ -688,6 +692,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn read_dir(&self) -> io::Result { fs::read_dir(self).await } @@ -717,6 +722,7 @@ impl Path { /// check errors, call [fs::metadata]. /// /// [fs::metadata]: ../fs/fn.metadata.html + #[cfg(not(target_os = "unknown"))] pub async fn exists(&self) -> bool { fs::metadata(self).await.is_ok() } @@ -749,6 +755,7 @@ impl Path { /// /// [fs::metadata]: ../fs/fn.metadata.html /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file + #[cfg(not(target_os = "unknown"))] pub async fn is_file(&self) -> bool { fs::metadata(self) .await @@ -785,6 +792,7 @@ impl Path { /// /// [fs::metadata]: ../fs/fn.metadata.html /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir + #[cfg(not(target_os = "unknown"))] pub async fn is_dir(&self) -> bool { fs::metadata(self) .await diff --git a/src/stream/interval.rs b/src/stream/interval.rs index fe249fb28..4e5c92b02 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use crate::stream::Stream; -use smol::Timer; +use crate::utils::Timer; /// Creates a new stream that yields at a set interval. /// diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index 754bef809..0ba42b052 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -3,10 +3,10 @@ use core::pin::Pin; use core::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::utils::Timer; pin_project! { #[doc(hidden)] diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 4d4cc878d..2f9333a7a 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -3,10 +3,10 @@ use std::pin::Pin; use std::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::utils::Timer; pin_project! { /// A stream that only yields one element once every `duration`. diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index f49aed31d..28e52aebd 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -5,10 +5,10 @@ use std::pin::Pin; use std::time::Duration; use pin_project_lite::pin_project; -use smol::Timer; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::utils::Timer; pin_project! { /// A stream with timeout time set diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 2822d5469..86e9a2d9d 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -202,7 +202,7 @@ impl BarrierWaitResult { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "unknown")))] mod test { use futures::channel::mpsc::unbounded; use futures::sink::SinkExt; diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 92a118796..fa66f915b 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -25,9 +25,20 @@ use crate::task::Builder; /// }) /// } /// ``` +#[cfg(not(target_os = "unknown"))] pub fn block_on(future: F) -> T where F: Future, { Builder::new().blocking(future) } + +/// Spawns a task and waits for it to finish. +#[cfg(target_os = "unknown")] +pub fn block_on(future: F) +where + F: Future + 'static, + T: 'static, +{ + Builder::new().local(future).unwrap(); +} diff --git a/src/task/builder.rs b/src/task/builder.rs index f1bf791aa..f48b6b4c1 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -3,7 +3,6 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -use kv_log_macro::trace; use pin_project_lite::pin_project; use crate::io; @@ -38,15 +37,16 @@ impl Builder { // Create a new task handle. let task = Task::new(name); + #[cfg(not(target_os = "unknown"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); let tag = TaskLocalsWrapper::new(task.clone()); - // FIXME: do not require all futures to be boxed. SupportTaskLocals { tag, future } } /// Spawns a task with the configured settings. + #[cfg(not(target_os = "unknown"))] pub fn spawn(self, future: F) -> io::Result> where F: Future + Send + 'static, @@ -61,6 +61,7 @@ impl Builder { } /// Spawns a task locally with the configured settings. + #[cfg(not(target_os = "unknown"))] pub fn local(self, future: F) -> io::Result> where F: Future + 'static, @@ -74,7 +75,29 @@ impl Builder { Ok(JoinHandle::new(smol_task, task)) } + /// Spawns a task locally with the configured settings. + #[cfg(target_arch = "wasm32")] + pub fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + use futures_channel::oneshot::channel; + let (sender, receiver) = channel(); + + let wrapped = self.build(async move { + let res = future.await; + let _ = sender.send(res); + }); + + let task = wrapped.tag.task().clone(); + wasm_bindgen_futures::spawn_local(wrapped); + + Ok(JoinHandle::new(receiver, task)) + } + /// Spawns a task with the configured settings, blocking on its execution. + #[cfg(not(target_os = "unknown"))] pub fn blocking(self, future: F) -> T where F: Future, @@ -82,7 +105,7 @@ impl Builder { let wrapped = self.build(future); // Log this `block_on` operation. - trace!("block_on", { + kv_log_macro::trace!("block_on", { task_id: wrapped.tag.id().0, parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), }); diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 3a632711b..110b827e2 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -13,13 +13,18 @@ use crate::task::{Context, Poll, Task}; /// [spawned]: fn.spawn.html #[derive(Debug)] pub struct JoinHandle { - handle: Option>, + handle: Option>, task: Task, } +#[cfg(not(target_os = "unknown"))] +type InnerHandle = async_task::JoinHandle; +#[cfg(target_arch = "wasm32")] +type InnerHandle = futures_channel::oneshot::Receiver; + impl JoinHandle { /// Creates a new `JoinHandle`. - pub(crate) fn new(inner: async_task::JoinHandle, task: Task) -> JoinHandle { + pub(crate) fn new(inner: InnerHandle, task: Task) -> JoinHandle { JoinHandle { handle: Some(inner), task, @@ -46,11 +51,20 @@ impl JoinHandle { } /// Cancel this task. + #[cfg(not(target_os = "unknown"))] pub async fn cancel(mut self) -> Option { let handle = self.handle.take().unwrap(); handle.cancel(); handle.await } + + /// Cancel this task. + #[cfg(target_arch = "wasm32")] + pub async fn cancel(mut self) -> Option { + let mut handle = self.handle.take().unwrap(); + handle.close(); + handle.await.ok() + } } impl Future for JoinHandle { diff --git a/src/task/mod.rs b/src/task/mod.rs index f5bc8641f..6a142ffc7 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -138,6 +138,7 @@ cfg_default! { pub use task_id::TaskId; pub use join_handle::JoinHandle; pub use sleep::sleep; + #[cfg(not(target_os = "unknown"))] pub use spawn::spawn; pub use spawn_local::spawn_local; pub use task_local::{AccessError, LocalKey}; @@ -150,7 +151,9 @@ cfg_default! { mod current; mod join_handle; mod sleep; + #[cfg(not(target_os = "unknown"))] mod spawn; + #[cfg(not(target_os = "unknown"))] mod spawn_blocking; mod spawn_local; mod task; @@ -158,8 +161,10 @@ cfg_default! { mod task_local; mod task_locals_wrapper; + #[cfg(not(target_os = "unknown"))] #[cfg(any(feature = "unstable", test))] pub use spawn_blocking::spawn_blocking; + #[cfg(not(target_os = "unknown"))] #[cfg(not(any(feature = "unstable", test)))] pub(crate) use spawn_blocking::spawn_blocking; } diff --git a/src/utils.rs b/src/utils.rs index 33e66044c..2ae5488e5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,6 +59,37 @@ pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; } +#[cfg(not(target_os = "unknown"))] +pub(crate) type Timer = smol::Timer; + +#[cfg(target_arch = "wasm32")] +#[derive(Debug)] +pub(crate) struct Timer(wasm_timer::Delay); + +#[cfg(target_arch = "wasm32")] +impl Timer { + pub(crate) fn after(dur: std::time::Duration) -> Self { + Timer(wasm_timer::Delay::new(dur)) + } +} + +#[cfg(target_arch = "wasm32")] +use std::pin::Pin; +#[cfg(target_arch = "wasm32")] +use std::task::Poll; + +#[cfg(target_arch = "wasm32")] +impl std::future::Future for Timer { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match Pin::new(&mut self.0).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => Poll::Ready(()), + } + } +} + /// Defers evaluation of a block of code until the end of the scope. #[cfg(feature = "default")] #[doc(hidden)] diff --git a/tests/addr.rs b/tests/addr.rs index aada557c3..fcd5aa1f0 100644 --- a/tests/addr.rs +++ b/tests/addr.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use async_std::net::ToSocketAddrs; diff --git a/tests/block_on.rs b/tests/block_on.rs index c422d0630..28902b018 100644 --- a/tests/block_on.rs +++ b/tests/block_on.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::task; #[test] diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index 5df90e08c..442cf8a4d 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -2,15 +2,19 @@ use async_std::io::{self, BufWriter, SeekFrom}; use async_std::prelude::*; use async_std::task; +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer() { #![allow(clippy::cognitive_complexity)] task::block_on(async { - let inner = Vec::new(); - let mut writer = BufWriter::with_capacity(2, inner); + let inner: Vec = Vec::new(); + let mut writer = BufWriter::>::with_capacity(2, inner); writer.write(&[0, 1]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1]); writer.write(&[2]).await.unwrap(); @@ -22,7 +26,7 @@ fn test_buffered_writer() { assert_eq!(*writer.get_ref(), [0, 1]); writer.flush().await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); writer.write(&[4]).await.unwrap(); @@ -35,31 +39,33 @@ fn test_buffered_writer() { assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); writer.write(&[7, 8]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); writer.write(&[9, 10, 11]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); writer.flush().await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); }) } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer_inner_into_inner_flushes() { task::block_on(async { - let mut w = BufWriter::with_capacity(3, Vec::new()); + let mut w = BufWriter::with_capacity(3, Vec::::new()); w.write(&[0, 1]).await.unwrap(); - assert_eq!(*w.get_ref(), []); + assert!(w.get_ref().is_empty()); let w = w.into_inner().await.unwrap(); assert_eq!(w, [0, 1]); }) } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer_seek() { task::block_on(async { let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); diff --git a/tests/channel.rs b/tests/channel.rs index f30290600..a218ea2ae 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -6,13 +6,22 @@ use std::time::Duration; use async_std::sync::channel; use async_std::task; -use rand::{thread_rng, Rng}; +use rand::{Rng, SeedableRng}; + +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); fn ms(ms: u64) -> Duration { Duration::from_millis(ms) } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn smoke() { task::block_on(async { let (s, r) = channel(1); @@ -35,6 +44,7 @@ fn smoke() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn capacity() { for i in 1..10 { let (s, r) = channel::<()>(i); @@ -44,6 +54,7 @@ fn capacity() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn len_empty_full() { #![allow(clippy::cognitive_complexity)] task::block_on(async { @@ -86,11 +97,12 @@ fn len_empty_full() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn recv() { task::block_on(async { let (s, r) = channel(100); - task::spawn(async move { + spawn(async move { assert_eq!(r.recv().await.unwrap(), 7); task::sleep(ms(1000)).await; assert_eq!(r.recv().await.unwrap(), 8); @@ -107,11 +119,12 @@ fn recv() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn send() { task::block_on(async { let (s, r) = channel(1); - task::spawn(async move { + spawn(async move { s.send(7).await; task::sleep(ms(1000)).await; s.send(8).await; @@ -129,6 +142,7 @@ fn send() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn recv_after_disconnect() { task::block_on(async { let (s, r) = channel(100); @@ -147,6 +161,7 @@ fn recv_after_disconnect() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn len() { const COUNT: usize = 25_000; const CAP: usize = 1000; @@ -184,7 +199,7 @@ fn len() { assert_eq!(s.len(), 0); assert_eq!(r.len(), 0); - let child = task::spawn({ + let child = spawn({ let r = r.clone(); async move { for i in 0..COUNT { @@ -209,11 +224,12 @@ fn len() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn disconnect_wakes_receiver() { task::block_on(async { let (s, r) = channel::<()>(1); - let child = task::spawn(async move { + let child = spawn(async move { assert!(r.recv().await.is_err()); }); @@ -225,13 +241,14 @@ fn disconnect_wakes_receiver() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn spsc() { const COUNT: usize = 100_000; task::block_on(async { let (s, r) = channel(3); - let child = task::spawn(async move { + let child = spawn(async move { for i in 0..COUNT { assert_eq!(r.recv().await.unwrap(), i); } @@ -248,6 +265,7 @@ fn spsc() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn mpmc() { const COUNT: usize = 25_000; const TASKS: usize = 4; @@ -262,7 +280,7 @@ fn mpmc() { for _ in 0..TASKS { let r = r.clone(); let v = v.clone(); - tasks.push(task::spawn(async move { + tasks.push(spawn(async move { for _ in 0..COUNT { let n = r.recv().await.unwrap(); v[n].fetch_add(1, Ordering::SeqCst); @@ -272,7 +290,7 @@ fn mpmc() { for _ in 0..TASKS { let s = s.clone(); - tasks.push(task::spawn(async move { + tasks.push(spawn(async move { for i in 0..COUNT { s.send(i).await; } @@ -290,6 +308,7 @@ fn mpmc() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn oneshot() { const COUNT: usize = 10_000; @@ -297,8 +316,8 @@ fn oneshot() { for _ in 0..COUNT { let (s, r) = channel(1); - let c1 = task::spawn(async move { r.recv().await.unwrap() }); - let c2 = task::spawn(async move { s.send(0).await }); + let c1 = spawn(async move { r.recv().await.unwrap() }); + let c2 = spawn(async move { s.send(0).await }); c1.await; c2.await; @@ -307,6 +326,7 @@ fn oneshot() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn drops() { const RUNS: usize = 100; @@ -321,17 +341,16 @@ fn drops() { } } - let mut rng = thread_rng(); - for _ in 0..RUNS { - task::block_on(async { + let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0); + task::block_on(async move { let steps = rng.gen_range(0, 10_000); let additional = rng.gen_range(0, 50); DROPS.store(0, Ordering::SeqCst); let (s, r) = channel::(50); - let child = task::spawn({ + let child = spawn({ let r = r.clone(); async move { for _ in 0..steps { diff --git a/tests/condvar.rs b/tests/condvar.rs index c4d680fc9..7b05b286c 100644 --- a/tests/condvar.rs +++ b/tests/condvar.rs @@ -5,13 +5,22 @@ use std::time::Duration; use async_std::sync::{Condvar, Mutex}; use async_std::task::{self, JoinHandle}; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn wait_timeout_with_lock() { task::block_on(async { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = pair.clone(); - task::spawn(async move { + spawn(async move { let (m, c) = &*pair2; let _g = m.lock().await; task::sleep(Duration::from_millis(20)).await; @@ -27,6 +36,7 @@ fn wait_timeout_with_lock() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn wait_timeout_without_lock() { task::block_on(async { let m = Mutex::new(false); @@ -40,6 +50,7 @@ fn wait_timeout_without_lock() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn wait_timeout_until_timed_out() { task::block_on(async { let m = Mutex::new(false); @@ -55,6 +66,7 @@ fn wait_timeout_until_timed_out() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn notify_all() { task::block_on(async { let mut tasks: Vec> = Vec::new(); @@ -62,7 +74,7 @@ fn notify_all() { for _ in 0..10 { let pair = pair.clone(); - tasks.push(task::spawn(async move { + tasks.push(spawn(async move { let (m, c) = &*pair; let mut count = m.lock().await; while *count == 0 { diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index 85a17ab75..fa30a68af 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -5,6 +5,7 @@ use async_std::task; #[test] #[should_panic(expected = "timed out")] +#[cfg(not(target_os = "unknown"))] fn io_timeout_timedout() { task::block_on(async { io::timeout(Duration::from_secs(1), async { diff --git a/tests/mutex.rs b/tests/mutex.rs index ebdd75201..76f42e285 100644 --- a/tests/mutex.rs +++ b/tests/mutex.rs @@ -5,7 +5,16 @@ use async_std::sync::Mutex; use async_std::task; use futures::channel::mpsc; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn smoke() { task::block_on(async { let m = Mutex::new(()); @@ -15,18 +24,21 @@ fn smoke() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn try_lock() { let m = Mutex::new(()); *m.try_lock().unwrap() = (); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner() { let m = Mutex::new(10); assert_eq!(m.into_inner(), 10); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn get_mut() { let mut m = Mutex::new(10); *m.get_mut() = 20; @@ -34,21 +46,21 @@ fn get_mut() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn contention() { task::block_on(async { let (tx, mut rx) = mpsc::unbounded(); let tx = Arc::new(tx); let mutex = Arc::new(Mutex::new(0)); - let num_tasks = 10; //000; + let num_tasks = 10000; let mut handles = Vec::new(); for _ in 0..num_tasks { let tx = tx.clone(); let mutex = mutex.clone(); - dbg!("spawn"); - handles.push(task::spawn(async move { + handles.push(spawn(async move { let mut lock = mutex.lock().await; *lock += 1; tx.unbounded_send(()).unwrap(); @@ -56,8 +68,7 @@ fn contention() { })); } - for i in 0..num_tasks { - dbg!(i); + for _ in 0..num_tasks { rx.next().await.unwrap(); } diff --git a/tests/rwlock.rs b/tests/rwlock.rs index 370dcb9fc..1d33a456d 100644 --- a/tests/rwlock.rs +++ b/tests/rwlock.rs @@ -10,6 +10,14 @@ use async_std::sync::RwLock; use async_std::task; use futures::channel::mpsc; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Generates a random number in `0..n`. pub fn random(n: u32) -> u32 { thread_local! { @@ -35,6 +43,7 @@ pub fn random(n: u32) -> u32 { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn smoke() { task::block_on(async { let lock = RwLock::new(()); @@ -46,6 +55,7 @@ fn smoke() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn try_write() { task::block_on(async { let lock = RwLock::new(0isize); @@ -56,12 +66,14 @@ fn try_write() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner() { let lock = RwLock::new(10); assert_eq!(lock.into_inner(), 10); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner_and_drop() { struct Counter(Arc); @@ -84,6 +96,7 @@ fn into_inner_and_drop() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn get_mut() { let mut lock = RwLock::new(10); *lock.get_mut() = 20; @@ -91,6 +104,7 @@ fn get_mut() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn contention() { const N: u32 = 10; const M: usize = 1000; @@ -104,7 +118,7 @@ fn contention() { let tx = tx.clone(); let rw = rw.clone(); - task::spawn(async move { + spawn(async move { for _ in 0..M { if random(N) == 0 { drop(rw.write().await); @@ -116,7 +130,7 @@ fn contention() { }); } - task::block_on(async { + task::block_on(async move { for _ in 0..N { rx.next().await.unwrap(); } @@ -124,6 +138,7 @@ fn contention() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn writer_and_readers() { #[derive(Default)] struct Yield(Cell); @@ -146,7 +161,7 @@ fn writer_and_readers() { let (tx, mut rx) = mpsc::unbounded(); // Spawn a writer task. - task::spawn({ + spawn({ let lock = lock.clone(); async move { let mut lock = lock.write().await; @@ -164,13 +179,13 @@ fn writer_and_readers() { let mut readers = Vec::new(); for _ in 0..5 { let lock = lock.clone(); - readers.push(task::spawn(async move { + readers.push(spawn(async move { let lock = lock.read().await; assert!(*lock >= 0); })); } - task::block_on(async { + task::block_on(async move { // Wait for readers to pass their asserts. for r in readers { r.await; diff --git a/tests/stream.rs b/tests/stream.rs index 42a6191fd..3576cb900 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -8,14 +8,23 @@ use async_std::stream; use async_std::sync::channel; use async_std::task; +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] /// Checks that streams are merged fully even if one of the components /// experiences delay. fn merging_delayed_streams_work() { let (sender, receiver) = channel::(10); let mut s = receiver.merge(stream::empty()); - let t = task::spawn(async move { + let t = spawn(async move { let mut xs = Vec::new(); while let Some(x) = s.next().await { xs.push(x); @@ -34,7 +43,7 @@ fn merging_delayed_streams_work() { let (sender, receiver) = channel::(10); let mut s = stream::empty().merge(receiver); - let t = task::spawn(async move { + let t = spawn(async move { let mut xs = Vec::new(); while let Some(x) = s.next().await { xs.push(x); @@ -85,16 +94,17 @@ fn explode(s: S) -> Explode { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn merge_works_with_unfused_streams() { let s1 = explode(stream::once(92)); let s2 = explode(stream::once(92)); let mut s = s1.merge(s2); - let xs = task::block_on(async move { + + task::block_on(async move { let mut xs = Vec::new(); while let Some(x) = s.next().await { xs.push(x) } - xs + assert_eq!(xs, vec![92, 92]); }); - assert_eq!(xs, vec![92, 92]); } diff --git a/tests/task_local.rs b/tests/task_local.rs index 813185c84..b5345fec3 100644 --- a/tests/task_local.rs +++ b/tests/task_local.rs @@ -3,7 +3,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; use async_std::task; use async_std::task_local; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn drop_local() { static DROP_LOCAL: AtomicBool = AtomicBool::new(false); @@ -20,7 +29,7 @@ fn drop_local() { } // Spawn a task that just touches its task-local. - let handle = task::spawn(async { + let handle = spawn(async { LOCAL.with(|_| ()); }); let task = handle.task().clone(); diff --git a/tests/tcp.rs b/tests/tcp.rs index d92cff0db..f21737e8f 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; diff --git a/tests/timeout.rs b/tests/timeout.rs index c9694f837..8ad358a40 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -3,7 +3,11 @@ use std::time::Duration; use async_std::future::timeout; use async_std::task; +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn timeout_future_many() { task::block_on(async { let futures = (0..100) diff --git a/tests/udp.rs b/tests/udp.rs index 319dc74ae..15404f87a 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::io; use async_std::net::UdpSocket; use async_std::task; diff --git a/tests/uds.rs b/tests/uds.rs index 3ab4d6ba4..038ac0ee9 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -1,4 +1,4 @@ -#![cfg(unix)] +#![cfg(all(unix, not(target_os = "unknown")))] use async_std::io; use async_std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 17d42611c..2876183ef 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::{fs, io, net::ToSocketAddrs, task}; #[test] diff --git a/wasm-test.sh b/wasm-test.sh new file mode 100755 index 000000000..3d8be9fd3 --- /dev/null +++ b/wasm-test.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +wasm-pack test --chrome --headless -- --features unstable --test buf_writer +wasm-pack test --chrome --headless -- --features unstable --test channel +wasm-pack test --chrome --headless -- --features unstable --test condvar +wasm-pack test --chrome --headless -- --features unstable --test mutex +wasm-pack test --chrome --headless -- --features unstable --test rwlock +wasm-pack test --chrome --headless -- --features unstable --test stream +wasm-pack test --chrome --headless -- --features unstable --test task_local +wasm-pack test --chrome --headless -- --features unstable --test timeout From 804a52b7fd538d1a6e6ee0bc9aefd100549a7826 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 26 Apr 2020 20:49:33 +0200 Subject: [PATCH 070/272] use published smol --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e74a2ed06..5b59bee3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "1.5.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", + "Friedel Ziegelmayer ", "Contributors to async-std", ] edition = "2018" @@ -65,7 +66,7 @@ pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { path = "../smol", optional = true } +smol = { version = "0.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-timer = "0.2.4" From 48dd683535b33a8b950e594257ff7808e491bb8a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 26 Apr 2020 21:02:01 +0200 Subject: [PATCH 071/272] fix feature settings --- Cargo.toml | 2 +- src/utils.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b59bee3a..4cdc5cf3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ default = [ "log", "num_cpus", "pin-project-lite", - "smol", ] docs = ["attributes", "unstable", "default"] unstable = ["std", "broadcaster"] @@ -43,6 +42,7 @@ std = [ "once_cell", "pin-utils", "slab", + "smol", ] alloc = [ "futures-core/alloc", diff --git a/src/utils.rs b/src/utils.rs index 2ae5488e5..ef068cb56 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,14 +59,14 @@ pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; } -#[cfg(not(target_os = "unknown"))] +#[cfg(all(not(target_os = "unknown"), feature = "default"))] pub(crate) type Timer = smol::Timer; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "default"))] #[derive(Debug)] pub(crate) struct Timer(wasm_timer::Delay); -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "default"))] impl Timer { pub(crate) fn after(dur: std::time::Duration) -> Self { Timer(wasm_timer::Delay::new(dur)) From 280b1a4344514dd20205c983e6d72b3b14ea2457 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 27 Apr 2020 11:11:16 +0200 Subject: [PATCH 072/272] remove invalid doc comment --- src/task/spawn_local.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/task/spawn_local.rs b/src/task/spawn_local.rs index 5ed7226d3..04da02012 100644 --- a/src/task/spawn_local.rs +++ b/src/task/spawn_local.rs @@ -4,10 +4,6 @@ use crate::task::{Builder, JoinHandle}; /// Spawns a task onto the thread-local executor. /// -/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task. -/// -/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html -/// /// # Examples /// /// ``` From 7a9afbd81c856a84a8940ff5c26e5e360afe4d06 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 27 Apr 2020 12:17:12 +0200 Subject: [PATCH 073/272] update smol --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4cdc5cf3b..57084ab0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1", optional = true } +smol = { version = "0.1.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-timer = "0.2.4" From 1a6d4f6a2f589e01ffe33facbf98d091056e2d07 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 27 Apr 2020 22:05:08 +0200 Subject: [PATCH 074/272] fix windows trait declarations for rawsocket --- src/net/tcp/listener.rs | 5 ++++- src/net/tcp/stream.rs | 2 +- src/os/windows/io.rs | 32 +++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index f31f1357f..49ee4f4f5 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -229,7 +229,10 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, AsRawSocket, RawSocket, FromRawSocket}; + use crate::os::windows::io::{ + AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, + AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, + }; impl AsRawSocket for TcpListener { fn as_raw_socket(&self) -> RawSocket { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 0dc43f5c9..7b71f98ec 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -402,7 +402,7 @@ cfg_windows! { } } - impl IntoRawSocket for crate::net::tcp::TcpListener { + impl IntoRawSocket for TcpStream { fn into_raw_socket(self) -> RawSocket { // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file // descriptor because it's possible that there are other clones of this `TcpStream` diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index e83d55711..30d37a0ef 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -2,7 +2,8 @@ cfg_not_docs! { pub use std::os::windows::io::{ - AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, + AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, + AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, }; } @@ -45,4 +46,33 @@ cfg_docs! { /// it once it's no longer needed. fn into_raw_handle(self) -> RawHandle; } + + /// Creates I/O objects from raw sockets. + pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will consume ownership of the socket provided and it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the + /// file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause + /// memory unsafety in code that relies on it being true. + unsafe fn from_raw_socket(sock: RawSocket) -> Self; + } + + /// Extracts raw sockets. + pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + fn as_raw_socket(&self) -> RawSocket; + } + + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw `SOCKET`. + pub trait IntoRawSocket { + /// Consumes this object, returning the raw underlying socket. + /// + /// This function **transfers ownership** of the underlying socket to the + /// caller. Callers are then the unique owners of the socket and must close + /// it once it's no longer needed. + fn into_raw_socket(self) -> RawSocket; + } } From 92532612b75d1e2d1ff93440f760e5718acb1824 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 27 Apr 2020 22:15:04 +0200 Subject: [PATCH 075/272] mark spawn_local unstable --- src/io/read/mod.rs | 2 +- src/task/builder.rs | 25 +++++++++++++++++++++++-- src/task/mod.rs | 8 ++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 0d429209d..388237c80 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -477,7 +477,7 @@ unsafe fn initialize(_reader: &R, buf: &mut [u8]) { std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } -#[cfg(test)] +#[cfg(all(test, not(target_os = "unknown")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/task/builder.rs b/src/task/builder.rs index f48b6b4c1..91e2cb6e9 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -61,7 +61,7 @@ impl Builder { } /// Spawns a task locally with the configured settings. - #[cfg(not(target_os = "unknown"))] + #[cfg(all(not(target_os = "unknown"), feature = "unstable"))] pub fn local(self, future: F) -> io::Result> where F: Future + 'static, @@ -76,7 +76,7 @@ impl Builder { } /// Spawns a task locally with the configured settings. - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", feature = "unstable"))] pub fn local(self, future: F) -> io::Result> where F: Future + 'static, @@ -96,6 +96,27 @@ impl Builder { Ok(JoinHandle::new(receiver, task)) } + /// Spawns a task locally with the configured settings. + #[cfg(all(target_arch = "wasm32", not(feature = "unstable")))] + pub(crate) fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + use futures_channel::oneshot::channel; + let (sender, receiver) = channel(); + + let wrapped = self.build(async move { + let res = future.await; + let _ = sender.send(res); + }); + + let task = wrapped.tag.task().clone(); + wasm_bindgen_futures::spawn_local(wrapped); + + Ok(JoinHandle::new(receiver, task)) + } + /// Spawns a task with the configured settings, blocking on its execution. #[cfg(not(target_os = "unknown"))] pub fn blocking(self, future: F) -> T diff --git a/src/task/mod.rs b/src/task/mod.rs index 6a142ffc7..eefc7c2a6 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -140,7 +140,6 @@ cfg_default! { pub use sleep::sleep; #[cfg(not(target_os = "unknown"))] pub use spawn::spawn; - pub use spawn_local::spawn_local; pub use task_local::{AccessError, LocalKey}; pub(crate) use task_local::LocalsMap; @@ -155,7 +154,6 @@ cfg_default! { mod spawn; #[cfg(not(target_os = "unknown"))] mod spawn_blocking; - mod spawn_local; mod task; mod task_id; mod task_local; @@ -168,3 +166,9 @@ cfg_default! { #[cfg(not(any(feature = "unstable", test)))] pub(crate) use spawn_blocking::spawn_blocking; } + +cfg_unstable! { + pub use spawn_local::spawn_local; + + mod spawn_local; +} From e0928463b166aba813c23c6fcc79f1a0204980a7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 28 Apr 2020 11:16:36 +0200 Subject: [PATCH 076/272] fix windows traits --- src/net/tcp/listener.rs | 3 +-- src/net/tcp/stream.rs | 8 +++++--- src/net/udp/mod.rs | 8 +++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 49ee4f4f5..72c5d3a80 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -230,7 +230,6 @@ cfg_unix! { cfg_windows! { use crate::os::windows::io::{ - AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, }; @@ -242,7 +241,7 @@ cfg_windows! { impl FromRawSocket for TcpListener { unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { - net::TcpListener::from_raw_socket(handle).try_into().unwrap() + std::net::TcpListener::from_raw_socket(handle).into() } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 7b71f98ec..b854143ff 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -388,17 +388,19 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket}; + use crate::os::windows::io::{ + RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket + }; impl AsRawSocket for TcpStream { fn as_raw_socket(&self) -> RawSocket { - self.raw_socket + self.watcher.get_ref().as_raw_socket() } } impl FromRawSocket for TcpStream { unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { - net::TcpStream::from_raw_socket(handle).try_into().unwrap() + std::net::TcpStream::from_raw_socket(handle).into() } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 53add8e7f..30cceb74c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -482,17 +482,19 @@ cfg_unix! { } cfg_windows! { - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket}; + use crate::os::windows::io::{ + RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket + }; impl AsRawSocket for UdpSocket { fn as_raw_socket(&self) -> RawSocket { - self.watcher.as_raw_socket() + self.watcher.get_ref().as_raw_socket() } } impl FromRawSocket for UdpSocket { unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { - net::UdpSocket::from_raw_socket(handle).into() + std::net::UdpSocket::from_raw_socket(handle).into() } } From 26f62aafd98e5e373142c7945fc78f8fa46b2618 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 28 Apr 2020 20:41:04 +0200 Subject: [PATCH 077/272] make wasm deps part of std --- Cargo.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57084ab0f..630b373a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,9 @@ std = [ "pin-utils", "slab", "smol", + "wasm-timer", + "wasm-bindgen-futures", + "futures-channel", ] alloc = [ "futures-core/alloc", @@ -69,9 +72,9 @@ slab = { version = "0.4.2", optional = true } smol = { version = "0.1.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-timer = "0.2.4" -wasm-bindgen-futures = "0.4.10" -futures-channel = "0.3.4" +wasm-timer = { version = "0.2.4", optional = true } +wasm-bindgen-futures = { version = "0.4.10", optional = true } +futures-channel = { version = "0.3.4", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.10" From 1214bc2dee891cc4d2713c6da7882a11cdedd87f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 29 Apr 2020 18:45:07 +0200 Subject: [PATCH 078/272] increase timeouts --- tests/condvar.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/condvar.rs b/tests/condvar.rs index 7b05b286c..76574a166 100644 --- a/tests/condvar.rs +++ b/tests/condvar.rs @@ -23,13 +23,13 @@ fn wait_timeout_with_lock() { spawn(async move { let (m, c) = &*pair2; let _g = m.lock().await; - task::sleep(Duration::from_millis(20)).await; + task::sleep(Duration::from_millis(200)).await; c.notify_one(); }); let (m, c) = &*pair; let (_, wait_result) = c - .wait_timeout(m.lock().await, Duration::from_millis(10)) + .wait_timeout(m.lock().await, Duration::from_millis(100)) .await; assert!(wait_result.timed_out()); }) @@ -57,7 +57,7 @@ fn wait_timeout_until_timed_out() { let c = Condvar::new(); let (_, wait_result) = c - .wait_timeout_until(m.lock().await, Duration::from_millis(10), |&mut started| { + .wait_timeout_until(m.lock().await, Duration::from_millis(100), |&mut started| { started }) .await; @@ -85,7 +85,7 @@ fn notify_all() { } // Give some time for tasks to start up - task::sleep(Duration::from_millis(5)).await; + task::sleep(Duration::from_millis(50)).await; let (m, c) = &*pair; { From faea222b9cb5e8cc8b0d3d69520f363af0ace2a7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 2 May 2020 20:24:59 +0200 Subject: [PATCH 079/272] fix: use run instead of block_on --- src/task/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/builder.rs b/src/task/builder.rs index 91e2cb6e9..3b71a5356 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -132,7 +132,7 @@ impl Builder { }); // Run the future as a task. - unsafe { TaskLocalsWrapper::set_current(&wrapped.tag, || smol::block_on(wrapped)) } + unsafe { TaskLocalsWrapper::set_current(&wrapped.tag, || smol::run(wrapped)) } } } From 27c605b4c99e1435d9d46aa790fde3cb3b7bfa43 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 7 May 2020 20:56:52 +0200 Subject: [PATCH 080/272] cr: bring back trace call --- src/task/builder.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/task/builder.rs b/src/task/builder.rs index 3b71a5356..cbb3187f2 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -54,6 +54,11 @@ impl Builder { { let wrapped = self.build(future); + kv_log_macro::trace!("spawn", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + let task = wrapped.tag.task().clone(); let smol_task = smol::Task::spawn(wrapped).into(); @@ -69,6 +74,11 @@ impl Builder { { let wrapped = self.build(future); + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + let task = wrapped.tag.task().clone(); let smol_task = smol::Task::local(wrapped).into(); @@ -89,6 +99,10 @@ impl Builder { let res = future.await; let _ = sender.send(res); }); + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); let task = wrapped.tag.task().clone(); wasm_bindgen_futures::spawn_local(wrapped); @@ -111,6 +125,11 @@ impl Builder { let _ = sender.send(res); }); + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + let task = wrapped.tag.task().clone(); wasm_bindgen_futures::spawn_local(wrapped); From 6f6fced1034ae8ef8c2bf91540630d2cc8e774d8 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Thu, 7 May 2020 14:26:46 -0600 Subject: [PATCH 081/272] feat: implement Barrier using Condvar --- Cargo.toml | 3 +-- src/sync/barrier.rs | 59 ++++++++++++--------------------------------- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 630b373a2..db26625bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ default = [ "pin-project-lite", ] docs = ["attributes", "unstable", "default"] -unstable = ["std", "broadcaster"] +unstable = ["std"] attributes = ["async-attributes"] std = [ "alloc", @@ -55,7 +55,6 @@ alloc = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } async-task = { version = "3.0.0", optional = true } -broadcaster = { version = "1.0.0", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 86e9a2d9d..f492ebe64 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -1,6 +1,4 @@ -use broadcaster::BroadcastChannel; - -use crate::sync::Mutex; +use crate::sync::{Condvar,Mutex}; /// A barrier enables multiple tasks to synchronize the beginning /// of some computation. @@ -36,14 +34,13 @@ use crate::sync::Mutex; #[derive(Debug)] pub struct Barrier { state: Mutex, - wait: BroadcastChannel<(usize, usize)>, - n: usize, + cvar: Condvar, + num_tasks: usize, } // The inner state of a double barrier #[derive(Debug)] struct BarrierState { - waker: BroadcastChannel<(usize, usize)>, count: usize, generation_id: usize, } @@ -81,25 +78,14 @@ impl Barrier { /// /// let barrier = Barrier::new(10); /// ``` - pub fn new(mut n: usize) -> Barrier { - let waker = BroadcastChannel::new(); - let wait = waker.clone(); - - if n == 0 { - // if n is 0, it's not clear what behavior the user wants. - // in std::sync::Barrier, an n of 0 exhibits the same behavior as n == 1, where every - // .wait() immediately unblocks, so we adopt that here as well. - n = 1; - } - + pub fn new(n: usize) -> Barrier { Barrier { state: Mutex::new(BarrierState { - waker, count: 0, generation_id: 1, }), - n, - wait, + cvar: Condvar::new(), + num_tasks: n, } } @@ -143,35 +129,20 @@ impl Barrier { /// # }); /// ``` pub async fn wait(&self) -> BarrierWaitResult { - let mut lock = self.state.lock().await; - let local_gen = lock.generation_id; - - lock.count += 1; + let mut state = self.state.lock().await; + let local_gen = state.generation_id; + state.count += 1; - if lock.count < self.n { - let mut wait = self.wait.clone(); - - let mut generation_id = lock.generation_id; - let mut count = lock.count; - - drop(lock); - - while local_gen == generation_id && count < self.n { - let (g, c) = wait.recv().await.expect("sender has not been closed"); - generation_id = g; - count = c; + if state.count < self.num_tasks { + while local_gen == state.generation_id && state.count < self.num_tasks { + state = self.cvar.wait(state).await; } BarrierWaitResult(false) } else { - lock.count = 0; - lock.generation_id = lock.generation_id.wrapping_add(1); - - lock.waker - .send(&(lock.generation_id, lock.count)) - .await - .expect("there should be at least one receiver"); - + state.count = 0; + state.generation_id = state.generation_id.wrapping_add(1); + self.cvar.notify_all(); BarrierWaitResult(true) } } From e4c4c93d29dac93d33eafcc0c677bf13e40bf5e4 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 7 May 2020 23:20:44 +0200 Subject: [PATCH 082/272] Test and fix 32 bit targets --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ Cargo.toml | 8 +++++++- src/task/task_id.rs | 9 +++++---- tests/io_timeout.rs | 9 ++++++++- tests/timeout.rs | 2 +- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f519e533..1a0c4323b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,6 +110,39 @@ jobs: command: check args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps + cross: + name: Cross compile + runs-on: ubuntu-latest + strategy: + matrix: + target: + - i686-unknown-linux-gnu + - powerpc-unknown-linux-gnu + - powerpc64-unknown-linux-gnu + - mips-unknown-linux-gnu + - arm-linux-androideabi + + steps: + - uses: actions/checkout@master + + - name: Install nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Install cross + run: cargo install cross + + - name: check + run: cross check --all --target ${{ matrix.target }} + + - name: check unstable + run: cross check --all --features unstable --target ${{ matrix.target }} + + - name: test + run: cross test --all --features unstable --target ${{ matrix.target }} + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index db26625bb..e6e810f47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,9 @@ pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } +# Devdepencency, but they are not allowed to be optional :/ +surf = { version = "1.0.3", optional = true } + [target.'cfg(not(target_os = "unknown"))'.dependencies] smol = { version = "0.1.1", optional = true } @@ -81,7 +84,6 @@ wasm-bindgen-test = "0.3.10" [dev-dependencies] femme = "1.3.0" rand = "0.7.3" -surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.4" rand_xorshift = "0.2.0" @@ -93,3 +95,7 @@ required-features = ["unstable"] [[example]] name = "tcp-ipv4-and-6-echo" required-features = ["unstable"] + +[[example]] +name = "surf-web" +required-features = ["surf"] \ No newline at end of file diff --git a/src/task/task_id.rs b/src/task/task_id.rs index 67eee154b..92c607c71 100644 --- a/src/task/task_id.rs +++ b/src/task/task_id.rs @@ -1,5 +1,5 @@ use std::fmt; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; /// A unique identifier for a task. /// @@ -13,15 +13,16 @@ use std::sync::atomic::{AtomicU64, Ordering}; /// }) /// ``` #[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct TaskId(pub(crate) u64); +pub struct TaskId(pub(crate) usize); impl TaskId { /// Generates a new `TaskId`. pub(crate) fn generate() -> TaskId { - static COUNTER: AtomicU64 = AtomicU64::new(1); + // TODO: find a good version to emulate u64 atomics on 32 bit systems. + static COUNTER: AtomicUsize = AtomicUsize::new(1); let id = COUNTER.fetch_add(1, Ordering::Relaxed); - if id > u64::max_value() / 2 { + if id > usize::max_value() / 2 { std::process::abort(); } TaskId(id) diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index fa30a68af..371150693 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -5,7 +5,14 @@ use async_std::task; #[test] #[should_panic(expected = "timed out")] -#[cfg(not(target_os = "unknown"))] +#[cfg(not(any( + target_os = "unknown", + target_arch = "arm", + target_arch = "mips", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "x86", +)))] // stdin tests fail when running through cross fn io_timeout_timedout() { task::block_on(async { io::timeout(Duration::from_secs(1), async { diff --git a/tests/timeout.rs b/tests/timeout.rs index 8ad358a40..e09acdfe4 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -12,7 +12,7 @@ fn timeout_future_many() { task::block_on(async { let futures = (0..100) .map(|i| { - timeout(Duration::from_millis(i * 10), async move { + timeout(Duration::from_millis(i * 20), async move { task::sleep(Duration::from_millis(i)).await; Ok::<(), async_std::future::TimeoutError>(()) }) From bd6a7e200bd380042eb811e37cd2b81089f0d55f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 7 May 2020 23:02:55 +0200 Subject: [PATCH 083/272] prepare v1.6.0-beta.1 --- CHANGELOG.md | 20 ++++++++++++++++++++ Cargo.toml | 2 +- src/lib.rs | 8 ++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e464ed762..44a36f3e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.0-beta.1] - 2020-05-07 + +## Added + +- Added `task::spawn_local`. ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added out of the box support for `wasm`. ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added `JoinHandle::cancel` ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added `sync::Condvar` ([#369](https://github.com/async-rs/async-std/pull/369)) +- Added `sync::Sender::try_send` and `sync::Receiver::try_recv` ([#585](https://github.com/async-rs/async-std/pull/585)) +- Added `no_std` support for `task`, `future` and `stream` ([#680](https://github.com/async-rs/async-std/pull/680)) + +## Changed + +- Switched underlying runtime to [`smol`](https://github.com/stjepang/smol/). ([#757](https://github.com/async-rs/async-std/pull/757)) +- Switched implementation of `sync::Barrier` to use `sync::Condvar` like `std` does. ([#581](https://github.com/async-rs/async-std/pull/581)) + +## Fixed + +- Allow compilation on 32 bit targets, by using `AtomicUsize` for `TaskId`. ([#756](https://github.com/async-rs/async-std/pull/756)) + # [1.5.0] - 2020-02-03 [API Documentation](https://docs.rs/async-std/1.5.0/async-std) diff --git a/Cargo.toml b/Cargo.toml index e6e810f47..a14a32b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.5.0" +version = "1.6.0-beta.1" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/src/lib.rs b/src/lib.rs index 408a7ab16..4be05e107 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,7 +194,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.0.0" +//! version = "1.6.0-beta.1" //! features = ["unstable"] //! ``` //! @@ -207,7 +207,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.0.0" +//! version = "1.6.0-beta.1" //! features = ["attributes"] //! ``` //! @@ -216,7 +216,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.0.0" +//! version = "1.6.0-beta.1" //! default-features = false //! features = ["std"] //! ``` @@ -226,7 +226,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.5.0" +//! version = "1.6.0-beta.1" //! default-features = false //! features = ["alloc"] //! ``` From 247c94ca06d6d04bc0ef061176b05b9a8e664842 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 7 May 2020 23:34:49 +0200 Subject: [PATCH 084/272] docs(changelog): add missing link --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a36f3e2..e7ac8b195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -698,6 +698,7 @@ task::blocking(async { - Initial beta release [Unreleased]: https://github.com/async-rs/async-std/compare/v1.5.0...HEAD +[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.6.0-beta.1 [1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0 [1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0 From 2762ec5800d94e7891c7b01f39f57c0b79eb3088 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 9 May 2020 11:36:13 +0200 Subject: [PATCH 085/272] fix(fs): use smol::block_on for drop handling of File Ref #766 --- src/fs/file.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 7fe99ee4f..74d2bfde3 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -12,7 +12,7 @@ use crate::future; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; -use crate::task::{self, spawn_blocking, Context, Poll, Waker}; +use crate::task::{spawn_blocking, Context, Poll, Waker}; use crate::utils::Context as _; /// An open file on the filesystem. @@ -315,7 +315,7 @@ impl Drop for File { // non-blocking fashion, but our only other option here is losing data remaining in the // write cache. Good task schedulers should be resilient to occasional blocking hiccups in // file destructors so we don't expect this to be a common problem in practice. - let _ = task::block_on(self.flush()); + let _ = smol::block_on(self.flush()); } } @@ -867,3 +867,15 @@ impl LockGuard { Poll::Ready(Ok(())) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn async_file_drop() { + crate::task::block_on(async move { + File::open(".").await.unwrap(); + }); + } +} From 19170aead40d73e773fe6784521cda92689cffef Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 9 May 2020 11:44:16 +0200 Subject: [PATCH 086/272] use local file --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 74d2bfde3..1930fdd67 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -875,7 +875,7 @@ mod tests { #[test] fn async_file_drop() { crate::task::block_on(async move { - File::open(".").await.unwrap(); + File::open(file!()).await.unwrap(); }); } } From cd5e17fe87746f10c823bf8a6103183fefbb82ec Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Sun, 10 May 2020 18:18:50 -0700 Subject: [PATCH 087/272] make UnixStream Clone --- src/os/unix/net/listener.rs | 3 ++- src/os/unix/net/stream.rs | 26 ++++++++++++++++---------- tests/uds.rs | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 4099bd6f5..ac033075d 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -13,6 +13,7 @@ use crate::io; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; +use crate::sync::Arc; use crate::task::{Context, Poll}; /// A Unix domain socket server, listening for connections. @@ -92,7 +93,7 @@ impl UnixListener { pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let (stream, addr) = self.watcher.accept().await?; - Ok((UnixStream { watcher: stream }, addr)) + Ok((UnixStream { watcher: Arc::new(stream) }, addr)) } /// Returns a stream of incoming connections. diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 7320c85be..b1ba5bca0 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -11,6 +11,7 @@ use super::SocketAddr; use crate::io::{self, Read, Write}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; +use crate::sync::Arc; use crate::task::{Context, Poll}; /// A Unix stream socket. @@ -36,8 +37,9 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` +#[derive(Clone)] pub struct UnixStream { - pub(super) watcher: Async, + pub(super) watcher: Arc>, } impl UnixStream { @@ -56,7 +58,7 @@ impl UnixStream { /// ``` pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let stream = Async::::connect(path).await?; + let stream = Arc::new(Async::::connect(path).await?); Ok(UnixStream { watcher: stream }) } @@ -78,8 +80,12 @@ impl UnixStream { /// ``` pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let (a, b) = Async::::pair()?; - let a = UnixStream { watcher: a }; - let b = UnixStream { watcher: b }; + let a = UnixStream { + watcher: Arc::new(a), + }; + let b = UnixStream { + watcher: Arc::new(b), + }; Ok((a, b)) } @@ -158,7 +164,7 @@ impl Read for &UnixStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut &self.watcher).poll_read(cx, buf) + Pin::new(&mut &*self.watcher).poll_read(cx, buf) } } @@ -186,15 +192,15 @@ impl Write for &UnixStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut &self.watcher).poll_write(cx, buf) + Pin::new(&mut &*self.watcher).poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.watcher).poll_flush(cx) + Pin::new(&mut &*self.watcher).poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.watcher).poll_close(cx) + Pin::new(&mut &*self.watcher).poll_close(cx) } } @@ -219,7 +225,7 @@ impl From for UnixStream { /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. fn from(stream: StdUnixStream) -> UnixStream { let stream = Async::new(stream).expect("UnixStream is known to be good"); - UnixStream { watcher: stream } + UnixStream { watcher: Arc::new(stream) } } } @@ -238,6 +244,6 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.watcher.into_raw_fd() + self.as_raw_fd() } } diff --git a/tests/uds.rs b/tests/uds.rs index 038ac0ee9..d081bdaee 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -94,3 +94,27 @@ async fn ping_pong_client(socket: &std::path::PathBuf, iterations: u32) -> std:: } Ok(()) } + +#[test] +fn uds_clone() -> io::Result<()> { + task::block_on(async { + let tmp_dir = TempDir::new("socket_ping_pong").expect("Temp dir not created"); + let sock_path = tmp_dir.as_ref().join("sock"); + let input = UnixListener::bind(&sock_path).await?; + + let mut writer = UnixStream::connect(&sock_path).await?; + let mut reader = input.incoming().next().await.unwrap()?; + + writer.write(b"original").await.unwrap(); + let mut original_buf = [0; 8]; + reader.read(&mut original_buf).await?; + assert_eq!(&original_buf, b"original"); + + writer.clone().write(b"clone").await.unwrap(); + let mut clone_buf = [0; 5]; + reader.clone().read(&mut clone_buf).await?; + assert_eq!(&clone_buf, b"clone"); + + Ok(()) + }) +} From d3e59370e78ce279f45a3943457e22ebf46292fe Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 13 May 2020 10:14:05 +1200 Subject: [PATCH 088/272] Switches `wasm-timer` for `futures-timer`. --- Cargo.toml | 4 ++-- src/utils.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a14a32b37..5a1d31e00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ std = [ "pin-utils", "slab", "smol", - "wasm-timer", + "futures-timer", "wasm-bindgen-futures", "futures-channel", ] @@ -74,7 +74,7 @@ surf = { version = "1.0.3", optional = true } smol = { version = "0.1.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-timer = { version = "0.2.4", optional = true } +futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } wasm-bindgen-futures = { version = "0.4.10", optional = true } futures-channel = { version = "0.3.4", optional = true } diff --git a/src/utils.rs b/src/utils.rs index ef068cb56..7c9aa996b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -64,12 +64,12 @@ pub(crate) type Timer = smol::Timer; #[cfg(all(target_arch = "wasm32", feature = "default"))] #[derive(Debug)] -pub(crate) struct Timer(wasm_timer::Delay); +pub(crate) struct Timer(futures_timer::Delay); #[cfg(all(target_arch = "wasm32", feature = "default"))] impl Timer { pub(crate) fn after(dur: std::time::Duration) -> Self { - Timer(wasm_timer::Delay::new(dur)) + Timer(futures_timer::Delay::new(dur)) } } From e9621af345d854bcf1e71690b54cc270558c4940 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 13 May 2020 10:37:19 +1200 Subject: [PATCH 089/272] Updates `CHANGELOG.md`. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7ac8b195..70d8ed893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +## Changed + +- For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776)) + # [1.6.0-beta.1] - 2020-05-07 ## Added From baead51a282e077103a17b30fc6f25726613e0b6 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 13 May 2020 10:38:40 +1200 Subject: [PATCH 090/272] Reduces duration in timeout test. Tries to get CI to pass. --- tests/condvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/condvar.rs b/tests/condvar.rs index 76574a166..b5ec12a1e 100644 --- a/tests/condvar.rs +++ b/tests/condvar.rs @@ -29,7 +29,7 @@ fn wait_timeout_with_lock() { let (m, c) = &*pair; let (_, wait_result) = c - .wait_timeout(m.lock().await, Duration::from_millis(100)) + .wait_timeout(m.lock().await, Duration::from_millis(50)) .await; assert!(wait_result.timed_out()); }) From 9e6a76af04a08d3e3d13f4366c5d254dc2c22b94 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Tue, 19 May 2020 02:16:01 -0700 Subject: [PATCH 091/272] feat: add env vars to configure the runtime threadpool size and name --- src/lib.rs | 15 +++++++++++++++ src/rt/mod.rs | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4be05e107..4704a9d77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -230,6 +230,21 @@ //! default-features = false //! features = ["alloc"] //! ``` +//! +//! # Runtime configuration +//! +//! Several environment variables are available to tune the async-std +//! runtime: +//! +//! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the +//! async-std runtime will start. By default, this is one per logical +//! cpu as reported by the [num_cpus](num_cpus) crate, which may be +//! different than the number of physical cpus. Async-std _will panic_ +//! if this is set to any value other than a positive integer. +//! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime +//! threads report to the operating system. The default value is +//! `"async-std/runtime"`. +//! #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "docs", feature(doc_cfg))] diff --git a/src/rt/mod.rs b/src/rt/mod.rs index d5d0d6105..65f4e248b 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -1,5 +1,6 @@ //! The runtime. +use std::env; use std::thread; use once_cell::sync::Lazy; @@ -12,10 +13,20 @@ pub struct Runtime {} /// The global runtime. pub static RUNTIME: Lazy = Lazy::new(|| { // Create an executor thread pool. - let num_threads = num_cpus::get().max(1); - for _ in 0..num_threads { + + let thread_count = env::var("ASYNC_STD_THREAD_COUNT") + .map(|env| { + env.parse() + .expect("ASYNC_STD_THREAD_COUNT must be a number") + }) + .unwrap_or_else(|_| num_cpus::get()) + .max(1); + + let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or("async-std/runtime".to_string()); + + for _ in 0..thread_count { thread::Builder::new() - .name("async-std/runtime".to_string()) + .name(thread_name.clone()) .spawn(|| smol::run(future::pending::<()>())) .expect("cannot start a runtime thread"); } From c9ecb5bbbdfaaececf369915852d748a73af726e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 19 May 2020 11:29:36 +0200 Subject: [PATCH 092/272] prepare v1.6.0-beta.2 --- CHANGELOG.md | 16 ++++++++++++++-- Cargo.toml | 4 ++-- src/lib.rs | 8 ++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d8ed893..fc96d1abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,21 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.0-beta.2] - 2020-05-19 + +## Added + +- Added an environment variable to configure the thread pool size of the runtime. ([#774](https://github.com/async-rs/async-std/pull/774)) +- Implement `Clone` for `UnixStream` ([#772](https://github.com/async-rs/async-std/pull/772)) + ## Changed - For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776)) +## Fixed + +- Use `smol::block_on` to handle drop of `File`, avoiding nested executor panic. ([#768](https://github.com/async-rs/async-std/pull/768)) + # [1.6.0-beta.1] - 2020-05-07 ## Added @@ -701,8 +712,9 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.5.0...HEAD -[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.6.0-beta.1 +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.2...HEAD +[1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2 +[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1 [1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0 [1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0 diff --git a/Cargo.toml b/Cargo.toml index 5a1d31e00..7dae8f074 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.0-beta.1" +version = "1.6.0-beta.2" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", @@ -71,7 +71,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1.1", optional = true } +smol = { version = "0.1.8", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/lib.rs b/src/lib.rs index 4704a9d77..c669b453f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,7 +194,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.1" +//! version = "1.6.0-beta.2" //! features = ["unstable"] //! ``` //! @@ -207,7 +207,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.1" +//! version = "1.6.0-beta.2" //! features = ["attributes"] //! ``` //! @@ -216,7 +216,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.1" +//! version = "1.6.0-beta.2" //! default-features = false //! features = ["std"] //! ``` @@ -226,7 +226,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.1" +//! version = "1.6.0-beta.2" //! default-features = false //! features = ["alloc"] //! ``` From 69806403c6cfd92eb273cc7391a396c10d69e209 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Wed, 20 May 2020 14:24:06 +0200 Subject: [PATCH 093/272] Fix readme for BufRead The `BufRead` readme points to `BufReadExt` being in `async_std::prelude` while it currently lives in `async_std::io::prelude` --- src/io/buf_read/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index d919a782c..7a0ecc606 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -29,7 +29,7 @@ extension_trait! { ``` # #[allow(unused_imports)] - use async_std::prelude::*; + use async_std::io::prelude::*; ``` [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html From 06eea4225b01329ac8faa9cc2a41a61d702b7d92 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 22 May 2020 22:08:23 +0200 Subject: [PATCH 094/272] feat: add PartialEq and Eq for channel Errors Closes #792 --- src/sync/channel.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 8ab1cc12a..3207846c1 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -992,6 +992,7 @@ impl Drop for Channel { /// An error returned from the `try_send` method. #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(PartialEq, Eq)] pub enum TrySendError { /// The channel is full but not disconnected. Full(T), @@ -1023,7 +1024,7 @@ impl Display for TrySendError { /// An error returned from the `try_recv` method. #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum TryRecvError { /// The channel is empty but not disconnected. Empty, @@ -1046,7 +1047,7 @@ impl Display for TryRecvError { /// An error returned from the `recv` method. #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct RecvError; impl Error for RecvError {} From e1c8638173e56f836f243f594079143804147f9e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 22 May 2020 22:08:36 +0200 Subject: [PATCH 095/272] chore: release v1.6.0 --- CHANGELOG.md | 7 ++++++- Cargo.toml | 4 ++-- src/lib.rs | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc96d1abf..ccefc2813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.0] - 2020-05-22 + +See `1.6.0-beta.1` and `1.6.0-beta.2`. + # [1.6.0-beta.2] - 2020-05-19 ## Added @@ -712,7 +716,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.2...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.0...HEAD +[1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 [1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2 [1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1 [1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0 diff --git a/Cargo.toml b/Cargo.toml index 7dae8f074..d02577e88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.0-beta.2" +version = "1.6.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", @@ -71,7 +71,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1.8", optional = true } +smol = { version = "0.1.10", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/lib.rs b/src/lib.rs index e3486d08a..2dbd258fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.2" +//! version = "1.6.0" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.2" +//! version = "1.6.0" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.2" +//! version = "1.6.0" //! default-features = false //! features = ["std"] //! ``` @@ -229,7 +229,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0-beta.2" +//! version = "1.6.0" //! default-features = false //! features = ["alloc"] //! ``` From d60e7cc27dffc57905e69c1a328cfba10b5eeddc Mon Sep 17 00:00:00 2001 From: jerry73204 Date: Fri, 29 May 2020 19:18:06 +0800 Subject: [PATCH 096/272] Fix wrong slice index when reading a file --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 1930fdd67..2ff5643e7 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -673,7 +673,7 @@ impl LockGuard { if available > 0 || self.cache.is_empty() { // Copy data from the cache into the buffer. let n = cmp::min(available, buf.len()); - buf[..n].copy_from_slice(&self.cache[start..n]); + buf[..n].copy_from_slice(&self.cache[start..(start + n)]); // Move the read cursor forward. self.mode = Mode::Reading(start + n); From 166c469d1c5a14a60b1f144cd40fae08e914678f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 3 Jun 2020 12:09:33 +0200 Subject: [PATCH 097/272] Add the tokio02 feature flag --- Cargo.toml | 3 ++- src/lib.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d02577e88..d51165b36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ alloc = [ "futures-core/alloc", "pin-project-lite", ] +tokio02 = ["smol/tokio02"] [dependencies] async-attributes = { version = "1.1.1", optional = true } @@ -98,4 +99,4 @@ required-features = ["unstable"] [[example]] name = "surf-web" -required-features = ["surf"] \ No newline at end of file +required-features = ["surf"] diff --git a/src/lib.rs b/src/lib.rs index 2dbd258fe..e5b043896 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,6 +214,15 @@ //! features = ["attributes"] //! ``` //! +//! Compatibility with the `tokio` runtime is possible using the `tokio02` +//! Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.6.0" +//! features = ["tokio02"] +//! ``` +//! //! Additionally it's possible to only use the core traits and combinators by //! only enabling the `std` Cargo feature: //! From 0df3c02b81f8b581dab0104fb13fdd662caa046f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 3 Jun 2020 12:40:02 +0200 Subject: [PATCH 098/272] check tokio02 features --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a0c4323b..fcbc4bd43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,6 +110,17 @@ jobs: command: check args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps + check_tokio_02_feature: + name: Check tokio02 feature + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: check tokio02 + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --features tokio02 + cross: name: Cross compile runs-on: ubuntu-latest From 52c72426c1c377504addda2ffaccea6557f776ff Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 3 Jun 2020 18:38:20 +0200 Subject: [PATCH 099/272] fix: do not require the runtime to use unstable features --- Cargo.toml | 9 ++++++--- src/future/mod.rs | 8 ++++---- src/task/mod.rs | 2 ++ src/utils.rs | 44 +++++++++++++++++++++++--------------------- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d02577e88..bf08bee3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,13 @@ default = [ "log", "num_cpus", "pin-project-lite", + "smol", ] docs = ["attributes", "unstable", "default"] -unstable = ["std"] +unstable = [ + "std", + "futures-timer", +] attributes = ["async-attributes"] std = [ "alloc", @@ -42,8 +46,6 @@ std = [ "once_cell", "pin-utils", "slab", - "smol", - "futures-timer", "wasm-bindgen-futures", "futures-channel", ] @@ -66,6 +68,7 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } +futures-timer = { version = "3.0.2", optional = true } # Devdepencency, but they are not allowed to be optional :/ surf = { version = "1.0.3", optional = true } diff --git a/src/future/mod.rs b/src/future/mod.rs index 9b75533d3..db0607adb 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -61,10 +61,10 @@ cfg_std! { mod ready; } -cfg_default! { - pub use timeout::{timeout, TimeoutError}; - mod timeout; -} +#[cfg(any(feature = "unstable", feature = "default"))] +pub use timeout::{timeout, TimeoutError}; +#[cfg(any(feature = "unstable", feature = "default"))] +mod timeout; cfg_unstable! { pub use into_future::IntoFuture; diff --git a/src/task/mod.rs b/src/task/mod.rs index eefc7c2a6..ca0b92a02 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -168,7 +168,9 @@ cfg_default! { } cfg_unstable! { + #[cfg(feature = "default")] pub use spawn_local::spawn_local; + #[cfg(feature = "default")] mod spawn_local; } diff --git a/src/utils.rs b/src/utils.rs index 7c9aa996b..e7ea9ecc1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -60,36 +60,38 @@ pub(crate) trait Context { } #[cfg(all(not(target_os = "unknown"), feature = "default"))] -pub(crate) type Timer = smol::Timer; +mod timer { + pub type Timer = smol::Timer; +} -#[cfg(all(target_arch = "wasm32", feature = "default"))] -#[derive(Debug)] -pub(crate) struct Timer(futures_timer::Delay); +#[cfg(any(all(target_arch = "wasm32", feature = "default"), feature = "unstable"))] +mod timer { + use std::pin::Pin; + use std::task::Poll; -#[cfg(all(target_arch = "wasm32", feature = "default"))] -impl Timer { - pub(crate) fn after(dur: std::time::Duration) -> Self { - Timer(futures_timer::Delay::new(dur)) - } -} + #[derive(Debug)] + pub(crate) struct Timer(futures_timer::Delay); -#[cfg(target_arch = "wasm32")] -use std::pin::Pin; -#[cfg(target_arch = "wasm32")] -use std::task::Poll; + impl Timer { + pub(crate) fn after(dur: std::time::Duration) -> Self { + Timer(futures_timer::Delay::new(dur)) + } + } -#[cfg(target_arch = "wasm32")] -impl std::future::Future for Timer { - type Output = (); + impl std::future::Future for Timer { + type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - match Pin::new(&mut self.0).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(_) => Poll::Ready(()), + fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match Pin::new(&mut self.0).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => Poll::Ready(()), + } } } } +pub(crate) use timer::*; + /// Defers evaluation of a block of code until the end of the scope. #[cfg(feature = "default")] #[doc(hidden)] From 8943ba82dd0e4cb8a29f6750a519a53080093944 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 3 Jun 2020 18:43:19 +0200 Subject: [PATCH 100/272] fix nostd --- src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.rs b/src/utils.rs index e7ea9ecc1..9ad4338dc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -90,6 +90,7 @@ mod timer { } } +#[cfg(any(feature = "unstable", feature = "default"))] pub(crate) use timer::*; /// Defers evaluation of a block of code until the end of the scope. From 8389041414a4c39f3e899415d51d3cffdb121eea Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 3 Jun 2020 18:50:12 +0200 Subject: [PATCH 101/272] fix --- src/utils.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 9ad4338dc..e064570ec 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -64,7 +64,10 @@ mod timer { pub type Timer = smol::Timer; } -#[cfg(any(all(target_arch = "wasm32", feature = "default"), feature = "unstable"))] +#[cfg(any( + all(target_arch = "wasm32", feature = "default"), + all(feature = "unstable", not(feature = "default")) +))] mod timer { use std::pin::Pin; use std::task::Poll; From 721760a7a612eabce6a536945bafee27dfa5ae99 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Thu, 4 Jun 2020 09:05:14 +0200 Subject: [PATCH 102/272] Remove stdio lock methods Fixes #805. --- src/io/mod.rs | 9 ------- src/io/stderr.rs | 70 ------------------------------------------------ src/io/stdin.rs | 61 ----------------------------------------- src/io/stdout.rs | 70 ------------------------------------------------ 4 files changed, 210 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index f5dd9e2c0..a673636ff 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -328,12 +328,3 @@ cfg_default! { #[cfg(not(target_os = "unknown"))] mod stdout; } - -cfg_unstable_default! { - #[cfg(not(target_os = "unknown"))] - pub use stderr::StderrLock; - #[cfg(not(target_os = "unknown"))] - pub use stdin::StdinLock; - #[cfg(not(target_os = "unknown"))] - pub use stdout::StdoutLock; -} diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5ff8a029d..5067ed4be 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -5,11 +5,6 @@ use std::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Write as _; -} - /// Constructs a new handle to the standard error of the current process. /// /// This function is an async version of [`std::io::stderr`]. @@ -58,22 +53,6 @@ pub fn stderr() -> Stderr { #[derive(Debug)] pub struct Stderr(Mutex); -/// A locked reference to the Stderr handle. -/// -/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`] -/// method. -/// -/// [`Write`]: trait.Read.html -/// [`Stderr::lock`]: struct.Stderr.html#method.lock -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct StderrLock<'a>(std::io::StderrLock<'a>); - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -unsafe impl Send for StderrLock<'_> {} - /// The state of the asynchronous stderr. /// /// The stderr can be either idle or busy performing an asynchronous operation. @@ -108,35 +87,6 @@ enum Operation { Flush(io::Result<()>), } -impl Stderr { - /// Locks this handle to the standard error stream, returning a writable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let stderr = io::stderr(); - /// let mut handle = stderr.lock().await; - /// - /// handle.write_all(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] - pub async fn lock(&self) -> StderrLock<'static> { - static STDERR: Lazy = Lazy::new(std::io::stderr); - - spawn_blocking(move || StderrLock(STDERR.lock())).await - } -} - impl Write for Stderr { fn poll_write( mut self: Pin<&mut Self>, @@ -239,23 +189,3 @@ cfg_windows! { } } } - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -impl io::Write for StderrLock<'_> { - fn poll_write( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready(self.0.write(buf)) - } - - fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.0.flush()) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 369ccae4c..fc280f8ca 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -7,11 +7,6 @@ use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::utils::Context as _; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Read as _; -} - /// Constructs a new handle to the standard input of the current process. /// /// This function is an async version of [`std::io::stdin`]. @@ -61,21 +56,6 @@ pub fn stdin() -> Stdin { #[derive(Debug)] pub struct Stdin(Mutex); -/// A locked reference to the Stdin handle. -/// -/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method. -/// -/// [`Read`]: trait.Read.html -/// [`Stdin::lock`]: struct.Stdin.html#method.lock -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] -#[derive(Debug)] -pub struct StdinLock<'a>(std::io::StdinLock<'a>); - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -unsafe impl Send for StdinLock<'_> {} - /// The state of the asynchronous stdin. /// /// The stdin can be either idle or busy performing an asynchronous operation. @@ -165,35 +145,6 @@ impl Stdin { .await .context(|| String::from("could not read line on stdin")) } - - /// Locks this handle to the standard input stream, returning a readable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let mut buffer = String::new(); - /// - /// let stdin = io::stdin(); - /// let mut handle = stdin.lock().await; - /// - /// handle.read_to_string(&mut buffer).await?; - /// # - /// # Ok(()) }) } - /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] - pub async fn lock(&self) -> StdinLock<'static> { - static STDIN: Lazy = Lazy::new(std::io::stdin); - - spawn_blocking(move || StdinLock(STDIN.lock())).await - } } impl Read for Stdin { @@ -265,15 +216,3 @@ cfg_windows! { } } } - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -impl Read for StdinLock<'_> { - fn poll_read( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - Poll::Ready(self.0.read(buf)) - } -} diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 1711c090e..b3dfe6444 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -5,11 +5,6 @@ use std::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; -cfg_unstable! { - use once_cell::sync::Lazy; - use std::io::Write as _; -} - /// Constructs a new handle to the standard output of the current process. /// /// This function is an async version of [`std::io::stdout`]. @@ -58,22 +53,6 @@ pub fn stdout() -> Stdout { #[derive(Debug)] pub struct Stdout(Mutex); -/// A locked reference to the Stderr handle. -/// -/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`] -/// method. -/// -/// [`Write`]: trait.Read.html -/// [`Stdout::lock`]: struct.Stdout.html#method.lock -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -unsafe impl Send for StdoutLock<'_> {} - /// The state of the asynchronous stdout. /// /// The stdout can be either idle or busy performing an asynchronous operation. @@ -108,35 +87,6 @@ enum Operation { Flush(io::Result<()>), } -impl Stdout { - /// Locks this handle to the standard error stream, returning a writable guard. - /// - /// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let stdout = io::stdout(); - /// let mut handle = stdout.lock().await; - /// - /// handle.write_all(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] - pub async fn lock(&self) -> StdoutLock<'static> { - static STDOUT: Lazy = Lazy::new(std::io::stdout); - - spawn_blocking(move || StdoutLock(STDOUT.lock())).await - } -} - impl Write for Stdout { fn poll_write( mut self: Pin<&mut Self>, @@ -239,23 +189,3 @@ cfg_windows! { } } } - -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -impl Write for StdoutLock<'_> { - fn poll_write( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready(self.0.write(buf)) - } - - fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.0.flush()) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} From e12cf80ab0290fa766871982803e39882121e4b0 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 4 Jun 2020 13:19:03 +0200 Subject: [PATCH 103/272] fix: allow for recursive block-on calls Fixes #798,#795,#760 --- Cargo.toml | 3 ++- src/task/builder.rs | 26 ++++++++++++++++++++++- tests/block_on.rs | 51 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2daaf035..284c8b813 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1.10", optional = true } +smol = { version = "0.1.11", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } @@ -103,3 +103,4 @@ required-features = ["unstable"] [[example]] name = "surf-web" required-features = ["surf"] + diff --git a/src/task/builder.rs b/src/task/builder.rs index cbb3187f2..0024f8ab8 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,3 +1,4 @@ +use std::cell::Cell; use std::future::Future; use std::pin::Pin; use std::sync::Arc; @@ -150,8 +151,31 @@ impl Builder { parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), }); + thread_local! { + /// Tracks the number of nested block_on calls. + static NUM_NESTED_BLOCKING: Cell = Cell::new(0); + } + // Run the future as a task. - unsafe { TaskLocalsWrapper::set_current(&wrapped.tag, || smol::run(wrapped)) } + NUM_NESTED_BLOCKING.with(|num_nested_blocking| { + let count = num_nested_blocking.get(); + let should_run = count == 0; + // increase the count + num_nested_blocking.replace(count + 1); + + unsafe { + TaskLocalsWrapper::set_current(&wrapped.tag, || { + let res = if should_run { + // The first call should use run. + smol::run(wrapped) + } else { + smol::block_on(wrapped) + }; + num_nested_blocking.replace(num_nested_blocking.get() - 1); + res + }) + } + }) } } diff --git a/tests/block_on.rs b/tests/block_on.rs index 28902b018..4c264804d 100644 --- a/tests/block_on.rs +++ b/tests/block_on.rs @@ -1,18 +1,63 @@ #![cfg(not(target_os = "unknown"))] -use async_std::task; +use async_std::{future::ready, task::block_on}; #[test] fn smoke() { - let res = task::block_on(async { 1 + 2 }); + let res = block_on(async { 1 + 2 }); assert_eq!(res, 3); } #[test] #[should_panic = "boom"] fn panic() { - task::block_on(async { + block_on(async { // This panic should get propagated into the parent thread. panic!("boom"); }); } + +#[cfg(feature = "unstable")] +#[test] +fn nested_block_on_local() { + use async_std::task::spawn_local; + + let x = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = spawn_local(async { block_on(async { ready(2).await }) }).await; + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(x, 3 + 2 + 1); + + let y = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = spawn_local(async { block_on(async { ready(2).await }) }).await; + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(y, 3 + 2 + 1); +} + +#[test] +fn nested_block_on() { + let x = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = block_on(async { block_on(async { ready(2).await }) }); + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(x, 3 + 2 + 1); + + let y = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = block_on(async { block_on(async { ready(2).await }) }); + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(y, 3 + 2 + 1); +} From 5a1a681d685763011c258edb8b6e2a3e22bc418e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 4 Jun 2020 18:25:07 +0200 Subject: [PATCH 104/272] fix(rt): use task::block_on on spawned threads This makes sure to capture threads into the recursive block_on detection. --- src/rt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rt/mod.rs b/src/rt/mod.rs index 65f4e248b..d8550aac8 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -27,7 +27,7 @@ pub static RUNTIME: Lazy = Lazy::new(|| { for _ in 0..thread_count { thread::Builder::new() .name(thread_name.clone()) - .spawn(|| smol::run(future::pending::<()>())) + .spawn(|| crate::task::block_on(future::pending::<()>())) .expect("cannot start a runtime thread"); } Runtime {} From 4555f193a51f6c46ed2d0bab1b190dec94dc5d33 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Sun, 7 Jun 2020 18:15:43 +0200 Subject: [PATCH 105/272] ci: update actions/cache to v2 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcbc4bd43..7b9439bde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,19 +30,19 @@ jobs: override: true - name: Cache cargo registry - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo/registry key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }} - name: Cache cargo index - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cargo/git key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.toml') }} - name: Cache cargo build - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: target key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }} @@ -58,7 +58,7 @@ jobs: with: command: check args: --features unstable --all --bins --examples --tests - + - name: check wasm uses: actions-rs/cargo@v1 with: From e9c6ea873c628a304b5f2d5a1306c603830e604f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 11 Jun 2020 13:17:31 +0200 Subject: [PATCH 106/272] chore: release v1.6.1 --- CHANGELOG.md | 19 ++++++++++++++++++- Cargo.toml | 2 +- src/lib.rs | 10 +++++----- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccefc2813..d6bb2ccd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.1] - 2020-06-11 + +## Added + +- Added `tokio02` feature flag, to allow compatability usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)). + +## Changed + +- Removed unstable `stdio` lock methods, due to their unsoundness ([#807](https://github.com/async-rs/async-std/pull/807)). + +## Fixed + +- Fixed wrong slice index for file reading ([#802](https://github.com/async-rs/async-std/pull/802)). +- Fixed recursive calls to `block_on` ([#799](https://github.com/async-rs/async-std/pull/799)) and ([#809](https://github.com/async-rs/async-std/pull/809)). +- Remove `default` feature requirement for the `unstable` feature ([#806](https://github.com/async-rs/async-std/pull/806)). + # [1.6.0] - 2020-05-22 See `1.6.0-beta.1` and `1.6.0-beta.2`. @@ -716,7 +732,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.1...HEAD +[1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 [1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2 [1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1 diff --git a/Cargo.toml b/Cargo.toml index 284c8b813..bf86da87c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.0" +version = "1.6.1" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/src/lib.rs b/src/lib.rs index e5b043896..6f7626548 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0" +//! version = "1.6.1" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0" +//! version = "1.6.1" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0" +//! version = "1.6.1" //! features = ["tokio02"] //! ``` //! @@ -228,7 +228,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0" +//! version = "1.6.1" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +238,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.0" +//! version = "1.6.1" //! default-features = false //! features = ["alloc"] //! ``` From 2323ac9a8eec2309073abc8861636351fbd7c28b Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 12 Jun 2020 18:03:07 +0300 Subject: [PATCH 107/272] Apply suggestions from code review Co-authored-by: nasa --- src/stream/stream/flat_map.rs | 20 +++++++------------- src/stream/stream/flatten.rs | 18 ++++++------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index f9ceb86af..97f57372d 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -51,22 +51,16 @@ where let mut this = self.project(); loop { if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { - let next_item = futures_core::ready!(inner.poll_next(cx)); - - if next_item.is_some() { - return Poll::Ready(next_item); - } else { - this.inner_stream.set(None); + match futures_core::ready!(inner.poll_next(cx)) { + item @ Some(_) => return Poll::Ready(item), + None => this.inner_stream.set(None), } } - let inner = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - - if inner.is_some() { - this.inner_stream.set(inner.map(IntoStream::into_stream)); - } else { - return Poll::Ready(None); + match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { + inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)), + None => return Poll::Ready(None), } } } -} \ No newline at end of file +} diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 13975f7bb..5f8d00038 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -52,21 +52,15 @@ where let mut this = self.project(); loop { if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { - let next_item = futures_core::ready!(inner.poll_next(cx)); - - if next_item.is_some() { - return Poll::Ready(next_item); - } else { - this.inner_stream.set(None); + match futures_core::ready!(inner.poll_next(cx)) { + item @ Some(_) => return Poll::Ready(next_item), + None => this.inner_stream.set(None), } } - let inner = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - - if inner.is_some() { - this.inner_stream.set(inner.map(IntoStream::into_stream)); - } else { - return Poll::Ready(None); + match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { + inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)), + None => Poll::Ready(None), } } } From df22d87d098397149e33a61b3cc676de0ad97c0b Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 12 Jun 2020 18:18:40 +0300 Subject: [PATCH 108/272] Removed unnecessary links + hotfix --- src/stream/stream/flatten.rs | 2 +- tests/stream.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 7a767e1e9..352360380 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -53,7 +53,7 @@ where loop { if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { match futures_core::ready!(inner.poll_next(cx)) { - item @ Some(_) => return Poll::Ready(next_item), + item @ Some(_) => return Poll::Ready(item), None => this.inner_stream.set(None), } } diff --git a/tests/stream.rs b/tests/stream.rs index 47ff228de..3a192339f 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -154,7 +154,6 @@ impl Stream for Interchanger { } } -// https://github.com/async-rs/async-std/pull/701 #[test] fn flat_map_doesnt_poll_completed_inner_stream() { task::block_on(async { @@ -169,7 +168,6 @@ fn flat_map_doesnt_poll_completed_inner_stream() { }); } -// https://github.com/async-rs/async-std/pull/701 #[test] fn flatten_doesnt_poll_completed_inner_stream() { task::block_on(async { From 9fa3ce3fd6125404f17901cda878897016c33584 Mon Sep 17 00:00:00 2001 From: Afirez <707627402@qq.com> Date: Sun, 14 Jun 2020 18:45:27 +0800 Subject: [PATCH 109/272] Add UdpSocket::PeerAddr #307 --- src/net/udp/mod.rs | 26 ++++++++++++++++++++++++++ tests/udp.rs | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 30cceb74c..18f6fc700 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -88,6 +88,32 @@ impl UdpSocket { })) } + /// Returns the peer address that this listener is connected to. + /// + /// This can be useful, for example, when connect to port 0 to figure out which port was + /// actually connected. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket1 = UdpSocket::bind("127.0.0.1:0").await?; + /// let socket2 = UdpSocket::bind("127.0.0.1:0").await?; + /// socket1.connect(socket2.local_addr()?).await?; + /// let addr = socket1.peer_addr()?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.watcher + .get_ref() + .peer_addr() + .context(|| String::from("could not get peer address")) + } + /// Returns the local address that this listener is bound to. /// /// This can be useful, for example, when binding to port 0 to figure out which port was diff --git a/tests/udp.rs b/tests/udp.rs index 15404f87a..37024c478 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -19,7 +19,7 @@ fn send_recv() -> io::Result<()> { socket1.connect(socket2.local_addr()?).await?; socket2.connect(socket1.local_addr()?).await?; - + assert_eq!(socket1.peer_addr()?, socket2.local_addr()?); socket1.send(THE_MERCHANT_OF_VENICE).await?; let mut buf = [0u8; 1024]; From 42425f6c1a8a944a4af6e3b1ec8d50c2251c0f46 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 14 Jun 2020 18:42:18 +0300 Subject: [PATCH 110/272] Another hotfix --- src/stream/stream/flatten.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 352360380..e7a498dc2 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -60,7 +60,7 @@ where match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)), - None => Poll::Ready(None), + None => return Poll::Ready(None), } } } From 093d640ad79d3eae43a57a0439174f0a470f7670 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 18 Jun 2020 12:11:37 +0200 Subject: [PATCH 111/272] fix(net): ensure the reactor and runtime are running If this is not done, then reactor is not running, resulting in the sockets not actually connecting. Closes #818 --- src/net/tcp/listener.rs | 4 ++++ src/net/tcp/stream.rs | 4 ++++ src/net/udp/mod.rs | 4 ++++ src/os/unix/net/datagram.rs | 8 ++++++++ src/os/unix/net/listener.rs | 11 ++++++++++- src/os/unix/net/stream.rs | 10 +++++++++- 6 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 72c5d3a80..09f5812fb 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -75,6 +75,8 @@ impl TcpListener { /// /// [`local_addr`]: #method.local_addr pub async fn bind(addrs: A) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -200,6 +202,8 @@ impl<'a> Stream for Incoming<'a> { impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. fn from(listener: std::net::TcpListener) -> TcpListener { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + TcpListener { watcher: Async::new(listener).expect("TcpListener is known to be good"), } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index b854143ff..63232fa35 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -71,6 +71,8 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn connect(addrs: A) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -356,6 +358,8 @@ impl Write for &TcpStream { impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. fn from(stream: std::net::TcpStream) -> TcpStream { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + TcpStream { watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")), } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 18f6fc700..d361a6fce 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -68,6 +68,8 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn bind(addrs: A) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -479,6 +481,8 @@ impl UdpSocket { impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. fn from(socket: std::net::UdpSocket) -> UdpSocket { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + UdpSocket { watcher: Async::new(socket).expect("UdpSocket is known to be good"), } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 6a30b0279..52c6b07f1 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -45,6 +45,8 @@ pub struct UnixDatagram { impl UnixDatagram { fn new(socket: StdUnixDatagram) -> UnixDatagram { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + UnixDatagram { watcher: Async::new(socket).expect("UnixDatagram is known to be good"), } @@ -64,6 +66,8 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn bind>(path: P) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let path = path.as_ref().to_owned(); let socket = Async::::bind(path)?; Ok(UnixDatagram { watcher: socket }) @@ -305,6 +309,8 @@ impl fmt::Debug for UnixDatagram { impl From for UnixDatagram { /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. fn from(datagram: StdUnixDatagram) -> UnixDatagram { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + UnixDatagram { watcher: Async::new(datagram).expect("UnixDatagram is known to be good"), } @@ -319,6 +325,8 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let raw = StdUnixDatagram::from_raw_fd(fd); let datagram = Async::::new(raw).expect("invalid file descriptor"); UnixDatagram { watcher: datagram } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index ac033075d..a63bd4b65 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -68,6 +68,8 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub async fn bind>(path: P) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let path = path.as_ref().to_owned(); let listener = Async::::bind(path)?; @@ -93,7 +95,12 @@ impl UnixListener { pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let (stream, addr) = self.watcher.accept().await?; - Ok((UnixStream { watcher: Arc::new(stream) }, addr)) + Ok(( + UnixStream { + watcher: Arc::new(stream), + }, + addr, + )) } /// Returns a stream of incoming connections. @@ -187,6 +194,8 @@ impl Stream for Incoming<'_> { impl From for UnixListener { /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. fn from(listener: StdUnixListener) -> UnixListener { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + UnixListener { watcher: Async::new(listener).expect("UnixListener is known to be good"), } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index b1ba5bca0..74bd6aef9 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -57,6 +57,8 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub async fn connect>(path: P) -> io::Result { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let path = path.as_ref().to_owned(); let stream = Arc::new(Async::::connect(path).await?); @@ -79,6 +81,8 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixStream, UnixStream)> { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let (a, b) = Async::::pair()?; let a = UnixStream { watcher: Arc::new(a), @@ -224,8 +228,12 @@ impl fmt::Debug for UnixStream { impl From for UnixStream { /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. fn from(stream: StdUnixStream) -> UnixStream { + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + let stream = Async::new(stream).expect("UnixStream is known to be good"); - UnixStream { watcher: Arc::new(stream) } + UnixStream { + watcher: Arc::new(stream), + } } } From 1c1c168e1b55e01f932a38bf0629ec7467bf6162 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 18 Jun 2020 12:37:14 +0200 Subject: [PATCH 112/272] fix(timer): ensure the runtime is working for timers --- src/future/future/delay.rs | 4 ++-- src/future/timeout.rs | 4 ++-- src/io/timeout.rs | 4 ++-- src/stream/interval.rs | 6 +++--- src/stream/stream/delay.rs | 4 ++-- src/stream/stream/throttle.rs | 6 +++--- src/stream/stream/timeout.rs | 4 ++-- src/utils.rs | 7 +++++++ 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index b6c30bcc3..092639d91 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -5,7 +5,7 @@ use std::time::Duration; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; pin_project! { #[doc(hidden)] @@ -20,7 +20,7 @@ pin_project! { impl DelayFuture { pub fn new(future: F, dur: Duration) -> DelayFuture { - let delay = Timer::after(dur); + let delay = timer_after(dur); DelayFuture { future, delay } } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index 4a9d93c7f..384662149 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -7,7 +7,7 @@ use std::time::Duration; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; /// Awaits a future or times out after a duration of time. /// @@ -51,7 +51,7 @@ impl TimeoutFuture { pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture { TimeoutFuture { future, - delay: Timer::after(dur), + delay: timer_after(dur), } } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index ce33fea1d..073c2f6e9 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -6,7 +6,7 @@ use std::time::Duration; use pin_project_lite::pin_project; use crate::io; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; /// Awaits an I/O future or times out after a duration of time. /// @@ -37,7 +37,7 @@ where F: Future>, { Timeout { - timeout: Timer::after(dur), + timeout: timer_after(dur), future: f, } .await diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 4e5c92b02..0a7eb4807 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use crate::stream::Stream; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; /// Creates a new stream that yields at a set interval. /// @@ -45,7 +45,7 @@ use crate::utils::Timer; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn interval(dur: Duration) -> Interval { Interval { - delay: Timer::after(dur), + delay: timer_after(dur), interval: dur, } } @@ -72,7 +72,7 @@ impl Stream for Interval { return Poll::Pending; } let interval = self.interval; - let _ = std::mem::replace(&mut self.delay, Timer::after(interval)); + let _ = std::mem::replace(&mut self.delay, timer_after(interval)); Poll::Ready(Some(())) } } diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index 0ba42b052..9a7f947c6 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -6,7 +6,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; pin_project! { #[doc(hidden)] @@ -24,7 +24,7 @@ impl Delay { pub(super) fn new(stream: S, dur: Duration) -> Self { Delay { stream, - delay: Timer::after(dur), + delay: timer_after(dur), delay_done: false, } } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 2f9333a7a..d0e2cdd14 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -6,7 +6,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; pin_project! { /// A stream that only yields one element once every `duration`. @@ -35,7 +35,7 @@ impl Throttle { stream, duration, blocked: false, - delay: Timer::after(Duration::default()), + delay: timer_after(Duration::default()), } } } @@ -59,7 +59,7 @@ impl Stream for Throttle { Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { *this.blocked = true; - let _ = std::mem::replace(&mut *this.delay, Timer::after(*this.duration)); + let _ = std::mem::replace(&mut *this.delay, timer_after(*this.duration)); Poll::Ready(Some(v)) } } diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 28e52aebd..0e0ee912c 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -8,7 +8,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Timer; +use crate::utils::{timer_after, Timer}; pin_project! { /// A stream with timeout time set @@ -23,7 +23,7 @@ pin_project! { impl Timeout { pub(crate) fn new(stream: S, dur: Duration) -> Self { - let delay = Timer::after(dur); + let delay = timer_after(dur); Self { stream, delay } } diff --git a/src/utils.rs b/src/utils.rs index e064570ec..3ca9d15b6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -64,6 +64,13 @@ mod timer { pub type Timer = smol::Timer; } +pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { + #[cfg(not(target_os = "unknown"))] + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + + Timer::after(dur) +} + #[cfg(any( all(target_arch = "wasm32", feature = "default"), all(feature = "unstable", not(feature = "default")) From 06a2fb8c4ff38ebcc31a1de9ab9c4b8b148b43a3 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 18 Jun 2020 13:10:37 +0200 Subject: [PATCH 113/272] fix export --- src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.rs b/src/utils.rs index 3ca9d15b6..31290e333 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -64,6 +64,7 @@ mod timer { pub type Timer = smol::Timer; } +#[cfg(any(feature = "unstable", feature = "default"))] pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { #[cfg(not(target_os = "unknown"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); From 0c2ce52ac41f926b39306088a514ad3fb34d4f6f Mon Sep 17 00:00:00 2001 From: Afirez <707627402@qq.com> Date: Thu, 18 Jun 2020 20:29:32 +0800 Subject: [PATCH 114/272] fix doc missing in #815 --- src/task/spawn_local.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/task/spawn_local.rs b/src/task/spawn_local.rs index 04da02012..7e0ce6c8f 100644 --- a/src/task/spawn_local.rs +++ b/src/task/spawn_local.rs @@ -7,6 +7,7 @@ use crate::task::{Builder, JoinHandle}; /// # Examples /// /// ``` +/// # #[cfg(feature = "unstable")] /// # async_std::task::block_on(async { /// # /// use async_std::task; @@ -19,6 +20,8 @@ use crate::task::{Builder, JoinHandle}; /// # /// # }) /// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[inline] pub fn spawn_local(future: F) -> JoinHandle where F: Future + 'static, From e495ba46b32c9ad9df29a0ca97558d2779e3ab5c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 19 Jun 2020 12:15:42 +0200 Subject: [PATCH 115/272] chore: release v1.6.2 --- CHANGELOG.md | 17 ++++++++++++++++- Cargo.toml | 4 ++-- src/lib.rs | 10 +++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6bb2ccd4..57542e819 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.2] - 2020-06-19 + +## Added + +- Add `UdpSocket::peer_addr` ([#816](https://github.com/async-rs/async-std/pull/816)) + +## Changed + +## Fixed + +- Ensure the reactor is running for sockets and timers ([#819](https://github.com/async-rs/async-std/pull/819)). +- Avoid excessive polling in `flatten` and `flat_map` ([#701](https://github.com/async-rs/async-std/pull/701)) + + # [1.6.1] - 2020-06-11 ## Added @@ -732,7 +746,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.1...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.2...HEAD +[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 [1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2 diff --git a/Cargo.toml b/Cargo.toml index bf86da87c..933023341 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.1" +version = "1.6.2" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", @@ -75,7 +75,7 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1.11", optional = true } +smol = { version = "0.1.14", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/lib.rs b/src/lib.rs index 6f7626548..a8ba46b26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.1" +//! version = "1.6.2" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.1" +//! version = "1.6.2" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.1" +//! version = "1.6.2" //! features = ["tokio02"] //! ``` //! @@ -228,7 +228,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.1" +//! version = "1.6.2" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +238,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.1" +//! version = "1.6.2" //! default-features = false //! features = ["alloc"] //! ``` From 2e7e804736383c6db79df1f61eb99647f2dae26f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 25 Jun 2020 17:44:39 +0100 Subject: [PATCH 116/272] Fix unused_mut warning in nightly --- src/io/stderr.rs | 10 ++++++---- src/io/stdin.rs | 5 +++-- src/io/stdout.rs | 10 ++++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5067ed4be..22dadd1f6 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -89,11 +89,12 @@ enum Operation { impl Write for Stderr { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -137,8 +138,9 @@ impl Write for Stderr { } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { diff --git a/src/io/stdin.rs b/src/io/stdin.rs index fc280f8ca..bf92bb04c 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -149,11 +149,12 @@ impl Stdin { impl Read for Stdin { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { diff --git a/src/io/stdout.rs b/src/io/stdout.rs index b3dfe6444..45244b140 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -89,11 +89,12 @@ enum Operation { impl Write for Stdout { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -137,8 +138,9 @@ impl Write for Stdout { } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { From 18dffe8b438238bf8a9966d488e575bc2c15c700 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 21 Jun 2020 21:23:21 +0200 Subject: [PATCH 117/272] refactor: switch to async-mutex for Mutex implementation --- Cargo.toml | 4 +- src/sync/condvar.rs | 8 +- src/sync/mod.rs | 5 +- src/sync/mutex.rs | 294 -------------------------------------------- 4 files changed, 10 insertions(+), 301 deletions(-) delete mode 100644 src/sync/mutex.rs diff --git a/Cargo.toml b/Cargo.toml index 933023341..e98700539 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ std = [ "slab", "wasm-bindgen-futures", "futures-channel", + "async-mutex", ] alloc = [ "futures-core/alloc", @@ -58,6 +59,7 @@ tokio02 = ["smol/tokio02"] [dependencies] async-attributes = { version = "1.1.1", optional = true } async-task = { version = "3.0.0", optional = true } +async-mutex = { version = "1.1.3", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } @@ -75,7 +77,7 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -smol = { version = "0.1.14", optional = true } +smol = { version = "0.1.17", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs index 67507f384..09aea3a1a 100644 --- a/src/sync/condvar.rs +++ b/src/sync/condvar.rs @@ -2,7 +2,7 @@ use std::fmt; use std::pin::Pin; use std::time::Duration; -use super::mutex::{guard_lock, MutexGuard}; +use super::MutexGuard; use crate::future::{timeout, Future}; use crate::sync::WakerSet; use crate::task::{Context, Poll}; @@ -120,7 +120,7 @@ impl Condvar { /// ``` #[allow(clippy::needless_lifetimes)] pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { - let mutex = guard_lock(&guard); + let mutex = MutexGuard::source(&guard); self.await_notify(guard).await; @@ -228,7 +228,7 @@ impl Condvar { guard: MutexGuard<'a, T>, dur: Duration, ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { - let mutex = guard_lock(&guard); + let mutex = MutexGuard::source(&guard); match timeout(dur, self.wait(guard)).await { Ok(guard) => (guard, WaitTimeoutResult(false)), Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), @@ -281,7 +281,7 @@ impl Condvar { where F: FnMut(&mut T) -> bool, { - let mutex = guard_lock(&guard); + let mutex = MutexGuard::source(&guard); match timeout(dur, self.wait_until(guard, condition)).await { Ok(guard) => (guard, WaitTimeoutResult(false)), Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), diff --git a/src/sync/mod.rs b/src/sync/mod.rs index bccc6ec87..8b7fe3102 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -176,10 +176,11 @@ #[doc(inline)] pub use std::sync::{Arc, Weak}; -pub use mutex::{Mutex, MutexGuard}; +#[doc(inline)] +pub use async_mutex::{Mutex, MutexGuard}; + pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -mod mutex; mod rwlock; cfg_unstable! { diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs deleted file mode 100644 index ae953fd82..000000000 --- a/src/sync/mutex.rs +++ /dev/null @@ -1,294 +0,0 @@ -use std::cell::UnsafeCell; -use std::fmt; -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::future::Future; - -use crate::sync::WakerSet; -use crate::task::{Context, Poll}; - -/// A mutual exclusion primitive for protecting shared data. -/// -/// This type is an async version of [`std::sync::Mutex`]. -/// -/// [`std::sync::Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::{Arc, Mutex}; -/// use async_std::task; -/// -/// let m = Arc::new(Mutex::new(0)); -/// let mut tasks = vec![]; -/// -/// for _ in 0..10 { -/// let m = m.clone(); -/// tasks.push(task::spawn(async move { -/// *m.lock().await += 1; -/// })); -/// } -/// -/// for t in tasks { -/// t.await; -/// } -/// assert_eq!(*m.lock().await, 10); -/// # -/// # }) -/// ``` -pub struct Mutex { - locked: AtomicBool, - wakers: WakerSet, - value: UnsafeCell, -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Mutex { - /// Creates a new mutex. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// ``` - pub fn new(t: T) -> Mutex { - Mutex { - locked: AtomicBool::new(false), - wakers: WakerSet::new(), - value: UnsafeCell::new(t), - } - } -} - -impl Mutex { - /// Acquires the lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::{Arc, Mutex}; - /// use async_std::task; - /// - /// let m1 = Arc::new(Mutex::new(10)); - /// let m2 = m1.clone(); - /// - /// task::spawn(async move { - /// *m1.lock().await = 20; - /// }) - /// .await; - /// - /// assert_eq!(*m2.lock().await, 20); - /// # - /// # }) - /// ``` - pub async fn lock(&self) -> MutexGuard<'_, T> { - pub struct LockFuture<'a, T: ?Sized> { - mutex: &'a Mutex, - opt_key: Option, - } - - impl<'a, T: ?Sized> Future for LockFuture<'a, T> { - type Output = MutexGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // If the current task is in the set, remove it. - if let Some(key) = self.opt_key.take() { - self.mutex.wakers.remove(key); - } - - // Try acquiring the lock. - match self.mutex.try_lock() { - Some(guard) => return Poll::Ready(guard), - None => { - // Insert this lock operation. - self.opt_key = Some(self.mutex.wakers.insert(cx)); - - // If the mutex is still locked, return. - if self.mutex.locked.load(Ordering::SeqCst) { - return Poll::Pending; - } - } - } - } - } - } - - impl Drop for LockFuture<'_, T> { - fn drop(&mut self) { - // If the current task is still in the set, that means it is being cancelled now. - if let Some(key) = self.opt_key { - self.mutex.wakers.cancel(key); - } - } - } - - LockFuture { - mutex: self, - opt_key: None, - } - .await - } - - /// Attempts to acquire the lock. - /// - /// If the lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::{Arc, Mutex}; - /// use async_std::task; - /// - /// let m1 = Arc::new(Mutex::new(10)); - /// let m2 = m1.clone(); - /// - /// task::spawn(async move { - /// if let Some(mut guard) = m1.try_lock() { - /// *guard = 20; - /// } else { - /// println!("try_lock failed"); - /// } - /// }) - /// .await; - /// - /// assert_eq!(*m2.lock().await, 20); - /// # - /// # }) - /// ``` - #[inline] - pub fn try_lock(&self) -> Option> { - if !self.locked.swap(true, Ordering::SeqCst) { - Some(MutexGuard(self)) - } else { - None - } - } - - /// Consumes the mutex, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::Mutex; - /// - /// let mutex = Mutex::new(10); - /// assert_eq!(mutex.into_inner(), 10); - /// ``` - pub fn into_inner(self) -> T where T: Sized { - self.value.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the mutex mutably, no actual locking takes place -- the mutable - /// borrow statically guarantees no locks exist. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(0); - /// *mutex.get_mut() = 10; - /// assert_eq!(*mutex.lock().await, 10); - /// # - /// # }) - /// ``` - pub fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } - } -} - -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - struct Locked; - impl fmt::Debug for Locked { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - match self.try_lock() { - None => f.debug_struct("Mutex").field("data", &Locked).finish(), - Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), - } - } -} - -impl From for Mutex { - fn from(val: T) -> Mutex { - Mutex::new(val) - } -} - -impl Default for Mutex { - fn default() -> Mutex { - Mutex::new(Default::default()) - } -} - -/// A guard that releases the lock when dropped. -pub struct MutexGuard<'a, T: ?Sized>(&'a Mutex); - -unsafe impl Send for MutexGuard<'_, T> {} -unsafe impl Sync for MutexGuard<'_, T> {} - -impl Drop for MutexGuard<'_, T> { - fn drop(&mut self) { - // Use `SeqCst` ordering to synchronize with `WakerSet::insert()` and `WakerSet::update()`. - self.0.locked.store(false, Ordering::SeqCst); - - // Notify a blocked `lock()` operation if none were notified already. - self.0.wakers.notify_any(); - } -} - -impl fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for MutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -impl DerefMut for MutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } - } -} - -#[cfg(feature = "unstable")] -pub fn guard_lock<'a, T>(guard: &MutexGuard<'a, T>) -> &'a Mutex { - guard.0 -} From 8f17e9275ba3f6470f5dad8945d97870602a79f1 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 26 Jun 2020 12:48:23 +0200 Subject: [PATCH 118/272] test: try to stabilize CI --- tests/timeout.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/timeout.rs b/tests/timeout.rs index e09acdfe4..60594d06b 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -10,9 +10,9 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn timeout_future_many() { task::block_on(async { - let futures = (0..100) + let futures = (0..10) .map(|i| { - timeout(Duration::from_millis(i * 20), async move { + timeout(Duration::from_millis(i * 50), async move { task::sleep(Duration::from_millis(i)).await; Ok::<(), async_std::future::TimeoutError>(()) }) From 2ab08ebbbcbd0df36922dfac91de6cec1f8fb381 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Sat, 27 Jun 2020 14:23:54 +0200 Subject: [PATCH 119/272] Update kv-log-macro to 1.0.6 --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e98700539..a5b5c4f06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ default = [ docs = ["attributes", "unstable", "default"] unstable = [ "std", - "futures-timer", + "futures-timer", ] attributes = ["async-attributes"] std = [ @@ -63,7 +63,7 @@ async-mutex = { version = "1.1.3", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } -kv-log-macro = { version = "1.0.4", optional = true } +kv-log-macro = { version = "1.0.6", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } num_cpus = { version = "1.12.0", optional = true } @@ -105,4 +105,3 @@ required-features = ["unstable"] [[example]] name = "surf-web" required-features = ["surf"] - From c82b1efb692751588bc3600d3def78166d707fb5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 27 Jun 2020 16:46:14 +0200 Subject: [PATCH 120/272] fix(stream): add send guards on collect Closes #639 Co-authored-by: dignifiedquire --- src/collections/binary_heap/extend.rs | 7 +++-- src/collections/binary_heap/from_stream.rs | 7 +++-- src/collections/btree_map/extend.rs | 7 +++-- src/collections/btree_map/from_stream.rs | 7 +++-- src/collections/btree_set/extend.rs | 7 +++-- src/collections/btree_set/from_stream.rs | 7 +++-- src/collections/hash_map/extend.rs | 10 +++++-- src/collections/hash_map/from_stream.rs | 10 +++++-- src/collections/hash_set/extend.rs | 9 ++++-- src/collections/hash_set/from_stream.rs | 9 ++++-- src/collections/linked_list/extend.rs | 7 +++-- src/collections/linked_list/from_stream.rs | 7 +++-- src/collections/vec_deque/extend.rs | 7 +++-- src/collections/vec_deque/from_stream.rs | 7 +++-- src/option/from_stream.rs | 7 +++-- src/path/pathbuf.rs | 12 ++++++-- src/result/from_stream.rs | 7 ++++- src/stream/extend.rs | 5 +++- src/stream/from_stream.rs | 13 ++++++--- src/stream/stream/mod.rs | 5 ++-- src/string/extend.rs | 25 ++++++++++++---- src/string/from_stream.rs | 25 ++++++++++++---- src/unit/extend.rs | 9 ++++-- src/unit/from_stream.rs | 5 +++- src/vec/extend.rs | 7 +++-- src/vec/from_stream.rs | 34 +++++++++++++++------- tests/collect.rs | 20 +++++++++++++ 27 files changed, 210 insertions(+), 72 deletions(-) create mode 100644 tests/collect.rs diff --git a/src/collections/binary_heap/extend.rs b/src/collections/binary_heap/extend.rs index 439bf139e..1a9479e14 100644 --- a/src/collections/binary_heap/extend.rs +++ b/src/collections/binary_heap/extend.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend for BinaryHeap { +impl stream::Extend for BinaryHeap { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index 6851948e6..614f5f5de 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for BinaryHeap { +impl FromStream for BinaryHeap { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/btree_map/extend.rs b/src/collections/btree_map/extend.rs index 19d306ffe..4a1ad0618 100644 --- a/src/collections/btree_map/extend.rs +++ b/src/collections/btree_map/extend.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend<(K, V)> for BTreeMap { +impl stream::Extend<(K, V)> for BTreeMap { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { Box::pin(stream.into_stream().for_each(move |(k, v)| { self.insert(k, v); })) diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index 853122361..ef01b6e0d 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream<(K, V)> for BTreeMap { +impl FromStream<(K, V)> for BTreeMap { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/btree_set/extend.rs b/src/collections/btree_set/extend.rs index 422640b15..0f7421023 100644 --- a/src/collections/btree_set/extend.rs +++ b/src/collections/btree_set/extend.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend for BTreeSet { +impl stream::Extend for BTreeSet { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { Box::pin(stream.into_stream().for_each(move |item| { self.insert(item); })) diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index 318af9e65..5fdb33e6c 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for BTreeSet { +impl FromStream for BTreeSet { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/hash_map/extend.rs b/src/collections/hash_map/extend.rs index 0f4ce0c6e..15b7e7d46 100644 --- a/src/collections/hash_map/extend.rs +++ b/src/collections/hash_map/extend.rs @@ -7,13 +7,17 @@ use crate::stream::{self, IntoStream}; impl stream::Extend<(K, V)> for HashMap where - K: Eq + Hash, - H: BuildHasher + Default, + K: Eq + Hash + Send, + V: Send, + H: BuildHasher + Default + Send, { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); // The following is adapted from the hashbrown source code: diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index d74a7ccfa..ae3f11a56 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -7,13 +7,17 @@ use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for HashMap where - K: Eq + Hash, - H: BuildHasher + Default, + K: Eq + Hash + Send, + H: BuildHasher + Default + Send, + V: Send, { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/hash_set/extend.rs b/src/collections/hash_set/extend.rs index ba872b438..0246a1e88 100644 --- a/src/collections/hash_set/extend.rs +++ b/src/collections/hash_set/extend.rs @@ -7,13 +7,16 @@ use crate::stream::{self, IntoStream}; impl stream::Extend for HashSet where - T: Eq + Hash, - H: BuildHasher + Default, + T: Eq + Hash + Send, + H: BuildHasher + Default + Send, { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { // The Extend impl for HashSet in the standard library delegates to the internal HashMap. // Thus, this impl is just a copy of the async Extend impl for HashMap in this crate. diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index dc5e61e39..6f640695a 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -7,13 +7,16 @@ use crate::stream::{self, FromStream, IntoStream}; impl FromStream for HashSet where - T: Eq + Hash, - H: BuildHasher + Default, + T: Eq + Hash + Send, + H: BuildHasher + Default + Send, { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/linked_list/extend.rs b/src/collections/linked_list/extend.rs index b0dff009d..8adc41d73 100644 --- a/src/collections/linked_list/extend.rs +++ b/src/collections/linked_list/extend.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend for LinkedList { +impl stream::Extend for LinkedList { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(stream.for_each(move |item| self.push_back(item))) } diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index d93bbb7be..7c3eb738c 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for LinkedList { +impl FromStream for LinkedList { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/vec_deque/extend.rs b/src/collections/vec_deque/extend.rs index dd2ddce3c..9dea92231 100644 --- a/src/collections/vec_deque/extend.rs +++ b/src/collections/vec_deque/extend.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend for VecDeque { +impl stream::Extend for VecDeque { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 241bd74e9..3e0719ab2 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -4,11 +4,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for VecDeque { +impl FromStream for VecDeque { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index de929ca94..7931d97a6 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use crate::stream::{FromStream, IntoStream}; use std::convert::identity; -impl FromStream> for Option +impl FromStream> for Option where V: FromStream, { @@ -14,7 +14,10 @@ where #[inline] fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index e684df89f..f9370cbab 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -323,7 +323,10 @@ impl> stream::Extend

for PathBuf { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -337,11 +340,14 @@ impl> stream::Extend

for PathBuf { } #[cfg(feature = "unstable")] -impl<'b, P: AsRef + 'b> FromStream

for PathBuf { +impl<'b, P: AsRef + 'b + Send> FromStream

( &mut self, p: P, - ) -> impl Future> + '_ [FindFuture<'_, Self, P>] + ) -> impl Future> [FindFuture<'_, Self, P>] where Self: Unpin + Sized, P: FnMut(&Self::Item) -> bool, @@ -1298,7 +1298,7 @@ extension_trait! { fn find_map( &mut self, f: F, - ) -> impl Future> + '_ [FindMapFuture<'_, Self, F>] + ) -> impl Future> [FindMapFuture<'_, Self, F>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> Option, @@ -1461,7 +1461,7 @@ extension_trait! { fn any( &mut self, f: F, - ) -> impl Future + '_ [AnyFuture<'_, Self, F, Self::Item>] + ) -> impl Future [AnyFuture<'_, Self, F, Self::Item>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1697,7 +1697,7 @@ extension_trait! { &mut self, init: T, f: F, - ) -> impl Future> + '_ [TryFoldFuture<'_, Self, F, T>] + ) -> impl Future> [TryFoldFuture<'_, Self, F, T>] where Self: Unpin + Sized, F: FnMut(B, Self::Item) -> Result, @@ -1742,7 +1742,7 @@ extension_trait! { fn try_for_each( &mut self, f: F, - ) -> impl Future + 'a [TryForEachFuture<'_, Self, F>] + ) -> impl Future [TryForEachFuture<'_, Self, F>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> Result<(), E>, @@ -1888,7 +1888,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn collect<'a, B>( self, - ) -> impl Future + 'a [Pin + 'a + Send>>] + ) -> impl Future [Pin + 'a + Send>>] where Self: Sized + 'a + Send, B: FromStream, @@ -2002,7 +2002,7 @@ extension_trait! { fn position

( &mut self, predicate: P, - ) -> impl Future> + '_ [PositionFuture<'_, Self, P>] + ) -> impl Future> [PositionFuture<'_, Self, P>] where Self: Unpin + Sized, P: FnMut(Self::Item) -> bool, @@ -2335,7 +2335,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn sum<'a, S>( self, - ) -> impl Future + 'a [Pin + 'a>>] + ) -> impl Future [Pin + 'a>>] where Self: Sized + Stream + 'a, S: Sum, @@ -2381,7 +2381,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn product<'a, P>( self, - ) -> impl Future + 'a [Pin + 'a>>] + ) -> impl Future [Pin + 'a>>] where Self: Sized + Stream + 'a, P: Product, diff --git a/src/utils.rs b/src/utils.rs index 6ae49115d..88731d286 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -302,10 +302,10 @@ macro_rules! extension_trait { }; // Parse the return type in an extension method. - (@doc [-> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { + (@doc [-> impl Future [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { extension_trait!(@doc [$($tail)*] -> [$($accum)* -> owned::ImplFuture<$out>]); }; - (@ext [-> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { + (@ext [-> impl Future [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { extension_trait!(@ext [$($tail)*] -> [$($accum)* -> $f]); }; From f56a8d6935112c70a98d023630d54d1f120d6c9c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 10:09:01 +1100 Subject: [PATCH 209/272] Remove unused `borrowed` module. --- src/utils.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 88731d286..771111436 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -270,13 +270,6 @@ macro_rules! extension_trait { pub struct ImplFuture(core::marker::PhantomData); } - // A fake `impl Future` type that borrows its environment. - #[allow(dead_code)] - mod borrowed { - #[doc(hidden)] - pub struct ImplFuture<'a, T>(core::marker::PhantomData<&'a T>); - } - // Render a fake trait combining the bodies of the base trait and the extension trait. #[cfg(feature = "docs")] #[doc = $doc] From ed2fcce5578b931a1db0df331661dae86c6c4c6d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 11:33:18 +1100 Subject: [PATCH 210/272] Remove `docs`-only features from `extension_trait`. This is the `@doc` rules, the shim trait impls, and the imports. --- src/future/future/mod.rs | 41 -------------------- src/io/buf_read/mod.rs | 58 ---------------------------- src/io/read/mod.rs | 50 ------------------------- src/io/seek/mod.rs | 40 -------------------- src/io/write/mod.rs | 81 ---------------------------------------- src/stream/stream/mod.rs | 40 -------------------- src/utils.rs | 38 +------------------ 7 files changed, 1 insertion(+), 347 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 356514509..194325551 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -21,11 +21,6 @@ cfg_unstable_default! { } extension_trait! { - use core::pin::Pin; - use core::ops::{Deref, DerefMut}; - - use crate::task::{Context, Poll}; - #[doc = r#" A future represents an asynchronous computation. @@ -393,40 +388,4 @@ extension_trait! { TimeoutFuture::new(self, dur) } } - - impl Future for Box { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Future for &mut F { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Future for Pin

- where - P: DerefMut + Unpin, -

::Target: Future, - { - type Output = <

::Target as Future>::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Future for std::panic::AssertUnwindSafe { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } } diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 3ee6e30ba..90bf339d0 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -16,8 +16,6 @@ use crate::io; use crate::task::{Context, Poll}; extension_trait! { - use std::ops::{Deref, DerefMut}; - #[doc = r#" Allows reading from a buffered byte stream. @@ -283,62 +281,6 @@ extension_trait! { } } } - - impl BufRead for Box { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl BufRead for &mut T { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

BufRead for Pin

- where - P: DerefMut + Unpin, -

::Target: BufRead, - { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl BufRead for &[u8] { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!() - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } } pub fn read_until_internal( diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index c8f4e28b9..8c6d3a3ea 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -22,12 +22,6 @@ pub use chain::Chain; pub use take::Take; extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; - - use crate::io; - use crate::task::{Context, Poll}; - #[doc = r#" Allows reading from a byte stream. @@ -422,50 +416,6 @@ extension_trait! { } } - - impl Read for Box { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Read for &mut T { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Read for Pin

- where - P: DerefMut + Unpin, -

::Target: Read, - { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Read for &[u8] { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } } /// Initializes a buffer if necessary. diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 3b5036c80..748a1ed9d 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -5,12 +5,6 @@ use seek::SeekFuture; use crate::io::SeekFrom; extension_trait! { - use std::ops::{Deref, DerefMut}; - use std::pin::Pin; - - use crate::io; - use crate::task::{Context, Poll}; - #[doc = r#" Allows seeking through a byte stream. @@ -83,38 +77,4 @@ extension_trait! { SeekFuture { seeker: self, pos } } } - - impl Seek for Box { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Seek for &mut T { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Seek for Pin

- where - P: DerefMut + Unpin, -

::Target: Seek, - { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } } diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 3dee9feb6..b0ed8eec9 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -13,11 +13,6 @@ use write_vectored::WriteVectoredFuture; use crate::io::{self, IoSlice}; extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; - - use crate::task::{Context, Poll}; - #[doc = r#" Allows writing to a byte stream. @@ -245,80 +240,4 @@ extension_trait! { WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } } } - - impl Write for Box { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Write for &mut T { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Write for Pin

- where - P: DerefMut + Unpin, -

::Target: Write, - { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Write for Vec { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index af7407a2d..90c5cb7a9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -144,10 +144,6 @@ cfg_unstable! { } extension_trait! { - use std::ops::{Deref, DerefMut}; - - use crate::task::{Context, Poll}; - #[doc = r#" An asynchronous stream of values. @@ -2389,40 +2385,4 @@ extension_trait! { Product::product(self) } } - - impl Stream for Box { - type Item = S::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Stream for &mut S { - type Item = S::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Stream for Pin

- where - P: DerefMut + Unpin, -

::Target: Stream, - { - type Item = <

::Target as Stream>::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Stream for std::panic::AssertUnwindSafe { - type Item = S::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } } diff --git a/src/utils.rs b/src/utils.rs index 771111436..8b8ee23f6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -259,26 +259,8 @@ macro_rules! extension_trait { pub trait $ext:ident: $base:path { $($body_ext:tt)* } - - // Shim trait impls that only appear in docs. - $($imp:item)* ) => { - // A fake `impl Future` type that doesn't borrow. - #[allow(dead_code)] - mod owned { - #[doc(hidden)] - pub struct ImplFuture(core::marker::PhantomData); - } - - // Render a fake trait combining the bodies of the base trait and the extension trait. - #[cfg(feature = "docs")] - #[doc = $doc] - pub trait $name { - extension_trait!(@doc [$($body_base)* $($body_ext)*] -> []); - } - - // When not rendering docs, re-export the base trait from the futures crate. - #[cfg(not(feature = "docs"))] + // Re-export the base trait from the futures crate. pub use $base as $name; // The extension trait that adds methods to any type implementing the base trait. @@ -289,36 +271,18 @@ macro_rules! extension_trait { // Blanket implementation of the extension trait for any type implementing the base trait. impl $ext for T {} - - // Shim trait impls that only appear in docs. - $(#[cfg(feature = "docs")] $imp)* }; // Parse the return type in an extension method. - (@doc [-> impl Future [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { - extension_trait!(@doc [$($tail)*] -> [$($accum)* -> owned::ImplFuture<$out>]); - }; (@ext [-> impl Future [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { extension_trait!(@ext [$($tail)*] -> [$($accum)* -> $f]); }; // Parse a token. - (@doc [$token:tt $($tail:tt)*] -> [$($accum:tt)*]) => { - extension_trait!(@doc [$($tail)*] -> [$($accum)* $token]); - }; (@ext [$token:tt $($tail:tt)*] -> [$($accum:tt)*]) => { extension_trait!(@ext [$($tail)*] -> [$($accum)* $token]); }; // Handle the end of the token list. - (@doc [] -> [$($accum:tt)*]) => { $($accum)* }; (@ext [] -> [$($accum:tt)*]) => { $($accum)* }; - - // Parse imports at the beginning of the macro. - ($import:item $($tail:tt)*) => { - #[cfg(feature = "docs")] - $import - - extension_trait!($($tail)*); - }; } From c10d2d3a6f2e87e4a82bdcd73cda8cd6ca173945 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 11:52:44 +1100 Subject: [PATCH 211/272] Simplify the first trait in `extension_trait`. The body and doc comment are no longer used. --- src/future/future/mod.rs | 105 +-------------------------------------- src/io/buf_read/mod.rs | 46 +---------------- src/io/read/mod.rs | 44 +--------------- src/io/seek/mod.rs | 32 +----------- src/io/write/mod.rs | 58 +-------------------- src/stream/stream/mod.rs | 81 +----------------------------- src/utils.rs | 5 +- 7 files changed, 7 insertions(+), 364 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 194325551..a913f4fc1 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -21,110 +21,7 @@ cfg_unstable_default! { } extension_trait! { - #[doc = r#" - A future represents an asynchronous computation. - - A future is a value that may not have finished computing yet. This kind of - "asynchronous value" makes it possible for a thread to continue doing useful - work while it waits for the value to become available. - - The [provided methods] do not really exist in the trait itself, but they become - available when [`FutureExt`] from the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - # The `poll` method - - The core method of future, `poll`, *attempts* to resolve the future into a - final value. This method does not block if the value is not ready. Instead, - the current task is scheduled to be woken up when it's possible to make - further progress by `poll`ing again. The `context` passed to the `poll` - method can provide a [`Waker`], which is a handle for waking up the current - task. - - When using a future, you generally won't call `poll` directly, but instead - `.await` the value. - - [`Waker`]: ../task/struct.Waker.html - [provided methods]: #provided-methods - [`FutureExt`]: ../prelude/trait.FutureExt.html - [prelude]: ../prelude/index.html - "#] - pub trait Future { - #[doc = r#" - The type of value produced on completion. - "#] - type Output; - - #[doc = r#" - Attempt to resolve the future to a final value, registering - the current task for wakeup if the value is not yet available. - - # Return value - - This function returns: - - - [`Poll::Pending`] if the future is not ready yet - - [`Poll::Ready(val)`] with the result `val` of this future if it - finished successfully. - - Once a future has finished, clients should not `poll` it again. - - When a future is not ready yet, `poll` returns `Poll::Pending` and - stores a clone of the [`Waker`] copied from the current [`Context`]. - This [`Waker`] is then woken once the future can make progress. - For example, a future waiting for a socket to become - readable would call `.clone()` on the [`Waker`] and store it. - When a signal arrives elsewhere indicating that the socket is readable, - [`Waker::wake`] is called and the socket future's task is awoken. - Once a task has been woken up, it should attempt to `poll` the future - again, which may or may not produce a final value. - - Note that on multiple calls to `poll`, only the [`Waker`] from the - [`Context`] passed to the most recent call should be scheduled to - receive a wakeup. - - # Runtime characteristics - - Futures alone are *inert*; they must be *actively* `poll`ed to make - progress, meaning that each time the current task is woken up, it should - actively re-`poll` pending futures that it still has an interest in. - - The `poll` function is not called repeatedly in a tight loop -- instead, - it should only be called when the future indicates that it is ready to - make progress (by calling `wake()`). If you're familiar with the - `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures - typically do *not* suffer the same problems of "all wakeups must poll - all events"; they are more like `epoll(4)`. - - An implementation of `poll` should strive to return quickly, and should - not block. Returning quickly prevents unnecessarily clogging up - threads or event loops. If it is known ahead of time that a call to - `poll` may end up taking awhile, the work should be offloaded to a - thread pool (or something similar) to ensure that `poll` can return - quickly. - - # Panics - - Once a future has completed (returned `Ready` from `poll`), calling its - `poll` method again may panic, block forever, or cause other kinds of - problems; the `Future` trait places no requirements on the effects of - such a call. However, as the `poll` method is not marked `unsafe`, - Rust's usual rules apply: calls must never cause undefined behavior - (memory corruption, incorrect use of `unsafe` functions, or the like), - regardless of the future's state. - - [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending - [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready - [`Context`]: ../task/struct.Context.html - [`Waker`]: ../task/struct.Waker.html - [`Waker::wake`]: ../task/struct.Waker.html#method.wake - "#] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll; - } + pub trait Future {} #[doc = r#" Extension methods for [`Future`]. diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 90bf339d0..d42cbd8a3 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -16,51 +16,7 @@ use crate::io; use crate::task::{Context, Poll}; extension_trait! { - #[doc = r#" - Allows reading from a buffered byte stream. - - This trait is a re-export of [`futures::io::AsyncBufRead`] and is an async version of - [`std::io::BufRead`]. - - The [provided methods] do not really exist in the trait itself, but they become - available when [`BufReadExt`] from the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::io::prelude::*; - ``` - - [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html - [`futures::io::AsyncBufRead`]: - https://docs.rs/futures/0.3/futures/io/trait.AsyncBufRead.html - [provided methods]: #provided-methods - [`BufReadExt`]: ../io/prelude/trait.BufReadExt.html - [prelude]: ../prelude/index.html - "#] - pub trait BufRead { - #[doc = r#" - Returns the contents of the internal buffer, filling it with more data from the - inner reader if it is empty. - - This function is a lower-level call. It needs to be paired with the [`consume`] - method to function properly. When calling this method, none of the contents will be - "read" in the sense that later calling `read` may return the same contents. As - such, [`consume`] must be called with the number of bytes that are consumed from - this buffer to ensure that the bytes are never returned twice. - - [`consume`]: #tymethod.consume - - An empty buffer returned indicates that the stream has reached EOF. - "#] - // TODO: write a proper doctest with `consume` - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - - #[doc = r#" - Tells this buffer that `amt` bytes have been consumed from the buffer, so they - should no longer be returned in calls to `read`. - "#] - fn consume(self: Pin<&mut Self>, amt: usize); - } + pub trait BufRead {} #[doc = r#" Extension methods for [`BufRead`]. diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 8c6d3a3ea..12c7960e7 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -22,49 +22,7 @@ pub use chain::Chain; pub use take::Take; extension_trait! { - #[doc = r#" - Allows reading from a byte stream. - - This trait is a re-export of [`futures::io::AsyncRead`] and is an async version of - [`std::io::Read`]. - - Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the - trait itself, but they become available when [`ReadExt`] from the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html - [`futures::io::AsyncRead`]: - https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html - [`poll_read`]: #tymethod.poll_read - [`poll_read_vectored`]: #method.poll_read_vectored - [`ReadExt`]: ../io/prelude/trait.ReadExt.html - [prelude]: ../prelude/index.html - "#] - pub trait Read { - #[doc = r#" - Attempt to read from the `AsyncRead` into `buf`. - "#] - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll>; - - #[doc = r#" - Attempt to read from the `AsyncRead` into `bufs` using vectored IO operations. - "#] - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + pub trait Read {} #[doc = r#" Extension methods for [`Read`]. diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 748a1ed9d..c5ca6c611 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -5,37 +5,7 @@ use seek::SeekFuture; use crate::io::SeekFrom; extension_trait! { - #[doc = r#" - Allows seeking through a byte stream. - - This trait is a re-export of [`futures::io::AsyncSeek`] and is an async version of - [`std::io::Seek`]. - - The [provided methods] do not really exist in the trait itself, but they become - available when [`SeekExt`] the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html - [`futures::io::AsyncSeek`]: - https://docs.rs/futures/0.3/futures/io/trait.AsyncSeek.html - [provided methods]: #provided-methods - [`SeekExt`]: ../io/prelude/trait.SeekExt.html - [prelude]: ../prelude/index.html - "#] - pub trait Seek { - #[doc = r#" - Attempt to seek to an offset, in bytes, in a stream. - "#] - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll>; - } + pub trait Seek {} #[doc = r#" Extension methods for [`Seek`]. diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index b0ed8eec9..d000ad7cd 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -13,63 +13,7 @@ use write_vectored::WriteVectoredFuture; use crate::io::{self, IoSlice}; extension_trait! { - #[doc = r#" - Allows writing to a byte stream. - - This trait is a re-export of [`futures::io::AsyncWrite`] and is an async version of - [`std::io::Write`]. - - Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and - [`poll_close`] do not really exist in the trait itself, but they become available when - [`WriteExt`] from the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html - [`futures::io::AsyncWrite`]: - https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html - [`poll_write`]: #tymethod.poll_write - [`poll_write_vectored`]: #method.poll_write_vectored - [`poll_flush`]: #tymethod.poll_flush - [`poll_close`]: #tymethod.poll_close - [`WriteExt`]: ../io/prelude/trait.WriteExt.html - [prelude]: ../prelude/index.html - "#] - pub trait Write { - #[doc = r#" - Attempt to write bytes from `buf` into the object. - "#] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll>; - - #[doc = r#" - Attempt to write bytes from `bufs` into the object using vectored IO operations. - "#] - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>] - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - #[doc = r#" - Attempt to flush the object, ensuring that any buffered data reach - their destination. - "#] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - - #[doc = r#" - Attempt to close the object. - "#] - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - } + pub trait Write {} #[doc = r#" Extension methods for [`Write`]. diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 90c5cb7a9..a316893d6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -144,86 +144,7 @@ cfg_unstable! { } extension_trait! { - #[doc = r#" - An asynchronous stream of values. - - This trait is a re-export of [`futures::stream::Stream`] and is an async version of - [`std::iter::Iterator`]. - - The [provided methods] do not really exist in the trait itself, but they become - available when [`StreamExt`] from the [prelude] is imported: - - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html - [`futures::stream::Stream`]: - https://docs.rs/futures/0.3/futures/stream/trait.Stream.html - [provided methods]: #provided-methods - [`StreamExt`]: ../prelude/trait.StreamExt.html - [prelude]: ../prelude/index.html - "#] - pub trait Stream { - #[doc = r#" - The type of items yielded by this stream. - "#] - type Item; - - #[doc = r#" - Attempts to receive the next item from the stream. - - There are several possible return values: - - * `Poll::Pending` means this stream's next value is not ready yet. - * `Poll::Ready(None)` means this stream has been exhausted. - * `Poll::Ready(Some(item))` means `item` was received out of the stream. - - # Examples - - ``` - # fn main() { async_std::task::block_on(async { - # - use std::pin::Pin; - - use async_std::prelude::*; - use async_std::stream; - use async_std::task::{Context, Poll}; - - fn increment( - s: impl Stream + Unpin, - ) -> impl Stream + Unpin { - struct Increment(S); - - impl + Unpin> Stream for Increment { - type Item = S::Item; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - match Pin::new(&mut self.0).poll_next(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Ready(None), - Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), - } - } - } - - Increment(s) - } - - let mut s = increment(stream::once(7)); - - assert_eq!(s.next().await, Some(8)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - } + pub trait Stream {} #[doc = r#" Extension methods for [`Stream`]. diff --git a/src/utils.rs b/src/utils.rs index 8b8ee23f6..42286ad11 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -250,10 +250,7 @@ macro_rules! extension_trait { // - `$name`: trait name that gets rendered in the docs // - `$ext`: name of the hidden extension trait // - `$base`: base trait - #[doc = $doc:tt] - pub trait $name:ident { - $($body_base:tt)* - } + pub trait $name:ident {} #[doc = $doc_ext:tt] pub trait $ext:ident: $base:path { From 6b3667d1a49adc8a666ed27d4331e085f0f766e3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:10:13 +1100 Subject: [PATCH 212/272] Remove unnecessary types in `extension_trait`. The remaining type requires the square brackets (for now) because a `ty` cannot immediately precede a `$(tt)*`. --- src/future/future/mod.rs | 15 +++++----- src/io/buf_read/mod.rs | 4 +-- src/io/read/mod.rs | 10 +++---- src/io/seek/mod.rs | 2 +- src/io/write/mod.rs | 10 +++---- src/stream/stream/mod.rs | 64 ++++++++++++++++++++-------------------- src/utils.rs | 2 +- 7 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index a913f4fc1..46780f8cf 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -45,7 +45,7 @@ extension_trait! { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn delay(self, dur: Duration) -> impl Future [DelayFuture] + fn delay(self, dur: Duration) -> [DelayFuture] where Self: Sized, { @@ -70,8 +70,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flatten( self, - ) -> impl Future::Output> - [FlattenFuture::Future>] + ) -> [FlattenFuture::Future>] where Self: Sized, ::Output: IntoFuture, @@ -113,7 +112,7 @@ extension_trait! { fn race( self, other: F, - ) -> impl Future::Output> [Race] + ) -> [Race] where Self: std::future::Future + Sized, F: std::future::Future::Output>, @@ -159,7 +158,7 @@ extension_trait! { fn try_race( self, other: F - ) -> impl Future::Output> [TryRace] + ) -> [TryRace] where Self: std::future::Future> + Sized, F: std::future::Future::Output>, @@ -196,7 +195,7 @@ extension_trait! { fn join( self, other: F - ) -> impl Future::Output, ::Output)> [Join] + ) -> [Join] where Self: std::future::Future + Sized, F: std::future::Future, @@ -243,7 +242,7 @@ extension_trait! { fn try_join( self, other: F - ) -> impl Future> [TryJoin] + ) -> [TryJoin] where Self: std::future::Future> + Sized, F: std::future::Future>, @@ -279,7 +278,7 @@ extension_trait! { "#] #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> impl Future [TimeoutFuture] + fn timeout(self, dur: Duration) -> [TimeoutFuture] where Self: Sized { TimeoutFuture::new(self, dur) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index d42cbd8a3..2b1ee86b5 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -78,7 +78,7 @@ extension_trait! { &'a mut self, byte: u8, buf: &'a mut Vec, - ) -> impl Future [ReadUntilFuture<'a, Self>] + ) -> [ReadUntilFuture<'a, Self>] where Self: Unpin, { @@ -131,7 +131,7 @@ extension_trait! { fn read_line<'a>( &'a mut self, buf: &'a mut String, - ) -> impl Future> [ReadLineFuture<'a, Self>] + ) -> [ReadLineFuture<'a, Self>] where Self: Unpin, { diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 12c7960e7..601bb7f1f 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -64,7 +64,7 @@ extension_trait! { fn read<'a>( &'a mut self, buf: &'a mut [u8], - ) -> impl Future> [ReadFuture<'a, Self>] + ) -> [ReadFuture<'a, Self>] where Self: Unpin { @@ -86,7 +86,7 @@ extension_trait! { fn read_vectored<'a>( &'a mut self, bufs: &'a mut [IoSliceMut<'a>], - ) -> impl Future> [ReadVectoredFuture<'a, Self>] + ) -> [ReadVectoredFuture<'a, Self>] where Self: Unpin, { @@ -123,7 +123,7 @@ extension_trait! { fn read_to_end<'a>( &'a mut self, buf: &'a mut Vec, - ) -> impl Future> [ReadToEndFuture<'a, Self>] + ) -> [ReadToEndFuture<'a, Self>] where Self: Unpin, { @@ -162,7 +162,7 @@ extension_trait! { fn read_to_string<'a>( &'a mut self, buf: &'a mut String, - ) -> impl Future> [ReadToStringFuture<'a, Self>] + ) -> [ReadToStringFuture<'a, Self>] where Self: Unpin, { @@ -217,7 +217,7 @@ extension_trait! { fn read_exact<'a>( &'a mut self, buf: &'a mut [u8], - ) -> impl Future> [ReadExactFuture<'a, Self>] + ) -> [ReadExactFuture<'a, Self>] where Self: Unpin, { diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index c5ca6c611..297232232 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -40,7 +40,7 @@ extension_trait! { fn seek( &mut self, pos: SeekFrom, - ) -> impl Future> [SeekFuture<'_, Self>] + ) -> [SeekFuture<'_, Self>] where Self: Unpin, { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index d000ad7cd..c8befae3d 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -49,7 +49,7 @@ extension_trait! { fn write<'a>( &'a mut self, buf: &'a [u8], - ) -> impl Future> [WriteFuture<'a, Self>] + ) -> [WriteFuture<'a, Self>] where Self: Unpin, { @@ -75,7 +75,7 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn flush(&mut self) -> impl Future> [FlushFuture<'_, Self>] + fn flush(&mut self) -> [FlushFuture<'_, Self>] where Self: Unpin, { @@ -97,7 +97,7 @@ extension_trait! { fn write_vectored<'a>( &'a mut self, bufs: &'a [IoSlice<'a>], - ) -> impl Future> [WriteVectoredFuture<'a, Self>] + ) -> [WriteVectoredFuture<'a, Self>] where Self: Unpin, { @@ -133,7 +133,7 @@ extension_trait! { fn write_all<'a>( &'a mut self, buf: &'a [u8], - ) -> impl Future> [WriteAllFuture<'a, Self>] + ) -> [WriteAllFuture<'a, Self>] where Self: Unpin, { @@ -170,7 +170,7 @@ extension_trait! { fn write_fmt<'a>( &'a mut self, fmt: std::fmt::Arguments<'_>, - ) -> impl Future> [WriteFmtFuture<'a, Self>] + ) -> [WriteFmtFuture<'a, Self>] where Self: Unpin, { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index a316893d6..d71dc6090 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -177,7 +177,7 @@ extension_trait! { # }) } ``` "#] - fn next(&mut self) -> impl Future> [NextFuture<'_, Self>] + fn next(&mut self) -> [NextFuture<'_, Self>] where Self: Unpin, { @@ -629,7 +629,7 @@ extension_trait! { "#] fn last( self, - ) -> impl Future> [LastFuture] + ) -> [LastFuture] where Self: Sized, { @@ -839,7 +839,7 @@ extension_trait! { fn min_by_key( self, key_by: F, - ) -> impl Future> [MinByKeyFuture] + ) -> [MinByKeyFuture] where Self: Sized, B: Ord, @@ -875,7 +875,7 @@ extension_trait! { fn max_by_key( self, key_by: F, - ) -> impl Future> [MaxByKeyFuture] + ) -> [MaxByKeyFuture] where Self: Sized, B: Ord, @@ -914,7 +914,7 @@ extension_trait! { fn min_by( self, compare: F, - ) -> impl Future> [MinByFuture] + ) -> [MinByFuture] where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -947,7 +947,7 @@ extension_trait! { "#] fn max( self, - ) -> impl Future> [MaxFuture] + ) -> [MaxFuture] where Self: Sized, Self::Item: Ord, @@ -980,7 +980,7 @@ extension_trait! { "#] fn min( self, - ) -> impl Future> [MinFuture] + ) -> [MinFuture] where Self: Sized, Self::Item: Ord, @@ -1018,7 +1018,7 @@ extension_trait! { fn max_by( self, compare: F, - ) -> impl Future> [MaxByFuture] + ) -> [MaxByFuture] where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -1082,7 +1082,7 @@ extension_trait! { fn nth( &mut self, n: usize, - ) -> impl Future> [NthFuture<'_, Self>] + ) -> [NthFuture<'_, Self>] where Self: Unpin + Sized, { @@ -1138,7 +1138,7 @@ extension_trait! { fn all( &mut self, f: F, - ) -> impl Future [AllFuture<'_, Self, F, Self::Item>] + ) -> [AllFuture<'_, Self, F, Self::Item>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1187,7 +1187,7 @@ extension_trait! { fn find

( &mut self, p: P, - ) -> impl Future> [FindFuture<'_, Self, P>] + ) -> [FindFuture<'_, Self, P>] where Self: Unpin + Sized, P: FnMut(&Self::Item) -> bool, @@ -1215,7 +1215,7 @@ extension_trait! { fn find_map( &mut self, f: F, - ) -> impl Future> [FindMapFuture<'_, Self, F>] + ) -> [FindMapFuture<'_, Self, F>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> Option, @@ -1249,7 +1249,7 @@ extension_trait! { self, init: B, f: F, - ) -> impl Future [FoldFuture] + ) -> [FoldFuture] where Self: Sized, F: FnMut(B, Self::Item) -> B, @@ -1286,7 +1286,7 @@ extension_trait! { fn partition( self, f: F, - ) -> impl Future [PartitionFuture] + ) -> [PartitionFuture] where Self: Sized, F: FnMut(&Self::Item) -> bool, @@ -1322,7 +1322,7 @@ extension_trait! { fn for_each( self, f: F, - ) -> impl Future [ForEachFuture] + ) -> [ForEachFuture] where Self: Sized, F: FnMut(Self::Item), @@ -1378,7 +1378,7 @@ extension_trait! { fn any( &mut self, f: F, - ) -> impl Future [AnyFuture<'_, Self, F, Self::Item>] + ) -> [AnyFuture<'_, Self, F, Self::Item>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1614,7 +1614,7 @@ extension_trait! { &mut self, init: T, f: F, - ) -> impl Future> [TryFoldFuture<'_, Self, F, T>] + ) -> [TryFoldFuture<'_, Self, F, T>] where Self: Unpin + Sized, F: FnMut(B, Self::Item) -> Result, @@ -1659,7 +1659,7 @@ extension_trait! { fn try_for_each( &mut self, f: F, - ) -> impl Future [TryForEachFuture<'_, Self, F>] + ) -> [TryForEachFuture<'_, Self, F>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> Result<(), E>, @@ -1741,7 +1741,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn unzip(self) -> impl Future [UnzipFuture] + fn unzip(self) -> [UnzipFuture] where FromA: Default + Extend, FromB: Default + Extend, @@ -1805,7 +1805,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn collect<'a, B>( self, - ) -> impl Future [Pin + 'a + Send>>] + ) -> [Pin + 'a + Send>>] where Self: Sized + 'a + Send, B: FromStream, @@ -1879,7 +1879,7 @@ extension_trait! { fn partial_cmp( self, other: S - ) -> impl Future> [PartialCmpFuture] + ) -> [PartialCmpFuture] where Self: Sized + Stream, S: Stream, @@ -1919,7 +1919,7 @@ extension_trait! { fn position

( &mut self, predicate: P, - ) -> impl Future> [PositionFuture<'_, Self, P>] + ) -> [PositionFuture<'_, Self, P>] where Self: Unpin + Sized, P: FnMut(Self::Item) -> bool, @@ -1957,7 +1957,7 @@ extension_trait! { fn cmp( self, other: S - ) -> impl Future [CmpFuture] + ) -> [CmpFuture] where Self: Sized + Stream, S: Stream, @@ -1988,7 +1988,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn count(self) -> impl Future [CountFuture] + fn count(self) -> [CountFuture] where Self: Sized, { @@ -2023,7 +2023,7 @@ extension_trait! { fn ne( self, other: S - ) -> impl Future [NeFuture] + ) -> [NeFuture] where Self: Sized, S: Sized + Stream, @@ -2060,7 +2060,7 @@ extension_trait! { fn ge( self, other: S - ) -> impl Future [GeFuture] + ) -> [GeFuture] where Self: Sized + Stream, S: Stream, @@ -2097,7 +2097,7 @@ extension_trait! { fn eq( self, other: S - ) -> impl Future [EqFuture] + ) -> [EqFuture] where Self: Sized + Stream, S: Sized + Stream, @@ -2134,7 +2134,7 @@ extension_trait! { fn gt( self, other: S - ) -> impl Future [GtFuture] + ) -> [GtFuture] where Self: Sized + Stream, S: Stream, @@ -2171,7 +2171,7 @@ extension_trait! { fn le( self, other: S - ) -> impl Future [LeFuture] + ) -> [LeFuture] where Self: Sized + Stream, S: Stream, @@ -2208,7 +2208,7 @@ extension_trait! { fn lt( self, other: S - ) -> impl Future [LtFuture] + ) -> [LtFuture] where Self: Sized + Stream, S: Stream, @@ -2252,7 +2252,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn sum<'a, S>( self, - ) -> impl Future [Pin + 'a>>] + ) -> [Pin + 'a>>] where Self: Sized + Stream + 'a, S: Sum, @@ -2298,7 +2298,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn product<'a, P>( self, - ) -> impl Future [Pin + 'a>>] + ) -> [Pin + 'a>>] where Self: Sized + Stream + 'a, P: Product, diff --git a/src/utils.rs b/src/utils.rs index 42286ad11..1f3b7fc82 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -271,7 +271,7 @@ macro_rules! extension_trait { }; // Parse the return type in an extension method. - (@ext [-> impl Future [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { + (@ext [-> [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { extension_trait!(@ext [$($tail)*] -> [$($accum)* -> $f]); }; From c626a696706020e5e0cc018db8ea468ea165f529 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:20:23 +1100 Subject: [PATCH 213/272] Move the blanket `impl` outside of `extension_trait`. --- src/future/future/mod.rs | 3 +++ src/io/buf_read/mod.rs | 2 ++ src/io/read/mod.rs | 3 ++- src/io/seek/mod.rs | 2 ++ src/io/write/mod.rs | 2 ++ src/stream/stream/mod.rs | 3 +++ src/utils.rs | 3 --- 7 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 46780f8cf..05d099b05 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -285,3 +285,6 @@ extension_trait! { } } } + +impl FutureExt for T {} + diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 2b1ee86b5..1314dc9f9 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -239,6 +239,8 @@ extension_trait! { } } +impl BufReadExt for T {} + pub fn read_until_internal( mut reader: Pin<&mut R>, cx: &mut Context<'_>, diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 601bb7f1f..9b5b9644e 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -372,10 +372,11 @@ extension_trait! { fn chain(self, next: R) -> Chain where Self: Sized { Chain { first: self, second: next, done_first: false } } - } } +impl ReadExt for T {} + /// Initializes a buffer if necessary. /// /// Currently, a buffer is always initialized because `read_initializer` diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 297232232..33ba25e3c 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -48,3 +48,5 @@ extension_trait! { } } } + +impl SeekExt for T {} diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index c8befae3d..3463d282d 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -185,3 +185,5 @@ extension_trait! { } } } + +impl WriteExt for T {} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d71dc6090..279a83699 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -2307,3 +2307,6 @@ extension_trait! { } } } + +impl StreamExt for T {} + diff --git a/src/utils.rs b/src/utils.rs index 1f3b7fc82..51b2fb0c7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -265,9 +265,6 @@ macro_rules! extension_trait { pub trait $ext: $name { extension_trait!(@ext [$($body_ext)*] -> []); } - - // Blanket implementation of the extension trait for any type implementing the base trait. - impl $ext for T {} }; // Parse the return type in an extension method. From 1c70420c5a1346d2162155ad0782bf86786b4767 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:24:15 +1100 Subject: [PATCH 214/272] Move the base trait re-export outside of `extension_trait`. --- src/future/future/mod.rs | 2 ++ src/io/buf_read/mod.rs | 2 ++ src/io/read/mod.rs | 2 ++ src/io/seek/mod.rs | 2 ++ src/io/write/mod.rs | 2 ++ src/stream/stream/mod.rs | 2 ++ src/utils.rs | 3 --- 7 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 05d099b05..d6c4d7e0f 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -20,6 +20,8 @@ cfg_unstable_default! { use crate::future::timeout::TimeoutFuture; } +pub use core::future::Future as Future; + extension_trait! { pub trait Future {} diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 1314dc9f9..829171568 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -15,6 +15,8 @@ use std::pin::Pin; use crate::io; use crate::task::{Context, Poll}; +pub use futures_io::AsyncBufRead as BufRead; + extension_trait! { pub trait BufRead {} diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 9b5b9644e..b8855886c 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -21,6 +21,8 @@ pub use bytes::Bytes; pub use chain::Chain; pub use take::Take; +pub use futures_io::AsyncRead as Read; + extension_trait! { pub trait Read {} diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 33ba25e3c..cafbfe626 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -4,6 +4,8 @@ use seek::SeekFuture; use crate::io::SeekFrom; +pub use futures_io::AsyncSeek as Seek; + extension_trait! { pub trait Seek {} diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 3463d282d..6e9a574f5 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -12,6 +12,8 @@ use write_vectored::WriteVectoredFuture; use crate::io::{self, IoSlice}; +pub use futures_io::AsyncWrite as Write; + extension_trait! { pub trait Write {} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 279a83699..48fdbb22f 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -143,6 +143,8 @@ cfg_unstable! { mod unzip; } +pub use futures_core::stream::Stream as Stream; + extension_trait! { pub trait Stream {} diff --git a/src/utils.rs b/src/utils.rs index 51b2fb0c7..d662e17f4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -257,9 +257,6 @@ macro_rules! extension_trait { $($body_ext:tt)* } ) => { - // Re-export the base trait from the futures crate. - pub use $base as $name; - // The extension trait that adds methods to any type implementing the base trait. #[doc = $doc_ext] pub trait $ext: $name { From 2dde8820fa2b108db011166467f20cabb4627b69 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:27:23 +1100 Subject: [PATCH 215/272] Remove what's left of the first trait in `extension_trait`. --- src/future/future/mod.rs | 4 +--- src/io/buf_read/mod.rs | 4 +--- src/io/read/mod.rs | 4 +--- src/io/seek/mod.rs | 4 +--- src/io/write/mod.rs | 4 +--- src/stream/stream/mod.rs | 4 +--- src/utils.rs | 8 +------- 7 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index d6c4d7e0f..e20f8baca 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -23,14 +23,12 @@ cfg_unstable_default! { pub use core::future::Future as Future; extension_trait! { - pub trait Future {} - #[doc = r#" Extension methods for [`Future`]. [`Future`]: ../future/trait.Future.html "#] - pub trait FutureExt: core::future::Future { + pub trait FutureExt: Future { /// Returns a Future that delays execution for a specified time. /// /// # Examples diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 829171568..8f248e2be 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -18,14 +18,12 @@ use crate::task::{Context, Poll}; pub use futures_io::AsyncBufRead as BufRead; extension_trait! { - pub trait BufRead {} - #[doc = r#" Extension methods for [`BufRead`]. [`BufRead`]: ../trait.BufRead.html "#] - pub trait BufReadExt: futures_io::AsyncBufRead { + pub trait BufReadExt: BufRead { #[doc = r#" Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index b8855886c..f96b81548 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -24,14 +24,12 @@ pub use take::Take; pub use futures_io::AsyncRead as Read; extension_trait! { - pub trait Read {} - #[doc = r#" Extension methods for [`Read`]. [`Read`]: ../trait.Read.html "#] - pub trait ReadExt: futures_io::AsyncRead { + pub trait ReadExt: Read { #[doc = r#" Reads some bytes from the byte stream. diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index cafbfe626..af7cafcf9 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -7,14 +7,12 @@ use crate::io::SeekFrom; pub use futures_io::AsyncSeek as Seek; extension_trait! { - pub trait Seek {} - #[doc = r#" Extension methods for [`Seek`]. [`Seek`]: ../trait.Seek.html "#] - pub trait SeekExt: futures_io::AsyncSeek { + pub trait SeekExt: Seek { #[doc = r#" Seeks to a new position in a byte stream. diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 6e9a574f5..b18d80f04 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -15,14 +15,12 @@ use crate::io::{self, IoSlice}; pub use futures_io::AsyncWrite as Write; extension_trait! { - pub trait Write {} - #[doc = r#" Extension methods for [`Write`]. [`Write`]: ../trait.Write.html "#] - pub trait WriteExt: futures_io::AsyncWrite { + pub trait WriteExt: Write { #[doc = r#" Writes some bytes into the byte stream. diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 48fdbb22f..0ead08008 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -146,14 +146,12 @@ cfg_unstable! { pub use futures_core::stream::Stream as Stream; extension_trait! { - pub trait Stream {} - #[doc = r#" Extension methods for [`Stream`]. [`Stream`]: ../stream/trait.Stream.html "#] - pub trait StreamExt: futures_core::stream::Stream { + pub trait StreamExt: Stream { #[doc = r#" Advances the stream and returns the next value. diff --git a/src/utils.rs b/src/utils.rs index d662e17f4..96f24fd24 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -246,14 +246,8 @@ macro_rules! cfg_default { #[doc(hidden)] macro_rules! extension_trait { ( - // Interesting patterns: - // - `$name`: trait name that gets rendered in the docs - // - `$ext`: name of the hidden extension trait - // - `$base`: base trait - pub trait $name:ident {} - #[doc = $doc_ext:tt] - pub trait $ext:ident: $base:path { + pub trait $ext:ident: $name:ident { $($body_ext:tt)* } ) => { From 1146c66f1b9448a31d3a4f486e2ea9b372f0335c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:34:22 +1100 Subject: [PATCH 216/272] Remove `extension_trait`. At this point, `extension_trait` is basically an expensive no-op. This commit removes it. The next commit will adjust the indentation. --- src/future/future/mod.rs | 16 +++++----- src/io/buf_read/mod.rs | 6 ++-- src/io/read/mod.rs | 12 +++----- src/io/seek/mod.rs | 4 +-- src/io/write/mod.rs | 12 +++----- src/stream/stream/mod.rs | 66 +++++++++++++++++++--------------------- src/utils.rs | 38 ----------------------- 7 files changed, 52 insertions(+), 102 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index e20f8baca..a244c2859 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -22,7 +22,6 @@ cfg_unstable_default! { pub use core::future::Future as Future; -extension_trait! { #[doc = r#" Extension methods for [`Future`]. @@ -45,7 +44,7 @@ extension_trait! { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn delay(self, dur: Duration) -> [DelayFuture] + fn delay(self, dur: Duration) -> DelayFuture where Self: Sized, { @@ -70,7 +69,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flatten( self, - ) -> [FlattenFuture::Future>] + ) -> FlattenFuture::Future> where Self: Sized, ::Output: IntoFuture, @@ -112,7 +111,7 @@ extension_trait! { fn race( self, other: F, - ) -> [Race] + ) -> Race where Self: std::future::Future + Sized, F: std::future::Future::Output>, @@ -158,7 +157,7 @@ extension_trait! { fn try_race( self, other: F - ) -> [TryRace] + ) -> TryRace where Self: std::future::Future> + Sized, F: std::future::Future::Output>, @@ -195,7 +194,7 @@ extension_trait! { fn join( self, other: F - ) -> [Join] + ) -> Join where Self: std::future::Future + Sized, F: std::future::Future, @@ -242,7 +241,7 @@ extension_trait! { fn try_join( self, other: F - ) -> [TryJoin] + ) -> TryJoin where Self: std::future::Future> + Sized, F: std::future::Future>, @@ -278,13 +277,12 @@ extension_trait! { "#] #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> [TimeoutFuture] + fn timeout(self, dur: Duration) -> TimeoutFuture where Self: Sized { TimeoutFuture::new(self, dur) } } -} impl FutureExt for T {} diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 8f248e2be..f8bffaf33 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -17,7 +17,6 @@ use crate::task::{Context, Poll}; pub use futures_io::AsyncBufRead as BufRead; -extension_trait! { #[doc = r#" Extension methods for [`BufRead`]. @@ -78,7 +77,7 @@ extension_trait! { &'a mut self, byte: u8, buf: &'a mut Vec, - ) -> [ReadUntilFuture<'a, Self>] + ) -> ReadUntilFuture<'a, Self> where Self: Unpin, { @@ -131,7 +130,7 @@ extension_trait! { fn read_line<'a>( &'a mut self, buf: &'a mut String, - ) -> [ReadLineFuture<'a, Self>] + ) -> ReadLineFuture<'a, Self> where Self: Unpin, { @@ -237,7 +236,6 @@ extension_trait! { } } } -} impl BufReadExt for T {} diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index f96b81548..0109a3ace 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -23,7 +23,6 @@ pub use take::Take; pub use futures_io::AsyncRead as Read; -extension_trait! { #[doc = r#" Extension methods for [`Read`]. @@ -64,7 +63,7 @@ extension_trait! { fn read<'a>( &'a mut self, buf: &'a mut [u8], - ) -> [ReadFuture<'a, Self>] + ) -> ReadFuture<'a, Self> where Self: Unpin { @@ -86,7 +85,7 @@ extension_trait! { fn read_vectored<'a>( &'a mut self, bufs: &'a mut [IoSliceMut<'a>], - ) -> [ReadVectoredFuture<'a, Self>] + ) -> ReadVectoredFuture<'a, Self> where Self: Unpin, { @@ -123,7 +122,7 @@ extension_trait! { fn read_to_end<'a>( &'a mut self, buf: &'a mut Vec, - ) -> [ReadToEndFuture<'a, Self>] + ) -> ReadToEndFuture<'a, Self> where Self: Unpin, { @@ -162,7 +161,7 @@ extension_trait! { fn read_to_string<'a>( &'a mut self, buf: &'a mut String, - ) -> [ReadToStringFuture<'a, Self>] + ) -> ReadToStringFuture<'a, Self> where Self: Unpin, { @@ -217,7 +216,7 @@ extension_trait! { fn read_exact<'a>( &'a mut self, buf: &'a mut [u8], - ) -> [ReadExactFuture<'a, Self>] + ) -> ReadExactFuture<'a, Self> where Self: Unpin, { @@ -373,7 +372,6 @@ extension_trait! { Chain { first: self, second: next, done_first: false } } } -} impl ReadExt for T {} diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index af7cafcf9..f2b609328 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -6,7 +6,6 @@ use crate::io::SeekFrom; pub use futures_io::AsyncSeek as Seek; -extension_trait! { #[doc = r#" Extension methods for [`Seek`]. @@ -40,13 +39,12 @@ extension_trait! { fn seek( &mut self, pos: SeekFrom, - ) -> [SeekFuture<'_, Self>] + ) -> SeekFuture<'_, Self> where Self: Unpin, { SeekFuture { seeker: self, pos } } } -} impl SeekExt for T {} diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index b18d80f04..d73c40d88 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -14,7 +14,6 @@ use crate::io::{self, IoSlice}; pub use futures_io::AsyncWrite as Write; -extension_trait! { #[doc = r#" Extension methods for [`Write`]. @@ -49,7 +48,7 @@ extension_trait! { fn write<'a>( &'a mut self, buf: &'a [u8], - ) -> [WriteFuture<'a, Self>] + ) -> WriteFuture<'a, Self> where Self: Unpin, { @@ -75,7 +74,7 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn flush(&mut self) -> [FlushFuture<'_, Self>] + fn flush(&mut self) -> FlushFuture<'_, Self> where Self: Unpin, { @@ -97,7 +96,7 @@ extension_trait! { fn write_vectored<'a>( &'a mut self, bufs: &'a [IoSlice<'a>], - ) -> [WriteVectoredFuture<'a, Self>] + ) -> WriteVectoredFuture<'a, Self> where Self: Unpin, { @@ -133,7 +132,7 @@ extension_trait! { fn write_all<'a>( &'a mut self, buf: &'a [u8], - ) -> [WriteAllFuture<'a, Self>] + ) -> WriteAllFuture<'a, Self> where Self: Unpin, { @@ -170,7 +169,7 @@ extension_trait! { fn write_fmt<'a>( &'a mut self, fmt: std::fmt::Arguments<'_>, - ) -> [WriteFmtFuture<'a, Self>] + ) -> WriteFmtFuture<'a, Self> where Self: Unpin, { @@ -184,6 +183,5 @@ extension_trait! { WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } } } -} impl WriteExt for T {} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 0ead08008..25aadfac8 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -145,7 +145,6 @@ cfg_unstable! { pub use futures_core::stream::Stream as Stream; -extension_trait! { #[doc = r#" Extension methods for [`Stream`]. @@ -177,7 +176,7 @@ extension_trait! { # }) } ``` "#] - fn next(&mut self) -> [NextFuture<'_, Self>] + fn next(&mut self) -> NextFuture<'_, Self> where Self: Unpin, { @@ -629,7 +628,7 @@ extension_trait! { "#] fn last( self, - ) -> [LastFuture] + ) -> LastFuture where Self: Sized, { @@ -839,7 +838,7 @@ extension_trait! { fn min_by_key( self, key_by: F, - ) -> [MinByKeyFuture] + ) -> MinByKeyFuture where Self: Sized, B: Ord, @@ -875,7 +874,7 @@ extension_trait! { fn max_by_key( self, key_by: F, - ) -> [MaxByKeyFuture] + ) -> MaxByKeyFuture where Self: Sized, B: Ord, @@ -914,7 +913,7 @@ extension_trait! { fn min_by( self, compare: F, - ) -> [MinByFuture] + ) -> MinByFuture where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -947,7 +946,7 @@ extension_trait! { "#] fn max( self, - ) -> [MaxFuture] + ) -> MaxFuture where Self: Sized, Self::Item: Ord, @@ -980,7 +979,7 @@ extension_trait! { "#] fn min( self, - ) -> [MinFuture] + ) -> MinFuture where Self: Sized, Self::Item: Ord, @@ -1018,7 +1017,7 @@ extension_trait! { fn max_by( self, compare: F, - ) -> [MaxByFuture] + ) -> MaxByFuture where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -1082,7 +1081,7 @@ extension_trait! { fn nth( &mut self, n: usize, - ) -> [NthFuture<'_, Self>] + ) -> NthFuture<'_, Self> where Self: Unpin + Sized, { @@ -1138,7 +1137,7 @@ extension_trait! { fn all( &mut self, f: F, - ) -> [AllFuture<'_, Self, F, Self::Item>] + ) -> AllFuture<'_, Self, F, Self::Item> where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1187,7 +1186,7 @@ extension_trait! { fn find

( &mut self, p: P, - ) -> [FindFuture<'_, Self, P>] + ) -> FindFuture<'_, Self, P> where Self: Unpin + Sized, P: FnMut(&Self::Item) -> bool, @@ -1215,7 +1214,7 @@ extension_trait! { fn find_map( &mut self, f: F, - ) -> [FindMapFuture<'_, Self, F>] + ) -> FindMapFuture<'_, Self, F> where Self: Unpin + Sized, F: FnMut(Self::Item) -> Option, @@ -1249,7 +1248,7 @@ extension_trait! { self, init: B, f: F, - ) -> [FoldFuture] + ) -> FoldFuture where Self: Sized, F: FnMut(B, Self::Item) -> B, @@ -1286,7 +1285,7 @@ extension_trait! { fn partition( self, f: F, - ) -> [PartitionFuture] + ) -> PartitionFuture where Self: Sized, F: FnMut(&Self::Item) -> bool, @@ -1322,7 +1321,7 @@ extension_trait! { fn for_each( self, f: F, - ) -> [ForEachFuture] + ) -> ForEachFuture where Self: Sized, F: FnMut(Self::Item), @@ -1378,7 +1377,7 @@ extension_trait! { fn any( &mut self, f: F, - ) -> [AnyFuture<'_, Self, F, Self::Item>] + ) -> AnyFuture<'_, Self, F, Self::Item> where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1614,7 +1613,7 @@ extension_trait! { &mut self, init: T, f: F, - ) -> [TryFoldFuture<'_, Self, F, T>] + ) -> TryFoldFuture<'_, Self, F, T> where Self: Unpin + Sized, F: FnMut(B, Self::Item) -> Result, @@ -1659,7 +1658,7 @@ extension_trait! { fn try_for_each( &mut self, f: F, - ) -> [TryForEachFuture<'_, Self, F>] + ) -> TryForEachFuture<'_, Self, F> where Self: Unpin + Sized, F: FnMut(Self::Item) -> Result<(), E>, @@ -1741,7 +1740,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn unzip(self) -> [UnzipFuture] + fn unzip(self) -> UnzipFuture where FromA: Default + Extend, FromB: Default + Extend, @@ -1805,7 +1804,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn collect<'a, B>( self, - ) -> [Pin + 'a + Send>>] + ) -> Pin + 'a + Send>> where Self: Sized + 'a + Send, B: FromStream, @@ -1879,7 +1878,7 @@ extension_trait! { fn partial_cmp( self, other: S - ) -> [PartialCmpFuture] + ) -> PartialCmpFuture where Self: Sized + Stream, S: Stream, @@ -1919,7 +1918,7 @@ extension_trait! { fn position

( &mut self, predicate: P, - ) -> [PositionFuture<'_, Self, P>] + ) -> PositionFuture<'_, Self, P> where Self: Unpin + Sized, P: FnMut(Self::Item) -> bool, @@ -1957,7 +1956,7 @@ extension_trait! { fn cmp( self, other: S - ) -> [CmpFuture] + ) -> CmpFuture where Self: Sized + Stream, S: Stream, @@ -1988,7 +1987,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn count(self) -> [CountFuture] + fn count(self) -> CountFuture where Self: Sized, { @@ -2023,7 +2022,7 @@ extension_trait! { fn ne( self, other: S - ) -> [NeFuture] + ) -> NeFuture where Self: Sized, S: Sized + Stream, @@ -2060,7 +2059,7 @@ extension_trait! { fn ge( self, other: S - ) -> [GeFuture] + ) -> GeFuture where Self: Sized + Stream, S: Stream, @@ -2097,7 +2096,7 @@ extension_trait! { fn eq( self, other: S - ) -> [EqFuture] + ) -> EqFuture where Self: Sized + Stream, S: Sized + Stream, @@ -2134,7 +2133,7 @@ extension_trait! { fn gt( self, other: S - ) -> [GtFuture] + ) -> GtFuture where Self: Sized + Stream, S: Stream, @@ -2171,7 +2170,7 @@ extension_trait! { fn le( self, other: S - ) -> [LeFuture] + ) -> LeFuture where Self: Sized + Stream, S: Stream, @@ -2208,7 +2207,7 @@ extension_trait! { fn lt( self, other: S - ) -> [LtFuture] + ) -> LtFuture where Self: Sized + Stream, S: Stream, @@ -2252,7 +2251,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn sum<'a, S>( self, - ) -> [Pin + 'a>>] + ) -> Pin + 'a>> where Self: Sized + Stream + 'a, S: Sum, @@ -2298,7 +2297,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn product<'a, P>( self, - ) -> [Pin + 'a>>] + ) -> Pin + 'a>> where Self: Sized + Stream + 'a, P: Product, @@ -2306,7 +2305,6 @@ extension_trait! { Product::product(self) } } -} impl StreamExt for T {} diff --git a/src/utils.rs b/src/utils.rs index 96f24fd24..d1b497273 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -233,41 +233,3 @@ macro_rules! cfg_default { )* } } - -/// Defines an extension trait for a base trait. -/// -/// In generated docs, the base trait will contain methods from the extension trait. In actual -/// code, the base trait will be re-exported and the extension trait will be hidden. We then -/// re-export the extension trait from the prelude. -/// -/// Inside invocations of this macro, we write a definitions that looks similar to the final -/// rendered docs, and the macro then generates all the boilerplate for us. -#[allow(unused_macros)] -#[doc(hidden)] -macro_rules! extension_trait { - ( - #[doc = $doc_ext:tt] - pub trait $ext:ident: $name:ident { - $($body_ext:tt)* - } - ) => { - // The extension trait that adds methods to any type implementing the base trait. - #[doc = $doc_ext] - pub trait $ext: $name { - extension_trait!(@ext [$($body_ext)*] -> []); - } - }; - - // Parse the return type in an extension method. - (@ext [-> [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { - extension_trait!(@ext [$($tail)*] -> [$($accum)* -> $f]); - }; - - // Parse a token. - (@ext [$token:tt $($tail:tt)*] -> [$($accum:tt)*]) => { - extension_trait!(@ext [$($tail)*] -> [$($accum)* $token]); - }; - - // Handle the end of the token list. - (@ext [] -> [$($accum:tt)*]) => { $($accum)* }; -} From 01ede03e0a68398d7a4b2bd0976750e078fd1a83 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 12:40:05 +1100 Subject: [PATCH 217/272] Reindent de-macrofied code. This commit only affects whitespace; `git diff -w` for it is empty. --- src/future/future/mod.rs | 514 +++--- src/io/buf_read/mod.rs | 406 ++--- src/io/read/mod.rs | 564 +++--- src/io/seek/mod.rs | 72 +- src/io/write/mod.rs | 330 ++-- src/stream/stream/mod.rs | 3624 +++++++++++++++++++------------------- 6 files changed, 2755 insertions(+), 2755 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index a244c2859..47187b235 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -22,267 +22,267 @@ cfg_unstable_default! { pub use core::future::Future as Future; +#[doc = r#" + Extension methods for [`Future`]. + + [`Future`]: ../future/trait.Future.html +"#] +pub trait FutureExt: Future { + /// Returns a Future that delays execution for a specified time. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use async_std::prelude::*; + /// use async_std::future; + /// use std::time::Duration; + /// + /// let a = future::ready(1).delay(Duration::from_millis(2000)); + /// dbg!(a.await); + /// # }) + /// ``` + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn delay(self, dur: Duration) -> DelayFuture + where + Self: Sized, + { + DelayFuture::new(self, dur) + } + + /// Flatten out the execution of this future when the result itself + /// can be converted into another future. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use async_std::prelude::*; + /// + /// let nested_future = async { async { 1 } }; + /// let future = nested_future.flatten(); + /// assert_eq!(future.await, 1); + /// # }) + /// ``` + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flatten( + self, + ) -> FlattenFuture::Future> + where + Self: Sized, + ::Output: IntoFuture, + { + FlattenFuture::new(self) + } + + #[doc = r#" + Waits for one of two similarly-typed futures to complete. + + Awaits multiple futures simultaneously, returning the output of the + first future that completes. + + This function will return a new future which awaits for either one of both + futures to complete. If multiple futures are completed at the same time, + resolution will occur in the order that they have been passed. + + Note that this function consumes all futures passed, and once a future is + completed, all other futures are dropped. + + # Examples + + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::future; + + let a = future::pending(); + let b = future::ready(1u8); + let c = future::ready(2u8); + + let f = a.race(b).race(c); + assert_eq!(f.await, 1u8); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn race( + self, + other: F, + ) -> Race + where + Self: std::future::Future + Sized, + F: std::future::Future::Output>, + { + Race::new(self, other) + } + + #[doc = r#" + Waits for one of two similarly-typed fallible futures to complete. + + Awaits multiple futures simultaneously, returning all results once complete. + + `try_race` is similar to [`race`], but keeps going if a future + resolved to an error until all futures have been resolved. In which case + an error is returned. + + The ordering of which value is yielded when two futures resolve + simultaneously is intentionally left unspecified. + + [`race`]: #method.race + + # Examples + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::future; + use std::io::{Error, ErrorKind}; + + let a = future::pending::>(); + let b = future::ready(Err(Error::from(ErrorKind::Other))); + let c = future::ready(Ok(1u8)); + + let f = a.try_race(b).try_race(c); + assert_eq!(f.await?, 1u8); + # + # Ok(()) }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_race( + self, + other: F + ) -> TryRace + where + Self: std::future::Future> + Sized, + F: std::future::Future::Output>, + { + TryRace::new(self, other) + } + #[doc = r#" - Extension methods for [`Future`]. + Waits for two similarly-typed futures to complete. + + Awaits multiple futures simultaneously, returning the output of the + futures once both complete. + + This function returns a new future which polls both futures + concurrently. + + # Examples + + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::future; + + let a = future::ready(1u8); + let b = future::ready(2u16); - [`Future`]: ../future/trait.Future.html + let f = a.join(b); + assert_eq!(f.await, (1u8, 2u16)); + # }); + ``` "#] - pub trait FutureExt: Future { - /// Returns a Future that delays execution for a specified time. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// use async_std::prelude::*; - /// use async_std::future; - /// use std::time::Duration; - /// - /// let a = future::ready(1).delay(Duration::from_millis(2000)); - /// dbg!(a.await); - /// # }) - /// ``` - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn delay(self, dur: Duration) -> DelayFuture - where - Self: Sized, - { - DelayFuture::new(self, dur) - } - - /// Flatten out the execution of this future when the result itself - /// can be converted into another future. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// use async_std::prelude::*; - /// - /// let nested_future = async { async { 1 } }; - /// let future = nested_future.flatten(); - /// assert_eq!(future.await, 1); - /// # }) - /// ``` - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flatten( - self, - ) -> FlattenFuture::Future> - where - Self: Sized, - ::Output: IntoFuture, - { - FlattenFuture::new(self) - } - - #[doc = r#" - Waits for one of two similarly-typed futures to complete. - - Awaits multiple futures simultaneously, returning the output of the - first future that completes. - - This function will return a new future which awaits for either one of both - futures to complete. If multiple futures are completed at the same time, - resolution will occur in the order that they have been passed. - - Note that this function consumes all futures passed, and once a future is - completed, all other futures are dropped. - - # Examples - - ``` - # async_std::task::block_on(async { - use async_std::prelude::*; - use async_std::future; - - let a = future::pending(); - let b = future::ready(1u8); - let c = future::ready(2u8); - - let f = a.race(b).race(c); - assert_eq!(f.await, 1u8); - # }); - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn race( - self, - other: F, - ) -> Race - where - Self: std::future::Future + Sized, - F: std::future::Future::Output>, - { - Race::new(self, other) - } - - #[doc = r#" - Waits for one of two similarly-typed fallible futures to complete. - - Awaits multiple futures simultaneously, returning all results once complete. - - `try_race` is similar to [`race`], but keeps going if a future - resolved to an error until all futures have been resolved. In which case - an error is returned. - - The ordering of which value is yielded when two futures resolve - simultaneously is intentionally left unspecified. - - [`race`]: #method.race - - # Examples - - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::future; - use std::io::{Error, ErrorKind}; - - let a = future::pending::>(); - let b = future::ready(Err(Error::from(ErrorKind::Other))); - let c = future::ready(Ok(1u8)); - - let f = a.try_race(b).try_race(c); - assert_eq!(f.await?, 1u8); - # - # Ok(()) }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn try_race( - self, - other: F - ) -> TryRace - where - Self: std::future::Future> + Sized, - F: std::future::Future::Output>, - { - TryRace::new(self, other) - } - - #[doc = r#" - Waits for two similarly-typed futures to complete. - - Awaits multiple futures simultaneously, returning the output of the - futures once both complete. - - This function returns a new future which polls both futures - concurrently. - - # Examples - - ``` - # async_std::task::block_on(async { - use async_std::prelude::*; - use async_std::future; - - let a = future::ready(1u8); - let b = future::ready(2u16); - - let f = a.join(b); - assert_eq!(f.await, (1u8, 2u16)); - # }); - ``` - "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn join( - self, - other: F - ) -> Join - where - Self: std::future::Future + Sized, - F: std::future::Future, - { - Join::new(self, other) - } - - #[doc = r#" - Waits for two similarly-typed fallible futures to complete. - - Awaits multiple futures simultaneously, returning all results once - complete. - - `try_join` is similar to [`join`], but returns an error immediately - if a future resolves to an error. - - [`join`]: #method.join - - # Examples - - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::future; - - let a = future::ready(Err::("Error")); - let b = future::ready(Ok(1u8)); - - let f = a.try_join(b); - assert_eq!(f.await, Err("Error")); - - let a = future::ready(Ok::(1u8)); - let b = future::ready(Ok::(2u16)); - - let f = a.try_join(b); - assert_eq!(f.await, Ok((1u8, 2u16))); - # - # Ok(()) }) } - ``` - "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn try_join( - self, - other: F - ) -> TryJoin - where - Self: std::future::Future> + Sized, - F: std::future::Future>, - { - TryJoin::new(self, other) - } - - #[doc = r#" - Waits for both the future and a timeout, if the timeout completes before - the future, it returns a TimeoutError. - - # Example - ``` - # async_std::task::block_on(async { - # - use std::time::Duration; - - use async_std::prelude::*; - use async_std::future; - - let fut = future::ready(0); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_ok()); - - let fut = future::pending::<()>(); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_err()) - # - # }); - ``` - "#] - #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> TimeoutFuture - where Self: Sized - { - TimeoutFuture::new(self, dur) - } + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn join( + self, + other: F + ) -> Join + where + Self: std::future::Future + Sized, + F: std::future::Future, + { + Join::new(self, other) } + #[doc = r#" + Waits for two similarly-typed fallible futures to complete. + + Awaits multiple futures simultaneously, returning all results once + complete. + + `try_join` is similar to [`join`], but returns an error immediately + if a future resolves to an error. + + [`join`]: #method.join + + # Examples + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::future; + + let a = future::ready(Err::("Error")); + let b = future::ready(Ok(1u8)); + + let f = a.try_join(b); + assert_eq!(f.await, Err("Error")); + + let a = future::ready(Ok::(1u8)); + let b = future::ready(Ok::(2u16)); + + let f = a.try_join(b); + assert_eq!(f.await, Ok((1u8, 2u16))); + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_join( + self, + other: F + ) -> TryJoin + where + Self: std::future::Future> + Sized, + F: std::future::Future>, + { + TryJoin::new(self, other) + } + + #[doc = r#" + Waits for both the future and a timeout, if the timeout completes before + the future, it returns a TimeoutError. + + # Example + ``` + # async_std::task::block_on(async { + # + use std::time::Duration; + + use async_std::prelude::*; + use async_std::future; + + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()); + + let fut = future::pending::<()>(); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_err()) + # + # }); + ``` + "#] + #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> TimeoutFuture + where Self: Sized + { + TimeoutFuture::new(self, dur) + } +} + impl FutureExt for T {} diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index f8bffaf33..75247a516 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -17,225 +17,225 @@ use crate::task::{Context, Poll}; pub use futures_io::AsyncBufRead as BufRead; - #[doc = r#" - Extension methods for [`BufRead`]. +#[doc = r#" + Extension methods for [`BufRead`]. - [`BufRead`]: ../trait.BufRead.html + [`BufRead`]: ../trait.BufRead.html +"#] +pub trait BufReadExt: BufRead { + #[doc = r#" + Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. + + This function will read bytes from the underlying stream until the delimiter or EOF + is found. Once found, all bytes up to, and including, the delimiter (if found) will + be appended to `buf`. + + If successful, this function will return the total number of bytes read. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; + use async_std::prelude::*; + + let mut file = BufReader::new(File::open("a.txt").await?); + + let mut buf = Vec::with_capacity(1024); + let n = file.read_until(b'\n', &mut buf).await?; + # + # Ok(()) }) } + ``` + + Multiple successful calls to `read_until` append all bytes up to and including to + `buf`: + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::BufReader; + use async_std::prelude::*; + + let from: &[u8] = b"append\nexample\n"; + let mut reader = BufReader::new(from); + let mut buf = vec![]; + + let mut size = reader.read_until(b'\n', &mut buf).await?; + assert_eq!(size, 7); + assert_eq!(buf, b"append\n"); + + size += reader.read_until(b'\n', &mut buf).await?; + assert_eq!(size, from.len()); + + assert_eq!(buf, from); + # + # Ok(()) }) } + ``` "#] - pub trait BufReadExt: BufRead { - #[doc = r#" - Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - - This function will read bytes from the underlying stream until the delimiter or EOF - is found. Once found, all bytes up to, and including, the delimiter (if found) will - be appended to `buf`. - - If successful, this function will return the total number of bytes read. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; - - let mut file = BufReader::new(File::open("a.txt").await?); - - let mut buf = Vec::with_capacity(1024); - let n = file.read_until(b'\n', &mut buf).await?; - # - # Ok(()) }) } - ``` - - Multiple successful calls to `read_until` append all bytes up to and including to - `buf`: - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::BufReader; - use async_std::prelude::*; - - let from: &[u8] = b"append\nexample\n"; - let mut reader = BufReader::new(from); - let mut buf = vec![]; - - let mut size = reader.read_until(b'\n', &mut buf).await?; - assert_eq!(size, 7); - assert_eq!(buf, b"append\n"); - - size += reader.read_until(b'\n', &mut buf).await?; - assert_eq!(size, from.len()); - - assert_eq!(buf, from); - # - # Ok(()) }) } - ``` - "#] - fn read_until<'a>( - &'a mut self, - byte: u8, - buf: &'a mut Vec, - ) -> ReadUntilFuture<'a, Self> - where - Self: Unpin, - { - ReadUntilFuture { - reader: self, - byte, - buf, - read: 0, - } + fn read_until<'a>( + &'a mut self, + byte: u8, + buf: &'a mut Vec, + ) -> ReadUntilFuture<'a, Self> + where + Self: Unpin, + { + ReadUntilFuture { + reader: self, + byte, + buf, + read: 0, } + } - #[doc = r#" - Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is - reached. - - This function will read bytes from the underlying stream until the newline - delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and - including, the delimiter (if found) will be appended to `buf`. - - If successful, this function will return the total number of bytes read. - - If this function returns `Ok(0)`, the stream has reached EOF. - - # Errors - - This function has the same error semantics as [`read_until`] and will also return - an error if the read bytes are not valid UTF-8. If an I/O error is encountered then - `buf` may contain some bytes already read in the event that all data read so far - was valid UTF-8. - - [`read_until`]: #method.read_until - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; - - let mut file = BufReader::new(File::open("a.txt").await?); - - let mut buf = String::new(); - file.read_line(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_line<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ReadLineFuture<'a, Self> - where - Self: Unpin, - { - ReadLineFuture { - reader: self, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - read: 0, - } + #[doc = r#" + Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is + reached. + + This function will read bytes from the underlying stream until the newline + delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and + including, the delimiter (if found) will be appended to `buf`. + + If successful, this function will return the total number of bytes read. + + If this function returns `Ok(0)`, the stream has reached EOF. + + # Errors + + This function has the same error semantics as [`read_until`] and will also return + an error if the read bytes are not valid UTF-8. If an I/O error is encountered then + `buf` may contain some bytes already read in the event that all data read so far + was valid UTF-8. + + [`read_until`]: #method.read_until + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; + use async_std::prelude::*; + + let mut file = BufReader::new(File::open("a.txt").await?); + + let mut buf = String::new(); + file.read_line(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_line<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ReadLineFuture<'a, Self> + where + Self: Unpin, + { + ReadLineFuture { + reader: self, + bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + buf, + read: 0, } + } - #[doc = r#" - Returns a stream over the lines of this byte stream. + #[doc = r#" + Returns a stream over the lines of this byte stream. - The stream returned from this function will yield instances of - [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte - (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. + The stream returned from this function will yield instances of + [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte + (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. - [`io::Result`]: type.Result.html - [`String`]: https://doc.rust-lang.org/std/string/struct.String.html + [`io::Result`]: type.Result.html + [`String`]: https://doc.rust-lang.org/std/string/struct.String.html - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; + use async_std::prelude::*; - let file = File::open("a.txt").await?; - let mut lines = BufReader::new(file).lines(); - let mut count = 0; + let file = File::open("a.txt").await?; + let mut lines = BufReader::new(file).lines(); + let mut count = 0; - while let Some(line) = lines.next().await { - line?; - count += 1; - } - # - # Ok(()) }) } - ``` - "#] - fn lines(self) -> Lines - where - Self: Unpin + Sized, - { - Lines { - reader: self, - buf: String::new(), - bytes: Vec::new(), - read: 0, - } + while let Some(line) = lines.next().await { + line?; + count += 1; } + # + # Ok(()) }) } + ``` + "#] + fn lines(self) -> Lines + where + Self: Unpin + Sized, + { + Lines { + reader: self, + buf: String::new(), + bytes: Vec::new(), + read: 0, + } + } - #[doc = r#" - Returns a stream over the contents of this reader split on the byte `byte`. - - The stream returned from this function will return instances of - [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have - the delimiter byte at the end. - - This function will yield errors whenever [`read_until`] would have - also yielded an error. - - [`io::Result`]: type.Result.html - [`Vec`]: ../vec/struct.Vec.html - [`read_until`]: #method.read_until - - # Examples - - [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - this example, we use [`Cursor`] to iterate over all hyphen delimited - segments in a byte slice - - [`Cursor`]: struct.Cursor.html - - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::io; - - let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - - let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - assert_eq!(split_iter.next().await, Some(b"lorem".to_vec())); - assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec())); - assert_eq!(split_iter.next().await, Some(b"dolor".to_vec())); - assert_eq!(split_iter.next().await, None); - # - # Ok(()) }) } - ``` - "#] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { - reader: self, - buf: Vec::new(), - delim: byte, - read: 0, - } + #[doc = r#" + Returns a stream over the contents of this reader split on the byte `byte`. + + The stream returned from this function will return instances of + [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have + the delimiter byte at the end. + + This function will yield errors whenever [`read_until`] would have + also yielded an error. + + [`io::Result`]: type.Result.html + [`Vec`]: ../vec/struct.Vec.html + [`read_until`]: #method.read_until + + # Examples + + [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + this example, we use [`Cursor`] to iterate over all hyphen delimited + segments in a byte slice + + [`Cursor`]: struct.Cursor.html + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::io; + + let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + + let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + assert_eq!(split_iter.next().await, Some(b"lorem".to_vec())); + assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec())); + assert_eq!(split_iter.next().await, Some(b"dolor".to_vec())); + assert_eq!(split_iter.next().await, None); + # + # Ok(()) }) } + ``` + "#] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { + reader: self, + buf: Vec::new(), + delim: byte, + read: 0, } } +} impl BufReadExt for T {} diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 0109a3ace..405422585 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -23,355 +23,355 @@ pub use take::Take; pub use futures_io::AsyncRead as Read; +#[doc = r#" + Extension methods for [`Read`]. + + [`Read`]: ../trait.Read.html +"#] +pub trait ReadExt: Read { #[doc = r#" - Extension methods for [`Read`]. + Reads some bytes from the byte stream. + + Returns the number of bytes read from the start of the buffer. + + If the return value is `Ok(n)`, then it must be guaranteed that + `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been + filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two + scenarios: + + 1. This reader has reached its "end of file" and will likely no longer be able to + produce bytes. Note that this does not mean that the reader will always no + longer be able to produce bytes. + 2. The buffer specified was 0 bytes in length. - [`Read`]: ../trait.Read.html + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; + + let mut file = File::open("a.txt").await?; + + let mut buf = vec![0; 1024]; + let n = file.read(&mut buf).await?; + # + # Ok(()) }) } + ``` "#] - pub trait ReadExt: Read { - #[doc = r#" - Reads some bytes from the byte stream. - - Returns the number of bytes read from the start of the buffer. - - If the return value is `Ok(n)`, then it must be guaranteed that - `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been - filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two - scenarios: - - 1. This reader has reached its "end of file" and will likely no longer be able to - produce bytes. Note that this does not mean that the reader will always no - longer be able to produce bytes. - 2. The buffer specified was 0 bytes in length. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::open("a.txt").await?; - - let mut buf = vec![0; 1024]; - let n = file.read(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> ReadFuture<'a, Self> - where - Self: Unpin - { - ReadFuture { reader: self, buf } - } + fn read<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> ReadFuture<'a, Self> + where + Self: Unpin + { + ReadFuture { reader: self, buf } + } - #[doc = r#" - Like [`read`], except that it reads into a slice of buffers. + #[doc = r#" + Like [`read`], except that it reads into a slice of buffers. - Data is copied to fill each buffer in order, with the final buffer written to - possibly being only partially filled. This method must behave as a single call to - [`read`] with the buffers concatenated would. + Data is copied to fill each buffer in order, with the final buffer written to + possibly being only partially filled. This method must behave as a single call to + [`read`] with the buffers concatenated would. - The default implementation calls [`read`] with either the first nonempty buffer - provided, or an empty one if none exists. + The default implementation calls [`read`] with either the first nonempty buffer + provided, or an empty one if none exists. - [`read`]: #tymethod.read - "#] - fn read_vectored<'a>( - &'a mut self, - bufs: &'a mut [IoSliceMut<'a>], - ) -> ReadVectoredFuture<'a, Self> - where - Self: Unpin, - { - ReadVectoredFuture { reader: self, bufs } - } + [`read`]: #tymethod.read + "#] + fn read_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSliceMut<'a>], + ) -> ReadVectoredFuture<'a, Self> + where + Self: Unpin, + { + ReadVectoredFuture { reader: self, bufs } + } - #[doc = r#" - Reads all bytes from the byte stream. + #[doc = r#" + Reads all bytes from the byte stream. - All bytes read from this stream will be appended to the specified buffer `buf`. - This function will continuously call [`read`] to append more data to `buf` until - [`read`] returns either `Ok(0)` or an error. + All bytes read from this stream will be appended to the specified buffer `buf`. + This function will continuously call [`read`] to append more data to `buf` until + [`read`] returns either `Ok(0)` or an error. - If successful, this function will return the total number of bytes read. + If successful, this function will return the total number of bytes read. - [`read`]: #tymethod.read + [`read`]: #tymethod.read - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = Vec::new(); - file.read_to_end(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_to_end<'a>( - &'a mut self, - buf: &'a mut Vec, - ) -> ReadToEndFuture<'a, Self> - where - Self: Unpin, - { - let start_len = buf.len(); - ReadToEndFuture { - reader: self, - buf, - start_len, - } + let mut buf = Vec::new(); + file.read_to_end(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_to_end<'a>( + &'a mut self, + buf: &'a mut Vec, + ) -> ReadToEndFuture<'a, Self> + where + Self: Unpin, + { + let start_len = buf.len(); + ReadToEndFuture { + reader: self, + buf, + start_len, } + } - #[doc = r#" - Reads all bytes from the byte stream and appends them into a string. + #[doc = r#" + Reads all bytes from the byte stream and appends them into a string. - If successful, this function will return the number of bytes read. + If successful, this function will return the number of bytes read. - If the data in this stream is not valid UTF-8 then an error will be returned and - `buf` will be left unmodified. + If the data in this stream is not valid UTF-8 then an error will be returned and + `buf` will be left unmodified. - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = String::new(); - file.read_to_string(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_to_string<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ReadToStringFuture<'a, Self> - where - Self: Unpin, - { - let start_len = buf.len(); - ReadToStringFuture { - reader: self, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - start_len, - } + let mut buf = String::new(); + file.read_to_string(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_to_string<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ReadToStringFuture<'a, Self> + where + Self: Unpin, + { + let start_len = buf.len(); + ReadToStringFuture { + reader: self, + bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + buf, + start_len, } + } - #[doc = r#" - Reads the exact number of bytes required to fill `buf`. + #[doc = r#" + Reads the exact number of bytes required to fill `buf`. - This function reads as many bytes as necessary to completely fill the specified - buffer `buf`. + This function reads as many bytes as necessary to completely fill the specified + buffer `buf`. - No guarantees are provided about the contents of `buf` when this function is - called, implementations cannot rely on any property of the contents of `buf` being - true. It is recommended that implementations only write data to `buf` instead of - reading its contents. + No guarantees are provided about the contents of `buf` when this function is + called, implementations cannot rely on any property of the contents of `buf` being + true. It is recommended that implementations only write data to `buf` instead of + reading its contents. - If this function encounters an "end of file" before completely filling the buffer, - it returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of - `buf` are unspecified in this case. + If this function encounters an "end of file" before completely filling the buffer, + it returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of + `buf` are unspecified in this case. - If any other read error is encountered then this function immediately returns. The - contents of `buf` are unspecified in this case. + If any other read error is encountered then this function immediately returns. The + contents of `buf` are unspecified in this case. - If this function returns an error, it is unspecified how many bytes it has read, - but it will never read more than would be necessary to completely fill the buffer. + If this function returns an error, it is unspecified how many bytes it has read, + but it will never read more than would be necessary to completely fill the buffer. - [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof + [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = vec![0; 10]; - file.read_exact(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_exact<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> ReadExactFuture<'a, Self> - where - Self: Unpin, - { - ReadExactFuture { reader: self, buf } - } + let mut buf = vec![0; 10]; + file.read_exact(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_exact<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> ReadExactFuture<'a, Self> + where + Self: Unpin, + { + ReadExactFuture { reader: self, buf } + } - #[doc = r#" - Creates an adaptor which will read at most `limit` bytes from it. + #[doc = r#" + Creates an adaptor which will read at most `limit` bytes from it. - This function returns a new instance of `Read` which will read at most - `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any - read errors will not count towards the number of bytes read and future - calls to [`read`] may succeed. + This function returns a new instance of `Read` which will read at most + `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + read errors will not count towards the number of bytes read and future + calls to [`read`] may succeed. - # Examples + # Examples - [`File`]s implement `Read`: + [`File`]s implement `Read`: - [`File`]: ../fs/struct.File.html - [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok - [`read`]: tymethod.read + [`File`]: ../fs/struct.File.html + [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok + [`read`]: tymethod.read - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::prelude::*; - use async_std::fs::File; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::prelude::*; + use async_std::fs::File; - let f = File::open("foo.txt").await?; - let mut buffer = [0; 5]; + let f = File::open("foo.txt").await?; + let mut buffer = [0; 5]; - // read at most five bytes - let mut handle = f.take(5); + // read at most five bytes + let mut handle = f.take(5); + + handle.read(&mut buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + Take { inner: self, limit } + } + + #[doc = r#" + Creates a "by reference" adaptor for this instance of `Read`. + + The returned adaptor also implements `Read` and will simply borrow this + current reader. + + # Examples + + [`File`][file]s implement `Read`: + + [file]: ../fs/struct.File.html + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; + + let mut f = File::open("foo.txt").await?; + let mut buffer = Vec::new(); + let mut other_buffer = Vec::new(); - handle.read(&mut buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn take(self, limit: u64) -> Take - where - Self: Sized, { - Take { inner: self, limit } - } + let reference = f.by_ref(); + + // read at most 5 bytes + reference.take(5).read_to_end(&mut buffer).await?; - #[doc = r#" - Creates a "by reference" adaptor for this instance of `Read`. + } // drop our &mut reference so we can use f again - The returned adaptor also implements `Read` and will simply borrow this - current reader. + // original file still usable, read the rest + f.read_to_end(&mut other_buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn by_ref(&mut self) -> &mut Self where Self: Sized { self } - # Examples - [`File`][file]s implement `Read`: + #[doc = r#" + Transforms this `Read` instance to a `Stream` over its bytes. - [file]: ../fs/struct.File.html + The returned type implements `Stream` where the `Item` is + `Result`. + The yielded item is `Ok` if a byte was successfully read and `Err` + otherwise. EOF is mapped to returning `None` from this iterator. - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; + # Examples - let mut f = File::open("foo.txt").await?; - let mut buffer = Vec::new(); - let mut other_buffer = Vec::new(); + [`File`][file]s implement `Read`: - { - let reference = f.by_ref(); + [file]: ../fs/struct.File.html - // read at most 5 bytes - reference.take(5).read_to_end(&mut buffer).await?; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; - } // drop our &mut reference so we can use f again + let f = File::open("foo.txt").await?; + let mut s = f.bytes(); - // original file still usable, read the rest - f.read_to_end(&mut other_buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn by_ref(&mut self) -> &mut Self where Self: Sized { self } - - - #[doc = r#" - Transforms this `Read` instance to a `Stream` over its bytes. - - The returned type implements `Stream` where the `Item` is - `Result`. - The yielded item is `Ok` if a byte was successfully read and `Err` - otherwise. EOF is mapped to returning `None` from this iterator. - - # Examples - - [`File`][file]s implement `Read`: - - [file]: ../fs/struct.File.html - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; - - let f = File::open("foo.txt").await?; - let mut s = f.bytes(); - - while let Some(byte) = s.next().await { - println!("{}", byte.unwrap()); - } - # - # Ok(()) }) } - ``` - "#] - fn bytes(self) -> Bytes where Self: Sized { - Bytes { inner: self } + while let Some(byte) = s.next().await { + println!("{}", byte.unwrap()); } + # + # Ok(()) }) } + ``` + "#] + fn bytes(self) -> Bytes where Self: Sized { + Bytes { inner: self } + } - #[doc = r#" - Creates an adaptor which will chain this stream with another. + #[doc = r#" + Creates an adaptor which will chain this stream with another. - The returned `Read` instance will first read all bytes from this object - until EOF is encountered. Afterwards the output is equivalent to the - output of `next`. + The returned `Read` instance will first read all bytes from this object + until EOF is encountered. Afterwards the output is equivalent to the + output of `next`. - # Examples + # Examples - [`File`][file]s implement `Read`: + [`File`][file]s implement `Read`: - [file]: ../fs/struct.File.html + [file]: ../fs/struct.File.html - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; - let f1 = File::open("foo.txt").await?; - let f2 = File::open("bar.txt").await?; + let f1 = File::open("foo.txt").await?; + let f2 = File::open("bar.txt").await?; - let mut handle = f1.chain(f2); - let mut buffer = String::new(); + let mut handle = f1.chain(f2); + let mut buffer = String::new(); - // read the value into a String. We could use any Read method here, - // this is just one example. - handle.read_to_string(&mut buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn chain(self, next: R) -> Chain where Self: Sized { - Chain { first: self, second: next, done_first: false } - } + // read the value into a String. We could use any Read method here, + // this is just one example. + handle.read_to_string(&mut buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn chain(self, next: R) -> Chain where Self: Sized { + Chain { first: self, second: next, done_first: false } } +} impl ReadExt for T {} diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index f2b609328..cb0b9e136 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -6,45 +6,45 @@ use crate::io::SeekFrom; pub use futures_io::AsyncSeek as Seek; +#[doc = r#" + Extension methods for [`Seek`]. + + [`Seek`]: ../trait.Seek.html +"#] +pub trait SeekExt: Seek { #[doc = r#" - Extension methods for [`Seek`]. + Seeks to a new position in a byte stream. + + Returns the new position in the byte stream. + + A seek beyond the end of stream is allowed, but behavior is defined by the + implementation. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::SeekFrom; + use async_std::prelude::*; + + let mut file = File::open("a.txt").await?; - [`Seek`]: ../trait.Seek.html + let file_len = file.seek(SeekFrom::End(0)).await?; + # + # Ok(()) }) } + ``` "#] - pub trait SeekExt: Seek { - #[doc = r#" - Seeks to a new position in a byte stream. - - Returns the new position in the byte stream. - - A seek beyond the end of stream is allowed, but behavior is defined by the - implementation. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::SeekFrom; - use async_std::prelude::*; - - let mut file = File::open("a.txt").await?; - - let file_len = file.seek(SeekFrom::End(0)).await?; - # - # Ok(()) }) } - ``` - "#] - fn seek( - &mut self, - pos: SeekFrom, - ) -> SeekFuture<'_, Self> - where - Self: Unpin, - { - SeekFuture { seeker: self, pos } - } + fn seek( + &mut self, + pos: SeekFrom, + ) -> SeekFuture<'_, Self> + where + Self: Unpin, + { + SeekFuture { seeker: self, pos } } +} impl SeekExt for T {} diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index d73c40d88..753e7e6aa 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -14,174 +14,174 @@ use crate::io::{self, IoSlice}; pub use futures_io::AsyncWrite as Write; +#[doc = r#" + Extension methods for [`Write`]. + + [`Write`]: ../trait.Write.html +"#] +pub trait WriteExt: Write { + #[doc = r#" + Writes some bytes into the byte stream. + + Returns the number of bytes written from the start of the buffer. + + If the return value is `Ok(n)` then it must be guaranteed that + `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying + object is no longer able to accept bytes and will likely not be able to in the + future as well, or that the buffer provided is empty. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; + + let mut file = File::create("a.txt").await?; + + let n = file.write(b"hello world").await?; + # + # Ok(()) }) } + ``` + "#] + fn write<'a>( + &'a mut self, + buf: &'a [u8], + ) -> WriteFuture<'a, Self> + where + Self: Unpin, + { + WriteFuture { writer: self, buf } + } + + #[doc = r#" + Flushes the stream to ensure that all buffered contents reach their destination. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; + + let mut file = File::create("a.txt").await?; + + file.write_all(b"hello world").await?; + file.flush().await?; + # + # Ok(()) }) } + ``` + "#] + fn flush(&mut self) -> FlushFuture<'_, Self> + where + Self: Unpin, + { + FlushFuture { writer: self } + } + + #[doc = r#" + Like [`write`], except that it writes from a slice of buffers. + + Data is copied from each buffer in order, with the final buffer read from possibly + being only partially consumed. This method must behave as a call to [`write`] with + the buffers concatenated would. + + The default implementation calls [`write`] with either the first nonempty buffer + provided, or an empty one if none exists. + + [`write`]: #tymethod.write + "#] + fn write_vectored<'a>( + &'a mut self, + bufs: &'a [IoSlice<'a>], + ) -> WriteVectoredFuture<'a, Self> + where + Self: Unpin, + { + WriteVectoredFuture { writer: self, bufs } + } + #[doc = r#" - Extension methods for [`Write`]. + Writes an entire buffer into the byte stream. + + This method will continuously call [`write`] until there is no more data to be + written or an error is returned. This method will not return until the entire + buffer has been successfully written or such an error occurs. + + [`write`]: #tymethod.write + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; + + let mut file = File::create("a.txt").await?; + + file.write_all(b"hello world").await?; + # + # Ok(()) }) } + ``` + + [`write`]: #tymethod.write + "#] + fn write_all<'a>( + &'a mut self, + buf: &'a [u8], + ) -> WriteAllFuture<'a, Self> + where + Self: Unpin, + { + WriteAllFuture { writer: self, buf } + } + + #[doc = r#" + Writes a formatted string into this writer, returning any error encountered. + + This method will continuously call [`write`] until there is no more data to be + written or an error is returned. This future will not resolve until the entire + buffer has been successfully written or such an error occurs. + + [`write`]: #tymethod.write + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::prelude::*; + use async_std::fs::File; + + let mut buffer = File::create("foo.txt").await?; - [`Write`]: ../trait.Write.html + // this call + write!(buffer, "{:.*}", 2, 1.234567).await?; + // turns into this: + buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?; + # + # Ok(()) }) } + ``` "#] - pub trait WriteExt: Write { - #[doc = r#" - Writes some bytes into the byte stream. - - Returns the number of bytes written from the start of the buffer. - - If the return value is `Ok(n)` then it must be guaranteed that - `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying - object is no longer able to accept bytes and will likely not be able to in the - future as well, or that the buffer provided is empty. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - let n = file.write(b"hello world").await?; - # - # Ok(()) }) } - ``` - "#] - fn write<'a>( - &'a mut self, - buf: &'a [u8], - ) -> WriteFuture<'a, Self> - where - Self: Unpin, - { - WriteFuture { writer: self, buf } - } - - #[doc = r#" - Flushes the stream to ensure that all buffered contents reach their destination. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - file.write_all(b"hello world").await?; - file.flush().await?; - # - # Ok(()) }) } - ``` - "#] - fn flush(&mut self) -> FlushFuture<'_, Self> - where - Self: Unpin, - { - FlushFuture { writer: self } - } - - #[doc = r#" - Like [`write`], except that it writes from a slice of buffers. - - Data is copied from each buffer in order, with the final buffer read from possibly - being only partially consumed. This method must behave as a call to [`write`] with - the buffers concatenated would. - - The default implementation calls [`write`] with either the first nonempty buffer - provided, or an empty one if none exists. - - [`write`]: #tymethod.write - "#] - fn write_vectored<'a>( - &'a mut self, - bufs: &'a [IoSlice<'a>], - ) -> WriteVectoredFuture<'a, Self> - where - Self: Unpin, - { - WriteVectoredFuture { writer: self, bufs } - } - - #[doc = r#" - Writes an entire buffer into the byte stream. - - This method will continuously call [`write`] until there is no more data to be - written or an error is returned. This method will not return until the entire - buffer has been successfully written or such an error occurs. - - [`write`]: #tymethod.write - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - file.write_all(b"hello world").await?; - # - # Ok(()) }) } - ``` - - [`write`]: #tymethod.write - "#] - fn write_all<'a>( - &'a mut self, - buf: &'a [u8], - ) -> WriteAllFuture<'a, Self> - where - Self: Unpin, - { - WriteAllFuture { writer: self, buf } - } - - #[doc = r#" - Writes a formatted string into this writer, returning any error encountered. - - This method will continuously call [`write`] until there is no more data to be - written or an error is returned. This future will not resolve until the entire - buffer has been successfully written or such an error occurs. - - [`write`]: #tymethod.write - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::prelude::*; - use async_std::fs::File; - - let mut buffer = File::create("foo.txt").await?; - - // this call - write!(buffer, "{:.*}", 2, 1.234567).await?; - // turns into this: - buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?; - # - # Ok(()) }) } - ``` - "#] - fn write_fmt<'a>( - &'a mut self, - fmt: std::fmt::Arguments<'_>, - ) -> WriteFmtFuture<'a, Self> - where - Self: Unpin, - { - // In order to not have to implement an async version of `fmt` including private types - // and all, we convert `Arguments` to a `Result>` and pass that to the Future. - // Doing an owned conversion saves us from juggling references. - let mut string = String::new(); - let res = std::fmt::write(&mut string, fmt) - .map(|_| string.into_bytes()) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error")); - WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } - } + fn write_fmt<'a>( + &'a mut self, + fmt: std::fmt::Arguments<'_>, + ) -> WriteFmtFuture<'a, Self> + where + Self: Unpin, + { + // In order to not have to implement an async version of `fmt` including private types + // and all, we convert `Arguments` to a `Result>` and pass that to the Future. + // Doing an owned conversion saves us from juggling references. + let mut string = String::new(); + let res = std::fmt::write(&mut string, fmt) + .map(|_| string.into_bytes()) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error")); + WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } } +} impl WriteExt for T {} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 25aadfac8..b0bf1f339 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -145,2166 +145,2166 @@ cfg_unstable! { pub use futures_core::stream::Stream as Stream; - #[doc = r#" - Extension methods for [`Stream`]. - - [`Stream`]: ../stream/trait.Stream.html - "#] - pub trait StreamExt: Stream { - #[doc = r#" - Advances the stream and returns the next value. +#[doc = r#" + Extension methods for [`Stream`]. - Returns [`None`] when iteration is finished. Individual stream implementations may - choose to resume iteration, and so calling `next()` again may or may not eventually - start returning more values. - - [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - - # Examples - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + [`Stream`]: ../stream/trait.Stream.html +"#] +pub trait StreamExt: Stream { + #[doc = r#" + Advances the stream and returns the next value. - let mut s = stream::once(7); + Returns [`None`] when iteration is finished. Individual stream implementations may + choose to resume iteration, and so calling `next()` again may or may not eventually + start returning more values. - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn next(&mut self) -> NextFuture<'_, Self> - where - Self: Unpin, - { - NextFuture { stream: self } - } + [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - #[doc = r#" - Creates a stream that yields its first `n` elements. + # Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let mut s = stream::once(7); - let mut s = stream::repeat(9).take(3); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn next(&mut self) -> NextFuture<'_, Self> + where + Self: Unpin, + { + NextFuture { stream: self } + } - while let Some(v) = s.next().await { - assert_eq!(v, 9); - } - # - # }) } - ``` - "#] - fn take(self, n: usize) -> Take - where - Self: Sized, - { - Take::new(self, n) - } + #[doc = r#" + Creates a stream that yields its first `n` elements. - #[doc = r#" - Creates a stream that yields elements based on a predicate. + # Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let mut s = stream::repeat(9).take(3); - let s = stream::from_iter(vec![1, 2, 3, 4]); - let mut s = s.take_while(|x| x < &3 ); - - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn take_while

(self, predicate: P) -> TakeWhile - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - TakeWhile::new(self, predicate) + while let Some(v) = s.next().await { + assert_eq!(v, 9); } + # + # }) } + ``` + "#] + fn take(self, n: usize) -> Take + where + Self: Sized, + { + Take::new(self, n) + } - #[doc = r#" - Limit the amount of items yielded per timeslice in a stream. - - This stream does not drop any items, but will only limit the rate at which items pass through. - # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; - use std::time::{Duration, Instant}; + #[doc = r#" + Creates a stream that yields elements based on a predicate. - let start = Instant::now(); + # Examples - // emit value every 5 milliseconds - let s = stream::interval(Duration::from_millis(5)).take(2); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - // throttle for 10 milliseconds - let mut s = s.throttle(Duration::from_millis(10)); + let s = stream::from_iter(vec![1, 2, 3, 4]); + let mut s = s.take_while(|x| x < &3 ); - s.next().await; - assert!(start.elapsed().as_millis() >= 5); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn take_while

(self, predicate: P) -> TakeWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + TakeWhile::new(self, predicate) + } - s.next().await; - assert!(start.elapsed().as_millis() >= 15); + #[doc = r#" + Limit the amount of items yielded per timeslice in a stream. - s.next().await; - assert!(start.elapsed().as_millis() >= 25); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn throttle(self, d: Duration) -> Throttle - where - Self: Sized, - { - Throttle::new(self, d) - } + This stream does not drop any items, but will only limit the rate at which items pass through. + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::time::{Duration, Instant}; - #[doc = r#" - Creates a stream that yields each `step`th element. + let start = Instant::now(); - # Panics + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)).take(2); - This method will panic if the given step is `0`. + // throttle for 10 milliseconds + let mut s = s.throttle(Duration::from_millis(10)); - # Examples + s.next().await; + assert!(start.elapsed().as_millis() >= 5); - Basic usage: + s.next().await; + assert!(start.elapsed().as_millis() >= 15); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + s.next().await; + assert!(start.elapsed().as_millis() >= 25); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn throttle(self, d: Duration) -> Throttle + where + Self: Sized, + { + Throttle::new(self, d) + } - let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); - let mut stepped = s.step_by(2); + #[doc = r#" + Creates a stream that yields each `step`th element. - assert_eq!(stepped.next().await, Some(0)); - assert_eq!(stepped.next().await, Some(2)); - assert_eq!(stepped.next().await, Some(4)); - assert_eq!(stepped.next().await, None); + # Panics - # - # }) } - ``` - "#] - fn step_by(self, step: usize) -> StepBy - where - Self: Sized, - { - StepBy::new(self, step) - } + This method will panic if the given step is `0`. - #[doc = r#" - Takes two streams and creates a new stream over both in sequence. + # Examples - # Examples + Basic usage: - Basic usage: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); + let mut stepped = s.step_by(2); - let first = stream::from_iter(vec![0u8, 1]); - let second = stream::from_iter(vec![2, 3]); - let mut c = first.chain(second); - - assert_eq!(c.next().await, Some(0)); - assert_eq!(c.next().await, Some(1)); - assert_eq!(c.next().await, Some(2)); - assert_eq!(c.next().await, Some(3)); - assert_eq!(c.next().await, None); - - # - # }) } - ``` - "#] - fn chain(self, other: U) -> Chain - where - Self: Sized, - U: Stream + Sized, - { - Chain::new(self, other) - } + assert_eq!(stepped.next().await, Some(0)); + assert_eq!(stepped.next().await, Some(2)); + assert_eq!(stepped.next().await, Some(4)); + assert_eq!(stepped.next().await, None); - #[doc = r#" - Creates an stream which copies all of its elements. + # + # }) } + ``` + "#] + fn step_by(self, step: usize) -> StepBy + where + Self: Sized, + { + StepBy::new(self, step) + } - # Examples + #[doc = r#" + Takes two streams and creates a new stream over both in sequence. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Basic usage: - let v = stream::from_iter(vec![&1, &2, &3]); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let mut v_cloned = v.cloned(); + let first = stream::from_iter(vec![0u8, 1]); + let second = stream::from_iter(vec![2, 3]); + let mut c = first.chain(second); - assert_eq!(v_cloned.next().await, Some(1)); - assert_eq!(v_cloned.next().await, Some(2)); - assert_eq!(v_cloned.next().await, Some(3)); - assert_eq!(v_cloned.next().await, None); - - # - # }) } - ``` - "#] - fn cloned<'a, T>(self) -> Cloned - where - Self: Sized + Stream, - T: Clone + 'a, - { - Cloned::new(self) - } + assert_eq!(c.next().await, Some(0)); + assert_eq!(c.next().await, Some(1)); + assert_eq!(c.next().await, Some(2)); + assert_eq!(c.next().await, Some(3)); + assert_eq!(c.next().await, None); + # + # }) } + ``` + "#] + fn chain(self, other: U) -> Chain + where + Self: Sized, + U: Stream + Sized, + { + Chain::new(self, other) + } #[doc = r#" - Creates an stream which copies all of its elements. - - # Examples - - Basic usage: - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Creates an stream which copies all of its elements. - let s = stream::from_iter(vec![&1, &2, &3]); - let mut s_copied = s.copied(); - - assert_eq!(s_copied.next().await, Some(1)); - assert_eq!(s_copied.next().await, Some(2)); - assert_eq!(s_copied.next().await, Some(3)); - assert_eq!(s_copied.next().await, None); - # - # }) } - ``` - "#] - fn copied<'a, T>(self) -> Copied - where - Self: Sized + Stream, - T: Copy + 'a, - { - Copied::new(self) - } + # Examples - #[doc = r#" - Creates a stream that yields the provided values infinitely and in order. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let v = stream::from_iter(vec![&1, &2, &3]); - ``` - # async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let mut v_cloned = v.cloned(); - let mut s = stream::once(7).cycle(); - - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, Some(7)); - # - # }) - ``` - "#] - fn cycle(self) -> Cycle - where - Self: Clone + Sized, - { - Cycle::new(self) - } + assert_eq!(v_cloned.next().await, Some(1)); + assert_eq!(v_cloned.next().await, Some(2)); + assert_eq!(v_cloned.next().await, Some(3)); + assert_eq!(v_cloned.next().await, None); - #[doc = r#" - Creates a stream that gives the current element's count as well as the next value. + # + # }) } + ``` + "#] + fn cloned<'a, T>(self) -> Cloned + where + Self: Sized + Stream, + T: Clone + 'a, + { + Cloned::new(self) + } - # Overflow behaviour. - This combinator does no guarding against overflows. + #[doc = r#" + Creates an stream which copies all of its elements. - # Examples + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Basic usage: - let s = stream::from_iter(vec!['a', 'b', 'c']); - let mut s = s.enumerate(); - - assert_eq!(s.next().await, Some((0, 'a'))); - assert_eq!(s.next().await, Some((1, 'b'))); - assert_eq!(s.next().await, Some((2, 'c'))); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn enumerate(self) -> Enumerate - where - Self: Sized, - { - Enumerate::new(self) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Creates a stream that is delayed before it starts yielding items. + let s = stream::from_iter(vec![&1, &2, &3]); + let mut s_copied = s.copied(); - # Examples + assert_eq!(s_copied.next().await, Some(1)); + assert_eq!(s_copied.next().await, Some(2)); + assert_eq!(s_copied.next().await, Some(3)); + assert_eq!(s_copied.next().await, None); + # + # }) } + ``` + "#] + fn copied<'a, T>(self) -> Copied + where + Self: Sized + Stream, + T: Copy + 'a, + { + Copied::new(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; - use std::time::{Duration, Instant}; - - let start = Instant::now(); - let mut s = stream::from_iter(vec![0u8, 1, 2]).delay(Duration::from_millis(200)); - - assert_eq!(s.next().await, Some(0)); - // The first time will take more than 200ms due to delay. - assert!(start.elapsed().as_millis() >= 200); - - assert_eq!(s.next().await, Some(1)); - // There will be no delay after the first time. - assert!(start.elapsed().as_millis() < 400); - - assert_eq!(s.next().await, Some(2)); - assert!(start.elapsed().as_millis() < 400); - - assert_eq!(s.next().await, None); - assert!(start.elapsed().as_millis() < 400); - # - # }) } - ``` - "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn delay(self, dur: std::time::Duration) -> Delay - where - Self: Sized, - { - Delay::new(self, dur) - } + #[doc = r#" + Creates a stream that yields the provided values infinitely and in order. - #[doc = r#" - Takes a closure and creates a stream that calls that closure on every element of this stream. + # Examples - # Examples + Basic usage: - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + ``` + # async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let s = stream::from_iter(vec![1, 2, 3]); - let mut s = s.map(|x| 2 * x); + let mut s = stream::once(7).cycle(); - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, Some(4)); - assert_eq!(s.next().await, Some(6)); - assert_eq!(s.next().await, None); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + # + # }) + ``` + "#] + fn cycle(self) -> Cycle + where + Self: Clone + Sized, + { + Cycle::new(self) + } - # - # }) } - ``` - "#] - fn map(self, f: F) -> Map - where - Self: Sized, - F: FnMut(Self::Item) -> B, - { - Map::new(self, f) - } + #[doc = r#" + Creates a stream that gives the current element's count as well as the next value. - #[doc = r#" - A combinator that does something with each element in the stream, passing the value - on. + # Overflow behaviour. - # Examples + This combinator does no guarding against overflows. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let s = stream::from_iter(vec![1, 2, 3, 4, 5]); - - let sum = s - .inspect(|x| println!("about to filter {}", x)) - .filter(|x| x % 2 == 0) - .inspect(|x| println!("made it through filter: {}", x)) - .fold(0, |sum, i| sum + i) - .await; - - assert_eq!(sum, 6); - # - # }) } - ``` - "#] - fn inspect(self, f: F) -> Inspect - where - Self: Sized, - F: FnMut(&Self::Item), - { - Inspect::new(self, f) - } + let s = stream::from_iter(vec!['a', 'b', 'c']); + let mut s = s.enumerate(); - #[doc = r#" - Returns the last element of the stream. + assert_eq!(s.next().await, Some((0, 'a'))); + assert_eq!(s.next().await, Some((1, 'b'))); + assert_eq!(s.next().await, Some((2, 'c'))); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate::new(self) + } - # Examples + #[doc = r#" + Creates a stream that is delayed before it starts yielding items. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::time::{Duration, Instant}; - let s = stream::from_iter(vec![1, 2, 3]); + let start = Instant::now(); + let mut s = stream::from_iter(vec![0u8, 1, 2]).delay(Duration::from_millis(200)); - let last = s.last().await; - assert_eq!(last, Some(3)); - # - # }) } - ``` + assert_eq!(s.next().await, Some(0)); + // The first time will take more than 200ms due to delay. + assert!(start.elapsed().as_millis() >= 200); - An empty stream will return `None`: - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream; - use crate::async_std::prelude::*; - - let s = stream::empty::<()>(); - - let last = s.last().await; - assert_eq!(last, None); - # - # }) } - ``` - "#] - fn last( - self, - ) -> LastFuture - where - Self: Sized, - { - LastFuture::new(self) - } + assert_eq!(s.next().await, Some(1)); + // There will be no delay after the first time. + assert!(start.elapsed().as_millis() < 400); - #[doc = r#" - Creates a stream which ends after the first `None`. + assert_eq!(s.next().await, Some(2)); + assert!(start.elapsed().as_millis() < 400); - After a stream returns `None`, future calls may or may not yield `Some(T)` again. - `fuse()` adapts an iterator, ensuring that after a `None` is given, it will always - return `None` forever. + assert_eq!(s.next().await, None); + assert!(start.elapsed().as_millis() < 400); + # + # }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn delay(self, dur: std::time::Duration) -> Delay + where + Self: Sized, + { + Delay::new(self, dur) + } - # Examples + #[doc = r#" + Takes a closure and creates a stream that calls that closure on every element of this stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + # Examples - let mut s = stream::once(1).fuse(); - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, None); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn fuse(self) -> Fuse - where - Self: Sized, - { - Fuse::new(self) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Creates a stream that uses a predicate to determine if an element should be yielded. + let s = stream::from_iter(vec![1, 2, 3]); + let mut s = s.map(|x| 2 * x); - # Examples + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, Some(4)); + assert_eq!(s.next().await, Some(6)); + assert_eq!(s.next().await, None); - Basic usage: + # + # }) } + ``` + "#] + fn map(self, f: F) -> Map + where + Self: Sized, + F: FnMut(Self::Item) -> B, + { + Map::new(self, f) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + A combinator that does something with each element in the stream, passing the value + on. - let s = stream::from_iter(vec![1, 2, 3, 4]); - let mut s = s.filter(|i| i % 2 == 0); - - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, Some(4)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn filter

(self, predicate: P) -> Filter - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - Filter::new(self, predicate) - } + # Examples - #[doc= r#" - Creates an stream that works like map, but flattens nested structure. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let s = stream::from_iter(vec![1, 2, 3, 4, 5]); - ``` - # async_std::task::block_on(async { + let sum = s + .inspect(|x| println!("about to filter {}", x)) + .filter(|x| x % 2 == 0) + .inspect(|x| println!("made it through filter: {}", x)) + .fold(0, |sum, i| sum + i) + .await; - use async_std::prelude::*; - use async_std::stream; + assert_eq!(sum, 6); + # + # }) } + ``` + "#] + fn inspect(self, f: F) -> Inspect + where + Self: Sized, + F: FnMut(&Self::Item), + { + Inspect::new(self, f) + } - let words = stream::from_iter(&["alpha", "beta", "gamma"]); - - let merged: String = words - .flat_map(|s| stream::from_iter(s.chars())) - .collect().await; - assert_eq!(merged, "alphabetagamma"); - - let d3 = stream::from_iter(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); - let d1: Vec<_> = d3 - .flat_map(|item| stream::from_iter(item)) - .flat_map(|item| stream::from_iter(item)) - .collect().await; - - assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); - # }); - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flat_map(self, f: F) -> FlatMap - where - Self: Sized, - U: IntoStream, - F: FnMut(Self::Item) -> U, - { - FlatMap::new(self, f) - } + #[doc = r#" + Returns the last element of the stream. - #[doc = r#" - Creates an stream that flattens nested structure. + # Examples - # Examples + Basic usage: - Basic usage: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # async_std::task::block_on(async { + let s = stream::from_iter(vec![1, 2, 3]); - use async_std::prelude::*; - use async_std::stream; + let last = s.last().await; + assert_eq!(last, Some(3)); + # + # }) } + ``` - let inner1 = stream::from_iter(vec![1u8,2,3]); - let inner2 = stream::from_iter(vec![4u8,5,6]); - let s = stream::from_iter(vec![inner1, inner2]); + An empty stream will return `None`: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use crate::async_std::prelude::*; - let v: Vec<_> = s.flatten().collect().await; + let s = stream::empty::<()>(); - assert_eq!(v, vec![1,2,3,4,5,6]); + let last = s.last().await; + assert_eq!(last, None); + # + # }) } + ``` + "#] + fn last( + self, + ) -> LastFuture + where + Self: Sized, + { + LastFuture::new(self) + } - # }); - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flatten(self) -> Flatten - where - Self: Sized, - Self::Item: IntoStream, - { - Flatten::new(self) - } + #[doc = r#" + Creates a stream which ends after the first `None`. + + After a stream returns `None`, future calls may or may not yield `Some(T)` again. + `fuse()` adapts an iterator, ensuring that after a `None` is given, it will always + return `None` forever. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::once(1).fuse(); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, None); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn fuse(self) -> Fuse + where + Self: Sized, + { + Fuse::new(self) + } - #[doc = r#" - Both filters and maps a stream. + #[doc = r#" + Creates a stream that uses a predicate to determine if an element should be yielded. - # Examples + # Examples - Basic usage: + Basic usage: - ``` - # fn main() { async_std::task::block_on(async { - # + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - use async_std::prelude::*; - use async_std::stream; + let s = stream::from_iter(vec![1, 2, 3, 4]); + let mut s = s.filter(|i| i % 2 == 0); - let s = stream::from_iter(vec!["1", "lol", "3", "NaN", "5"]); + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, Some(4)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn filter

(self, predicate: P) -> Filter + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + Filter::new(self, predicate) + } - let mut parsed = s.filter_map(|a| a.parse::().ok()); + #[doc= r#" + Creates an stream that works like map, but flattens nested structure. - let one = parsed.next().await; - assert_eq!(one, Some(1)); + # Examples - let three = parsed.next().await; - assert_eq!(three, Some(3)); + Basic usage: - let five = parsed.next().await; - assert_eq!(five, Some(5)); + ``` + # async_std::task::block_on(async { - let end = parsed.next().await; - assert_eq!(end, None); - # - # }) } - ``` - "#] - fn filter_map(self, f: F) -> FilterMap - where - Self: Sized, - F: FnMut(Self::Item) -> Option, - { - FilterMap::new(self, f) - } + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Returns the element that gives the minimum value with respect to the - specified key function. If several elements are equally minimum, - the first element is returned. If the stream is empty, `None` is returned. + let words = stream::from_iter(&["alpha", "beta", "gamma"]); - # Examples + let merged: String = words + .flat_map(|s| stream::from_iter(s.chars())) + .collect().await; + assert_eq!(merged, "alphabetagamma"); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let d3 = stream::from_iter(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + let d1: Vec<_> = d3 + .flat_map(|item| stream::from_iter(item)) + .flat_map(|item| stream::from_iter(item)) + .collect().await; - let s = stream::from_iter(vec![-1isize, 2, -3]); - - let min = s.clone().min_by_key(|x| x.abs()).await; - assert_eq!(min, Some(-1)); - - let min = stream::empty::().min_by_key(|x| x.abs()).await; - assert_eq!(min, None); - # - # }) } - ``` - "#] - fn min_by_key( - self, - key_by: F, - ) -> MinByKeyFuture + assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flat_map(self, f: F) -> FlatMap where Self: Sized, - B: Ord, - F: FnMut(&Self::Item) -> B, - { - MinByKeyFuture::new(self, key_by) - } + U: IntoStream, + F: FnMut(Self::Item) -> U, + { + FlatMap::new(self, f) + } - #[doc = r#" - Returns the element that gives the maximum value with respect to the - specified key function. If several elements are equally maximum, - the first element is returned. If the stream is empty, `None` is returned. + #[doc = r#" + Creates an stream that flattens nested structure. - # Examples + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Basic usage: - let s = stream::from_iter(vec![-3_i32, 0, 1, 5, -10]); - - let max = s.clone().max_by_key(|x| x.abs()).await; - assert_eq!(max, Some(-10)); - - let max = stream::empty::().max_by_key(|x| x.abs()).await; - assert_eq!(max, None); - # - # }) } - ``` - "#] - fn max_by_key( - self, - key_by: F, - ) -> MaxByKeyFuture - where - Self: Sized, - B: Ord, - F: FnMut(&Self::Item) -> B, - { - MaxByKeyFuture::new(self, key_by) - } + ``` + # async_std::task::block_on(async { - #[doc = r#" - Returns the element that gives the minimum value with respect to the - specified comparison function. If several elements are equally minimum, - the first element is returned. If the stream is empty, `None` is returned. + use async_std::prelude::*; + use async_std::stream; - # Examples + let inner1 = stream::from_iter(vec![1u8,2,3]); + let inner2 = stream::from_iter(vec![4u8,5,6]); + let s = stream::from_iter(vec![inner1, inner2]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let v: Vec<_> = s.flatten().collect().await; - let s = stream::from_iter(vec![1u8, 2, 3]); + assert_eq!(v, vec![1,2,3,4,5,6]); - let min = s.clone().min_by(|x, y| x.cmp(y)).await; - assert_eq!(min, Some(1)); + # }); + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flatten(self) -> Flatten + where + Self: Sized, + Self::Item: IntoStream, + { + Flatten::new(self) + } - let min = s.min_by(|x, y| y.cmp(x)).await; - assert_eq!(min, Some(3)); + #[doc = r#" + Both filters and maps a stream. - let min = stream::empty::().min_by(|x, y| x.cmp(y)).await; - assert_eq!(min, None); - # - # }) } - ``` - "#] - fn min_by( - self, - compare: F, - ) -> MinByFuture - where - Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, - { - MinByFuture::new(self, compare) - } + # Examples - #[doc = r#" - Returns the element that gives the maximum value. If several elements are equally maximum, - the first element is returned. If the stream is empty, `None` is returned. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + use async_std::prelude::*; + use async_std::stream; - let s = stream::from_iter(vec![1usize, 2, 3]); + let s = stream::from_iter(vec!["1", "lol", "3", "NaN", "5"]); - let max = s.clone().max().await; - assert_eq!(max, Some(3)); + let mut parsed = s.filter_map(|a| a.parse::().ok()); - let max = stream::empty::().max().await; - assert_eq!(max, None); - # - # }) } - ``` - "#] - fn max( - self, - ) -> MaxFuture - where - Self: Sized, - Self::Item: Ord, - { - MaxFuture::new(self) - } + let one = parsed.next().await; + assert_eq!(one, Some(1)); - #[doc = r#" - Returns the element that gives the minimum value. If several elements are equally minimum, - the first element is returned. If the stream is empty, `None` is returned. + let three = parsed.next().await; + assert_eq!(three, Some(3)); - # Examples + let five = parsed.next().await; + assert_eq!(five, Some(5)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let end = parsed.next().await; + assert_eq!(end, None); + # + # }) } + ``` + "#] + fn filter_map(self, f: F) -> FilterMap + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + FilterMap::new(self, f) + } - let s = stream::from_iter(vec![1usize, 2, 3]); + #[doc = r#" + Returns the element that gives the minimum value with respect to the + specified key function. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. - let min = s.clone().min().await; - assert_eq!(min, Some(1)); + # Examples - let min = stream::empty::().min().await; - assert_eq!(min, None); - # - # }) } - ``` - "#] - fn min( - self, - ) -> MinFuture - where - Self: Sized, - Self::Item: Ord, - { - MinFuture::new(self) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Returns the element that gives the maximum value with respect to the - specified comparison function. If several elements are equally maximum, - the first element is returned. If the stream is empty, `None` is returned. + let s = stream::from_iter(vec![-1isize, 2, -3]); - # Examples + let min = s.clone().min_by_key(|x| x.abs()).await; + assert_eq!(min, Some(-1)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let min = stream::empty::().min_by_key(|x| x.abs()).await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by_key( + self, + key_by: F, + ) -> MinByKeyFuture + where + Self: Sized, + B: Ord, + F: FnMut(&Self::Item) -> B, + { + MinByKeyFuture::new(self, key_by) + } - let s = stream::from_iter(vec![1u8, 2, 3]); + #[doc = r#" + Returns the element that gives the maximum value with respect to the + specified key function. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. - let max = s.clone().max_by(|x, y| x.cmp(y)).await; - assert_eq!(max, Some(3)); + # Examples - let max = s.max_by(|x, y| y.cmp(x)).await; - assert_eq!(max, Some(1)); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let max = stream::empty::().max_by(|x, y| x.cmp(y)).await; - assert_eq!(max, None); - # - # }) } - ``` - "#] - fn max_by( - self, - compare: F, - ) -> MaxByFuture - where - Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, - { - MaxByFuture::new(self, compare) - } + let s = stream::from_iter(vec![-3_i32, 0, 1, 5, -10]); - #[doc = r#" - Returns the nth element of the stream. + let max = s.clone().max_by_key(|x| x.abs()).await; + assert_eq!(max, Some(-10)); - # Examples + let max = stream::empty::().max_by_key(|x| x.abs()).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by_key( + self, + key_by: F, + ) -> MaxByKeyFuture + where + Self: Sized, + B: Ord, + F: FnMut(&Self::Item) -> B, + { + MaxByKeyFuture::new(self, key_by) + } - Basic usage: + #[doc = r#" + Returns the element that gives the minimum value with respect to the + specified comparison function. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + # Examples - let mut s = stream::from_iter(vec![1u8, 2, 3]); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let second = s.nth(1).await; - assert_eq!(second, Some(2)); - # - # }) } - ``` - Calling `nth()` multiple times: + let s = stream::from_iter(vec![1u8, 2, 3]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream; - use async_std::prelude::*; + let min = s.clone().min_by(|x, y| x.cmp(y)).await; + assert_eq!(min, Some(1)); - let mut s = stream::from_iter(vec![1u8, 2, 3]); + let min = s.min_by(|x, y| y.cmp(x)).await; + assert_eq!(min, Some(3)); - let second = s.nth(0).await; - assert_eq!(second, Some(1)); + let min = stream::empty::().min_by(|x, y| x.cmp(y)).await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by( + self, + compare: F, + ) -> MinByFuture + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MinByFuture::new(self, compare) + } - let second = s.nth(0).await; - assert_eq!(second, Some(2)); - # - # }) } - ``` - Returning `None` if the stream finished before returning `n` elements: - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Returns the element that gives the maximum value. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. - let mut s = stream::from_iter(vec![1u8, 2, 3]); - - let fourth = s.nth(4).await; - assert_eq!(fourth, None); - # - # }) } - ``` - "#] - fn nth( - &mut self, - n: usize, - ) -> NthFuture<'_, Self> - where - Self: Unpin + Sized, - { - NthFuture::new(self, n) - } + # Examples - #[doc = r#" - Tests if every element of the stream matches a predicate. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - `all()` takes a closure that returns `true` or `false`. It applies - this closure to each element of the stream, and if they all return - `true`, then so does `all()`. If any of them return `false`, it - returns `false`. + let s = stream::from_iter(vec![1usize, 2, 3]); - `all()` is short-circuiting; in other words, it will stop processing - as soon as it finds a `false`, given that no matter what else happens, - the result will also be `false`. + let max = s.clone().max().await; + assert_eq!(max, Some(3)); - An empty stream returns `true`. + let max = stream::empty::().max().await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max( + self, + ) -> MaxFuture + where + Self: Sized, + Self::Item: Ord, + { + MaxFuture::new(self) + } - # Examples + #[doc = r#" + Returns the element that gives the minimum value. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let mut s = stream::repeat::(42).take(3); - assert!(s.all(|x| x == 42).await); + let s = stream::from_iter(vec![1usize, 2, 3]); - # - # }) } - ``` + let min = s.clone().min().await; + assert_eq!(min, Some(1)); - Empty stream: + let min = stream::empty::().min().await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min( + self, + ) -> MinFuture + where + Self: Sized, + Self::Item: Ord, + { + MinFuture::new(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Returns the element that gives the maximum value with respect to the + specified comparison function. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. - let mut s = stream::empty::(); - assert!(s.all(|_| false).await); - # - # }) } - ``` - "#] - #[inline] - fn all( - &mut self, - f: F, - ) -> AllFuture<'_, Self, F, Self::Item> - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> bool, - { - AllFuture::new(self, f) - } + # Examples - #[doc = r#" - Searches for an element in a stream that satisfies a predicate. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s = stream::from_iter(vec![1u8, 2, 3]); - Basic usage: + let max = s.clone().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, Some(3)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let max = s.max_by(|x, y| y.cmp(x)).await; + assert_eq!(max, Some(1)); - let mut s = stream::from_iter(vec![1u8, 2, 3]); - let res = s.find(|x| *x == 2).await; - assert_eq!(res, Some(2)); - # - # }) } - ``` + let max = stream::empty::().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by( + self, + compare: F, + ) -> MaxByFuture + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MaxByFuture::new(self, compare) + } - Resuming after a first find: + #[doc = r#" + Returns the nth element of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let second = s.nth(1).await; + assert_eq!(second, Some(2)); + # + # }) } + ``` + Calling `nth()` multiple times: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use async_std::prelude::*; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let second = s.nth(0).await; + assert_eq!(second, Some(1)); + + let second = s.nth(0).await; + assert_eq!(second, Some(2)); + # + # }) } + ``` + Returning `None` if the stream finished before returning `n` elements: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let fourth = s.nth(4).await; + assert_eq!(fourth, None); + # + # }) } + ``` + "#] + fn nth( + &mut self, + n: usize, + ) -> NthFuture<'_, Self> + where + Self: Unpin + Sized, + { + NthFuture::new(self, n) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Tests if every element of the stream matches a predicate. - let mut s= stream::from_iter(vec![1, 2, 3]); - let res = s.find(|x| *x == 2).await; - assert_eq!(res, Some(2)); - - let next = s.next().await; - assert_eq!(next, Some(3)); - # - # }) } - ``` - "#] - fn find

( - &mut self, - p: P, - ) -> FindFuture<'_, Self, P> - where - Self: Unpin + Sized, - P: FnMut(&Self::Item) -> bool, - { - FindFuture::new(self, p) - } + `all()` takes a closure that returns `true` or `false`. It applies + this closure to each element of the stream, and if they all return + `true`, then so does `all()`. If any of them return `false`, it + returns `false`. - #[doc = r#" - Applies function to the elements of stream and returns the first non-none result. + `all()` is short-circuiting; in other words, it will stop processing + as soon as it finds a `false`, given that no matter what else happens, + the result will also be `false`. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + An empty stream returns `true`. - let mut s = stream::from_iter(vec!["lol", "NaN", "2", "5"]); - let first_number = s.find_map(|s| s.parse().ok()).await; - - assert_eq!(first_number, Some(2)); - # - # }) } - ``` - "#] - fn find_map( - &mut self, - f: F, - ) -> FindMapFuture<'_, Self, F> - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> Option, - { - FindMapFuture::new(self, f) - } + # Examples - #[doc = r#" - A combinator that applies a function to every element in a stream - producing a single, final value. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let mut s = stream::repeat::(42).take(3); + assert!(s.all(|x| x == 42).await); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + # + # }) } + ``` - let s = stream::from_iter(vec![1u8, 2, 3]); - let sum = s.fold(0, |acc, x| acc + x).await; - - assert_eq!(sum, 6); - # - # }) } - ``` - "#] - fn fold( - self, - init: B, - f: F, - ) -> FoldFuture - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - FoldFuture::new(self, init, f) - } + Empty stream: - #[doc = r#" - A combinator that applies a function to every element in a stream - creating two collections from it. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let mut s = stream::empty::(); + assert!(s.all(|_| false).await); + # + # }) } + ``` + "#] + #[inline] + fn all( + &mut self, + f: F, + ) -> AllFuture<'_, Self, F, Self::Item> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + AllFuture::new(self, f) + } - Basic usage: + #[doc = r#" + Searches for an element in a stream that satisfies a predicate. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + let res = s.find(|x| *x == 2).await; + assert_eq!(res, Some(2)); + # + # }) } + ``` + + Resuming after a first find: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s= stream::from_iter(vec![1, 2, 3]); + let res = s.find(|x| *x == 2).await; + assert_eq!(res, Some(2)); + + let next = s.next().await; + assert_eq!(next, Some(3)); + # + # }) } + ``` + "#] + fn find

( + &mut self, + p: P, + ) -> FindFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(&Self::Item) -> bool, + { + FindFuture::new(self, p) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Applies function to the elements of stream and returns the first non-none result. - let (even, odd): (Vec, Vec) = stream::from_iter(vec![1, 2, 3]) - .partition(|&n| n % 2 == 0).await; - - assert_eq!(even, vec![2]); - assert_eq!(odd, vec![1, 3]); - - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn partition( - self, - f: F, - ) -> PartitionFuture - where - Self: Sized, - F: FnMut(&Self::Item) -> bool, - B: Default + Extend, - { - PartitionFuture::new(self, f) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Call a closure on each element of the stream. + let mut s = stream::from_iter(vec!["lol", "NaN", "2", "5"]); + let first_number = s.find_map(|s| s.parse().ok()).await; - # Examples + assert_eq!(first_number, Some(2)); + # + # }) } + ``` + "#] + fn find_map( + &mut self, + f: F, + ) -> FindMapFuture<'_, Self, F> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> Option, + { + FindMapFuture::new(self, f) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; - use std::sync::mpsc::channel; + #[doc = r#" + A combinator that applies a function to every element in a stream + producing a single, final value. - let (tx, rx) = channel(); + # Examples - let s = stream::from_iter(vec![1usize, 2, 3]); - let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await; + Basic usage: - let v: Vec<_> = rx.iter().collect(); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - assert_eq!(v, vec![1, 2, 3]); - # - # }) } - ``` - "#] - fn for_each( - self, - f: F, - ) -> ForEachFuture - where - Self: Sized, - F: FnMut(Self::Item), - { - ForEachFuture::new(self, f) - } + let s = stream::from_iter(vec![1u8, 2, 3]); + let sum = s.fold(0, |acc, x| acc + x).await; - #[doc = r#" - Tests if any element of the stream matches a predicate. + assert_eq!(sum, 6); + # + # }) } + ``` + "#] + fn fold( + self, + init: B, + f: F, + ) -> FoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + FoldFuture::new(self, init, f) + } - `any()` takes a closure that returns `true` or `false`. It applies - this closure to each element of the stream, and if any of them return - `true`, then so does `any()`. If they all return `false`, it - returns `false`. + #[doc = r#" + A combinator that applies a function to every element in a stream + creating two collections from it. - `any()` is short-circuiting; in other words, it will stop processing - as soon as it finds a `true`, given that no matter what else happens, - the result will also be `true`. + # Examples - An empty stream returns `false`. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let (even, odd): (Vec, Vec) = stream::from_iter(vec![1, 2, 3]) + .partition(|&n| n % 2 == 0).await; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + assert_eq!(even, vec![2]); + assert_eq!(odd, vec![1, 3]); - let mut s = stream::repeat::(42).take(3); - assert!(s.any(|x| x == 42).await); - # - # }) } - ``` + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn partition( + self, + f: F, + ) -> PartitionFuture + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + B: Default + Extend, + { + PartitionFuture::new(self, f) + } - Empty stream: + #[doc = r#" + Call a closure on each element of the stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + # Examples - let mut s = stream::empty::(); - assert!(!s.any(|_| false).await); - # - # }) } - ``` - "#] - #[inline] - fn any( - &mut self, - f: F, - ) -> AnyFuture<'_, Self, F, Self::Item> - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> bool, - { - AnyFuture::new(self, f) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::sync::mpsc::channel; - #[doc = r#" - Borrows an stream, rather than consuming it. + let (tx, rx) = channel(); - This is useful to allow applying stream adaptors while still retaining ownership of the original stream. + let s = stream::from_iter(vec![1usize, 2, 3]); + let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await; - # Examples + let v: Vec<_> = rx.iter().collect(); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + assert_eq!(v, vec![1, 2, 3]); + # + # }) } + ``` + "#] + fn for_each( + self, + f: F, + ) -> ForEachFuture + where + Self: Sized, + F: FnMut(Self::Item), + { + ForEachFuture::new(self, f) + } - let a = vec![1isize, 2, 3]; + #[doc = r#" + Tests if any element of the stream matches a predicate. - let stream = stream::from_iter(a); + `any()` takes a closure that returns `true` or `false`. It applies + this closure to each element of the stream, and if any of them return + `true`, then so does `any()`. If they all return `false`, it + returns `false`. - let sum: isize = stream.take(5).sum().await; + `any()` is short-circuiting; in other words, it will stop processing + as soon as it finds a `true`, given that no matter what else happens, + the result will also be `true`. - assert_eq!(sum, 6); + An empty stream returns `false`. - // if we try to use stream again, it won't work. The following line - // gives error: use of moved value: `stream` - // assert_eq!(stream.next(), None); + # Examples - // let's try that again - let a = vec![1isize, 2, 3]; + Basic usage: - let mut stream = stream::from_iter(a); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - // instead, we add in a .by_ref() - let sum: isize = stream.by_ref().take(2).sum().await; + let mut s = stream::repeat::(42).take(3); + assert!(s.any(|x| x == 42).await); + # + # }) } + ``` - assert_eq!(sum, 3); + Empty stream: - // now this is just fine: - assert_eq!(stream.next().await, Some(3)); - assert_eq!(stream.next().await, None); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn by_ref(&mut self) -> &mut Self { - self - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - A stream adaptor similar to [`fold`] that holds internal state and produces a new - stream. + let mut s = stream::empty::(); + assert!(!s.any(|_| false).await); + # + # }) } + ``` + "#] + #[inline] + fn any( + &mut self, + f: F, + ) -> AnyFuture<'_, Self, F, Self::Item> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + AnyFuture::new(self, f) + } - [`fold`]: #method.fold + #[doc = r#" + Borrows an stream, rather than consuming it. - `scan()` takes two arguments: an initial value which seeds the internal state, and - a closure with two arguments, the first being a mutable reference to the internal - state and the second a stream element. The closure can assign to the internal state - to share state between iterations. + This is useful to allow applying stream adaptors while still retaining ownership of the original stream. - On iteration, the closure will be applied to each element of the stream and the - return value from the closure, an `Option`, is yielded by the stream. + # Examples - ## Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let a = vec![1isize, 2, 3]; - let s = stream::from_iter(vec![1isize, 2, 3]); - let mut s = s.scan(1, |state, x| { - *state = *state * x; - Some(-*state) - }); - - assert_eq!(s.next().await, Some(-1)); - assert_eq!(s.next().await, Some(-2)); - assert_eq!(s.next().await, Some(-6)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - #[inline] - fn scan(self, initial_state: St, f: F) -> Scan - where - Self: Sized, - F: FnMut(&mut St, Self::Item) -> Option, - { - Scan::new(self, initial_state, f) - } + let stream = stream::from_iter(a); - #[doc = r#" - Combinator that `skip`s elements based on a predicate. + let sum: isize = stream.take(5).sum().await; - Takes a closure argument. It will call this closure on every element in - the stream and ignore elements until it returns `false`. + assert_eq!(sum, 6); - After `false` is returned, `SkipWhile`'s job is over and all further - elements in the strem are yielded. + // if we try to use stream again, it won't work. The following line + // gives error: use of moved value: `stream` + // assert_eq!(stream.next(), None); - ## Examples + // let's try that again + let a = vec![1isize, 2, 3]; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let mut stream = stream::from_iter(a); - let a = stream::from_iter(vec![-1i32, 0, 1]); - let mut s = a.skip_while(|x| x.is_negative()); - - assert_eq!(s.next().await, Some(0)); - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn skip_while

(self, predicate: P) -> SkipWhile - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - SkipWhile::new(self, predicate) - } + // instead, we add in a .by_ref() + let sum: isize = stream.by_ref().take(2).sum().await; - #[doc = r#" - Creates a combinator that skips the first `n` elements. + assert_eq!(sum, 3); - ## Examples + // now this is just fine: + assert_eq!(stream.next().await, Some(3)); + assert_eq!(stream.next().await, None); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn by_ref(&mut self) -> &mut Self { + self + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + A stream adaptor similar to [`fold`] that holds internal state and produces a new + stream. + + [`fold`]: #method.fold + + `scan()` takes two arguments: an initial value which seeds the internal state, and + a closure with two arguments, the first being a mutable reference to the internal + state and the second a stream element. The closure can assign to the internal state + to share state between iterations. + + On iteration, the closure will be applied to each element of the stream and the + return value from the closure, an `Option`, is yielded by the stream. + + ## Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1isize, 2, 3]); + let mut s = s.scan(1, |state, x| { + *state = *state * x; + Some(-*state) + }); + + assert_eq!(s.next().await, Some(-1)); + assert_eq!(s.next().await, Some(-2)); + assert_eq!(s.next().await, Some(-6)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + #[inline] + fn scan(self, initial_state: St, f: F) -> Scan + where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option, + { + Scan::new(self, initial_state, f) + } - let s = stream::from_iter(vec![1u8, 2, 3]); - let mut skipped = s.skip(2); + #[doc = r#" + Combinator that `skip`s elements based on a predicate. - assert_eq!(skipped.next().await, Some(3)); - assert_eq!(skipped.next().await, None); - # - # }) } - ``` - "#] - fn skip(self, n: usize) -> Skip - where - Self: Sized, - { - Skip::new(self, n) - } + Takes a closure argument. It will call this closure on every element in + the stream and ignore elements until it returns `false`. - #[doc=r#" - Await a stream or times out after a duration of time. + After `false` is returned, `SkipWhile`'s job is over and all further + elements in the strem are yielded. - If you want to await an I/O future consider using - [`io::timeout`](../io/fn.timeout.html) instead. + ## Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use std::time::Duration; + let a = stream::from_iter(vec![-1i32, 0, 1]); + let mut s = a.skip_while(|x| x.is_negative()); - use async_std::stream; - use async_std::prelude::*; + assert_eq!(s.next().await, Some(0)); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn skip_while

(self, predicate: P) -> SkipWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + SkipWhile::new(self, predicate) + } - let mut s = stream::repeat(1).take(3).timeout(Duration::from_secs(1)); + #[doc = r#" + Creates a combinator that skips the first `n` elements. - while let Some(v) = s.next().await { - assert_eq!(v, Ok(1)); - } + ## Examples - // when timeout - let mut s = stream::pending::<()>().timeout(Duration::from_millis(10)); - match s.next().await { - Some(item) => assert!(item.is_err()), - None => panic!() - }; - # - # Ok(()) }) } - ``` - "#] - #[cfg(any(feature = "unstable", feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> Timeout - where - Self: Stream + Sized, - { - Timeout::new(self, dur) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - A combinator that applies a function as long as it returns successfully, producing a single, final value. - Immediately returns the error when the function returns unsuccessfully. + let s = stream::from_iter(vec![1u8, 2, 3]); + let mut skipped = s.skip(2); - # Examples + assert_eq!(skipped.next().await, Some(3)); + assert_eq!(skipped.next().await, None); + # + # }) } + ``` + "#] + fn skip(self, n: usize) -> Skip + where + Self: Sized, + { + Skip::new(self, n) + } - Basic usage: + #[doc=r#" + Await a stream or times out after a duration of time. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + If you want to await an I/O future consider using + [`io::timeout`](../io/fn.timeout.html) instead. - let mut s = stream::from_iter(vec![1usize, 2, 3]); - let sum = s.try_fold(0, |acc, v| { - if (acc+v) % 2 == 1 { - Ok(v+3) - } else { - Err("fail") - } - }).await; - - assert_eq!(sum, Err("fail")); - # - # }) } - ``` - "#] - fn try_fold( - &mut self, - init: T, - f: F, - ) -> TryFoldFuture<'_, Self, F, T> - where - Self: Unpin + Sized, - F: FnMut(B, Self::Item) -> Result, - { - TryFoldFuture::new(self, init, f) - } + # Examples - #[doc = r#" - Applies a falliable function to each element in a stream, stopping at first error and returning it. + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use std::time::Duration; - # Examples + use async_std::stream; + use async_std::prelude::*; - ``` - # fn main() { async_std::task::block_on(async { - # - use std::sync::mpsc::channel; - use async_std::prelude::*; - use async_std::stream; + let mut s = stream::repeat(1).take(3).timeout(Duration::from_secs(1)); - let (tx, rx) = channel(); - - let mut s = stream::from_iter(vec![1u8, 2, 3]); - let s = s.try_for_each(|v| { - if v % 2 == 1 { - tx.clone().send(v).unwrap(); - Ok(()) - } else { - Err("even") - } - }); - - let res = s.await; - drop(tx); - let values: Vec<_> = rx.iter().collect(); - - assert_eq!(values, vec![1]); - assert_eq!(res, Err("even")); - # - # }) } - ``` - "#] - fn try_for_each( - &mut self, - f: F, - ) -> TryForEachFuture<'_, Self, F> - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> Result<(), E>, - { - TryForEachFuture::new(self, f) + while let Some(v) = s.next().await { + assert_eq!(v, Ok(1)); } - #[doc = r#" - 'Zips up' two streams into a single stream of pairs. + // when timeout + let mut s = stream::pending::<()>().timeout(Duration::from_millis(10)); + match s.next().await { + Some(item) => assert!(item.is_err()), + None => panic!() + }; + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> Timeout + where + Self: Stream + Sized, + { + Timeout::new(self, dur) + } - `zip()` returns a new stream that will iterate over two other streams, returning a - tuple where the first element comes from the first stream, and the second element - comes from the second stream. + #[doc = r#" + A combinator that applies a function as long as it returns successfully, producing a single, final value. + Immediately returns the error when the function returns unsuccessfully. - In other words, it zips two streams together, into a single one. + # Examples - If either stream returns [`None`], [`poll_next`] from the zipped stream will return - [`None`]. If the first stream returns [`None`], `zip` will short-circuit and - `poll_next` will not be called on the second stream. + Basic usage: - [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - [`poll_next`]: #tymethod.poll_next + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ## Examples + let mut s = stream::from_iter(vec![1usize, 2, 3]); + let sum = s.try_fold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") + } + }).await; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_fold( + &mut self, + init: T, + f: F, + ) -> TryFoldFuture<'_, Self, F, T> + where + Self: Unpin + Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryFoldFuture::new(self, init, f) + } - let l = stream::from_iter(vec![1u8, 2, 3]); - let r = stream::from_iter(vec![4u8, 5, 6, 7]); - let mut s = l.zip(r); - - assert_eq!(s.next().await, Some((1, 4))); - assert_eq!(s.next().await, Some((2, 5))); - assert_eq!(s.next().await, Some((3, 6))); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - #[inline] - fn zip(self, other: U) -> Zip - where - Self: Sized, - U: Stream, - { - Zip::new(self, other) - } + #[doc = r#" + Applies a falliable function to each element in a stream, stopping at first error and returning it. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::sync::mpsc::channel; + use async_std::prelude::*; + use async_std::stream; + + let (tx, rx) = channel(); + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + let s = s.try_for_each(|v| { + if v % 2 == 1 { + tx.clone().send(v).unwrap(); + Ok(()) + } else { + Err("even") + } + }); - #[doc = r#" - Converts an stream of pairs into a pair of containers. + let res = s.await; + drop(tx); + let values: Vec<_> = rx.iter().collect(); - `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. + assert_eq!(values, vec![1]); + assert_eq!(res, Err("even")); + # + # }) } + ``` + "#] + fn try_for_each( + &mut self, + f: F, + ) -> TryForEachFuture<'_, Self, F> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> Result<(), E>, + { + TryForEachFuture::new(self, f) + } - This function is, in some sense, the opposite of [`zip`]. + #[doc = r#" + 'Zips up' two streams into a single stream of pairs. - [`zip`]: trait.Stream.html#method.zip + `zip()` returns a new stream that will iterate over two other streams, returning a + tuple where the first element comes from the first stream, and the second element + comes from the second stream. - # Example + In other words, it zips two streams together, into a single one. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + If either stream returns [`None`], [`poll_next`] from the zipped stream will return + [`None`]. If the first stream returns [`None`], `zip` will short-circuit and + `poll_next` will not be called on the second stream. - let s = stream::from_iter(vec![(1,2), (3,4)]); + [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + [`poll_next`]: #tymethod.poll_next - let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; + ## Examples - assert_eq!(left, [1, 3]); - assert_eq!(right, [2, 4]); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn unzip(self) -> UnzipFuture - where - FromA: Default + Extend, - FromB: Default + Extend, - Self: Stream + Sized, - { - UnzipFuture::new(self) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Transforms a stream into a collection. + let l = stream::from_iter(vec![1u8, 2, 3]); + let r = stream::from_iter(vec![4u8, 5, 6, 7]); + let mut s = l.zip(r); - `collect()` can take anything streamable, and turn it into a relevant - collection. This is one of the more powerful methods in the async - standard library, used in a variety of contexts. + assert_eq!(s.next().await, Some((1, 4))); + assert_eq!(s.next().await, Some((2, 5))); + assert_eq!(s.next().await, Some((3, 6))); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + #[inline] + fn zip(self, other: U) -> Zip + where + Self: Sized, + U: Stream, + { + Zip::new(self, other) + } - The most basic pattern in which `collect()` is used is to turn one - collection into another. You take a collection, call [`into_stream`] on it, - do a bunch of transformations, and then `collect()` at the end. + #[doc = r#" + Converts an stream of pairs into a pair of containers. - Because `collect()` is so general, it can cause problems with type - inference. As such, `collect()` is one of the few times you'll see - the syntax affectionately known as the 'turbofish': `::<>`. This - helps the inference algorithm understand specifically which collection - you're trying to collect into. + `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. - # Examples + This function is, in some sense, the opposite of [`zip`]. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + [`zip`]: trait.Stream.html#method.zip - let s = stream::repeat(9u8).take(3); - let buf: Vec = s.collect().await; - - assert_eq!(buf, vec![9; 3]); - - // You can also collect streams of Result values - // into any collection that implements FromStream - let s = stream::repeat(Ok(9)).take(3); - // We are using Vec here, but other collections - // are supported as well - let buf: Result, ()> = s.collect().await; - - assert_eq!(buf, Ok(vec![9; 3])); - - // The stream will stop on the first Err and - // return that instead - let s = stream::repeat(Err(5)).take(3); - let buf: Result, u8> = s.collect().await; - - assert_eq!(buf, Err(5)); - # - # }) } - ``` - - [`into_stream`]: trait.IntoStream.html#tymethod.into_stream - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn collect<'a, B>( - self, - ) -> Pin + 'a + Send>> - where - Self: Sized + 'a + Send, - B: FromStream, - Self::Item: Send, - { - FromStream::from_stream(self) - } + # Example - #[doc = r#" - Combines multiple streams into a single stream of all their outputs. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Items are yielded as soon as they're received, and the stream continues yield until - both streams have been exhausted. The output ordering between streams is not guaranteed. + let s = stream::from_iter(vec![(1,2), (3,4)]); - # Examples + let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; - ``` - # async_std::task::block_on(async { - use async_std::prelude::*; - use async_std::stream::{self, FromStream}; - - let a = stream::once(1u8); - let b = stream::once(2u8); - let c = stream::once(3u8); - - let s = a.merge(b).merge(c); - let mut lst = Vec::from_stream(s).await; - - lst.sort_unstable(); - assert_eq!(&lst, &[1u8, 2u8, 3u8]); - # }); - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn merge(self, other: U) -> Merge - where - Self: Sized, - U: Stream + Sized, - { - Merge::new(self, other) - } + assert_eq!(left, [1, 3]); + assert_eq!(right, [2, 4]); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn unzip(self) -> UnzipFuture + where + FromA: Default + Extend, + FromB: Default + Extend, + Self: Stream + Sized, + { + UnzipFuture::new(self) + } - #[doc = r#" - Lexicographically compares the elements of this `Stream` with those - of another. + #[doc = r#" + Transforms a stream into a collection. - # Examples + `collect()` can take anything streamable, and turn it into a relevant + collection. This is one of the more powerful methods in the async + standard library, used in a variety of contexts. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + The most basic pattern in which `collect()` is used is to turn one + collection into another. You take a collection, call [`into_stream`] on it, + do a bunch of transformations, and then `collect()` at the end. - use std::cmp::Ordering; - - let s1 = stream::from_iter(vec![1]); - let s2 = stream::from_iter(vec![1, 2]); - let s3 = stream::from_iter(vec![1, 2, 3]); - let s4 = stream::from_iter(vec![1, 2, 4]); - assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); - assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); - assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); - assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); - assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); - # - # }) } - ``` - "#] - fn partial_cmp( - self, - other: S - ) -> PartialCmpFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - PartialCmpFuture::new(self, other) - } + Because `collect()` is so general, it can cause problems with type + inference. As such, `collect()` is one of the few times you'll see + the syntax affectionately known as the 'turbofish': `::<>`. This + helps the inference algorithm understand specifically which collection + you're trying to collect into. - #[doc = r#" - Searches for an element in a Stream that satisfies a predicate, returning - its index. + # Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let s = stream::repeat(9u8).take(3); + let buf: Vec = s.collect().await; - let s = stream::from_iter(vec![1usize, 2, 3]); - let res = s.clone().position(|x| x == 1).await; - assert_eq!(res, Some(0)); - - let res = s.clone().position(|x| x == 2).await; - assert_eq!(res, Some(1)); - - let res = s.clone().position(|x| x == 3).await; - assert_eq!(res, Some(2)); - - let res = s.clone().position(|x| x == 4).await; - assert_eq!(res, None); - # - # }) } - ``` - "#] - fn position

( - &mut self, - predicate: P, - ) -> PositionFuture<'_, Self, P> - where - Self: Unpin + Sized, - P: FnMut(Self::Item) -> bool, - { - PositionFuture::new(self, predicate) - } + assert_eq!(buf, vec![9; 3]); - #[doc = r#" - Lexicographically compares the elements of this `Stream` with those - of another using 'Ord'. + // You can also collect streams of Result values + // into any collection that implements FromStream + let s = stream::repeat(Ok(9)).take(3); + // We are using Vec here, but other collections + // are supported as well + let buf: Result, ()> = s.collect().await; - # Examples + assert_eq!(buf, Ok(vec![9; 3])); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; - use std::cmp::Ordering; - - let s1 = stream::from_iter(vec![1]); - let s2 = stream::from_iter(vec![1, 2]); - let s3 = stream::from_iter(vec![1, 2, 3]); - let s4 = stream::from_iter(vec![1, 2, 4]); - - assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); - assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); - assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); - assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); - assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); - # - # }) } - ``` - "#] - fn cmp( - self, - other: S - ) -> CmpFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: Ord - { - CmpFuture::new(self, other) - } + // The stream will stop on the first Err and + // return that instead + let s = stream::repeat(Err(5)).take(3); + let buf: Result, u8> = s.collect().await; - #[doc = r#" - Counts the number of elements in the stream. + assert_eq!(buf, Err(5)); + # + # }) } + ``` - # Examples + [`into_stream`]: trait.IntoStream.html#tymethod.into_stream + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn collect<'a, B>( + self, + ) -> Pin + 'a + Send>> + where + Self: Sized + 'a + Send, + B: FromStream, + Self::Item: Send, + { + FromStream::from_stream(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Combines multiple streams into a single stream of all their outputs. - let s1 = stream::from_iter(vec![0]); - let s2 = stream::from_iter(vec![1, 2, 3]); - - assert_eq!(s1.count().await, 1); - assert_eq!(s2.count().await, 3); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn count(self) -> CountFuture - where - Self: Sized, - { - CountFuture::new(self) - } + Items are yielded as soon as they're received, and the stream continues yield until + both streams have been exhausted. The output ordering between streams is not guaranteed. - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - not equal to those of another. + # Examples - # Examples + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::stream::{self, FromStream}; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let a = stream::once(1u8); + let b = stream::once(2u8); + let c = stream::once(3u8); - let single = stream::from_iter(vec![1usize]); - let single_ne = stream::from_iter(vec![10usize]); - let multi = stream::from_iter(vec![1usize,2]); - let multi_ne = stream::from_iter(vec![1usize,5]); - - assert_eq!(single.clone().ne(single.clone()).await, false); - assert_eq!(single_ne.clone().ne(single.clone()).await, true); - assert_eq!(multi.clone().ne(single_ne.clone()).await, true); - assert_eq!(multi_ne.clone().ne(multi.clone()).await, true); - # - # }) } - ``` - "#] - fn ne( - self, - other: S - ) -> NeFuture - where - Self: Sized, - S: Sized + Stream, - ::Item: PartialEq, - { - NeFuture::new(self, other) - } + let s = a.merge(b).merge(c); + let mut lst = Vec::from_stream(s).await; - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - greater than or equal to those of another. + lst.sort_unstable(); + assert_eq!(&lst, &[1u8, 2u8, 3u8]); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn merge(self, other: U) -> Merge + where + Self: Sized, + U: Stream + Sized, + { + Merge::new(self, other) + } - # Examples + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + use std::cmp::Ordering; + + let s1 = stream::from_iter(vec![1]); + let s2 = stream::from_iter(vec![1, 2]); + let s3 = stream::from_iter(vec![1, 2, 3]); + let s4 = stream::from_iter(vec![1, 2, 4]); + assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); + assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); + assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); + assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); + assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); + # + # }) } + ``` + "#] + fn partial_cmp( + self, + other: S + ) -> PartialCmpFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + PartialCmpFuture::new(self, other) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Searches for an element in a Stream that satisfies a predicate, returning + its index. - let single = stream::from_iter(vec![1]); - let single_gt = stream::from_iter(vec![10]); - let multi = stream::from_iter(vec![1,2]); - let multi_gt = stream::from_iter(vec![1,5]); - - assert_eq!(single.clone().ge(single.clone()).await, true); - assert_eq!(single_gt.clone().ge(single.clone()).await, true); - assert_eq!(multi.clone().ge(single_gt.clone()).await, false); - assert_eq!(multi_gt.clone().ge(multi.clone()).await, true); - # - # }) } - ``` - "#] - fn ge( - self, - other: S - ) -> GeFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - GeFuture::new(self, other) - } + # Examples - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - equal to those of another. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s = stream::from_iter(vec![1usize, 2, 3]); + let res = s.clone().position(|x| x == 1).await; + assert_eq!(res, Some(0)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let res = s.clone().position(|x| x == 2).await; + assert_eq!(res, Some(1)); - let single = stream::from_iter(vec![1]); - let single_eq = stream::from_iter(vec![10]); - let multi = stream::from_iter(vec![1,2]); - let multi_eq = stream::from_iter(vec![1,5]); - - assert_eq!(single.clone().eq(single.clone()).await, true); - assert_eq!(single_eq.clone().eq(single.clone()).await, false); - assert_eq!(multi.clone().eq(single_eq.clone()).await, false); - assert_eq!(multi_eq.clone().eq(multi.clone()).await, false); - # - # }) } - ``` - "#] - fn eq( - self, - other: S - ) -> EqFuture - where - Self: Sized + Stream, - S: Sized + Stream, - ::Item: PartialEq, - { - EqFuture::new(self, other) - } + let res = s.clone().position(|x| x == 3).await; + assert_eq!(res, Some(2)); - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - greater than those of another. + let res = s.clone().position(|x| x == 4).await; + assert_eq!(res, None); + # + # }) } + ``` + "#] + fn position

( + &mut self, + predicate: P, + ) -> PositionFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(Self::Item) -> bool, + { + PositionFuture::new(self, predicate) + } - # Examples + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another using 'Ord'. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::cmp::Ordering; + + let s1 = stream::from_iter(vec![1]); + let s2 = stream::from_iter(vec![1, 2]); + let s3 = stream::from_iter(vec![1, 2, 3]); + let s4 = stream::from_iter(vec![1, 2, 4]); + + assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); + assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); + assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); + assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); + assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); + # + # }) } + ``` + "#] + fn cmp( + self, + other: S + ) -> CmpFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: Ord + { + CmpFuture::new(self, other) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Counts the number of elements in the stream. - let single = stream::from_iter(vec![1]); - let single_gt = stream::from_iter(vec![10]); - let multi = stream::from_iter(vec![1,2]); - let multi_gt = stream::from_iter(vec![1,5]); - - assert_eq!(single.clone().gt(single.clone()).await, false); - assert_eq!(single_gt.clone().gt(single.clone()).await, true); - assert_eq!(multi.clone().gt(single_gt.clone()).await, false); - assert_eq!(multi_gt.clone().gt(multi.clone()).await, true); - # - # }) } - ``` - "#] - fn gt( - self, - other: S - ) -> GtFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - GtFuture::new(self, other) - } + # Examples - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - less or equal to those of another. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s1 = stream::from_iter(vec![0]); + let s2 = stream::from_iter(vec![1, 2, 3]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + assert_eq!(s1.count().await, 1); + assert_eq!(s2.count().await, 3); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn count(self) -> CountFuture + where + Self: Sized, + { + CountFuture::new(self) + } - let single = stream::from_iter(vec![1]); - let single_gt = stream::from_iter(vec![10]); - let multi = stream::from_iter(vec![1,2]); - let multi_gt = stream::from_iter(vec![1,5]); - - assert_eq!(single.clone().le(single.clone()).await, true); - assert_eq!(single.clone().le(single_gt.clone()).await, true); - assert_eq!(multi.clone().le(single_gt.clone()).await, true); - assert_eq!(multi_gt.clone().le(multi.clone()).await, false); - # - # }) } - ``` - "#] - fn le( - self, - other: S - ) -> LeFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - LeFuture::new(self, other) - } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + not equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1usize]); + let single_ne = stream::from_iter(vec![10usize]); + let multi = stream::from_iter(vec![1usize,2]); + let multi_ne = stream::from_iter(vec![1usize,5]); + + assert_eq!(single.clone().ne(single.clone()).await, false); + assert_eq!(single_ne.clone().ne(single.clone()).await, true); + assert_eq!(multi.clone().ne(single_ne.clone()).await, true); + assert_eq!(multi_ne.clone().ne(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn ne( + self, + other: S + ) -> NeFuture + where + Self: Sized, + S: Sized + Stream, + ::Item: PartialEq, + { + NeFuture::new(self, other) + } - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - less than those of another. + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than or equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().ge(single.clone()).await, true); + assert_eq!(single_gt.clone().ge(single.clone()).await, true); + assert_eq!(multi.clone().ge(single_gt.clone()).await, false); + assert_eq!(multi_gt.clone().ge(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn ge( + self, + other: S + ) -> GeFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GeFuture::new(self, other) + } - # Examples + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_eq = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_eq = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().eq(single.clone()).await, true); + assert_eq!(single_eq.clone().eq(single.clone()).await, false); + assert_eq!(multi.clone().eq(single_eq.clone()).await, false); + assert_eq!(multi_eq.clone().eq(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn eq( + self, + other: S + ) -> EqFuture + where + Self: Sized + Stream, + S: Sized + Stream, + ::Item: PartialEq, + { + EqFuture::new(self, other) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().gt(single.clone()).await, false); + assert_eq!(single_gt.clone().gt(single.clone()).await, true); + assert_eq!(multi.clone().gt(single_gt.clone()).await, false); + assert_eq!(multi_gt.clone().gt(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn gt( + self, + other: S + ) -> GtFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GtFuture::new(self, other) + } - let single = stream::from_iter(vec![1]); - let single_gt = stream::from_iter(vec![10]); - let multi = stream::from_iter(vec![1,2]); - let multi_gt = stream::from_iter(vec![1,5]); - - assert_eq!(single.clone().lt(single.clone()).await, false); - assert_eq!(single.clone().lt(single_gt.clone()).await, true); - assert_eq!(multi.clone().lt(single_gt.clone()).await, true); - assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); - # - # }) } - ``` - "#] - fn lt( - self, - other: S - ) -> LtFuture - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - LtFuture::new(self, other) - } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less or equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().le(single.clone()).await, true); + assert_eq!(single.clone().le(single_gt.clone()).await, true); + assert_eq!(multi.clone().le(single_gt.clone()).await, true); + assert_eq!(multi_gt.clone().le(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn le( + self, + other: S + ) -> LeFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + LeFuture::new(self, other) + } - #[doc = r#" - Sums the elements of a stream. + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less than those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().lt(single.clone()).await, false); + assert_eq!(single.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn lt( + self, + other: S + ) -> LtFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + LtFuture::new(self, other) + } - Takes each element, adds them together, and returns the result. + #[doc = r#" + Sums the elements of a stream. - An empty streams returns the zero value of the type. + Takes each element, adds them together, and returns the result. - # Panics + An empty streams returns the zero value of the type. - When calling `sum()` and a primitive integer type is being returned, this - method will panic if the computation overflows and debug assertions are - enabled. + # Panics - # Examples + When calling `sum()` and a primitive integer type is being returned, this + method will panic if the computation overflows and debug assertions are + enabled. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Basic usage: - let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); - let sum: u8 = s.sum().await; - - assert_eq!(sum, 10); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn sum<'a, S>( - self, - ) -> Pin + 'a>> - where - Self: Sized + Stream + 'a, - S: Sum, - { - Sum::sum(self) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Multiplies all elements of the stream. + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); + let sum: u8 = s.sum().await; - An empty stream returns the one value of the type. + assert_eq!(sum, 10); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn sum<'a, S>( + self, + ) -> Pin + 'a>> + where + Self: Sized + Stream + 'a, + S: Sum, + { + Sum::sum(self) + } - # Panics + #[doc = r#" + Multiplies all elements of the stream. - When calling `product()` and a primitive integer type is being returned, - method will panic if the computation overflows and debug assertions are - enabled. + An empty stream returns the one value of the type. - # Examples + # Panics - This example calculates the factorial of n (i.e. the product of the numbers from 1 to - n, inclusive): + When calling `product()` and a primitive integer type is being returned, + method will panic if the computation overflows and debug assertions are + enabled. - ``` - # fn main() { async_std::task::block_on(async { - # - async fn factorial(n: u32) -> u32 { - use async_std::prelude::*; - use async_std::stream; + # Examples - let s = stream::from_iter(1..=n); - s.product().await - } + This example calculates the factorial of n (i.e. the product of the numbers from 1 to + n, inclusive): - assert_eq!(factorial(0).await, 1); - assert_eq!(factorial(1).await, 1); - assert_eq!(factorial(5).await, 120); - # - # }) } - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn product<'a, P>( - self, - ) -> Pin + 'a>> - where - Self: Sized + Stream + 'a, - P: Product, - { - Product::product(self) + ``` + # fn main() { async_std::task::block_on(async { + # + async fn factorial(n: u32) -> u32 { + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(1..=n); + s.product().await } + + assert_eq!(factorial(0).await, 1); + assert_eq!(factorial(1).await, 1); + assert_eq!(factorial(5).await, 120); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn product<'a, P>( + self, + ) -> Pin + 'a>> + where + Self: Sized + Stream + 'a, + P: Product, + { + Product::product(self) } +} impl StreamExt for T {} From cca0f3e3212e62427cb972a21bf8a46478166315 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 15 Mar 2022 09:53:28 +1100 Subject: [PATCH 218/272] Use the default `recursion_limit`. Now that `extension_trait!` is gone, an increased limit isn't necessary. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ebbb3965a..669d05a7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,6 @@ #![doc(test(attr(deny(rust_2018_idioms, warnings))))] #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] -#![recursion_limit = "2048"] extern crate alloc; From 1b8c7dc4815f1db0a4b9bad4c9accb25644264d3 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 16 Mar 2022 20:48:19 +0100 Subject: [PATCH 219/272] prepare 1.11.0 Signed-off-by: Marc-Antoine Perennou --- CHANGELOG.md | 17 +++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa3c8c3c8..5747ff199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,23 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## Removed ## Changed +# [1.11.0] - 2022-03-22 + +This release improves compile times by up to 55% on initial builds, and up to 75% on recompilation. Additionally we've added a few new APIs and made some tweaks. + +## Added +- `TcpListener::into_incoming` to convert a `TcpListener` into a stream of incoming TCP connections + +## Removed +- The internal `extension_trait` macro had been removed. This drastically improves compile times for `async-std`, but changes the way our documentation is rendered. This is a cosmetic change only, and all existing code should continue to work as it did before. + +## Changed +- Some internal code has been de-macro-ified, making for quicker compile times. +- We now use the default recursion limit. + +## Docs +- Several docs improvements / fixes. + # [1.10.0] - 2021-08-25 This release comes with an assortment of small features and fixes. diff --git a/Cargo.toml b/Cargo.toml index bc9078cd4..c2ea9d781 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.10.0" +version = "1.11.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From f6ecd5ff330d593dae926cfe98f6b672712b0166 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 23 Apr 2022 11:40:33 -0700 Subject: [PATCH 220/272] Remove unused num_cpus dependency (handled by async_global_executor) async-std doesn't use num_cpus directly, only via async_global_executor. --- Cargo.toml | 2 -- src/lib.rs | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c2ea9d781..644355fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ default = [ "futures-lite", "kv-log-macro", "log", - "num_cpus", "pin-project-lite", "gloo-timers", ] @@ -71,7 +70,6 @@ futures-io = { version = "0.3.4", optional = true } kv-log-macro = { version = "1.0.6", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } -num_cpus = { version = "1.12.0", optional = true } once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 669d05a7a..73e722233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,9 +267,10 @@ //! //! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the //! async-std runtime will start. By default, this is one per logical -//! cpu as reported by the [num_cpus](num_cpus) crate, which may be -//! different than the number of physical cpus. Async-std _will panic_ -//! if this is set to any value other than a positive integer. +//! cpu as determined by [async-global-executor](async_global_executor), +//! which may be different than the number of physical cpus. Async-std +//! _will panic_ if this is set to any value other than a positive +//! integer. //! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime //! threads report to the operating system. The default value is //! `"async-std/runtime"`. From ab112d5db67563783e6729fb3ed8025733acc546 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Wed, 27 Apr 2022 14:18:31 +0800 Subject: [PATCH 221/272] fix some typos Signed-off-by: cuishuang --- CHANGELOG.md | 4 ++-- src/future/mod.rs | 2 +- src/stream/stream/cmp.rs | 2 +- src/stream/stream/partial_cmp.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5747ff199..f47d6a58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,7 +155,7 @@ release, and updates internal dependencies. ## Fixed - Fix `TcpListener::incoming`. ([#889](https://github.com/async-rs/async-std/pull/889)) -- Fix tokio compatability flag. ([#882](https://github.com/async-rs/async-std/pull/882)) +- Fix tokio compatibility flag. ([#882](https://github.com/async-rs/async-std/pull/882)) # [1.6.4] - 2020-09-16 @@ -207,7 +207,7 @@ release, and updates internal dependencies. ## Added -- Added `tokio02` feature flag, to allow compatability usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)). +- Added `tokio02` feature flag, to allow compatibility usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)). ## Changed diff --git a/src/future/mod.rs b/src/future/mod.rs index db0607adb..1b3cd5863 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -2,7 +2,7 @@ //! //! ## Base Futures Concurrency //! -//! Often it's desireable to await multiple futures as if it was a single +//! Often it's desirable to await multiple futures as if it was a single //! future. The `join` family of operations converts multiple futures into a //! single future that returns all of their outputs. The `race` family of //! operations converts multiple future into a single future that returns the diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index 9d2b0eccc..bf93408a6 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -58,7 +58,7 @@ where return Poll::Ready(Ordering::Greater); } - // Get next value if possible and necesary + // Get next value if possible and necessary if !this.l.done && this.l_cache.is_none() { let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index 928a03b0f..c328a9119 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -59,7 +59,7 @@ where return Poll::Ready(Some(Ordering::Greater)); } - // Get next value if possible and necesary + // Get next value if possible and necessary if !this.l.done && this.l_cache.is_none() { let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { From 1356551ba638c53c85f03fb3392860a182a35dda Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 27 Apr 2022 01:57:11 -0700 Subject: [PATCH 222/272] Add `TryFrom` impls to convert async types to corresponding sync types Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode. --- src/net/tcp/listener.rs | 10 ++++++++++ src/net/tcp/stream.rs | 15 +++++++++++++++ src/net/udp/mod.rs | 10 ++++++++++ src/os/unix/net/datagram.rs | 10 ++++++++++ src/os/unix/net/listener.rs | 10 ++++++++++ src/os/unix/net/stream.rs | 15 +++++++++++++++ 6 files changed, 70 insertions(+) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index a9f4d52b2..69340db4e 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -252,6 +252,16 @@ impl From for TcpListener { } } +impl std::convert::TryFrom for std::net::TcpListener { + type Error = io::Error; + /// Converts a `TcpListener` into its synchronous equivalent. + fn try_from(listener: TcpListener) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index c2a6277ba..f30e4714c 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -378,6 +378,21 @@ impl From for TcpStream { } } +impl std::convert::TryFrom for std::net::TcpStream { + type Error = io::Error; + /// Converts a `TcpStream` into its synchronous equivalent. + fn try_from(stream: TcpStream) -> io::Result { + let inner = Arc::try_unwrap(stream.watcher) + .map_err(|_| io::Error::new( + io::ErrorKind::Other, + "Cannot convert TcpStream to synchronous: multiple references", + ))? + .into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 377e300f7..e216af43a 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -532,6 +532,16 @@ impl From for UdpSocket { } } +impl std::convert::TryFrom for std::net::UdpSocket { + type Error = io::Error; + /// Converts a `UdpSocket` into its synchronous equivalent. + fn try_from(listener: UdpSocket) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 99a9e8d23..7b0fc32ec 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -311,6 +311,16 @@ impl From for UnixDatagram { } } +impl std::convert::TryFrom for StdUnixDatagram { + type Error = io::Error; + /// Converts a `UnixDatagram` into its synchronous equivalent. + fn try_from(listener: UnixDatagram) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { self.watcher.as_raw_fd() diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index e86502b59..1f983656f 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -205,6 +205,16 @@ impl From for UnixListener { } } +impl std::convert::TryFrom for StdUnixListener { + type Error = io::Error; + /// Converts a `UnixListener` into its synchronous equivalent. + fn try_from(listener: UnixListener) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { self.watcher.as_raw_fd() diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 9e8dbe749..8674e7c32 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -231,6 +231,21 @@ impl From for UnixStream { } } +impl std::convert::TryFrom for StdUnixStream { + type Error = io::Error; + /// Converts a `UnixStream` into its synchronous equivalent. + fn try_from(stream: UnixStream) -> io::Result { + let inner = Arc::try_unwrap(stream.watcher) + .map_err(|_| io::Error::new( + io::ErrorKind::Other, + "Cannot convert UnixStream to synchronous: multiple references", + ))? + .into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { self.watcher.as_raw_fd() From 07ba24cd871cf6d069b9f5cb464328d6b3ac748d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 1 Jun 2022 15:55:24 -0700 Subject: [PATCH 223/272] Stabilize `std::task::spawn_blocking` Given how widely used spawn_blocking is within async-std itself, and how useful it is for building other APIs, I think it makes sense to offer it just as we do `spawn`, even though it isn't standard in Rust itself. --- CHANGELOG.md | 3 +++ src/task/mod.rs | 4 ---- src/task/spawn_blocking.rs | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f47d6a58c..ab063d36f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [Unreleased] ## Added + +- `std::task_spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. + ## Removed ## Changed diff --git a/src/task/mod.rs b/src/task/mod.rs index fe574ec68..0eda72b71 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -160,11 +160,7 @@ cfg_default! { mod task_locals_wrapper; #[cfg(not(target_os = "unknown"))] - #[cfg(any(feature = "unstable", test))] pub use spawn_blocking::spawn_blocking; - #[cfg(not(target_os = "unknown"))] - #[cfg(not(any(feature = "unstable", test)))] - pub(crate) use spawn_blocking::spawn_blocking; } cfg_unstable! { diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 3c57a751b..2439c123f 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -16,7 +16,6 @@ use crate::task::{self, JoinHandle}; /// Basic usage: /// /// ``` -/// # #[cfg(feature = "unstable")] /// # async_std::task::block_on(async { /// # /// use async_std::task; @@ -28,7 +27,6 @@ use crate::task::{self, JoinHandle}; /// # /// # }) /// ``` -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub fn spawn_blocking(f: F) -> JoinHandle where From abbf9443713076618590fb72d7664dc48329fec2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 1 Jun 2022 19:02:51 -0700 Subject: [PATCH 224/272] Fix CI errors about unused-macro-rules float_product and float_sum had unused rules, because they weren't successfully using their second branch, and weren't successfully defining wrapping types. That then led to the discovery that those types *can't* be defined, because std doesn't actually define any operations on `Wrapping` or `Wrapping`. So, drop those portions of the float macros. Fix that, and in the process, unify the integer and float macros. --- src/stream/product.rs | 30 ++++++++---------------------- src/stream/sum.rs | 30 ++++++++---------------------- 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/stream/product.rs b/src/stream/product.rs index 15497e87c..991727d29 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -27,8 +27,8 @@ use core::ops::Mul; use core::num::Wrapping; use crate::stream::stream::StreamExt; -macro_rules! integer_product { - (@impls $one: expr, $($a:ty)*) => ($( +macro_rules! num_product { + ($one:expr, $($a:ty)*) => ($( impl Product for $a { fn product<'a, S>(stream: S) -> Pin+ 'a>> where @@ -46,32 +46,18 @@ macro_rules! integer_product { } } )*); +} + +macro_rules! integer_product { ($($a:ty)*) => ( - integer_product!(@impls 1, $($a)*); - integer_product!(@impls Wrapping(1), $(Wrapping<$a>)*); + num_product!(1, $($a)*); + num_product!(Wrapping(1), $(Wrapping<$a>)*); ); } macro_rules! float_product { - ($($a:ty)*) => ($( - impl Product for $a { - fn product<'a, S>(stream: S) -> Pin+ 'a>> - where S: Stream + 'a, - { - Box::pin(async move { stream.fold(1.0, |a, b| a * b).await } ) - } - } - impl<'a> Product<&'a $a> for $a { - fn product<'b, S>(stream: S) -> Pin+ 'b>> - where S: Stream + 'b, - { - Box::pin(async move { stream.fold(1.0, |a, b| a * b).await } ) - } - } - )*); ($($a:ty)*) => ( - float_product!($($a)*); - float_product!($(Wrapping<$a>)*); + num_product!(1.0, $($a)*); ); } diff --git a/src/stream/sum.rs b/src/stream/sum.rs index 3b3144e5e..65f6e8881 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -27,8 +27,8 @@ use crate::stream::stream::StreamExt; use core::num::Wrapping; use core::ops::Add; -macro_rules! integer_sum { - (@impls $zero: expr, $($a:ty)*) => ($( +macro_rules! num_sum { + ($zero:expr, $($a:ty)*) => ($( impl Sum for $a { fn sum<'a, S>(stream: S) -> Pin+ 'a>> where @@ -46,32 +46,18 @@ macro_rules! integer_sum { } } )*); +} + +macro_rules! integer_sum { ($($a:ty)*) => ( - integer_sum!(@impls 0, $($a)*); - integer_sum!(@impls Wrapping(0), $(Wrapping<$a>)*); + num_sum!(0, $($a)*); + num_sum!(Wrapping(0), $(Wrapping<$a>)*); ); } macro_rules! float_sum { - ($($a:ty)*) => ($( - impl Sum for $a { - fn sum<'a, S>(stream: S) -> Pin + 'a>> - where S: Stream + 'a, - { - Box::pin(async move { stream.fold(0.0, |a, b| a + b).await } ) - } - } - impl<'a> Sum<&'a $a> for $a { - fn sum<'b, S>(stream: S) -> Pin + 'b>> - where S: Stream + 'b, - { - Box::pin(async move { stream.fold(0.0, |a, b| a + b).await } ) - } - } - )*); ($($a:ty)*) => ( - float_sum!(@impls 0.0, $($a)*); - float_sum!(@impls Wrapping(0.0), $(Wrapping<$a>)*); + num_sum!(0.0, $($a)*); ); } From 8e583ec76c1196e40c8ff0c87316aaea63c56754 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 10:03:08 -0700 Subject: [PATCH 225/272] Use actions/checkout@v3 rather than top of tree --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36a9a4254..d4401cab8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: rust: [nightly, beta, stable] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: Install ${{ matrix.rust }} uses: actions-rs/toolchain@v1 @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: setup run: | @@ -102,7 +102,7 @@ jobs: name: Check tokio02 feature runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: check tokio02 uses: actions-rs/cargo@v1 with: @@ -122,7 +122,7 @@ jobs: - arm-linux-androideabi steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: Install nightly uses: actions-rs/toolchain@v1 @@ -150,7 +150,7 @@ jobs: rust: [nightly, beta, stable] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: Install rust with wasm32-unknown-unknown uses: actions-rs/toolchain@v1 @@ -181,7 +181,7 @@ jobs: name: Checking fmt and docs runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: From 8f18e8de396bc8bb885c5bdea937d572cc82b6d6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 10:06:16 -0700 Subject: [PATCH 226/272] Cargo.toml: Fix typo (depencency -> dependency) and spacing --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 644355fb9..103f2a5cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } async-channel = { version = "1.5.1", optional = true } -# Devdepencency, but they are not allowed to be optional :/ +# dev dependency, but they are not allowed to be optional :/ surf = { version = "2.0.0", optional = true } From fc7f2bc2b3f8d4fdf0c829502b0dbf1e3822521d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 10:17:04 -0700 Subject: [PATCH 227/272] Remove rustfmt.toml - zero changes to formatting rustfmt's `version = "Two"` option doesn't result in any formatting changes anywhere in async-std, but it prevents formatting with a stable toolchain. Remove rustfmt.toml and allow formatting to work on stable rustfmt. --- rustfmt.toml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 1082fd888..000000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -version = "Two" From 267794c0bc3e2c307536547e43c94b015c019d39 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 10:20:20 -0700 Subject: [PATCH 228/272] Cargo.toml: Remove redundant settings that match the defaults --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 103f2a5cf..cad707232 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,9 @@ edition = "2018" license = "Apache-2.0/MIT" repository = "https://github.com/async-rs/async-std" homepage = "https://async.rs" -documentation = "https://docs.rs/async-std" description = "Async version of the Rust standard library" keywords = ["async", "await", "future", "std", "task"] categories = ["asynchronous", "concurrency", "network-programming"] -readme = "README.md" [package.metadata.docs.rs] features = ["docs"] From 6f2b6d3340a89ceedec0d2b03d75b8d1408d039b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 10:25:33 -0700 Subject: [PATCH 229/272] futures now re-exports std Future; remove docs about differences --- docs/src/SUMMARY.md | 1 - docs/src/overview/std-and-library-futures.md | 27 -------------------- 2 files changed, 28 deletions(-) delete mode 100644 docs/src/overview/std-and-library-futures.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 8b49ce7ab..9e828f660 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -2,7 +2,6 @@ - [Introduction](./introduction.md) - [Welcome to `async-std`!](./overview/async-std.md) - - [`std::future` and `futures-rs`](./overview/std-and-library-futures.md) - [Stability guarantees](./overview/stability-guarantees.md) - [Async concepts using async-std](./concepts.md) - [Futures](./concepts/futures.md) diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md deleted file mode 100644 index 7f2b98d6a..000000000 --- a/docs/src/overview/std-and-library-futures.md +++ /dev/null @@ -1,27 +0,0 @@ -# `std::future` and `futures-rs` - -Rust has two kinds of types commonly referred to as `Future`: - - -- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html). -- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html). - -The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`. - -It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FutureExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. - -In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures` crate by adding it to your `Cargo.toml` and importing `FutureExt`. - -## Interfaces and Stability - - `async-std` aims to be a stable and reliable library, at the level of the Rust standard library. This also means that we don't rely on the `futures` library for our interface. Yet, we appreciate that many users have come to like the conveniences that `futures-rs` brings. For that reason, `async-std` implements all `futures` traits for its types. - - Luckily, the approach from above gives you full flexibility. If you care about stability a lot, you can just use `async-std` as is. If you prefer the `futures` library interfaces, you link those in. Both uses are first class. - -## `async_std::future` - -There’s some support functions that we see as important for working with futures of any kind. These can be found in the `async_std::future` module and are covered by our stability guarantees. - -## Streams and Read/Write/Seek/BufRead traits - -Due to limitations of the Rust compiler, those are currently implemented in `async_std`, but cannot be implemented by users themselves. From 6852812d857c659f65786c4eccb3d0eb931fc226 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 11:10:54 -0700 Subject: [PATCH 230/272] Remove old bors.toml This seems to be an artifact from when we used bors rather than GitHub Actions. --- bors.toml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 bors.toml diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 732f8fcfc..000000000 --- a/bors.toml +++ /dev/null @@ -1,7 +0,0 @@ -status = [ - "Build and test (ubuntu-latest, nightly)", - "Build and test (windows-latest, nightly)", - "Build and test (macOS-latest, nightly)", - "Checking fmt and docs", - "Clippy check", -] From 422c3ddb85f20f0c6dbbfc3463b8645791e58b98 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 11:12:03 -0700 Subject: [PATCH 231/272] Remove wasm-test.sh (not invoked in CI) We build-test wasm in CI, and nothing runs this script. Ideally, in the future, we should run wasm tests in CI, such as via wasmtime. --- wasm-test.sh | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100755 wasm-test.sh diff --git a/wasm-test.sh b/wasm-test.sh deleted file mode 100755 index 3d8be9fd3..000000000 --- a/wasm-test.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -wasm-pack test --chrome --headless -- --features unstable --test buf_writer -wasm-pack test --chrome --headless -- --features unstable --test channel -wasm-pack test --chrome --headless -- --features unstable --test condvar -wasm-pack test --chrome --headless -- --features unstable --test mutex -wasm-pack test --chrome --headless -- --features unstable --test rwlock -wasm-pack test --chrome --headless -- --features unstable --test stream -wasm-pack test --chrome --headless -- --features unstable --test task_local -wasm-pack test --chrome --headless -- --features unstable --test timeout From ca8305064bee7d27f0b9e98c30825dbfe3da8468 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Jun 2022 11:17:20 -0700 Subject: [PATCH 232/272] Switch branch name to `main` Update all references. --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- ci/install-mdbook.sh | 2 +- docs/src/tutorial/index.md | 2 +- examples/README.md | 30 +++++++++++++++--------------- src/lib.rs | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4401cab8..5a717826f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: pull_request: push: branches: - - master + - main - staging - trying diff --git a/CHANGELOG.md b/CHANGELOG.md index ab063d36f..819ca8bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -419,7 +419,7 @@ assert_eq!(right, [2, 4]); ## Changed -- Enabled CI on master branch. +- Enabled CI. - `Future::join` and `Future::try_join` can now join futures with different output types. diff --git a/README.md b/README.md index 39876bf7b..a6d8276db 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ fn main() { More examples, including networking and file access, can be found in our [`examples`] directory and in our [documentation]. -[`examples`]: https://github.com/async-rs/async-std/tree/master/examples +[`examples`]: https://github.com/async-rs/async-std/tree/HEAD/examples [documentation]: https://docs.rs/async-std#examples [`task::block_on`]: https://docs.rs/async-std/*/async_std/task/fn.block_on.html [`"attributes"` feature]: https://docs.rs/async-std/#features diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh index 01c87f883..52422b970 100755 --- a/ci/install-mdbook.sh +++ b/ci/install-mdbook.sh @@ -1,7 +1,7 @@ set -euxo pipefail # Based on the Rust-Embedded WG's book CI -# https://github.com/rust-embedded/book/blob/master/ci/install.sh +# https://github.com/rust-embedded/book/blob/HEAD/ci/install.sh main() { # Note - this will only accept releases tagged with v0.3.x diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md index aee0b3f40..076ecf418 100644 --- a/docs/src/tutorial/index.md +++ b/docs/src/tutorial/index.md @@ -11,4 +11,4 @@ How will it distribute the messages? This tutorial explains how to write a chat server in `async-std`. -You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat). +You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/HEAD/examples/a-chat). diff --git a/examples/README.md b/examples/README.md index 903ed2cdb..bcaae42c8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -154,18 +154,18 @@ Make requests by running the client example: cargo run --example udp-client ``` -[hello-world]: https://github.com/async-rs/async-std/blob/master/examples/hello-world.rs -[line-count]: https://github.com/async-rs/async-std/blob/master/examples/line-count.rs -[list-dir]: https://github.com/async-rs/async-std/blob/master/examples/list-dir.rs -[logging]: https://github.com/async-rs/async-std/blob/master/examples/logging.rs -[print-file]: https://github.com/async-rs/async-std/blob/master/examples/print-file.rs -[socket-timeouts]: https://github.com/async-rs/async-std/blob/master/examples/socket-timeouts.rs -[stdin-echo]: https://github.com/async-rs/async-std/blob/master/examples/stdin-echo.rs -[stdin-timeout]: https://github.com/async-rs/async-std/blob/master/examples/stdin-timeout.rs -[surf-web]: https://github.com/async-rs/async-std/blob/master/examples/surf-web.rs -[task-local]: https://github.com/async-rs/async-std/blob/master/examples/task-local.rs -[task-name]: https://github.com/async-rs/async-std/blob/master/examples/task-name.rs -[tcp-client]: https://github.com/async-rs/async-std/blob/master/examples/tcp-client.rs -[tcp-echo]: https://github.com/async-rs/async-std/blob/master/examples/tcp-echo.rs -[udp-client]: https://github.com/async-rs/async-std/blob/master/examples/udp-client.rs -[udp-echo]: https://github.com/async-rs/async-std/blob/master/examples/udp-echo.rs +[hello-world]: https://github.com/async-rs/async-std/blob/HEAD/examples/hello-world.rs +[line-count]: https://github.com/async-rs/async-std/blob/HEAD/examples/line-count.rs +[list-dir]: https://github.com/async-rs/async-std/blob/HEAD/examples/list-dir.rs +[logging]: https://github.com/async-rs/async-std/blob/HEAD/examples/logging.rs +[print-file]: https://github.com/async-rs/async-std/blob/HEAD/examples/print-file.rs +[socket-timeouts]: https://github.com/async-rs/async-std/blob/HEAD/examples/socket-timeouts.rs +[stdin-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-echo.rs +[stdin-timeout]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-timeout.rs +[surf-web]: https://github.com/async-rs/async-std/blob/HEAD/examples/surf-web.rs +[task-local]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-local.rs +[task-name]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-name.rs +[tcp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-client.rs +[tcp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-echo.rs +[udp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-client.rs +[udp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-echo.rs diff --git a/src/lib.rs b/src/lib.rs index 73e722233..86786e814 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! * [The async-std website](https://async.rs/) //! * [The async-std book](https://book.async.rs) //! * [GitHub repository](https://github.com/async-rs/async-std) -//! * [List of code examples](https://github.com/async-rs/async-std/tree/master/examples) +//! * [List of code examples](https://github.com/async-rs/async-std/tree/HEAD/examples) //! * [Discord chat](https://discord.gg/JvZeVNe) //! //! # What is in the `async-std` documentation? From ae817ca1a217bebc5ef3f4015a237e77b5c6767b Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Wed, 14 Oct 2020 23:54:33 -0700 Subject: [PATCH 233/272] don't poll the reader again after eof while waiting for the writer to flush --- src/io/copy.rs | 32 ++++++++++++++++++--- tests/io_copy.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 tests/io_copy.rs diff --git a/src/io/copy.rs b/src/io/copy.rs index f05ed0e17..e8d5d733f 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -7,6 +7,12 @@ use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; use crate::utils::Context as _; +// Note: There are two otherwise-identical implementations of this +// function because unstable has removed the `?Sized` bound for the +// reader and writer and accepts `R` and `W` instead of `&mut R` and +// `&mut W`. If making a change to either of the implementations, +// ensure that you copy it into the other. + /// Copies the entire contents of a reader into a writer. /// /// This function will continuously read data from `reader` and then @@ -57,6 +63,7 @@ where #[pin] writer: W, amt: u64, + reader_eof: bool } } @@ -69,13 +76,20 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); + loop { - let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; - if buffer.is_empty() { + if *this.reader_eof { futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; return Poll::Ready(Ok(*this.amt)); } + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; + + if buffer.is_empty() { + *this.reader_eof = true; + continue; + } + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); @@ -89,6 +103,7 @@ where let future = CopyFuture { reader: BufReader::new(reader), writer, + reader_eof: false, amt: 0, }; future.await.context(|| String::from("io::copy failed")) @@ -144,6 +159,7 @@ where #[pin] writer: W, amt: u64, + reader_eof: bool } } @@ -156,13 +172,20 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); + loop { - let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; - if buffer.is_empty() { + if *this.reader_eof { futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; return Poll::Ready(Ok(*this.amt)); } + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; + + if buffer.is_empty() { + *this.reader_eof = true; + continue; + } + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); @@ -176,6 +199,7 @@ where let future = CopyFuture { reader: BufReader::new(reader), writer, + reader_eof: false, amt: 0, }; future.await.context(|| String::from("io::copy failed")) diff --git a/tests/io_copy.rs b/tests/io_copy.rs new file mode 100644 index 000000000..394c34f8e --- /dev/null +++ b/tests/io_copy.rs @@ -0,0 +1,72 @@ +use std::{ + io::Result, + pin::Pin, + task::{Context, Poll}, +}; + +struct ReaderThatPanicsAfterEof { + read_count: usize, + has_sent_eof: bool, + max_read: usize, +} + +impl async_std::io::Read for ReaderThatPanicsAfterEof { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + if self.has_sent_eof { + panic!("this should be unreachable because we should not poll after eof (Ready(Ok(0)))") + } else if self.read_count >= self.max_read { + self.has_sent_eof = true; + Poll::Ready(Ok(0)) + } else { + self.read_count += 1; + Poll::Ready(Ok(buf.len())) + } + } +} + +struct WriterThatTakesAWhileToFlush { + max_flush: usize, + flush_count: usize, +} + +impl async_std::io::Write for WriterThatTakesAWhileToFlush { + fn poll_write(self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + Poll::Ready(Ok(buf.len())) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.flush_count += 1; + if self.flush_count >= self.max_flush { + Poll::Ready(Ok(())) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +#[test] +fn io_copy_does_not_poll_after_eof() { + async_std::task::block_on(async { + let mut reader = ReaderThatPanicsAfterEof { + has_sent_eof: false, + max_read: 10, + read_count: 0, + }; + + let mut writer = WriterThatTakesAWhileToFlush { + flush_count: 0, + max_flush: 10, + }; + + assert!(async_std::io::copy(&mut reader, &mut writer).await.is_ok()); + }) +} From 27ed889c67c70c585d0e88358586103fbb7759c9 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 18 Jun 2022 10:27:40 -0700 Subject: [PATCH 234/272] CHANGELOG.md: Fix typo --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 819ca8bac..4aab4ac03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [Unreleased] ## Added - - `std::task_spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. ## Removed From df53df2b5379d2f25b59712baf1dffe1fad81c57 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 18 Jun 2022 10:23:15 -0700 Subject: [PATCH 235/272] CHANGELOG.md: Changelog for 1.12.0 --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aab4ac03..e8a0a0a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,15 @@ All notable changes to async-std will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). -# [Unreleased] +# [1.12.0] - 2022-06-18 ## Added - `std::task_spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. +- Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode. -## Removed ## Changed +- async-std no longer depends on `num_cpus`; it uses functionality in the standard library instead (via `async-global-executor`). +- Miscellaneous documentation fixes and cleanups. # [1.11.0] - 2022-03-22 From 6856d509cbecc8751a4f0705aa258aee12a08682 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 18 Jun 2022 10:24:06 -0700 Subject: [PATCH 236/272] Cargo.toml: Bump version to 1.12.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cad707232..9a7a3fe2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.11.0" +version = "1.12.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 1785b90e6f1abb39602bfb278b33f72fc12a4b88 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 18 Jun 2022 15:16:43 -0700 Subject: [PATCH 237/272] CHANGELOG.md: Fix typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8a0a0a76..20d5bd7bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [1.12.0] - 2022-06-18 ## Added -- `std::task_spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. +- `std::task::spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. - Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode. ## Changed From ba245611466cdfbd3ad9492d372ac1f81d31c787 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 20 Jun 2022 00:57:53 -0700 Subject: [PATCH 238/272] Export `BufReadExt` and `SeekExt` from `async_std::io` `async_std::io` exports `Read`, `ReadExt`, `Write`, `WriteExt`, `BufRead`, and `Seek`. But it does not export `BufReadExt` and `SeekExt`; those only appear in `async_std::io::prelude`. Export both `BufReadExt` and `SeekExt` from `async_std::io`. This makes it easier for code not using the prelude to import all of the I/O traits it uses from the same module. --- src/io/buf_read/mod.rs | 2 +- src/io/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 75247a516..bcb9d907c 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -239,7 +239,7 @@ pub trait BufReadExt: BufRead { impl BufReadExt for T {} -pub fn read_until_internal( +pub(crate) fn read_until_internal( mut reader: Pin<&mut R>, cx: &mut Context<'_>, byte: u8, diff --git a/src/io/mod.rs b/src/io/mod.rs index e20ed800b..8a415eb71 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -275,7 +275,7 @@ cfg_std! { #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; - pub use buf_read::{BufRead, Lines, Split}; + pub use buf_read::*; pub use buf_reader::BufReader; pub use buf_writer::{BufWriter, IntoInnerError}; pub use copy::copy; @@ -283,7 +283,7 @@ cfg_std! { pub use empty::{empty, Empty}; pub use read::*; pub use repeat::{repeat, Repeat}; - pub use seek::Seek; + pub use seek::*; pub use sink::{sink, Sink}; pub use write::*; From 955fa65a643038acd0edf105b33d055b66430f9d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 20 Jun 2022 03:53:33 -0700 Subject: [PATCH 239/272] Fix whitespace errors in a test --- src/fs/file.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index aae201b6d..47e2fee7c 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -904,13 +904,13 @@ mod tests { } #[test] - fn async_file_create_error () { + fn async_file_create_error() { let file_name = Path::new("/tmp/does_not_exist/test"); let expect = std::fs::File::create(file_name).unwrap_err(); crate::task::block_on(async move { let actual = File::create(file_name).await.unwrap_err(); - assert_eq!(format!("{}", expect), format!("{}", actual)); + assert_eq!(format!("{}", expect), format!("{}", actual)); }) } } From 64b7791ee5810744a478708d3dd5d4b88b74fe71 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 30 Jun 2022 02:00:09 -0700 Subject: [PATCH 240/272] When read returns EOF, ensure future reads still check the file for EOF Go back to the idle mode when returning EOF, so that the next read will make another attempt to read from the file in case the file grew. --- src/fs/file.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 47e2fee7c..a074eae28 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -670,14 +670,19 @@ impl LockGuard { match self.mode { Mode::Idle => {} + Mode::Reading(0) if self.cache.is_empty() => { + // If the cache is empty in reading mode, the last operation didn't read any bytes, + // which indicates that it reached the end of the file. In this case we need to + // reset the mode to idle so that next time we try to read again, since the file + // may grow after the first EOF. + self.mode = Mode::Idle; + return Poll::Ready(Ok(0)); + } Mode::Reading(start) => { // How many bytes in the cache are available for reading. let available = self.cache.len() - start; - // If there is cached unconsumed data or if the cache is empty, we can read from - // it. Empty cache in reading mode indicates that the last operation didn't read - // any bytes, i.e. it reached the end of the file. - if available > 0 || self.cache.is_empty() { + if available > 0 { // Copy data from the cache into the buffer. let n = cmp::min(available, buf.len()); buf[..n].copy_from_slice(&self.cache[start..(start + n)]); From dfdf56cc9a97c853e7e6caa775fdb439e87a649c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 20 Jun 2022 04:10:00 -0700 Subject: [PATCH 241/272] Test that file EOF is not permanent: reading again should give new data --- src/fs/file.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/fs/file.rs b/src/fs/file.rs index a074eae28..9a4ab77b4 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -918,4 +918,40 @@ mod tests { assert_eq!(format!("{}", expect), format!("{}", actual)); }) } + + #[test] + fn file_eof_is_not_permanent() -> crate::io::Result<()> { + let tempdir = tempfile::Builder::new() + .prefix("async-std-file-eof-test") + .tempdir()?; + let path = tempdir.path().join("testfile"); + + crate::task::block_on(async { + let mut file_w = File::create(&path).await?; + let mut file_r = File::open(&path).await?; + + file_w.write_all(b"data").await?; + file_w.flush().await?; + + let mut buf = [0u8; 4]; + let mut len = file_r.read(&mut buf).await?; + assert_eq!(len, 4); + assert_eq!(&buf, b"data"); + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 0); + + file_w.write_all(b"more").await?; + file_w.flush().await?; + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 4); + assert_eq!(&buf, b"more"); + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 0); + + Ok(()) + }) + } } From fc69db17032be235d9068bc4d8ae05eacf984752 Mon Sep 17 00:00:00 2001 From: Kian-Meng Ang Date: Thu, 30 Jun 2022 20:46:04 +0800 Subject: [PATCH 242/272] Fix typos --- CHANGELOG.md | 2 +- docs/src/security/index.md | 2 +- docs/src/tutorial/clean_shutdown.md | 2 +- src/fs/open_options.rs | 2 +- src/io/write/write_fmt.rs | 2 +- src/stream/stream/mod.rs | 2 +- src/sync/condvar.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d5bd7bb..b3b4d04b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,7 @@ deprecated `sync::channel` types, and introduces the `tokio1` feature. As part of our `1.8.0` release last month we introduced the new `async_std::channel` submodule and deprecated the unstable -`async_std::sync::channel` types. You can read our full motiviation for this +`async_std::sync::channel` types. You can read our full motivation for this change in the last patch notes. But the short version is that the old channels had some fundamental problems, and the `sync` submodule is a bit of a mess. diff --git a/docs/src/security/index.md b/docs/src/security/index.md index 5a94a5401..02fef4c0a 100644 --- a/docs/src/security/index.md +++ b/docs/src/security/index.md @@ -1,6 +1,6 @@ # Security -Writing a highly perfomant async core library is a task involving some instances of unsafe code. +Writing a highly performant async core library is a task involving some instances of unsafe code. We take great care in vetting all unsafe code included in `async-std` and do follow generally accepted practices. diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index bd112c934..0937d7086 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -245,7 +245,7 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { Notice what happens with all of the channels once we exit the accept loop: 1. First, we drop the main broker's sender. - That way when the readers are done, there's no sender for the broker's channel, and the chanel closes. + That way when the readers are done, there's no sender for the broker's channel, and the channel closes. 2. Next, the broker exits `while let Some(event) = events.next().await` loop. 3. It's crucial that, at this stage, we drop the `peers` map. This drops writer's senders. diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 91ad8cab5..18aa89eb3 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -136,7 +136,7 @@ impl OpenOptions { /// Configures the option for append mode. /// /// When set to `true`, this option means the file will be writable after opening and the file - /// cursor will be moved to the end of file before every write operaiton. + /// cursor will be moved to the end of file before every write operation. /// /// # Examples /// diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index 318b1c37f..a65e06a64 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -18,7 +18,7 @@ impl Future for WriteFmtFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Process the interal Result the first time we run. + // Process the internal Result the first time we run. if self.buffer.is_none() { match self.res.take().unwrap() { Err(err) => return Poll::Ready(Err(err)), diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b0bf1f339..144194d24 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1485,7 +1485,7 @@ pub trait StreamExt: Stream { the stream and ignore elements until it returns `false`. After `false` is returned, `SkipWhile`'s job is over and all further - elements in the strem are yielded. + elements in the stream are yielded. ## Examples diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs index 1a208efdd..b7e81ed29 100644 --- a/src/sync/condvar.rs +++ b/src/sync/condvar.rs @@ -135,7 +135,7 @@ impl Condvar { } } - /// Blocks the current taks until this condition variable receives a notification and the + /// Blocks the current task until this condition variable receives a notification and the /// required condition is met. Spurious wakeups are ignored and this function will only /// return once the condition has been met. /// From 4d5049948131aaaa203616f3849c702a23b00c31 Mon Sep 17 00:00:00 2001 From: Colerar <62297254+Colerar@users.noreply.github.com> Date: Fri, 29 Jul 2022 20:00:14 +0800 Subject: [PATCH 243/272] normalized badges style --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6d8276db..4201b9f46 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

- CI Status From fbf1ef6e72e052ee3ca5f83d8b9dc72eee3a623b Mon Sep 17 00:00:00 2001 From: jtnunley Date: Fri, 25 Nov 2022 14:13:18 -0800 Subject: [PATCH 244/272] Round up ms timeout --- src/utils.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index d1b497273..ab451a660 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -84,7 +84,13 @@ mod timer { impl Timer { pub(crate) fn after(dur: std::time::Duration) -> Self { - Timer(TimeoutFuture::new(dur.as_millis() as u32)) + // Round up to the nearest millisecond. + let mut timeout_ms = dur.as_millis() as u32; + if std::time::Duration::from_millis(timeout_ms as u64) < dur { + timeout_ms += 1; + } + + Timer(TimeoutFuture::new(timeout_ms)) } } From 0e3b11e637b882e64f0a2e30c6c0a01dece3edda Mon Sep 17 00:00:00 2001 From: icedrocket <114203630+icedrocket@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:05:12 +0900 Subject: [PATCH 245/272] docs: fix github actions badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4201b9f46..aa4469fee 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@
- CI Status From d22585d7deffad0d500b0324fb6c807653c2a67e Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Fri, 7 Apr 2023 05:09:05 +0200 Subject: [PATCH 246/272] Prevent races between dropping File LockGuard and waking its tasks By changing the inner `LockGuard` value to an `Option` and setting it to `None` in `drop()` so we can drop the `Arc` _before_ waking its tasks. --- src/fs/file.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 9a4ab77b4..6fb7ad59e 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -517,14 +517,16 @@ impl Lock { } // The lock was successfully acquired. - Poll::Ready(LockGuard(self.0.clone())) + Poll::Ready(LockGuard(Some(self.0.clone()))) } } /// A lock guard. /// /// When dropped, ownership of the inner value is returned back to the lock. -struct LockGuard(Arc>); +/// The inner value is always Some, except when the lock is dropped, where we +/// set it to None. See comment in drop(). +struct LockGuard(Option>>); unsafe impl Send for LockGuard {} unsafe impl Sync for LockGuard {} @@ -534,7 +536,7 @@ impl LockGuard { /// /// When this lock guard gets dropped, all registered tasks will be woken up. fn register(&self, cx: &Context<'_>) { - let mut list = self.0.wakers.lock().unwrap(); + let mut list = self.0.as_ref().unwrap().wakers.lock().unwrap(); if list.iter().all(|w| !w.will_wake(cx.waker())) { list.push(cx.waker().clone()); @@ -544,11 +546,22 @@ impl LockGuard { impl Drop for LockGuard { fn drop(&mut self) { + // Set the Option to None and take its value so we can drop the Arc + // before we wake up the tasks. + let lock = self.0.take().unwrap(); + + // Prepare to wake up all registered tasks interested in acquiring the lock. + let wakers: Vec<_> = lock.wakers.lock().unwrap().drain(..).collect(); + // Release the lock. - self.0.locked.store(false, Ordering::Release); + lock.locked.store(false, Ordering::Release); + + // Drop the Arc _before_ waking up the tasks, to avoid races. See + // reproducer and discussion in https://github.com/async-rs/async-std/issues/1001. + drop(lock); // Wake up all registered tasks interested in acquiring the lock. - for w in self.0.wakers.lock().unwrap().drain(..) { + for w in wakers { w.wake(); } } @@ -558,13 +571,19 @@ impl Deref for LockGuard { type Target = T; fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } + // SAFETY: Safe because the lock is held when this method is called. And + // the inner value is always Some since it is only set to None in + // drop(). + unsafe { &*self.0.as_ref().unwrap().value.get() } } } impl DerefMut for LockGuard { fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } + // SAFETY: Safe because the lock is held when this method is called. And + // the inner value is always Some since it is only set to None in + // drop(). + unsafe { &mut *self.0.as_ref().unwrap().value.get() } } } From 7c408f11dce86870a0bff17a82f346de864536bb Mon Sep 17 00:00:00 2001 From: jtnunley Date: Thu, 11 Aug 2022 13:47:00 -0700 Subject: [PATCH 247/272] add I/O safe traits --- .github/workflows/ci.yml | 15 ++++++++++ Cargo.toml | 1 + src/fs/file.rs | 60 +++++++++++++++++++++++++++++++++++-- src/io/stderr.rs | 20 +++++++++++++ src/io/stdin.rs | 20 +++++++++++++ src/io/stdout.rs | 20 +++++++++++++ src/net/tcp/listener.rs | 44 +++++++++++++++++++++++++++ src/net/tcp/stream.rs | 44 +++++++++++++++++++++++++++ src/net/udp/mod.rs | 44 +++++++++++++++++++++++++++ src/os/unix/io.rs | 4 +++ src/os/unix/net/datagram.rs | 22 ++++++++++++++ src/os/unix/net/listener.rs | 22 ++++++++++++++ src/os/unix/net/stream.rs | 22 ++++++++++++++ src/os/windows/io.rs | 7 +++++ src/utils.rs | 12 ++++++++ 15 files changed, 355 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a717826f..90c2d7f5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,6 +108,21 @@ jobs: with: command: check args: --all --features tokio02 + + check_io_safety_feature: + name: Check io_safety feature + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v3 + - name: check io_safety + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --features io_safety + cross: name: Cross compile diff --git a/Cargo.toml b/Cargo.toml index 9a7a3fe2a..c9947686a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ alloc = [ tokio1 = ["async-global-executor/tokio"] tokio02 = ["async-global-executor/tokio02"] tokio03 = ["async-global-executor/tokio03"] +io_safety = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 6fb7ad59e..dc6e5bcc1 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -415,6 +415,8 @@ impl From for File { cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() @@ -432,10 +434,36 @@ cfg_unix! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") + .expect(ARC_TRY_UNWRAP_EXPECT) .into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.file.as_fd() + } + } + + impl From for File { + fn from(fd: OwnedFd) -> Self { + std::fs::File::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(val: File) -> OwnedFd { + let file = val.file.clone(); + drop(val); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + .into() + } + } + } } cfg_windows! { @@ -458,10 +486,36 @@ cfg_windows! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") + .expect(ARC_TRY_UNWRAP_EXPECT) .into_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle}; + + impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.file.as_handle() + } + } + + impl From for File { + fn from(handle: OwnedHandle) -> Self { + std::fs::File::from(handle).into() + } + } + + impl From for OwnedHandle { + fn from(val: File) -> OwnedHandle { + let file = val.file.clone(); + drop(val); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + .into() + } + } + } } /// An async mutex with non-borrowing lock guards. @@ -974,3 +1028,5 @@ mod tests { }) } } + +const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; \ No newline at end of file diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 22dadd1f6..3f38e8dea 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -180,6 +180,16 @@ cfg_unix! { std::io::stderr().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stderr { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stderr().as_fd() + } + } + } } cfg_windows! { @@ -190,4 +200,14 @@ cfg_windows! { std::io::stderr().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stderr { + fn as_handle(&self) -> BorrowedHandle<'_> { + std::io::stderr().as_handle() + } + } + } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index c969574e5..52c31f110 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -206,6 +206,16 @@ cfg_unix! { std::io::stdin().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stderr { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdin().as_fd() + } + } + } } cfg_windows! { @@ -216,4 +226,14 @@ cfg_windows! { std::io::stdin().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdin { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdin().as_fd() + } + } + } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 45244b140..c1cd2bcfb 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -180,6 +180,16 @@ cfg_unix! { std::io::stdout().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdout { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdout().as_fd() + } + } + } } cfg_windows! { @@ -190,4 +200,14 @@ cfg_windows! { std::io::stdout().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stdout { + fn as_handle(&self) -> BorrowedHandle<'_> { + std::io::stdout().as_handle() + } + } + } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 69340db4e..d014f3387 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -282,6 +282,28 @@ cfg_unix! { self.watcher.into_inner().unwrap().into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpListener { + fn from(fd: OwnedFd) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(listener: TcpListener) -> OwnedFd { + listener.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -306,4 +328,26 @@ cfg_windows! { self.watcher.into_inner().unwrap().into_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpListener { + fn from(fd: OwnedSocket) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(listener: TcpListener) -> OwnedSocket { + listener.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index f30e4714c..955df82d0 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -416,6 +416,28 @@ cfg_unix! { self.as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpStream { + fn from(fd: OwnedFd) -> TcpStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: TcpStream) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -443,4 +465,26 @@ cfg_windows! { self.as_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpStream { + fn from(fd: OwnedSocket) -> TcpStream { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: TcpStream) -> OwnedSocket { + stream.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index e216af43a..90fdf3e25 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -562,6 +562,28 @@ cfg_unix! { self.watcher.into_inner().unwrap().into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UdpSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedFd) -> UdpSocket { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UdpSocket) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -586,4 +608,26 @@ cfg_windows! { self.watcher.into_inner().unwrap().into_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for UdpSocket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedSocket) -> UdpSocket { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: UdpSocket) -> OwnedSocket { + stream.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 0b9846074..a7708eee3 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -2,6 +2,10 @@ cfg_not_docs! { pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + cfg_io_safety! { + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } cfg_docs! { diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 7b0fc32ec..3a92c78b7 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -340,3 +340,25 @@ impl IntoRawFd for UnixDatagram { self.watcher.into_inner().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixDatagram { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixDatagram { + fn from(fd: OwnedFd) -> UnixDatagram { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixDatagram) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 1f983656f..48a6e36d7 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -233,3 +233,25 @@ impl IntoRawFd for UnixListener { self.watcher.into_inner().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixListener { + fn from(fd: OwnedFd) -> UnixListener { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixListener) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 8674e7c32..9a2387b10 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -264,3 +264,25 @@ impl IntoRawFd for UnixStream { (*self.watcher).get_ref().try_clone().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixStream { + fn from(fd: OwnedFd) -> UnixStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixStream) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 30d37a0ef..5fea53dc4 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -5,6 +5,13 @@ cfg_not_docs! { AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, }; + + cfg_io_safety! { + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } cfg_docs! { diff --git a/src/utils.rs b/src/utils.rs index ab451a660..e541b82e6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -239,3 +239,15 @@ macro_rules! cfg_default { )* } } + +/// Declares items that use I/O safety. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_io_safety { + ($($item:item)*) => { + $( + #[cfg(feature = "io-safety")] + $item + )* + } +} From e1d66f53a2a0e05da27c4417f85b371d2a9070df Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:04:04 -0700 Subject: [PATCH 248/272] code review --- src/fs/file.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index dc6e5bcc1..0dba3da97 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -15,6 +15,8 @@ use crate::prelude::*; use crate::task::{spawn_blocking, Context, Poll, Waker}; use crate::utils::Context as _; +const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; + /// An open file on the filesystem. /// /// Depending on what options the file was opened with, this type can be used for reading and/or @@ -413,9 +415,7 @@ impl From for File { } cfg_unix! { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - - + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { @@ -1027,6 +1027,4 @@ mod tests { Ok(()) }) } -} - -const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; \ No newline at end of file +} \ No newline at end of file From c63c43341a678a42963b8e4019abc5b636a4f26c Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:05:47 -0700 Subject: [PATCH 249/272] add end-of-file newline --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 0dba3da97..e0ac42904 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1027,4 +1027,4 @@ mod tests { Ok(()) }) } -} \ No newline at end of file +} From 27f26ea430337ae15ceed87ba2b4947772d2338b Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:15:59 -0700 Subject: [PATCH 250/272] iline docs --- src/os/unix/io.rs | 5 +++++ src/os/windows/io.rs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index a7708eee3..9d1fbaede 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -55,4 +55,9 @@ cfg_docs! { /// and must close the descriptor once it's no longer needed. fn into_raw_fd(self) -> RawFd; } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 5fea53dc4..caffc6fc6 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -82,4 +82,12 @@ cfg_docs! { /// it once it's no longer needed. fn into_raw_socket(self) -> RawSocket; } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } From dc7bb8e97d805c6fe26f2f2e6cf8a821a437e0d6 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Sun, 30 Apr 2023 10:00:35 -0700 Subject: [PATCH 251/272] Fix minor issues --- src/fs/file.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index e0ac42904..db80ee7e3 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -415,7 +415,16 @@ impl From for File { } cfg_unix! { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + impl File { + fn into_std_file(self) -> std::fs::File { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + } + } impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { @@ -431,11 +440,7 @@ cfg_unix! { impl IntoRawFd for File { fn into_raw_fd(self) -> RawFd { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect(ARC_TRY_UNWRAP_EXPECT) - .into_raw_fd() + self.into_std_file().into_raw_fd() } } @@ -456,11 +461,7 @@ cfg_unix! { impl From for OwnedFd { fn from(val: File) -> OwnedFd { - let file = val.file.clone(); - drop(val); - Arc::try_unwrap(file) - .expect(ARC_TRY_UNWRAP_EXPECT) - .into() + self.into_std_file() } } } From c17710366b3f5222dd7cda985b8392347a126c38 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Mon, 1 May 2023 07:05:30 -0700 Subject: [PATCH 252/272] Update src/fs/file.rs Co-authored-by: Josh Triplett --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index db80ee7e3..67dfbe7f5 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -461,7 +461,7 @@ cfg_unix! { impl From for OwnedFd { fn from(val: File) -> OwnedFd { - self.into_std_file() + self.into_std_file().into() } } } From e393c7333967b5ad3b0c79976efffb0fffe9d2e2 Mon Sep 17 00:00:00 2001 From: xxchan Date: Wed, 3 May 2023 21:52:23 +0200 Subject: [PATCH 253/272] update async-* dependencies --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9947686a..516ccc8e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,8 +61,8 @@ tokio03 = ["async-global-executor/tokio03"] io_safety = [] [dependencies] -async-attributes = { version = "1.1.1", optional = true } -async-lock = { version = "2.3.0", optional = true } +async-attributes = { version = "1.1.2", optional = true } +async-lock = { version = "2.7.0", optional = true } crossbeam-utils = { version = "0.8.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } @@ -73,17 +73,17 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } -async-channel = { version = "1.5.1", optional = true } +async-channel = { version = "1.8.0", optional = true } # dev dependency, but they are not allowed to be optional :/ surf = { version = "2.0.0", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "2.0.0", optional = true, features = ["async-io"] } -async-io = { version = "1.0.1", optional = true } +async-global-executor = { version = "2.3.1", optional = true, features = ["async-io"] } +async-io = { version = "1.13.0", optional = true } futures-lite = { version = "1.0.0", optional = true } -async-process = { version = "1.0.1", optional = true } +async-process = { version = "1.7.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] gloo-timers = { version = "0.2.1", features = ["futures"], optional = true } From 7562c3fde6bb42959c6d7aa463afe4f655eb227f Mon Sep 17 00:00:00 2001 From: Andrea Frigido Date: Sun, 13 Aug 2023 14:59:51 +0100 Subject: [PATCH 254/272] chore: Update license field following SPDX 2.1 license expression standard --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 516ccc8e5..f0bc59265 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ authors = [ "Contributors to async-std", ] edition = "2018" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" repository = "https://github.com/async-rs/async-std" homepage = "https://async.rs" description = "Async version of the Rust standard library" From 1adaa096268ddbcbbffcc33be5cd725747ded944 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 23 Nov 2023 11:58:04 +0100 Subject: [PATCH 255/272] update async-* dependencies Don't update async-channel as Receiver is now `!Unpin`. --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 516ccc8e5..2195f2450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ io_safety = [] [dependencies] async-attributes = { version = "1.1.2", optional = true } -async-lock = { version = "2.7.0", optional = true } +async-lock = { version = "3.1.0", optional = true } crossbeam-utils = { version = "0.8.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } @@ -80,10 +80,10 @@ surf = { version = "2.0.0", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "2.3.1", optional = true, features = ["async-io"] } -async-io = { version = "1.13.0", optional = true } -futures-lite = { version = "1.0.0", optional = true } -async-process = { version = "1.7.0", optional = true } +async-global-executor = { version = "2.4.0", optional = true, features = ["async-io"] } +async-io = { version = "2.2.0", optional = true } +futures-lite = { version = "2.0.0", optional = true } +async-process = { version = "2.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] gloo-timers = { version = "0.2.1", features = ["futures"], optional = true } From bbde18ffbdaf60f221c25a0d6df491fbf1df7a39 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 23 Nov 2023 14:39:23 +0100 Subject: [PATCH 256/272] fix CI for recent rustc Allow for unused `pub use` in experimental API which doesn't have its mods public for noiw. MIPS CI is fully broken (doesn't find the MIPS toolchain) so disable it for now. Reenable powerpc64 which is no longer broken --- .github/workflows/ci.yml | 4 ++-- src/collections/mod.rs | 7 +++++++ src/option/mod.rs | 1 + src/result/mod.rs | 1 + src/string/mod.rs | 1 + src/sync/waker_set.rs | 2 +- src/vec/mod.rs | 1 + 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90c2d7f5b..ca775eac8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,8 +132,8 @@ jobs: target: - i686-unknown-linux-gnu - powerpc-unknown-linux-gnu -# - powerpc64-unknown-linux-gnu - - mips-unknown-linux-gnu + - powerpc64-unknown-linux-gnu +# - mips-unknown-linux-gnu - arm-linux-androideabi steps: diff --git a/src/collections/mod.rs b/src/collections/mod.rs index ae9efaa92..745aed01c 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -11,10 +11,17 @@ pub mod hash_set; pub mod linked_list; pub mod vec_deque; +#[allow(unused)] pub use binary_heap::BinaryHeap; +#[allow(unused)] pub use btree_map::BTreeMap; +#[allow(unused)] pub use btree_set::BTreeSet; +#[allow(unused)] pub use hash_map::HashMap; +#[allow(unused)] pub use hash_set::HashSet; +#[allow(unused)] pub use linked_list::LinkedList; +#[allow(unused)] pub use vec_deque::VecDeque; diff --git a/src/option/mod.rs b/src/option/mod.rs index 76f096b3f..f0d67b77b 100644 --- a/src/option/mod.rs +++ b/src/option/mod.rs @@ -5,6 +5,7 @@ mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::option::Option; diff --git a/src/result/mod.rs b/src/result/mod.rs index cae0ebd93..fc263318b 100644 --- a/src/result/mod.rs +++ b/src/result/mod.rs @@ -5,6 +5,7 @@ mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::result::Result; diff --git a/src/string/mod.rs b/src/string/mod.rs index e382fcf2c..3f6141fa3 100644 --- a/src/string/mod.rs +++ b/src/string/mod.rs @@ -5,5 +5,6 @@ mod extend; mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::string::String; diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 243b3e33e..05590f488 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -149,7 +149,7 @@ impl WakerSet { /// Returns `true` if at least one operation was notified. #[cold] fn notify(&self, n: Notify) -> bool { - let mut inner = &mut *self.lock(); + let inner = &mut *self.lock(); let mut notified = false; for (_, opt_waker) in inner.entries.iter_mut() { diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 77a0b746b..2efd3c3f9 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -6,5 +6,6 @@ mod extend; mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::vec::Vec; From 2e8c5792185c6eee86b0d5fc7a18dd22f485d17e Mon Sep 17 00:00:00 2001 From: Rajas Paranjpe <52586855+ChocolateLoverRaj@users.noreply.github.com> Date: Sat, 25 Nov 2023 19:55:26 -0800 Subject: [PATCH 257/272] Fix typo: at a time instead of at time --- docs/src/tutorial/receiving_messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index f62b65d9c..036bc4597 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -118,7 +118,7 @@ handle.await? The `.await` waits until the client finishes, and `?` propagates the result. There are two problems with this solution however! -*First*, because we immediately await the client, we can only handle one client at time, and that completely defeats the purpose of async! +*First*, because we immediately await the client, we can only handle one client at a time, and that completely defeats the purpose of async! *Second*, if a client encounters an IO error, the whole server immediately exits. That is, a flaky internet connection of one peer brings down the whole chat room! From 79c2345e0f02a9e6da36dcab39ffac44112a9344 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:23:47 +0200 Subject: [PATCH 258/272] Updated note about cargo add since it is now integrated in cargo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa4469fee..9d25315dc 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ using Rust's familiar stdlib API. ## Installation -With [cargo-edit](https://github.com/killercup/cargo-edit) installed run: +Run this in your projects folder: ```sh $ cargo add async-std @@ -122,7 +122,7 @@ $ cargo add async-std We also provide a set of "unstable" features with async-std. See the [features documentation] on how to enable them. -[cargo-add]: https://github.com/killercup/cargo-edit +[cargo add]: https://doc.rust-lang.org/cargo/commands/cargo-add.html [features documentation]: https://docs.rs/async-std/#features ## Ecosystem From 0633c94c7b47308b61fe08b37aba69d0fc32997b Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Tue, 20 Aug 2024 08:03:49 +0800 Subject: [PATCH 259/272] Fix typos --- CHANGELOG.md | 4 ++-- src/stream/successors.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b4d04b2..4130cc021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -686,9 +686,9 @@ use async_std::prelude::*; use async_std::task; task::spawn(async { - let x = fibonnacci(1000); // Do expensive work + let x = fibonacci(1000); // Do expensive work task::yield_now().await; // Allow other tasks to run - x + fibonnacci(100) // Do more work + x + fibonacci(100) // Do more work }) ``` diff --git a/src/stream/successors.rs b/src/stream/successors.rs index a9ce40ffe..eb0e4bf48 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -42,7 +42,7 @@ pin_project! { /// /// This stream is constructed by [`successors`] function /// - /// [`successors`]: fn.succssors.html + /// [`successors`]: fn.successors.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] From 590386a388cff8af31515c0b740b8124b3d76bc4 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 20 Aug 2024 17:39:25 -0700 Subject: [PATCH 260/272] Fix compilation errors with `feature = io_safety`. Fix a typo of "io-safety" in place of "io_safety", and fix various compilation errors exposed by this fix. --- src/fs/file.rs | 2 +- src/io/stderr.rs | 4 +++- src/io/stdin.rs | 6 ++++-- src/io/stdout.rs | 4 +++- src/net/tcp/stream.rs | 2 +- src/net/udp/mod.rs | 2 +- src/os/unix/net/datagram.rs | 4 ++-- src/os/unix/net/listener.rs | 4 ++-- src/os/unix/net/stream.rs | 6 +++--- src/utils.rs | 2 +- 10 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 67dfbe7f5..7dc12dd0e 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -461,7 +461,7 @@ cfg_unix! { impl From for OwnedFd { fn from(val: File) -> OwnedFd { - self.into_std_file().into() + val.into_std_file().into() } } } diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 3f38e8dea..ca8d23d5f 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -186,7 +186,9 @@ cfg_unix! { impl AsFd for Stderr { fn as_fd(&self) -> BorrowedFd<'_> { - std::io::stderr().as_fd() + unsafe { + BorrowedFd::borrow_raw(std::io::stderr().as_raw_fd()) + } } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 52c31f110..b994bcd07 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -210,9 +210,11 @@ cfg_unix! { cfg_io_safety! { use crate::os::unix::io::{AsFd, BorrowedFd}; - impl AsFd for Stderr { + impl AsFd for Stdin { fn as_fd(&self) -> BorrowedFd<'_> { - std::io::stdin().as_fd() + unsafe { + BorrowedFd::borrow_raw(std::io::stdin().as_raw_fd()) + } } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index c1cd2bcfb..2444bbd7d 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -186,7 +186,9 @@ cfg_unix! { impl AsFd for Stdout { fn as_fd(&self) -> BorrowedFd<'_> { - std::io::stdout().as_fd() + unsafe { + BorrowedFd::borrow_raw(std::io::stdout().as_raw_fd()) + } } } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 955df82d0..a6cbc927c 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -434,7 +434,7 @@ cfg_unix! { impl From for OwnedFd { fn from(stream: TcpStream) -> OwnedFd { - stream.watcher.into_inner().unwrap().into() + stream.watcher.get_ref().try_clone().unwrap().into() } } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 90fdf3e25..7c8798f5c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -574,7 +574,7 @@ cfg_unix! { impl From for UdpSocket { fn from(fd: OwnedFd) -> UdpSocket { - std::net::TcpStream::from(fd).into() + std::net::UdpSocket::from(fd).into() } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 3a92c78b7..792860223 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -352,7 +352,7 @@ cfg_io_safety! { impl From for UnixDatagram { fn from(fd: OwnedFd) -> UnixDatagram { - std::net::TcpStream::from(fd).into() + StdUnixDatagram::from(fd).into() } } @@ -361,4 +361,4 @@ cfg_io_safety! { stream.watcher.into_inner().unwrap().into() } } -} \ No newline at end of file +} diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 48a6e36d7..b87b781f2 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -245,7 +245,7 @@ cfg_io_safety! { impl From for UnixListener { fn from(fd: OwnedFd) -> UnixListener { - std::net::TcpStream::from(fd).into() + std::os::unix::net::UnixListener::from(fd).into() } } @@ -254,4 +254,4 @@ cfg_io_safety! { stream.watcher.into_inner().unwrap().into() } } -} \ No newline at end of file +} diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 9a2387b10..4a4f45ad6 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -276,13 +276,13 @@ cfg_io_safety! { impl From for UnixStream { fn from(fd: OwnedFd) -> UnixStream { - std::net::TcpStream::from(fd).into() + std::os::unix::net::UnixStream::from(fd).into() } } impl From for OwnedFd { fn from(stream: UnixStream) -> OwnedFd { - stream.watcher.into_inner().unwrap().into() + stream.watcher.get_ref().try_clone().unwrap().into() } } -} \ No newline at end of file +} diff --git a/src/utils.rs b/src/utils.rs index e541b82e6..853ace0e9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -246,7 +246,7 @@ macro_rules! cfg_default { macro_rules! cfg_io_safety { ($($item:item)*) => { $( - #[cfg(feature = "io-safety")] + #[cfg(feature = "io_safety")] $item )* } From 79c89d779da9ebcd9d098d623e26c5c7e9d72220 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 20 Aug 2024 19:43:54 +0200 Subject: [PATCH 261/272] deps: update gloo-timers to 0.3 --- Cargo.toml | 2 +- src/stream/mod.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 094dae161..34ae7add5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ futures-lite = { version = "2.0.0", optional = true } async-process = { version = "2.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -gloo-timers = { version = "0.2.1", features = ["futures"], optional = true } +gloo-timers = { version = "0.3.0", features = ["futures"], optional = true } wasm-bindgen-futures = { version = "0.4.10", optional = true } futures-channel = { version = "0.3.4", optional = true } diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f7f2727a1..504face8b 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -40,6 +40,10 @@ //! type Item; //! fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; //! } +//! # impl Stream for () { +//! # type Item = (); +//! # fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Pending } +//! # } //! ``` //! //! A stream has a method, [`next`], which when called, returns an From 57eafb41d085d9b9f771f67889e890b80670fe54 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 20 Aug 2024 22:23:57 +0200 Subject: [PATCH 262/272] silence warnings reported by newer rust versions --- examples/a-chat/server.rs | 1 + src/future/future/mod.rs | 2 ++ src/io/buf_writer.rs | 2 +- src/io/read/bytes.rs | 2 +- src/io/read/chain.rs | 2 +- src/io/stderr.rs | 6 ++++-- src/io/stdin.rs | 10 ++++++---- src/io/stdout.rs | 6 ++++-- src/lib.rs | 2 -- src/net/tcp/stream.rs | 4 ++-- src/net/udp/mod.rs | 2 +- src/stream/mod.rs | 3 ++- src/utils.rs | 7 ++----- 13 files changed, 27 insertions(+), 22 deletions(-) diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs index e049a490e..d3ac74699 100644 --- a/examples/a-chat/server.rs +++ b/examples/a-chat/server.rs @@ -96,6 +96,7 @@ async fn connection_writer_loop( None => break, }, void = shutdown.next().fuse() => match void { + #[allow(unreachable_patterns)] Some(void) => match void {}, None => break, } diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 47187b235..9a8bfcc1f 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -27,6 +27,7 @@ pub use core::future::Future as Future; [`Future`]: ../future/trait.Future.html "#] +#[cfg(any(feature = "std", feature = "docs"))] pub trait FutureExt: Future { /// Returns a Future that delays execution for a specified time. /// @@ -284,5 +285,6 @@ pub trait FutureExt: Future { } } +#[cfg(any(feature = "std", feature = "docs"))] impl FutureExt for T {} diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index c972937fd..230954e88 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -114,7 +114,7 @@ pin_project! { /// # Ok(()) }) } ///``` #[derive(Debug)] -pub struct IntoInnerError(W, crate::io::Error); +pub struct IntoInnerError(W, #[allow(dead_code)] crate::io::Error); impl BufWriter { /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, diff --git a/src/io/read/bytes.rs b/src/io/read/bytes.rs index ab9259611..786fdaa57 100644 --- a/src/io/read/bytes.rs +++ b/src/io/read/bytes.rs @@ -32,7 +32,7 @@ impl Stream for Bytes { } } -#[cfg(all(test, default))] +#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 4fcdb0ec9..b5eac5814 100644 --- a/src/io/read/chain.rs +++ b/src/io/read/chain.rs @@ -165,7 +165,7 @@ impl BufRead for Chain { } } -#[cfg(all(test, default))] +#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index ca8d23d5f..81cc197b9 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -204,11 +204,13 @@ cfg_windows! { } cfg_io_safety! { - use crate::os::unix::io::{AsHandle, BorrowedHandle}; + use crate::os::windows::io::{AsHandle, BorrowedHandle}; impl AsHandle for Stderr { fn as_handle(&self) -> BorrowedHandle<'_> { - std::io::stderr().as_handle() + unsafe { + BorrowedHandle::borrow_raw(std::io::stderr().as_raw_handle()) + } } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index b994bcd07..d8f96d49e 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -230,11 +230,13 @@ cfg_windows! { } cfg_io_safety! { - use crate::os::unix::io::{AsFd, BorrowedFd}; + use crate::os::windows::io::{AsHandle, BorrowedHandle}; - impl AsFd for Stdin { - fn as_fd(&self) -> BorrowedFd<'_> { - std::io::stdin().as_fd() + impl AsHandle for Stdin { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { + BorrowedHandle::borrow_raw(std::io::stdin().as_raw_handle()) + } } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 2444bbd7d..3cc570dc8 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -204,11 +204,13 @@ cfg_windows! { } cfg_io_safety! { - use crate::os::unix::io::{AsHandle, BorrowedHandle}; + use crate::os::windows::io::{AsHandle, BorrowedHandle}; impl AsHandle for Stdout { fn as_handle(&self) -> BorrowedHandle<'_> { - std::io::stdout().as_handle() + unsafe { + BorrowedHandle::borrow_raw(std::io::stdout().as_raw_handle()) + } } } } diff --git a/src/lib.rs b/src/lib.rs index 86786e814..48eaa0693 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,8 +284,6 @@ #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] -extern crate alloc; - #[macro_use] mod utils; diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index a6cbc927c..3311f904c 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -477,13 +477,13 @@ cfg_windows! { impl From for TcpStream { fn from(fd: OwnedSocket) -> TcpStream { - std::net::TcpListener::from(fd).into() + std::net::TcpStream::from(fd).into() } } impl From for OwnedSocket { fn from(stream: TcpStream) -> OwnedSocket { - stream.watcher.into_inner().unwrap().into() + stream.watcher.get_ref().try_clone().unwrap().into() } } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 7c8798f5c..3bb2c6e9c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -620,7 +620,7 @@ cfg_windows! { impl From for UdpSocket { fn from(fd: OwnedSocket) -> UdpSocket { - std::net::TcpListener::from(fd).into() + std::net::UdpSocket::from(fd).into() } } diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 504face8b..8dd7d6339 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -34,9 +34,10 @@ //! [`Stream`] looks like this: //! //! ``` +//! #![allow(dead_code)] //! # use async_std::task::{Context, Poll}; //! # use std::pin::Pin; -//! trait Stream { +//! pub trait Stream { //! type Item; //! fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; //! } diff --git a/src/utils.rs b/src/utils.rs index 853ace0e9..d1cb063bd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,3 @@ -use alloc::string::String; - /// Calls a function and aborts if it panics. /// /// This is useful in unsafe code where we can't recover from panics. @@ -55,6 +53,7 @@ pub fn random(n: u32) -> u32 { } /// Add additional context to errors +#[cfg(feature = "std")] pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; } @@ -148,7 +147,7 @@ macro_rules! cfg_unstable_default { ($($item:item)*) => { $( #[cfg(all(feature = "default", feature = "unstable"))] - #[cfg_attr(feature = "docs", doc(unstable))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] $item )* } @@ -161,7 +160,6 @@ macro_rules! cfg_unix { ($($item:item)*) => { $( #[cfg(any(unix, feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unix)))] $item )* } @@ -174,7 +172,6 @@ macro_rules! cfg_windows { ($($item:item)*) => { $( #[cfg(any(windows, feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(windows)))] $item )* } From 566684c240bf3d5bfd4df9290791d30104e9bed8 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 20 Aug 2024 22:14:17 +0200 Subject: [PATCH 263/272] CHANGELOG.md: updates for 1.13.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4130cc021..79b290bf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to async-std will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). +# [1.13.0] - 2024-09-06 + +## Added +- IO Safety traits implementations + +## Changed +- Various dependencies updates +- Export `BufReadExt` and `SeekExt` from `async_std::io` + # [1.12.0] - 2022-06-18 ## Added From 0c382dbf370544d9173ceadd38c93bad09b1f984 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 20 Aug 2024 22:15:14 +0200 Subject: [PATCH 264/272] Bump version to 1.13.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 34ae7add5..e074a30d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.12.0" +version = "1.13.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 355ce2660592f077847f3d99fc27d582e67a36e0 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sun, 8 Sep 2024 08:55:46 +0800 Subject: [PATCH 265/272] docs: Minor fixes to CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b290bf1..e314ffbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [1.12.0] - 2022-06-18 ## Added -- `std::task::spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. +- `async_std::task::spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. - Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode. ## Changed @@ -307,7 +307,7 @@ Including improved performance, stability, and the addition of various - Fixed documentation for `UdpSocket::send` ([#671](https://github.com/async-rs/async-std/pull/671)) - Fixed typo in stream documentation ([#650](https://github.com/async-rs/async-std/pull/650)) - Fixed typo on `sync::JoinHandle` documentation ([#659](https://github.com/async-rs/async-std/pull/659)) -- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/662)) +- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/661)) - Removed the use of rustfmt's unstable `format_code_in_doc_comments` option which failed CI ([#685](https://github.com/async-rs/async-std/pull/685)) - Fixed a code typo in the `task::sleep` example ([#688](https://github.com/async-rs/async-std/pull/688)) From dba67cd14eb5bc70f15c5846633f46828daee902 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sun, 8 Sep 2024 09:38:19 +0800 Subject: [PATCH 266/272] Add rust-version 1.63 --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index e074a30d2..85237a2cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = [ "Contributors to async-std", ] edition = "2018" +rust-version = "1.63" license = "Apache-2.0 OR MIT" repository = "https://github.com/async-rs/async-std" homepage = "https://async.rs" From 6fd127808d2db180d588a4e54302a09c9b3101ce Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sun, 8 Sep 2024 10:04:38 +0800 Subject: [PATCH 267/272] chore: Fix rustdoc lints --- src/lib.rs | 5 +++-- src/sync/mod.rs | 4 ++-- src/task/block_on.rs | 8 +++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 48eaa0693..21f5f8acc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(rustdoc::invalid_html_tags)] //! # Async version of the Rust standard library //! //! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested @@ -191,7 +192,7 @@ //! unstable +//! > unstable //! are available only when the `unstable` Cargo feature is enabled: //! //! ```toml @@ -204,7 +205,7 @@ //! attributes +//! > attributes //! are available only when the `attributes` Cargo feature is enabled: //! //! ```toml diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 35203a6ea..1d8579614 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -95,9 +95,9 @@ //! at the same time: In multi-threaded scenarios, you can use two //! kinds of primitives to deal with synchronization: //! - [memory fences] to ensure memory accesses are made visible to -//! other CPUs in the right order. +//! other CPUs in the right order. //! - [atomic operations] to ensure simultaneous access to the same -//! memory location doesn't lead to undefined behavior. +//! memory location doesn't lead to undefined behavior. //! //! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching //! [compiler fences]: https://doc.rust-lang.org/std/sync/atomic/fn.compiler_fence.html diff --git a/src/task/block_on.rs b/src/task/block_on.rs index fa66f915b..3ab4dc06f 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -19,11 +19,9 @@ use crate::task::Builder; /// ```no_run /// use async_std::task; /// -/// fn main() { -/// task::block_on(async { -/// println!("Hello, world!"); -/// }) -/// } +/// task::block_on(async { +/// println!("Hello, world!"); +/// }) /// ``` #[cfg(not(target_os = "unknown"))] pub fn block_on(future: F) -> T From 7b3839bf21d63fc0402f8716fa93f6ea5d796b0e Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Wed, 11 Sep 2024 06:51:22 +0800 Subject: [PATCH 268/272] Add MSRV 1.63 to CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca775eac8..a3eb933dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [nightly, beta, stable] + rust: [nightly, beta, stable, 1.63] steps: - uses: actions/checkout@v3 From 5e74d1b88da2233c6f4d443804eb27545c74164b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Mar 2025 21:20:14 +0000 Subject: [PATCH 269/272] Remove `deny(warnings)` which is causing CI to fail --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 21f5f8acc..b807d448d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,7 +281,7 @@ #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![allow(clippy::mutex_atomic, clippy::module_inception)] -#![doc(test(attr(deny(rust_2018_idioms, warnings))))] +#![doc(test(attr(deny(rust_2018_idioms))))] #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] From fb56bffdbb4699e1add70a0f834dee6f57c398eb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 21 Feb 2025 21:37:15 +0100 Subject: [PATCH 270/272] Officially sunset async-std --- CHANGELOG.md | 6 ++++ Cargo.toml | 4 +-- README.md | 88 +++++++++------------------------------------------- 3 files changed, 22 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e314ffbce..ebb3ffd80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to async-std will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). +# [1.13.1] - 2025-02-21 + +`async-std` has officially been discontinued. We recommend that all users and +libraries migrate to the excellent [`smol`](https://github.com/smol-rs/smol/) +project. + # [1.13.0] - 2024-09-06 ## Added diff --git a/Cargo.toml b/Cargo.toml index 85237a2cd..fed440172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.13.0" +version = "1.13.1" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", @@ -12,7 +12,7 @@ rust-version = "1.63" license = "Apache-2.0 OR MIT" repository = "https://github.com/async-rs/async-std" homepage = "https://async.rs" -description = "Async version of the Rust standard library" +description = "Deprecated in favor of `smol` - Async version of the Rust standard library" keywords = ["async", "await", "future", "std", "task"] categories = ["asynchronous", "concurrency", "network-programming"] diff --git a/README.md b/README.md index 9d25315dc..f432898dc 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,19 @@ -

async-std

-
- - Async version of the Rust standard library - -
+# `async-std` has been discontinued; use `smol` instead -
+We created `async-std` to demonstrate the value of making a library as close to +`std` as possible, but async. We think that demonstration was successful, and +we hope it will influence future design and development directions of async in +`std`. However, in the meantime, the [`smol`](https://github.com/smol-rs/smol/) +project came about and provided a great executor and libraries for asynchronous +use in the Rust ecosystem. We think that resources would be better spent +consolidating around `smol`, rather than continuing to provide occasional +maintenance of `async-std`. As such, we recommend that all users of +`async-std`, and all libraries built on `async-std`, switch to `smol` instead. + +In addition to the `smol` project as a direct replacement, you may find other +parts of the futures ecosystem useful, including `futures-concurrency`, +`async-io`, `futures-lite`, and `async-compat`. -
- - - CI Status - - - - Crates.io version - - - - Download - - - - docs.rs docs - - - - chat - -

@@ -44,14 +24,6 @@ Book - | - - Releases - - | - - Contributing -

@@ -111,38 +83,6 @@ creation, with an adaptive lock-free executor, threadpool and network driver to create a smooth system that processes work at a high pace with low latency, using Rust's familiar stdlib API. -## Installation - -Run this in your projects folder: - -```sh -$ cargo add async-std -``` - -We also provide a set of "unstable" features with async-std. See the [features -documentation] on how to enable them. - -[cargo add]: https://doc.rust-lang.org/cargo/commands/cargo-add.html -[features documentation]: https://docs.rs/async-std/#features - -## Ecosystem - - * [async-tls](https://crates.io/crates/async-tls) — Async TLS/SSL streams using **Rustls**. - - * [async-native-tls](https://crates.io/crates/async-native-tls) — **Native TLS** for Async. Native TLS for futures and async-std. - - * [async-tungstenite](https://crates.io/crates/async-tungstenite) — Asynchronous **WebSockets** for async-std, tokio, gio and any std Futures runtime. - - * [Tide](https://crates.io/crates/tide) — Serve the web. A modular **web framework** built around async/await. - - * [SQLx](https://crates.io/crates/sqlx) — The Rust **SQL** Toolkit. SQLx is a 100% safe Rust library for Postgres and MySQL with compile-time checked queries. - - * [Surf](https://crates.io/crates/surf) — Surf the web. Surf is a friendly **HTTP client** built for casual Rustaceans and veterans alike. - - * [Xactor](https://crates.io/crates/xactor) — Xactor is a rust actors framework based on async-std. - - * [async-graphql](https://crates.io/crates/async-graphql) — A GraphQL server library implemented in rust, with full support for async/await. - ## License From 812cc80987ddc16a9b853ade5e760dd921f2bace Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 18:41:55 -0700 Subject: [PATCH 271/272] Add deprecation notice to the top of the library documentation This ensures it'll show up on docs.rs, in addition to the notice currently in the README and crates.io. --- src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b807d448d..0caf23ee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,20 @@ #![allow(rustdoc::invalid_html_tags)] +//! # `async-std` has been discontinued; use `smol` instead +//! +//! We created `async-std` to demonstrate the value of making a library as close to +//! `std` as possible, but async. We think that demonstration was successful, and +//! we hope it will influence future design and development directions of async in +//! `std`. However, in the meantime, the [`smol`](https://github.com/smol-rs/smol/) +//! project came about and provided a great executor and libraries for asynchronous +//! use in the Rust ecosystem. We think that resources would be better spent +//! consolidating around `smol`, rather than continuing to provide occasional +//! maintenance of `async-std`. As such, we recommend that all users of +//! `async-std`, and all libraries built on `async-std`, switch to `smol` instead. +//! +//! In addition to the `smol` project as a direct replacement, you may find other +//! parts of the futures ecosystem useful, including `futures-concurrency`, +//! `async-io`, `futures-lite`, and `async-compat`. +//! //! # Async version of the Rust standard library //! //! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested From 844b552531521f517a2b02c4ed685ae176608247 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 18:42:49 -0700 Subject: [PATCH 272/272] Bump version to 1.13.2; no changes other than deprecation notice in docs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fed440172..2e944394b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.13.1" +version = "1.13.2" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ",

for PathBuf { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 8a8e0eaf3..68fa535fa 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -5,6 +5,8 @@ use crate::stream::{FromStream, IntoStream}; impl FromStream> for Result where + T: Send, + E: Send, V: FromStream, { /// Takes each element in the stream: if it is an `Err`, no further @@ -30,7 +32,10 @@ where #[inline] fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 702cbcac6..0c7d41049 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -34,7 +34,9 @@ pub trait Extend { fn extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, - ) -> Pin + 'a>>; + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send; } /// Extends a collection with the contents of a stream. @@ -69,6 +71,7 @@ pub async fn extend<'a, C, T, S>(collection: &mut C, stream: S) where C: Extend, S: IntoStream + 'a, + ::IntoStream: Send, { Extend::extend(collection, stream).await } diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 12d89e813..c4001917c 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -72,7 +72,10 @@ use crate::stream::IntoStream; /// impl FromStream for MyCollection { /// fn from_stream<'a, S: IntoStream + 'a>( /// stream: S, -/// ) -> Pin + 'a>> { +/// ) -> Pin + 'a + Send>> +/// where +/// ::IntoStream: Send, +/// { /// let stream = stream.into_stream(); /// /// Box::pin(async move { @@ -107,12 +110,12 @@ use crate::stream::IntoStream; /// assert_eq!(c.0, vec![5, 5, 5, 5, 5]); /// # /// # Ok(()) }) } -///``` +/// ``` /// /// [`IntoStream`]: trait.IntoStream.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub trait FromStream { +pub trait FromStream { /// Creates a value from a stream. /// /// # Examples @@ -135,5 +138,7 @@ pub trait FromStream { /// ``` fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>>; + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send; } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4be0eb5f9..4e074a2f3 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1888,10 +1888,11 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn collect<'a, B>( self, - ) -> impl Future + 'a [Pin + 'a>>] + ) -> impl Future + 'a [Pin + 'a + Send>>] where - Self: Sized + 'a, + Self: Sized + 'a + Send, B: FromStream, + Self::Item: Send, { FromStream::from_stream(self) } diff --git a/src/string/extend.rs b/src/string/extend.rs index 55bec0c55..eae824cbd 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -8,7 +8,10 @@ impl stream::Extend for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); @@ -26,7 +29,10 @@ impl<'b> stream::Extend<&'b char> for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -43,7 +49,10 @@ impl<'b> stream::Extend<&'b str> for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -60,7 +69,10 @@ impl stream::Extend for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -77,7 +89,10 @@ impl<'b> stream::Extend> for String { fn extend<'a, S: IntoStream> + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs index 375ac3715..d9e824681 100644 --- a/src/string/from_stream.rs +++ b/src/string/from_stream.rs @@ -8,7 +8,10 @@ impl FromStream for String { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -23,7 +26,10 @@ impl<'b> FromStream<&'b char> for String { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -38,7 +44,10 @@ impl<'b> FromStream<&'b str> for String { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -53,7 +62,10 @@ impl FromStream for String { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { @@ -68,7 +80,10 @@ impl<'b> FromStream> for String { #[inline] fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/unit/extend.rs b/src/unit/extend.rs index 55c8e0d08..ffc0c2d9d 100644 --- a/src/unit/extend.rs +++ b/src/unit/extend.rs @@ -4,10 +4,13 @@ use crate::prelude::*; use crate::stream::{self, IntoStream}; impl stream::Extend<()> for () { - fn extend<'a, T: IntoStream + 'a>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, - stream: T, - ) -> Pin + 'a>> { + stream: S, + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/unit/from_stream.rs b/src/unit/from_stream.rs index 7b784383d..e88758323 100644 --- a/src/unit/from_stream.rs +++ b/src/unit/from_stream.rs @@ -7,7 +7,10 @@ impl FromStream<()> for () { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { Box::pin(stream.into_stream().for_each(drop)) } } diff --git a/src/vec/extend.rs b/src/vec/extend.rs index 302fc7a87..717338ab7 100644 --- a/src/vec/extend.rs +++ b/src/vec/extend.rs @@ -3,11 +3,14 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{self, IntoStream}; -impl stream::Extend for Vec { +impl stream::Extend for Vec { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index e88e8202e..95a1f98c9 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -6,13 +6,13 @@ use std::sync::Arc; use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for Vec { +impl FromStream for Vec { #[inline] fn from_stream<'a, S: IntoStream>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: 'a + Send, { let stream = stream.into_stream(); @@ -24,11 +24,14 @@ impl FromStream for Vec { } } -impl<'b, T: Clone> FromStream for Cow<'b, [T]> { +impl<'b, T: Clone + Send> FromStream for Cow<'b, [T]> { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send + { let stream = stream.into_stream(); Box::pin(async move { @@ -37,11 +40,14 @@ impl<'b, T: Clone> FromStream for Cow<'b, [T]> { } } -impl FromStream for Box<[T]> { +impl FromStream for Box<[T]> { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send + { let stream = stream.into_stream(); Box::pin(async move { @@ -50,11 +56,14 @@ impl FromStream for Box<[T]> { } } -impl FromStream for Rc<[T]> { +impl FromStream for Rc<[T]> { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send + { let stream = stream.into_stream(); Box::pin(async move { @@ -63,11 +72,14 @@ impl FromStream for Rc<[T]> { } } -impl FromStream for Arc<[T]> { +impl FromStream for Arc<[T]> { #[inline] fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send + { let stream = stream.into_stream(); Box::pin(async move { diff --git a/tests/collect.rs b/tests/collect.rs new file mode 100644 index 000000000..d24484f4e --- /dev/null +++ b/tests/collect.rs @@ -0,0 +1,20 @@ +#[cfg(feature = "unstable")] +#[test] +fn test_send() -> async_std::io::Result<()> { + use async_std::prelude::*; + use async_std::{stream, task}; + + task::block_on(async { + fn test_send_trait(_: &T) {} + + let stream = stream::repeat(1u8).take(10); + test_send_trait(&stream); + + let fut = stream.collect::>(); + + // This line triggers a compilation error + test_send_trait(&fut); + + Ok(()) + }) +} From cd7fb9dec248fc6a79d3af96cc86297363b3a5be Mon Sep 17 00:00:00 2001 From: Jonathas-Conceicao Date: Mon, 13 Jul 2020 10:37:18 -0300 Subject: [PATCH 121/272] channel doc: Fix misleading reference to None return on Receiver Signed-off-by: Jonathas-Conceicao --- src/sync/channel.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 3207846c1..928cfc5de 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -22,8 +22,8 @@ use crate::sync::WakerSet; /// This channel has a buffer that can hold at most `cap` messages at a time. /// /// Senders and receivers can be cloned. When all senders associated with a channel get dropped, it -/// becomes closed. Receive operations on a closed and empty channel return `None` instead of -/// trying to await a message. +/// becomes closed. Receive operations on a closed and empty channel return [RecvError] instead of +/// trying to await a message when using [Receiver::recv] or `None` when used as a [Stream]. /// /// # Panics /// @@ -376,7 +376,7 @@ impl Receiver { /// /// If the channel is empty and still has senders, this method /// will wait until a message is sent into it. Once all senders - /// have been dropped it will return `None`. + /// have been dropped it will return [RecvError]. /// /// # Examples /// From 4ce1d61cb3051a145a3f9daa5809e07ca7fe4535 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Mon, 20 Jul 2020 09:26:22 +0200 Subject: [PATCH 122/272] ci: disable ppc64 cross build for now It's broken, see #839 Signed-off-by: Marc-Antoine Perennou --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b9439bde..a24367c85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,7 +129,7 @@ jobs: target: - i686-unknown-linux-gnu - powerpc-unknown-linux-gnu - - powerpc64-unknown-linux-gnu +# - powerpc64-unknown-linux-gnu - mips-unknown-linux-gnu - arm-linux-androideabi From 8886039ac585a87b88a0765a1455a0fa9b9ca820 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 24 Jul 2020 21:03:50 +0200 Subject: [PATCH 123/272] fix build with -default +unstable and add a CI check for it Fixes #842 Signed-off-by: Marc-Antoine Perennou --- .github/workflows/ci.yml | 6 ++++++ src/os/windows/mod.rs | 1 + src/utils.rs | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a24367c85..a4f85ea2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,12 @@ jobs: command: check args: --features attributes + - name: build unstable only + uses: actions-rs/cargo@v1 + with: + command: build + args: --no-default-features --features unstable + - name: tests uses: actions-rs/cargo@v1 with: diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 6dac11b6e..67bf0372a 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -5,5 +5,6 @@ cfg_std! { } cfg_unstable! { + #[cfg(feature = "default")] pub mod fs; } diff --git a/src/utils.rs b/src/utils.rs index 31290e333..db916ed6b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -66,7 +66,7 @@ mod timer { #[cfg(any(feature = "unstable", feature = "default"))] pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { - #[cfg(not(target_os = "unknown"))] + #[cfg(all(not(target_os = "unknown"), feature = "default"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); Timer::after(dur) From 25e0e1abdc7d5b3fac5f28d2d3d47288fec761ab Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 15 Jul 2020 22:05:28 +0200 Subject: [PATCH 124/272] switch to async-io Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 ++ src/net/tcp/listener.rs | 8 ++++---- src/net/tcp/stream.rs | 4 ++-- src/net/udp/mod.rs | 8 ++++---- src/os/unix/net/datagram.rs | 4 ++-- src/os/unix/net/listener.rs | 4 ++-- src/os/unix/net/stream.rs | 2 +- src/utils.rs | 6 +++--- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a5b5c4f06..e748953da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] default = [ "std", + "async-io", "async-task", "kv-log-macro", "log", @@ -77,6 +78,7 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] +async-io = { version = "0.1.5", optional = true } smol = { version = "0.1.17", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 09f5812fb..8c87fc5f0 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; -use smol::Async; +use async_io::Async; use crate::io; use crate::net::{TcpStream, ToSocketAddrs}; @@ -81,7 +81,7 @@ impl TcpListener { let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - match Async::::bind(&addr) { + match Async::::bind(addr) { Ok(listener) => { return Ok(TcpListener { watcher: listener }); } @@ -227,7 +227,7 @@ cfg_unix! { impl IntoRawFd for TcpListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() } } } @@ -251,7 +251,7 @@ cfg_windows! { impl IntoRawSocket for TcpListener { fn into_raw_socket(self) -> RawSocket { - self.watcher.into_raw_socket() + self.watcher.into_inner().unwrap().into_raw_socket() } } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 63232fa35..f7bd5c919 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -2,7 +2,7 @@ use std::io::{IoSlice, IoSliceMut}; use std::net::SocketAddr; use std::pin::Pin; -use smol::Async; +use async_io::Async; use crate::io::{self, Read, Write}; use crate::net::ToSocketAddrs; @@ -77,7 +77,7 @@ impl TcpStream { let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - match Async::::connect(&addr).await { + match Async::::connect(addr).await { Ok(stream) => { return Ok(TcpStream { watcher: Arc::new(stream), diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index d361a6fce..dd47e0582 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -2,7 +2,7 @@ use std::io; use std::net::SocketAddr; use std::net::{Ipv4Addr, Ipv6Addr}; -use smol::Async; +use async_io::Async; use crate::net::ToSocketAddrs; use crate::utils::Context as _; @@ -74,7 +74,7 @@ impl UdpSocket { let addrs = addrs.to_socket_addrs().await?; for addr in addrs { - match Async::::bind(&addr) { + match Async::::bind(addr) { Ok(socket) => { return Ok(UdpSocket { watcher: socket }); } @@ -506,7 +506,7 @@ cfg_unix! { impl IntoRawFd for UdpSocket { fn into_raw_fd(self) -> RawFd { - self.watcher.into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() } } } @@ -530,7 +530,7 @@ cfg_windows! { impl IntoRawSocket for UdpSocket { fn into_raw_socket(self) -> RawSocket { - self.watcher.into_raw_socket() + self.watcher.into_inner().unwrap().into_raw_socket() } } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 52c6b07f1..83ef9fe95 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -4,7 +4,7 @@ use std::fmt; use std::net::Shutdown; use std::os::unix::net::UnixDatagram as StdUnixDatagram; -use smol::Async; +use async_io::Async; use super::SocketAddr; use crate::io; @@ -335,6 +335,6 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { - self.watcher.into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index a63bd4b65..078b780d0 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -5,7 +5,7 @@ use std::future::Future; use std::os::unix::net::UnixListener as StdUnixListener; use std::pin::Pin; -use smol::Async; +use async_io::Async; use super::SocketAddr; use super::UnixStream; @@ -217,6 +217,6 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 74bd6aef9..3b2fe36f4 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -5,7 +5,7 @@ use std::net::Shutdown; use std::os::unix::net::UnixStream as StdUnixStream; use std::pin::Pin; -use smol::Async; +use async_io::Async; use super::SocketAddr; use crate::io::{self, Read, Write}; diff --git a/src/utils.rs b/src/utils.rs index db916ed6b..f3299f636 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -61,7 +61,7 @@ pub(crate) trait Context { #[cfg(all(not(target_os = "unknown"), feature = "default"))] mod timer { - pub type Timer = smol::Timer; + pub type Timer = async_io::Timer; } #[cfg(any(feature = "unstable", feature = "default"))] @@ -69,7 +69,7 @@ pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { #[cfg(all(not(target_os = "unknown"), feature = "default"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - Timer::after(dur) + Timer::new(dur) } #[cfg(any( @@ -84,7 +84,7 @@ mod timer { pub(crate) struct Timer(futures_timer::Delay); impl Timer { - pub(crate) fn after(dur: std::time::Duration) -> Self { + pub(crate) fn new(dur: std::time::Duration) -> Self { Timer(futures_timer::Delay::new(dur)) } } From 2fe087bd0aff4e585904dc42c565e34bf7836ae4 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 22 Jul 2020 09:12:48 +0200 Subject: [PATCH 125/272] switch to blocking Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 ++ src/task/spawn_blocking.rs | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e748953da..36322d99a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ default = [ "std", "async-io", "async-task", + "blocking", "kv-log-macro", "log", "num_cpus", @@ -79,6 +80,7 @@ surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] async-io = { version = "0.1.5", optional = true } +blocking = { version = "0.5.0", optional = true } smol = { version = "0.1.17", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index e9ed0c5a0..9c836f24b 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,4 +1,4 @@ -use crate::task::{JoinHandle, Task}; +use crate::task::{self, JoinHandle}; /// Spawns a blocking task. /// @@ -35,8 +35,5 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - - let handle = smol::Task::blocking(async move { f() }).into(); - JoinHandle::new(handle, Task::new(None)) + task::spawn(async move { blocking::unblock!(f()) }) } From 48693fccc3e7fff633441ddd644c804cef9ba4ae Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 22 Jul 2020 09:13:16 +0200 Subject: [PATCH 126/272] switch to futures-lite Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 ++ src/fs/file.rs | 2 +- src/task/builder.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 36322d99a..1f880665f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ default = [ "async-io", "async-task", "blocking", + "futures-lite", "kv-log-macro", "log", "num_cpus", @@ -81,6 +82,7 @@ surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] async-io = { version = "0.1.5", optional = true } blocking = { version = "0.5.0", optional = true } +futures-lite = { version = "0.1.8", optional = true } smol = { version = "0.1.17", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/fs/file.rs b/src/fs/file.rs index 2ff5643e7..56d292b97 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -315,7 +315,7 @@ impl Drop for File { // non-blocking fashion, but our only other option here is losing data remaining in the // write cache. Good task schedulers should be resilient to occasional blocking hiccups in // file destructors so we don't expect this to be a common problem in practice. - let _ = smol::block_on(self.flush()); + let _ = futures_lite::future::block_on(self.flush()); } } diff --git a/src/task/builder.rs b/src/task/builder.rs index 0024f8ab8..2fbcff923 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -169,7 +169,7 @@ impl Builder { // The first call should use run. smol::run(wrapped) } else { - smol::block_on(wrapped) + futures_lite::future::block_on(wrapped) }; num_nested_blocking.replace(num_nested_blocking.get() - 1); res From 0c51283bfcfb1b76796431b3d668f47510abab23 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Sat, 18 Jul 2020 20:32:56 +0200 Subject: [PATCH 127/272] switch to multitask Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 12 ++++-- src/task/builder.rs | 14 +++---- src/task/executor.rs | 91 +++++++++++++++++++++++++++++++++++++++++ src/task/join_handle.rs | 21 ++++++---- src/task/mod.rs | 2 + 5 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/task/executor.rs diff --git a/Cargo.toml b/Cargo.toml index 1f880665f..e58401463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,9 @@ default = [ "futures-lite", "kv-log-macro", "log", + "multitask", "num_cpus", "pin-project-lite", - "smol", ] docs = ["attributes", "unstable", "default"] unstable = [ @@ -57,7 +57,7 @@ alloc = [ "futures-core/alloc", "pin-project-lite", ] -tokio02 = ["smol/tokio02"] +tokio02 = ["tokio"] [dependencies] async-attributes = { version = "1.1.1", optional = true } @@ -83,7 +83,7 @@ surf = { version = "1.0.3", optional = true } async-io = { version = "0.1.5", optional = true } blocking = { version = "0.5.0", optional = true } futures-lite = { version = "0.1.8", optional = true } -smol = { version = "0.1.17", optional = true } +multitask = { version = "0.2.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } @@ -93,6 +93,12 @@ futures-channel = { version = "0.3.4", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.10" +[dependencies.tokio] +version = "0.2" +default-features = false +features = ["rt-threaded"] +optional = true + [dev-dependencies] femme = "1.3.0" rand = "0.7.3" diff --git a/src/task/builder.rs b/src/task/builder.rs index 2fbcff923..236081c05 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -7,7 +7,7 @@ use std::task::{Context, Poll}; use pin_project_lite::pin_project; use crate::io; -use crate::task::{JoinHandle, Task, TaskLocalsWrapper}; +use crate::task::{self, JoinHandle, Task, TaskLocalsWrapper}; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -61,9 +61,9 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let smol_task = smol::Task::spawn(wrapped).into(); + let handle = task::executor::spawn(wrapped); - Ok(JoinHandle::new(smol_task, task)) + Ok(JoinHandle::new(handle, task)) } /// Spawns a task locally with the configured settings. @@ -81,9 +81,9 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let smol_task = smol::Task::local(wrapped).into(); + let handle = task::executor::local(wrapped); - Ok(JoinHandle::new(smol_task, task)) + Ok(JoinHandle::new(handle, task)) } /// Spawns a task locally with the configured settings. @@ -166,8 +166,8 @@ impl Builder { unsafe { TaskLocalsWrapper::set_current(&wrapped.tag, || { let res = if should_run { - // The first call should use run. - smol::run(wrapped) + // The first call should run the executor + task::executor::run(wrapped) } else { futures_lite::future::block_on(wrapped) }; diff --git a/src/task/executor.rs b/src/task/executor.rs new file mode 100644 index 000000000..02fa4ca7e --- /dev/null +++ b/src/task/executor.rs @@ -0,0 +1,91 @@ +use std::cell::RefCell; +use std::future::Future; +use std::task::{Context, Poll}; + +static GLOBAL_EXECUTOR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(multitask::Executor::new); + +struct Executor { + local_executor: multitask::LocalExecutor, + parker: async_io::parking::Parker, +} + +thread_local! { + static EXECUTOR: RefCell = RefCell::new({ + let (parker, unparker) = async_io::parking::pair(); + let local_executor = multitask::LocalExecutor::new(move || unparker.unpark()); + Executor { local_executor, parker } + }); +} + +pub(crate) fn spawn(future: F) -> multitask::Task +where + F: Future + Send + 'static, + T: Send + 'static, +{ + GLOBAL_EXECUTOR.spawn(future) +} + +#[cfg(feature = "unstable")] +pub(crate) fn local(future: F) -> multitask::Task +where + F: Future + 'static, + T: 'static, +{ + EXECUTOR.with(|executor| executor.borrow().local_executor.spawn(future)) +} + +pub(crate) fn run(future: F) -> T +where + F: Future, +{ + enter(|| EXECUTOR.with(|executor| { + let executor = executor.borrow(); + let unparker = executor.parker.unparker(); + let global_ticker = GLOBAL_EXECUTOR.ticker(move || unparker.unpark()); + let unparker = executor.parker.unparker(); + let waker = async_task::waker_fn(move || unparker.unpark()); + let cx = &mut Context::from_waker(&waker); + pin_utils::pin_mut!(future); + loop { + if let Poll::Ready(res) = future.as_mut().poll(cx) { + return res; + } + if let Ok(false) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| executor.local_executor.tick() || global_ticker.tick())) { + executor.parker.park(); + } + } + })) +} + +/// Enters the tokio context if the `tokio` feature is enabled. +fn enter(f: impl FnOnce() -> T) -> T { + #[cfg(not(feature = "tokio02"))] + return f(); + + #[cfg(feature = "tokio02")] + { + use std::cell::Cell; + use tokio::runtime::Runtime; + + thread_local! { + /// The level of nested `enter` calls we are in, to ensure that the outermost always + /// has a runtime spawned. + static NESTING: Cell = Cell::new(0); + } + + /// The global tokio runtime. + static RT: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| Runtime::new().expect("cannot initialize tokio")); + + NESTING.with(|nesting| { + let res = if nesting.get() == 0 { + nesting.replace(1); + RT.enter(f) + } else { + nesting.replace(nesting.get() + 1); + f() + }; + nesting.replace(nesting.get() - 1); + res + }) + } +} diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 110b827e2..fd0d0fb77 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -18,7 +18,7 @@ pub struct JoinHandle { } #[cfg(not(target_os = "unknown"))] -type InnerHandle = async_task::JoinHandle; +type InnerHandle = multitask::Task; #[cfg(target_arch = "wasm32")] type InnerHandle = futures_channel::oneshot::Receiver; @@ -54,8 +54,7 @@ impl JoinHandle { #[cfg(not(target_os = "unknown"))] pub async fn cancel(mut self) -> Option { let handle = self.handle.take().unwrap(); - handle.cancel(); - handle.await + handle.cancel().await } /// Cancel this task. @@ -67,15 +66,19 @@ impl JoinHandle { } } +#[cfg(not(target_os = "unknown"))] +impl Drop for JoinHandle { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + handle.detach(); + } + } +} + impl Future for JoinHandle { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(output) => { - Poll::Ready(output.expect("cannot await the result of a panicked task")) - } - } + Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) } } diff --git a/src/task/mod.rs b/src/task/mod.rs index ca0b92a02..9e025baf4 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -148,6 +148,8 @@ cfg_default! { mod block_on; mod builder; mod current; + #[cfg(not(target_os = "unknown"))] + mod executor; mod join_handle; mod sleep; #[cfg(not(target_os = "unknown"))] From abc2929a8e0794e8f834a77d7ccd9ab4e80a01b5 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 23 Jul 2020 17:46:10 +0200 Subject: [PATCH 128/272] switch to async-executor Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 4 ++-- src/rt/mod.rs | 2 +- src/task/executor.rs | 45 ++++++++++++----------------------------- src/task/join_handle.rs | 2 +- src/task/mod.rs | 2 +- 5 files changed, 18 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e58401463..8c890125d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,13 +24,13 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] default = [ "std", + "async-executor", "async-io", "async-task", "blocking", "futures-lite", "kv-log-macro", "log", - "multitask", "num_cpus", "pin-project-lite", ] @@ -80,10 +80,10 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] +async-executor = { version = "0.1.1", features = ["async-io"], optional = true } async-io = { version = "0.1.5", optional = true } blocking = { version = "0.5.0", optional = true } futures-lite = { version = "0.1.8", optional = true } -multitask = { version = "0.2.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/rt/mod.rs b/src/rt/mod.rs index d8550aac8..f58afb180 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -27,7 +27,7 @@ pub static RUNTIME: Lazy = Lazy::new(|| { for _ in 0..thread_count { thread::Builder::new() .name(thread_name.clone()) - .spawn(|| crate::task::block_on(future::pending::<()>())) + .spawn(|| crate::task::executor::run_global(future::pending::<()>())) .expect("cannot start a runtime thread"); } Runtime {} diff --git a/src/task/executor.rs b/src/task/executor.rs index 02fa4ca7e..d9caf0531 100644 --- a/src/task/executor.rs +++ b/src/task/executor.rs @@ -1,23 +1,13 @@ use std::cell::RefCell; use std::future::Future; -use std::task::{Context, Poll}; -static GLOBAL_EXECUTOR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(multitask::Executor::new); - -struct Executor { - local_executor: multitask::LocalExecutor, - parker: async_io::parking::Parker, -} +static GLOBAL_EXECUTOR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(async_executor::Executor::new); thread_local! { - static EXECUTOR: RefCell = RefCell::new({ - let (parker, unparker) = async_io::parking::pair(); - let local_executor = multitask::LocalExecutor::new(move || unparker.unpark()); - Executor { local_executor, parker } - }); + static EXECUTOR: RefCell = RefCell::new(async_executor::LocalExecutor::new()); } -pub(crate) fn spawn(future: F) -> multitask::Task +pub(crate) fn spawn(future: F) -> async_executor::Task where F: Future + Send + 'static, T: Send + 'static, @@ -26,35 +16,26 @@ where } #[cfg(feature = "unstable")] -pub(crate) fn local(future: F) -> multitask::Task +pub(crate) fn local(future: F) -> async_executor::Task where F: Future + 'static, T: 'static, { - EXECUTOR.with(|executor| executor.borrow().local_executor.spawn(future)) + EXECUTOR.with(|executor| executor.borrow().spawn(future)) } pub(crate) fn run(future: F) -> T where F: Future, { - enter(|| EXECUTOR.with(|executor| { - let executor = executor.borrow(); - let unparker = executor.parker.unparker(); - let global_ticker = GLOBAL_EXECUTOR.ticker(move || unparker.unpark()); - let unparker = executor.parker.unparker(); - let waker = async_task::waker_fn(move || unparker.unpark()); - let cx = &mut Context::from_waker(&waker); - pin_utils::pin_mut!(future); - loop { - if let Poll::Ready(res) = future.as_mut().poll(cx) { - return res; - } - if let Ok(false) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| executor.local_executor.tick() || global_ticker.tick())) { - executor.parker.park(); - } - } - })) + EXECUTOR.with(|executor| enter(|| GLOBAL_EXECUTOR.enter(|| executor.borrow().run(future)))) +} + +pub(crate) fn run_global(future: F) -> T +where + F: Future, +{ + enter(|| GLOBAL_EXECUTOR.run(future)) } /// Enters the tokio context if the `tokio` feature is enabled. diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index fd0d0fb77..9189ea576 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -18,7 +18,7 @@ pub struct JoinHandle { } #[cfg(not(target_os = "unknown"))] -type InnerHandle = multitask::Task; +type InnerHandle = async_executor::Task; #[cfg(target_arch = "wasm32")] type InnerHandle = futures_channel::oneshot::Receiver; diff --git a/src/task/mod.rs b/src/task/mod.rs index 9e025baf4..b528a788e 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -149,7 +149,7 @@ cfg_default! { mod builder; mod current; #[cfg(not(target_os = "unknown"))] - mod executor; + pub(crate) mod executor; mod join_handle; mod sleep; #[cfg(not(target_os = "unknown"))] From 8e2a540bca834a5cb8b0e3b9c1f187c696f3cc9f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 31 Jul 2020 17:05:41 +0200 Subject: [PATCH 129/272] deps: bump executor related deps to latest --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8c890125d..62eb54a38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,8 +80,8 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-executor = { version = "0.1.1", features = ["async-io"], optional = true } -async-io = { version = "0.1.5", optional = true } +async-executor = { version = "0.1.2", features = ["async-io"], optional = true } +async-io = { version = "0.1.8", optional = true } blocking = { version = "0.5.0", optional = true } futures-lite = { version = "0.1.8", optional = true } From a1e83c182e3906e40eab1948f494f0ad382be9a2 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 31 Jul 2020 17:09:23 +0200 Subject: [PATCH 130/272] chore: release 1.6.3 --- CHANGELOG.md | 17 ++++++++++++++++- Cargo.toml | 2 +- src/lib.rs | 10 +++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57542e819..9d2e522db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.3] - 2020-07-31 + +## Added + +## Changed + +- Switched from smol to individual executor parts. ([#836](https://github.com/async-rs/async-std/pull/836)) +- Replaced internal `Mutex` implementation with `async-mutex`. ([#822](https://github.com/async-rs/async-std/pull/822)) + +## Fixed + +- Added missing `Send` guards to `Stream::collect`. ([#665](https://github.com/async-rs/async-std/pull/665)) + + # [1.6.2] - 2020-06-19 ## Added @@ -746,7 +760,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.2...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.3...HEAD +[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 [1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 diff --git a/Cargo.toml b/Cargo.toml index 62eb54a38..71b031f57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.2" +version = "1.6.3" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/src/lib.rs b/src/lib.rs index a8ba46b26..a21033132 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.2" +//! version = "1.6.3" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.2" +//! version = "1.6.3" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.2" +//! version = "1.6.3" //! features = ["tokio02"] //! ``` //! @@ -228,7 +228,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.2" +//! version = "1.6.3" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +238,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.2" +//! version = "1.6.3" //! default-features = false //! features = ["alloc"] //! ``` From c5631996c9a40d14a81762b5770097471654caa8 Mon Sep 17 00:00:00 2001 From: Ryan Brainard <966764+ryanbrainard@users.noreply.github.com> Date: Sun, 2 Aug 2020 17:49:19 +0900 Subject: [PATCH 131/272] Match on result (input) not stream (output) --- docs/src/patterns/accept-loop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md index 4508baf47..982a7ee38 100644 --- a/docs/src/patterns/accept-loop.md +++ b/docs/src/patterns/accept-loop.md @@ -121,7 +121,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); while let Some(result) = incoming.next().await { - let stream = match stream { + let stream = match result { Err(ref e) if is_connection_error(e) => continue, // 1 Err(e) => { eprintln!("Error: {}. Pausing for 500ms."); // 3 From 50e7cf0e431806efac61b68bd2137df2340204be Mon Sep 17 00:00:00 2001 From: Ryan Brainard <966764+ryanbrainard@users.noreply.github.com> Date: Sun, 2 Aug 2020 17:49:56 +0900 Subject: [PATCH 132/272] Pass in error to log line with placeholder --- docs/src/patterns/accept-loop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md index 982a7ee38..4bc43e7d1 100644 --- a/docs/src/patterns/accept-loop.md +++ b/docs/src/patterns/accept-loop.md @@ -124,7 +124,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let stream = match result { Err(ref e) if is_connection_error(e) => continue, // 1 Err(e) => { - eprintln!("Error: {}. Pausing for 500ms."); // 3 + eprintln!("Error: {}. Pausing for 500ms.", e); // 3 task::sleep(Duration::from_millis(500)).await; // 2 continue; } From e068ab413bddda24bfa7b936a550cd2daa9f6e0b Mon Sep 17 00:00:00 2001 From: Observer42 Date: Tue, 11 Aug 2020 12:01:40 +0800 Subject: [PATCH 133/272] Fix wrong link in condvar doc --- src/sync/condvar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs index 09aea3a1a..1a208efdd 100644 --- a/src/sync/condvar.rs +++ b/src/sync/condvar.rs @@ -21,7 +21,7 @@ impl WaitTimeoutResult { /// A Condition Variable /// -/// This type is an async version of [`std::sync::Mutex`]. +/// This type is an async version of [`std::sync::Condvar`]. /// /// [`std::sync::Condvar`]: https://doc.rust-lang.org/std/sync/struct.Condvar.html /// From dbc98faf1f0f3dadc606e6d1b1c6ab8c88429449 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 13 Aug 2020 15:06:26 +0200 Subject: [PATCH 134/272] docs: fix changelog link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2e522db..85a156f7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -761,7 +761,7 @@ task::blocking(async { - Initial beta release [Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.3...HEAD -[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 +[1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 [1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 From 47ce9a370c6fc1550f04d60880a690946933fa3c Mon Sep 17 00:00:00 2001 From: "Matthieu Le brazidec (r3v2d0g)" Date: Sat, 15 Aug 2020 15:06:33 +0200 Subject: [PATCH 135/272] Add peek{,from} methods to UdpSocket --- src/net/udp/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++ tests/udp.rs | 5 ++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index dd47e0582..47a29facd 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -206,6 +206,29 @@ impl UdpSocket { self.watcher.recv_from(buf).await } + /// Receives data from socket without removing it from the queue. + /// + /// On success, returns the number of bytes peeked and the origin. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let (n, peer) = socket.peek_from(&mut buf).await?; + /// println!("Peeked {} bytes from {}", n, peer); + /// # + /// # Ok (()) }) } + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.watcher.peek_from(buf).await + } + /// Connects the UDP socket to a remote address. /// /// When connected, methods [`send`] and [`recv`] will use the specified address for sending @@ -301,6 +324,30 @@ impl UdpSocket { self.watcher.recv(buf).await } + /// Receives data from the socket without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = socket.peek(&mut buf).await?; + /// println!("Peeked {} bytes", n); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.watcher.peek(buf).await + } + /// Gets the value of the `SO_BROADCAST` option for this socket. /// /// For more information about this option, see [`set_broadcast`]. diff --git a/tests/udp.rs b/tests/udp.rs index 37024c478..cd119ddcd 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -12,7 +12,7 @@ const THE_MERCHANT_OF_VENICE: &[u8] = b" "; #[test] -fn send_recv() -> io::Result<()> { +fn send_recv_peek() -> io::Result<()> { task::block_on(async { let socket1 = UdpSocket::bind("127.0.0.1:0").await?; let socket2 = UdpSocket::bind("127.0.0.1:0").await?; @@ -23,6 +23,9 @@ fn send_recv() -> io::Result<()> { socket1.send(THE_MERCHANT_OF_VENICE).await?; let mut buf = [0u8; 1024]; + let n = socket2.peek(&mut buf).await?; + assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE); + let n = socket2.recv(&mut buf).await?; assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE); From 59874d639cade4c36a81acaf603372f594fba2d6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 18 Aug 2020 11:06:01 +0100 Subject: [PATCH 136/272] tests: add test case for UnixStream::into_raw_fd Signed-off-by: Yuxuan Shui --- tests/uds.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/uds.rs b/tests/uds.rs index d081bdaee..5375a3ca2 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -29,7 +29,7 @@ fn send_recv() -> io::Result<()> { } #[test] -fn into_raw_fd() -> io::Result<()> { +fn into_raw_fd_datagram() -> io::Result<()> { use async_std::os::unix::io::{FromRawFd, IntoRawFd}; task::block_on(async { let (socket1, socket2) = UnixDatagram::pair().unwrap(); @@ -45,6 +45,23 @@ fn into_raw_fd() -> io::Result<()> { }) } +#[test] +fn into_raw_fd_stream() -> io::Result<()> { + use async_std::os::unix::io::{FromRawFd, IntoRawFd}; + task::block_on(async { + let (mut socket1, socket2) = UnixStream::pair().unwrap(); + socket1.write(JULIUS_CAESAR).await?; + + let mut buf = vec![0; 1024]; + + let mut socket2 = unsafe { UnixStream::from_raw_fd(socket2.into_raw_fd()) }; + let n = socket2.read(&mut buf).await?; + assert_eq!(&buf[..n], JULIUS_CAESAR); + + Ok(()) + }) +} + const PING: &[u8] = b"ping"; const PONG: &[u8] = b"pong"; const TEST_TIMEOUT: Duration = Duration::from_secs(3); From b0ac73cb57c2b4488a5738b702d5215d96897601 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 18 Aug 2020 11:06:34 +0100 Subject: [PATCH 137/272] os/unix/stream: stop into_raw_fd from closing the fd `UnixStream::into_raw_fd` calls `as_raw_fd`, which doesn't take the ownership of the file descriptor, so the file descriptor is closed when `self` is dropped upon returning from the function. Because `UnixStream` uses a `Arc` to support Clone, there could be an arbitrary number of instances around. We cannot take ownership of the descriptor from all of the instances. Therefore we have no choice but to duplicate the file descriptor and return that. Fixes #855 Signed-off-by: Yuxuan Shui --- CHANGELOG.md | 4 ++++ src/os/unix/net/stream.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a156f7b..681fa8dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +## Fixed + +- Ensure `UnixStream::into_raw_fd` doesn't close the file descriptor ([#855](https://github.com/async-rs/async-std/issues/855)) + # [1.6.3] - 2020-07-31 ## Added diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 3b2fe36f4..08e05e947 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -252,6 +252,6 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.as_raw_fd() + (*self.watcher).get_ref().try_clone().unwrap().into_raw_fd() } } From 1898f18a5c0f76a21f4b0b429e0691e2071225d8 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 19 Aug 2020 13:34:07 +0200 Subject: [PATCH 138/272] update blocking Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 +- src/task/spawn_blocking.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71b031f57..9c772167d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] async-executor = { version = "0.1.2", features = ["async-io"], optional = true } async-io = { version = "0.1.8", optional = true } -blocking = { version = "0.5.0", optional = true } +blocking = { version = "0.5.2", optional = true } futures-lite = { version = "0.1.8", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 9c836f24b..8d6f3a51a 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -35,5 +35,5 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - task::spawn(async move { blocking::unblock!(f()) }) + task::spawn(blocking::unblock(f)) } From 58c0aca6cc0e39d22156958fcdbd7885102c5fa0 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 19 Aug 2020 13:37:43 +0200 Subject: [PATCH 139/272] update femme Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 +- examples/logging.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71b031f57..a97f402f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ features = ["rt-threaded"] optional = true [dev-dependencies] -femme = "1.3.0" +femme = "2.1.1" rand = "0.7.3" tempdir = "0.3.7" futures = "0.3.4" diff --git a/examples/logging.rs b/examples/logging.rs index bd55aaa0c..0e4526b38 100644 --- a/examples/logging.rs +++ b/examples/logging.rs @@ -3,7 +3,7 @@ use async_std::task; fn main() { - femme::start(log::LevelFilter::Trace).unwrap(); + femme::with_level(log::LevelFilter::Trace); task::block_on(async { let handle = task::spawn(async { From 14d3e9865bb4f79e36a41fd0a00ae0b55922211a Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 19 Aug 2020 13:49:36 +0200 Subject: [PATCH 140/272] switch from tempdir to tempfile Uses a more recent version of rand Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 +- tests/uds.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71b031f57..82128b4d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ optional = true [dev-dependencies] femme = "1.3.0" rand = "0.7.3" -tempdir = "0.3.7" +tempfile = "3.1.0" futures = "0.3.4" rand_xorshift = "0.2.0" diff --git a/tests/uds.rs b/tests/uds.rs index d081bdaee..e19b41e45 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -5,8 +5,6 @@ use async_std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; use async_std::prelude::*; use async_std::task; -use tempdir::TempDir; - use std::time::Duration; const JULIUS_CAESAR: &[u8] = b" @@ -51,7 +49,10 @@ const TEST_TIMEOUT: Duration = Duration::from_secs(3); #[test] fn socket_ping_pong() { - let tmp_dir = TempDir::new("socket_ping_pong").expect("Temp dir not created"); + let tmp_dir = tempfile::Builder::new() + .prefix("socket_ping_pong") + .tempdir() + .expect("Temp dir not created"); let sock_path = tmp_dir.as_ref().join("sock"); let iter_cnt = 16; @@ -98,7 +99,10 @@ async fn ping_pong_client(socket: &std::path::PathBuf, iterations: u32) -> std:: #[test] fn uds_clone() -> io::Result<()> { task::block_on(async { - let tmp_dir = TempDir::new("socket_ping_pong").expect("Temp dir not created"); + let tmp_dir = tempfile::Builder::new() + .prefix("socket_ping_pong") + .tempdir() + .expect("Temp dir not created"); let sock_path = tmp_dir.as_ref().join("sock"); let input = UnixListener::bind(&sock_path).await?; From e4fb4b6128067d2357b860ee2da6cff8eb5d3004 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 27 Aug 2020 09:23:00 +0200 Subject: [PATCH 141/272] update smol dependencies Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 8 ++++---- src/task/executor.rs | 4 ++-- src/utils.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 109c7ad74..5e3ee19e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,10 +80,10 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-executor = { version = "0.1.2", features = ["async-io"], optional = true } -async-io = { version = "0.1.8", optional = true } -blocking = { version = "0.5.2", optional = true } -futures-lite = { version = "0.1.8", optional = true } +async-executor = { version = "0.2.0", optional = true } +async-io = { version = "0.2.1", optional = true } +blocking = { version = "0.6.0", optional = true } +futures-lite = { version = "1.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } diff --git a/src/task/executor.rs b/src/task/executor.rs index d9caf0531..0f511d0f0 100644 --- a/src/task/executor.rs +++ b/src/task/executor.rs @@ -28,14 +28,14 @@ pub(crate) fn run(future: F) -> T where F: Future, { - EXECUTOR.with(|executor| enter(|| GLOBAL_EXECUTOR.enter(|| executor.borrow().run(future)))) + EXECUTOR.with(|executor| enter(|| async_io::block_on(executor.borrow().run(future)))) } pub(crate) fn run_global(future: F) -> T where F: Future, { - enter(|| GLOBAL_EXECUTOR.run(future)) + enter(|| async_io::block_on(GLOBAL_EXECUTOR.run(future))) } /// Enters the tokio context if the `tokio` feature is enabled. diff --git a/src/utils.rs b/src/utils.rs index f3299f636..3699f89e8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -69,7 +69,7 @@ pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { #[cfg(all(not(target_os = "unknown"), feature = "default"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - Timer::new(dur) + Timer::after(dur) } #[cfg(any( @@ -84,7 +84,7 @@ mod timer { pub(crate) struct Timer(futures_timer::Delay); impl Timer { - pub(crate) fn new(dur: std::time::Duration) -> Self { + pub(crate) fn after(dur: std::time::Duration) -> Self { Timer(futures_timer::Delay::new(dur)) } } From 949ff90306d52cb4089af202bd0bdbc6165647d1 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Sun, 30 Aug 2020 12:20:00 -0700 Subject: [PATCH 142/272] Fix BufWriter documentation: BufWriters do not flush when dropped. This was partially fixed in #586, but there's another sentence later that makes the same claim. --- src/io/buf_writer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index c527d0270..c972937fd 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -74,7 +74,8 @@ pin_project! { /// /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped /// together by the buffer, and will all be written out in one system call when - /// the `stream` is dropped. + /// `stream.flush()` completes. (As mentioned above, dropping a `BufWriter` + /// does not flush its buffers, so a `flush` call is essential.) /// /// [`Write`]: trait.Write.html /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write From e2f638496cf57f55ba3915039e4151d4523b5ab8 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Mon, 31 Aug 2020 21:43:21 +0200 Subject: [PATCH 143/272] don't init runtime threadpool unless necessary Signed-off-by: Marc-Antoine Perennou --- src/net/tcp/listener.rs | 4 ---- src/net/tcp/stream.rs | 4 ---- src/net/udp/mod.rs | 4 ---- src/os/unix/net/datagram.rs | 8 -------- src/os/unix/net/listener.rs | 4 ---- src/os/unix/net/stream.rs | 6 ------ src/utils.rs | 3 --- 7 files changed, 33 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 8c87fc5f0..fc88cda56 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -75,8 +75,6 @@ impl TcpListener { /// /// [`local_addr`]: #method.local_addr pub async fn bind(addrs: A) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -202,8 +200,6 @@ impl<'a> Stream for Incoming<'a> { impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. fn from(listener: std::net::TcpListener) -> TcpListener { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - TcpListener { watcher: Async::new(listener).expect("TcpListener is known to be good"), } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index f7bd5c919..2e14806fb 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -71,8 +71,6 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn connect(addrs: A) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -358,8 +356,6 @@ impl Write for &TcpStream { impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. fn from(stream: std::net::TcpStream) -> TcpStream { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - TcpStream { watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")), } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index dd47e0582..00cfd298c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -68,8 +68,6 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn bind(addrs: A) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let mut last_err = None; let addrs = addrs.to_socket_addrs().await?; @@ -481,8 +479,6 @@ impl UdpSocket { impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. fn from(socket: std::net::UdpSocket) -> UdpSocket { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - UdpSocket { watcher: Async::new(socket).expect("UdpSocket is known to be good"), } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 83ef9fe95..99a9e8d23 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -45,8 +45,6 @@ pub struct UnixDatagram { impl UnixDatagram { fn new(socket: StdUnixDatagram) -> UnixDatagram { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - UnixDatagram { watcher: Async::new(socket).expect("UnixDatagram is known to be good"), } @@ -66,8 +64,6 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn bind>(path: P) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let path = path.as_ref().to_owned(); let socket = Async::::bind(path)?; Ok(UnixDatagram { watcher: socket }) @@ -309,8 +305,6 @@ impl fmt::Debug for UnixDatagram { impl From for UnixDatagram { /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. fn from(datagram: StdUnixDatagram) -> UnixDatagram { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - UnixDatagram { watcher: Async::new(datagram).expect("UnixDatagram is known to be good"), } @@ -325,8 +319,6 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let raw = StdUnixDatagram::from_raw_fd(fd); let datagram = Async::::new(raw).expect("invalid file descriptor"); UnixDatagram { watcher: datagram } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 078b780d0..dc4c64c7a 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -68,8 +68,6 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub async fn bind>(path: P) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let path = path.as_ref().to_owned(); let listener = Async::::bind(path)?; @@ -194,8 +192,6 @@ impl Stream for Incoming<'_> { impl From for UnixListener { /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. fn from(listener: StdUnixListener) -> UnixListener { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - UnixListener { watcher: Async::new(listener).expect("UnixListener is known to be good"), } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 3b2fe36f4..11cdb8e91 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -57,8 +57,6 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub async fn connect>(path: P) -> io::Result { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let path = path.as_ref().to_owned(); let stream = Arc::new(Async::::connect(path).await?); @@ -81,8 +79,6 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let (a, b) = Async::::pair()?; let a = UnixStream { watcher: Arc::new(a), @@ -228,8 +224,6 @@ impl fmt::Debug for UnixStream { impl From for UnixStream { /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. fn from(stream: StdUnixStream) -> UnixStream { - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let stream = Async::new(stream).expect("UnixStream is known to be good"); UnixStream { watcher: Arc::new(stream), diff --git a/src/utils.rs b/src/utils.rs index 3699f89e8..528a7074e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -66,9 +66,6 @@ mod timer { #[cfg(any(feature = "unstable", feature = "default"))] pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { - #[cfg(all(not(target_os = "unknown"), feature = "default"))] - once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - Timer::after(dur) } From 04bb83f86efaece670f9c54e9fd834590f6ca759 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:37:28 +0900 Subject: [PATCH 144/272] fix clippy warnings --- src/rt/mod.rs | 3 ++- src/stream/stream/inspect.rs | 4 ++-- src/task/builder.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rt/mod.rs b/src/rt/mod.rs index f58afb180..917db198a 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -22,7 +22,8 @@ pub static RUNTIME: Lazy = Lazy::new(|| { .unwrap_or_else(|_| num_cpus::get()) .max(1); - let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or("async-std/runtime".to_string()); + let thread_name = + env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string()); for _ in 0..thread_count { thread::Builder::new() diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index 481810d72..d2f6cf395 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -41,9 +41,9 @@ where let mut this = self.project(); let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - Poll::Ready(next.and_then(|x| { + Poll::Ready(next.map(|x| { (this.f)(&x); - Some(x) + x })) } } diff --git a/src/task/builder.rs b/src/task/builder.rs index 236081c05..d3a353691 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -41,7 +41,7 @@ impl Builder { #[cfg(not(target_os = "unknown"))] once_cell::sync::Lazy::force(&crate::rt::RUNTIME); - let tag = TaskLocalsWrapper::new(task.clone()); + let tag = TaskLocalsWrapper::new(task); SupportTaskLocals { tag, future } } From 15798bd72b46909c8de282aa24a14ba66af77fab Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Mon, 7 Sep 2020 17:01:29 +0200 Subject: [PATCH 145/272] update to smol 1.0 subcrates Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e3ee19e7..0c18ff457 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,9 +80,9 @@ futures-timer = { version = "3.0.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-executor = { version = "0.2.0", optional = true } -async-io = { version = "0.2.1", optional = true } -blocking = { version = "0.6.0", optional = true } +async-executor = { version = "1.0.0", optional = true } +async-io = { version = "1.0.1", optional = true } +blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] From efaeadce888532f5d8d2d34ff9be483382da84ef Mon Sep 17 00:00:00 2001 From: Akshat Agarwal Date: Wed, 9 Sep 2020 09:35:00 +0530 Subject: [PATCH 146/272] (typo) s/panicing/panicking --- docs/src/concepts/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index 2142cac46..2db8cb0c9 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -118,7 +118,7 @@ thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` -While panicing a spawned task will abort: +While panicking a spawned task will abort: ```rust,edition2018,should_panic # extern crate async_std; From e9cb238f49b6097c3dc3d6f06a6a54f22dc955d4 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Mon, 14 Sep 2020 21:31:19 +0200 Subject: [PATCH 147/272] fix wasm and nostd builds Co-authored-by: Jacob Rothstein --- .github/workflows/ci.yml | 53 ++++++++++++++++++++++++++-------------- Cargo.toml | 6 ++--- src/task/builder.rs | 11 +++++---- src/task/join_handle.rs | 10 ++++++++ src/utils.rs | 16 ++++++------ tests/collect.rs | 6 ++--- 6 files changed, 65 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4f85ea2a..36a9a4254 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,24 +29,6 @@ jobs: toolchain: ${{ matrix.rust }} override: true - - name: Cache cargo registry - uses: actions/cache@v2 - with: - path: ~/.cargo/registry - key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }} - - - name: Cache cargo index - uses: actions/cache@v2 - with: - path: ~/.cargo/git - key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.toml') }} - - - name: Cache cargo build - uses: actions/cache@v2 - with: - path: target - key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }} - - name: check uses: actions-rs/cargo@v1 with: @@ -160,6 +142,41 @@ jobs: - name: test run: cross test --all --features unstable --target ${{ matrix.target }} + check_wasm: + name: Check wasm targets + runs-on: ubuntu-latest + strategy: + matrix: + rust: [nightly, beta, stable] + + steps: + - uses: actions/checkout@master + + - name: Install rust with wasm32-unknown-unknown + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: wasm32-unknown-unknown + override: true + + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: ~/.cargo/registry + key: wasm32-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }} + + - name: check + uses: actions-rs/cargo@v1 + with: + command: check + args: --target wasm32-unknown-unknown + + - name: check unstable + uses: actions-rs/cargo@v1 + with: + command: check + args: --target wasm32-unknown-unknown --tests --all --features unstable + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 0c18ff457..6f6cfe4bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,11 +33,12 @@ default = [ "log", "num_cpus", "pin-project-lite", + "gloo-timers", ] docs = ["attributes", "unstable", "default"] unstable = [ "std", - "futures-timer", + "async-io" ] attributes = ["async-attributes"] std = [ @@ -74,7 +75,6 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } -futures-timer = { version = "3.0.2", optional = true } # Devdepencency, but they are not allowed to be optional :/ surf = { version = "1.0.3", optional = true } @@ -86,7 +86,7 @@ blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] } +gloo-timers = { version = "0.2.1", features = ["futures"], optional = true } wasm-bindgen-futures = { version = "0.4.10", optional = true } futures-channel = { version = "0.3.4", optional = true } diff --git a/src/task/builder.rs b/src/task/builder.rs index d3a353691..391201d84 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,4 +1,3 @@ -use std::cell::Cell; use std::future::Future; use std::pin::Pin; use std::sync::Arc; @@ -7,7 +6,7 @@ use std::task::{Context, Poll}; use pin_project_lite::pin_project; use crate::io; -use crate::task::{self, JoinHandle, Task, TaskLocalsWrapper}; +use crate::task::{JoinHandle, Task, TaskLocalsWrapper}; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -61,7 +60,7 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let handle = task::executor::spawn(wrapped); + let handle = crate::task::executor::spawn(wrapped); Ok(JoinHandle::new(handle, task)) } @@ -81,7 +80,7 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let handle = task::executor::local(wrapped); + let handle = crate::task::executor::local(wrapped); Ok(JoinHandle::new(handle, task)) } @@ -143,6 +142,8 @@ impl Builder { where F: Future, { + use std::cell::Cell; + let wrapped = self.build(future); // Log this `block_on` operation. @@ -167,7 +168,7 @@ impl Builder { TaskLocalsWrapper::set_current(&wrapped.tag, || { let res = if should_run { // The first call should run the executor - task::executor::run(wrapped) + crate::task::executor::run(wrapped) } else { futures_lite::future::block_on(wrapped) }; diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 9189ea576..25ca79dad 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -78,7 +78,17 @@ impl Drop for JoinHandle { impl Future for JoinHandle { type Output = T; + #[cfg(not(target_os = "unknown"))] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) } + + #[cfg(target_arch = "wasm32")] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) { + Poll::Ready(Ok(t)) => Poll::Ready(t), + Poll::Ready(Err(_)) => unreachable!("channel must not be canceled"), + Poll::Pending => Poll::Pending, + } + } } diff --git a/src/utils.rs b/src/utils.rs index 528a7074e..d80524446 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,7 +59,10 @@ pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; } -#[cfg(all(not(target_os = "unknown"), feature = "default"))] +#[cfg(all( + not(target_os = "unknown"), + any(feature = "default", feature = "unstable") +))] mod timer { pub type Timer = async_io::Timer; } @@ -69,20 +72,19 @@ pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { Timer::after(dur) } -#[cfg(any( - all(target_arch = "wasm32", feature = "default"), - all(feature = "unstable", not(feature = "default")) -))] +#[cfg(any(all(target_arch = "wasm32", feature = "default"),))] mod timer { use std::pin::Pin; use std::task::Poll; + use gloo_timers::future::TimeoutFuture; + #[derive(Debug)] - pub(crate) struct Timer(futures_timer::Delay); + pub(crate) struct Timer(TimeoutFuture); impl Timer { pub(crate) fn after(dur: std::time::Duration) -> Self { - Timer(futures_timer::Delay::new(dur)) + Timer(TimeoutFuture::new(dur.as_millis() as u32)) } } diff --git a/tests/collect.rs b/tests/collect.rs index d24484f4e..7ab80ccc9 100644 --- a/tests/collect.rs +++ b/tests/collect.rs @@ -1,6 +1,6 @@ #[cfg(feature = "unstable")] #[test] -fn test_send() -> async_std::io::Result<()> { +fn test_send() { use async_std::prelude::*; use async_std::{stream, task}; @@ -14,7 +14,5 @@ fn test_send() -> async_std::io::Result<()> { // This line triggers a compilation error test_send_trait(&fut); - - Ok(()) - }) + }); } From 352c54bfe6701bbe782f2b80100056e497fa3e30 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 15 Sep 2020 19:04:12 +0200 Subject: [PATCH 148/272] feat: move executor to async-global-executo --- Cargo.toml | 4 ++-- src/rt/mod.rs | 21 ++------------------- src/task/builder.rs | 4 ++-- src/task/executor.rs | 33 +-------------------------------- src/task/join_handle.rs | 2 +- 5 files changed, 8 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f6cfe4bd..00a238453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] default = [ "std", - "async-executor", + "async-global-executor", "async-io", "async-task", "blocking", @@ -80,7 +80,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-executor = { version = "1.0.0", optional = true } +async-global-executor = { version = "1.0.1", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } diff --git a/src/rt/mod.rs b/src/rt/mod.rs index 917db198a..da78d9f89 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -1,12 +1,9 @@ //! The runtime. use std::env; -use std::thread; use once_cell::sync::Lazy; -use crate::future; - /// Dummy runtime struct. pub struct Runtime {} @@ -14,22 +11,8 @@ pub struct Runtime {} pub static RUNTIME: Lazy = Lazy::new(|| { // Create an executor thread pool. - let thread_count = env::var("ASYNC_STD_THREAD_COUNT") - .map(|env| { - env.parse() - .expect("ASYNC_STD_THREAD_COUNT must be a number") - }) - .unwrap_or_else(|_| num_cpus::get()) - .max(1); - - let thread_name = - env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string()); + let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string()); + async_global_executor::init_with_config(async_global_executor::GlobalExecutorConfig::default().with_env_var("ASYNC_STD_THREAD_COUNT").with_thread_name(thread_name)); - for _ in 0..thread_count { - thread::Builder::new() - .name(thread_name.clone()) - .spawn(|| crate::task::executor::run_global(future::pending::<()>())) - .expect("cannot start a runtime thread"); - } Runtime {} }); diff --git a/src/task/builder.rs b/src/task/builder.rs index 391201d84..8e69a10c2 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -60,7 +60,7 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let handle = crate::task::executor::spawn(wrapped); + let handle = async_global_executor::spawn(wrapped); Ok(JoinHandle::new(handle, task)) } @@ -80,7 +80,7 @@ impl Builder { }); let task = wrapped.tag.task().clone(); - let handle = crate::task::executor::local(wrapped); + let handle = async_global_executor::spawn_local(wrapped); Ok(JoinHandle::new(handle, task)) } diff --git a/src/task/executor.rs b/src/task/executor.rs index 0f511d0f0..0cd05032d 100644 --- a/src/task/executor.rs +++ b/src/task/executor.rs @@ -1,41 +1,10 @@ -use std::cell::RefCell; use std::future::Future; -static GLOBAL_EXECUTOR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(async_executor::Executor::new); - -thread_local! { - static EXECUTOR: RefCell = RefCell::new(async_executor::LocalExecutor::new()); -} - -pub(crate) fn spawn(future: F) -> async_executor::Task -where - F: Future + Send + 'static, - T: Send + 'static, -{ - GLOBAL_EXECUTOR.spawn(future) -} - -#[cfg(feature = "unstable")] -pub(crate) fn local(future: F) -> async_executor::Task -where - F: Future + 'static, - T: 'static, -{ - EXECUTOR.with(|executor| executor.borrow().spawn(future)) -} - pub(crate) fn run(future: F) -> T where F: Future, { - EXECUTOR.with(|executor| enter(|| async_io::block_on(executor.borrow().run(future)))) -} - -pub(crate) fn run_global(future: F) -> T -where - F: Future, -{ - enter(|| async_io::block_on(GLOBAL_EXECUTOR.run(future))) + enter(|| async_global_executor::block_on(future)) } /// Enters the tokio context if the `tokio` feature is enabled. diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs index 25ca79dad..9fbab44c7 100644 --- a/src/task/join_handle.rs +++ b/src/task/join_handle.rs @@ -18,7 +18,7 @@ pub struct JoinHandle { } #[cfg(not(target_os = "unknown"))] -type InnerHandle = async_executor::Task; +type InnerHandle = async_global_executor::Task; #[cfg(target_arch = "wasm32")] type InnerHandle = futures_channel::oneshot::Receiver; From 55fb871ab88158f85d422f6d6937cdbb9f864756 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 17 Sep 2020 13:20:57 +0200 Subject: [PATCH 149/272] chore: release v1.6.4 Co-authored-by: Yoshua Wuyts --- CHANGELOG.md | 17 ++++++++++++++++- Cargo.toml | 2 +- src/lib.rs | 10 +++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681fa8dd5..d22240e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,23 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.4] - 2020-09-16 + +## Added + +- Added `UdpSocket::peek` and `UdpSocket::peek_from` ([#853](https://github.com/async-rs/async-std/pull/853)) + +## Changed + +- Extracted the executor into [async-global-executor](https://github.com/async-rs/async-global-executor) ([#867](https://github.com/async-rs/async-std/pull/867)) + +- Updated various dependencies + ## Fixed - Ensure `UnixStream::into_raw_fd` doesn't close the file descriptor ([#855](https://github.com/async-rs/async-std/issues/855)) +- Fixed wasm builds and ensured better dependency management depending on the compile target ([#863](https://github.com/async-rs/async-std/pull/863)) + # [1.6.3] - 2020-07-31 @@ -764,7 +778,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.3...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.4...HEAD +[1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4 [1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 [1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 diff --git a/Cargo.toml b/Cargo.toml index 00a238453..c9efe6006 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.3" +version = "1.6.4" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/src/lib.rs b/src/lib.rs index a21033132..c44f4a408 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.3" +//! version = "1.6.4" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.3" +//! version = "1.6.4" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.3" +//! version = "1.6.4" //! features = ["tokio02"] //! ``` //! @@ -228,7 +228,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.3" +//! version = "1.6.4" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +238,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.3" +//! version = "1.6.4" //! default-features = false //! features = ["alloc"] //! ``` From df8b38cb7b9148c98f1f9ad5b65abbc3de25fd07 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 18 Sep 2020 10:04:35 +0200 Subject: [PATCH 150/272] drop async-task dependency We no longer directly depend on it Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9efe6006..ef3be30b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ default = [ "std", "async-global-executor", "async-io", - "async-task", "blocking", "futures-lite", "kv-log-macro", @@ -62,7 +61,6 @@ tokio02 = ["tokio"] [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-task = { version = "3.0.0", optional = true } async-mutex = { version = "1.1.3", optional = true } crossbeam-utils = { version = "0.7.2", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } From 67b9a210b345bb004443a5eb57a350b5aec87528 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 18 Sep 2020 10:05:09 +0200 Subject: [PATCH 151/272] update async-global-executor make sure we pull in the deadlock fix Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ef3be30b5..6ea6275de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "1.0.1", optional = true, features = ["async-io"] } +async-global-executor = { version = "1.0.2", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } From 3e94498741e61cd460ef12685d1229eeca6250e8 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 22 Sep 2020 17:07:45 +0200 Subject: [PATCH 152/272] fix tokio compatibility Move it into async-global-executor Fixes #881 Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 4 ++-- src/task/builder.rs | 2 +- src/task/executor.rs | 41 ----------------------------------------- src/task/mod.rs | 2 -- 4 files changed, 3 insertions(+), 46 deletions(-) delete mode 100644 src/task/executor.rs diff --git a/Cargo.toml b/Cargo.toml index 6ea6275de..2503750b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ alloc = [ "futures-core/alloc", "pin-project-lite", ] -tokio02 = ["tokio"] +tokio02 = ["async-global-executor/tokio02"] [dependencies] async-attributes = { version = "1.1.1", optional = true } @@ -78,7 +78,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "1.0.2", optional = true, features = ["async-io"] } +async-global-executor = { version = "1.2.1", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } diff --git a/src/task/builder.rs b/src/task/builder.rs index 8e69a10c2..aba0d6115 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -168,7 +168,7 @@ impl Builder { TaskLocalsWrapper::set_current(&wrapped.tag, || { let res = if should_run { // The first call should run the executor - crate::task::executor::run(wrapped) + async_global_executor::block_on(wrapped) } else { futures_lite::future::block_on(wrapped) }; diff --git a/src/task/executor.rs b/src/task/executor.rs deleted file mode 100644 index 0cd05032d..000000000 --- a/src/task/executor.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::future::Future; - -pub(crate) fn run(future: F) -> T -where - F: Future, -{ - enter(|| async_global_executor::block_on(future)) -} - -/// Enters the tokio context if the `tokio` feature is enabled. -fn enter(f: impl FnOnce() -> T) -> T { - #[cfg(not(feature = "tokio02"))] - return f(); - - #[cfg(feature = "tokio02")] - { - use std::cell::Cell; - use tokio::runtime::Runtime; - - thread_local! { - /// The level of nested `enter` calls we are in, to ensure that the outermost always - /// has a runtime spawned. - static NESTING: Cell = Cell::new(0); - } - - /// The global tokio runtime. - static RT: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| Runtime::new().expect("cannot initialize tokio")); - - NESTING.with(|nesting| { - let res = if nesting.get() == 0 { - nesting.replace(1); - RT.enter(f) - } else { - nesting.replace(nesting.get() + 1); - f() - }; - nesting.replace(nesting.get() - 1); - res - }) - } -} diff --git a/src/task/mod.rs b/src/task/mod.rs index b528a788e..ca0b92a02 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -148,8 +148,6 @@ cfg_default! { mod block_on; mod builder; mod current; - #[cfg(not(target_os = "unknown"))] - pub(crate) mod executor; mod join_handle; mod sleep; #[cfg(not(target_os = "unknown"))] From f7aa962dafc8e49437440148505fd4a114270e17 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 28 Sep 2020 18:49:55 +0200 Subject: [PATCH 153/272] Store a future inside Incoming --- src/net/tcp/listener.rs | 42 ++++++++++++++++++++++++++++--------- src/os/unix/net/listener.rs | 39 ++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index fc88cda56..cfefc7d24 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; @@ -8,7 +9,7 @@ use crate::io; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::sync::Arc; -use crate::task::{Context, Poll}; +use crate::task::{ready, Context, Poll}; /// A TCP socket server, listening for connections. /// @@ -146,7 +147,10 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub fn incoming(&self) -> Incoming<'_> { - Incoming(self) + Incoming { + listener: self, + accept: None, + } } /// Returns the local address that this listener is bound to. @@ -182,18 +186,36 @@ impl TcpListener { /// [`incoming`]: struct.TcpListener.html#method.incoming /// [`TcpListener`]: struct.TcpListener.html /// [`std::net::Incoming`]: https://doc.rust-lang.org/std/net/struct.Incoming.html -#[derive(Debug)] -pub struct Incoming<'a>(&'a TcpListener); +pub struct Incoming<'a> { + listener: &'a TcpListener, + accept: Option< + Pin> + Send + Sync + 'a>>, + >, +} -impl<'a> Stream for Incoming<'a> { +impl Stream for Incoming<'_> { type Item = io::Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let future = self.0.accept(); - pin_utils::pin_mut!(future); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + if self.accept.is_none() { + self.accept = Some(Box::pin(self.listener.accept())); + } + + if let Some(f) = &mut self.accept { + let res = ready!(f.as_mut().poll(cx)); + self.accept = None; + return Poll::Ready(Some(res.map(|(stream, _)| stream))); + } + } + } +} - let (socket, _) = futures_core::ready!(future.poll(cx))?; - Poll::Ready(Some(Ok(socket))) +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Incoming") + .field("listener", self.listener) + .finish() } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index dc4c64c7a..3573d7d34 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -14,7 +14,7 @@ use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; use crate::sync::Arc; -use crate::task::{Context, Poll}; +use crate::task::{ready, Context, Poll}; /// A Unix domain socket server, listening for connections. /// @@ -128,7 +128,10 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub fn incoming(&self) -> Incoming<'_> { - Incoming(self) + Incoming { + listener: self, + accept: None, + } } /// Returns the local socket address of this listener. @@ -174,18 +177,36 @@ impl fmt::Debug for UnixListener { /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`incoming`]: struct.UnixListener.html#method.incoming /// [`UnixListener`]: struct.UnixListener.html -#[derive(Debug)] -pub struct Incoming<'a>(&'a UnixListener); +pub struct Incoming<'a> { + listener: &'a UnixListener, + accept: Option< + Pin> + Send + Sync + 'a>>, + >, +} impl Stream for Incoming<'_> { type Item = io::Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let future = self.0.accept(); - pin_utils::pin_mut!(future); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + if self.accept.is_none() { + self.accept = Some(Box::pin(self.listener.accept())); + } + + if let Some(f) = &mut self.accept { + let res = ready!(f.as_mut().poll(cx)); + self.accept = None; + return Poll::Ready(Some(res.map(|(stream, _)| stream))); + } + } + } +} - let (socket, _) = futures_core::ready!(future.poll(cx))?; - Poll::Ready(Some(Ok(socket))) +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Incoming") + .field("listener", self.listener) + .finish() } } From 0d50906a80120195aaa828cca175ccc4cb1dcb03 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 28 Sep 2020 19:11:21 +0200 Subject: [PATCH 154/272] chore: release v1.6.5 --- CHANGELOG.md | 12 ++++++++++-- Cargo.toml | 2 +- src/lib.rs | 10 +++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d22240e6e..4b94ae586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.6.5] - 2020-09-28 + +## Fixed + +- Fix `TcpListener::incoming`. ([#889](https://github.com/async-rs/async-std/pull/889)) +- Fix tokio compatability flag. ([#882](https://github.com/async-rs/async-std/pull/882)) + # [1.6.4] - 2020-09-16 ## Added @@ -778,8 +785,9 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.4...HEAD -[1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4 +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.5...HEAD +[1.6.5]: https://github.com/async-rs/async-std/compare/v1.6.4...v1.6.5 +[1.6.4]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4 [1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 [1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 diff --git a/Cargo.toml b/Cargo.toml index 2503750b2..329581afa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.4" +version = "1.6.5" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/src/lib.rs b/src/lib.rs index c44f4a408..bd3afbdf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.4" +//! version = "1.6.5" //! features = ["unstable"] //! ``` //! @@ -210,7 +210,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.4" +//! version = "1.6.5" //! features = ["attributes"] //! ``` //! @@ -219,7 +219,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.4" +//! version = "1.6.5" //! features = ["tokio02"] //! ``` //! @@ -228,7 +228,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.4" +//! version = "1.6.5" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +238,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.4" +//! version = "1.6.5" //! default-features = false //! features = ["alloc"] //! ``` From a5f72f140fa239f760c0ab9a03c36784978564fa Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 16 Oct 2020 10:51:39 +0200 Subject: [PATCH 155/272] add tokio03 feature Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 329581afa..3f8f1fd2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ alloc = [ "pin-project-lite", ] tokio02 = ["async-global-executor/tokio02"] +tokio03 = ["async-global-executor/tokio03"] [dependencies] async-attributes = { version = "1.1.1", optional = true } @@ -78,7 +79,7 @@ slab = { version = "0.4.2", optional = true } surf = { version = "1.0.3", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "1.2.1", optional = true, features = ["async-io"] } +async-global-executor = { version = "1.4.0", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } From 11196c853dc42e86d608c4ed29af1a8f0c5c5084 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 20 Oct 2020 14:45:37 +0200 Subject: [PATCH 156/272] chore: update dependencies --- Cargo.toml | 10 ++-------- examples/surf-web.rs | 7 ++++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f8f1fd2c..1d431d741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ tokio03 = ["async-global-executor/tokio03"] [dependencies] async-attributes = { version = "1.1.1", optional = true } async-mutex = { version = "1.1.3", optional = true } -crossbeam-utils = { version = "0.7.2", optional = true } +crossbeam-utils = { version = "0.8.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } kv-log-macro = { version = "1.0.6", optional = true } @@ -76,7 +76,7 @@ pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } # Devdepencency, but they are not allowed to be optional :/ -surf = { version = "1.0.3", optional = true } +surf = { version = "2.0.0", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] async-global-executor = { version = "1.4.0", optional = true, features = ["async-io"] } @@ -92,12 +92,6 @@ futures-channel = { version = "0.3.4", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.10" -[dependencies.tokio] -version = "0.2" -default-features = false -features = ["rt-threaded"] -optional = true - [dev-dependencies] femme = "2.1.1" rand = "0.7.3" diff --git a/examples/surf-web.rs b/examples/surf-web.rs index df139e5b5..6ff043d99 100644 --- a/examples/surf-web.rs +++ b/examples/surf-web.rs @@ -1,15 +1,16 @@ use async_std::task; -fn main() -> Result<(), surf::Exception> { +fn main() -> Result<(), surf::Error> { task::block_on(async { let url = "https://www.rust-lang.org"; - let mut response = surf::get(url).await?; + let mut response = surf::get(url).send().await?; let body = response.body_string().await?; dbg!(url); dbg!(response.status()); dbg!(response.version()); - dbg!(response.headers()); + dbg!(response.header_names()); + dbg!(response.header_values()); dbg!(body.len()); Ok(()) From ca84dbdd4090439410605a8a269a3f4f8be4fbce Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 30 Oct 2020 12:37:03 +0100 Subject: [PATCH 157/272] v1.7.0 --- CHANGELOG.md | 13 +++++++++++++ Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b94ae586..80f2ab28e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.7.0] - 2020-10-30 + +This patch adds a feature to enable compatibility with the new `tokio` 0.3.0 +release, and updates internal dependencies. + +## Added + +- Add `tokio03` feature ([#895](https://github.com/async-rs/async-std/pull/895)) + +## Internal + +- chore: update dependencies ([#897](https://github.com/async-rs/async-std/pull/897)) + # [1.6.5] - 2020-09-28 ## Fixed diff --git a/Cargo.toml b/Cargo.toml index 1d431d741..25912921c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.6.5" +version = "1.7.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From e8dc2c0571484b9410bdc0fe5b632ec45fc087c1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 2 Nov 2020 07:10:18 +0900 Subject: [PATCH 158/272] Fix double drop in StreamExt::cycle --- src/stream/stream/cycle.rs | 42 ++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index ef46d1a77..dc4c3a177 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -1,14 +1,19 @@ -use core::mem::ManuallyDrop; use core::pin::Pin; +use futures_core::ready; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that will repeatedly yield the same list of elements. -#[derive(Debug)] -pub struct Cycle { - orig: S, - source: ManuallyDrop, +pin_project! { + /// A stream that will repeatedly yield the same list of elements. + #[derive(Debug)] + pub struct Cycle { + orig: S, + #[pin] + source: S, + } } impl Cycle @@ -18,15 +23,7 @@ where pub(crate) fn new(source: S) -> Self { Self { orig: source.clone(), - source: ManuallyDrop::new(source), - } - } -} - -impl Drop for Cycle { - fn drop(&mut self) { - unsafe { - ManuallyDrop::drop(&mut self.source); + source, } } } @@ -38,17 +35,14 @@ where type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - let this = self.get_unchecked_mut(); + let mut this = self.project(); - match futures_core::ready!(Pin::new_unchecked(&mut *this.source).poll_next(cx)) { - Some(item) => Poll::Ready(Some(item)), - None => { - ManuallyDrop::drop(&mut this.source); - this.source = ManuallyDrop::new(this.orig.clone()); - Pin::new_unchecked(&mut *this.source).poll_next(cx) - } + match ready!(this.source.as_mut().poll_next(cx)) { + None => { + this.source.set(this.orig.clone()); + this.source.poll_next(cx) } + item => Poll::Ready(item), } } } From 738fd466188572d29efb121503cb0a07ee5d2370 Mon Sep 17 00:00:00 2001 From: Andrew Silver Date: Thu, 5 Nov 2020 08:12:38 +1100 Subject: [PATCH 159/272] Updated docs to correct version + mention tokio03 feature flag, updated CHANGELOG.md to add diff for 1.6.5...1.7.0 --- CHANGELOG.md | 3 ++- src/lib.rs | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f2ab28e..c0cd2f83c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -798,7 +798,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.5...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.7.0...HEAD +[1.7.0]: https://github.com/async-rs/async-std/compare/v1.6.5...1.7.0 [1.6.5]: https://github.com/async-rs/async-std/compare/v1.6.4...v1.6.5 [1.6.4]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4 [1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 diff --git a/src/lib.rs b/src/lib.rs index bd3afbdf7..47aac45de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,7 +197,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.5" +//! version = "1.7.0" //! features = ["unstable"] //! ``` //! @@ -210,25 +210,34 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.5" +//! version = "1.7.0" //! features = ["attributes"] //! ``` //! -//! Compatibility with the `tokio` runtime is possible using the `tokio02` +//! Compatibility with the `tokio` 0.2 runtime is possible using the `tokio02` //! Cargo feature: //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.5" +//! version = "1.7.0" //! features = ["tokio02"] //! ``` //! +//! Compatibility with the `tokio` 0.3 runtime is also simultaneously possible using the `tokio03` +//! Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["tokio03"] +//! ``` +//! //! Additionally it's possible to only use the core traits and combinators by //! only enabling the `std` Cargo feature: //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.5" +//! version = "1.7.0" //! default-features = false //! features = ["std"] //! ``` @@ -238,7 +247,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.6.5" +//! version = "1.7.0" //! default-features = false //! features = ["alloc"] //! ``` From 7d20a4435c2e70f9407129ba259b68e82b376fe1 Mon Sep 17 00:00:00 2001 From: Andrew Silver Date: Thu, 5 Nov 2020 08:15:34 +1100 Subject: [PATCH 160/272] Fixed updated docs to match the 80 column style the rest of the docs use --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 47aac45de..6f97bdcae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,8 +223,8 @@ //! features = ["tokio02"] //! ``` //! -//! Compatibility with the `tokio` 0.3 runtime is also simultaneously possible using the `tokio03` -//! Cargo feature: +//! Compatibility with the `tokio` 0.3 runtime is also simultaneously possible +//! using the `tokio03` Cargo feature: //! //! ```toml //! [dependencies.async-std] From 42c44045add8ff19237d29f33861300878961a59 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 14 Nov 2020 05:28:27 +0900 Subject: [PATCH 161/272] Update pin-project-lite to 0.2.0 --- Cargo.toml | 2 +- src/future/future/flatten.rs | 53 ++++++++++++++++++++++-------------- src/net/addr.rs | 5 +++- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 25912921c..eaa2d028b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.3.3", optional = true } num_cpus = { version = "1.12.0", optional = true } once_cell = { version = "1.3.1", optional = true } -pin-project-lite = { version = "0.1.4", optional = true } +pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs index a07b140cc..0ea77bbfb 100644 --- a/src/future/future/flatten.rs +++ b/src/future/future/flatten.rs @@ -1,26 +1,39 @@ +use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; use crate::future::IntoFuture; use crate::task::{ready, Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FlattenFuture { - state: State, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FlattenFuture { + #[pin] + state: State, + } } -#[derive(Debug)] -enum State { - First(Fut1), - Second(Fut2), - Empty, +pin_project! { + #[project = StateProj] + #[derive(Debug)] + enum State { + First { + #[pin] + fut1: Fut1, + }, + Second { + #[pin] + fut2: Fut2, + }, + Empty, + } } impl FlattenFuture { - pub(crate) fn new(future: Fut1) -> FlattenFuture { + pub(crate) fn new(fut1: Fut1) -> FlattenFuture { FlattenFuture { - state: State::First(future), + state: State::First { fut1 }, } } } @@ -33,19 +46,19 @@ where type Output = ::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { state } = unsafe { self.get_unchecked_mut() }; + let mut state = self.project().state; loop { - match state { - State::First(fut1) => { - let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future(); - *state = State::Second(fut2); + match state.as_mut().project() { + StateProj::First { fut1 } => { + let fut2 = ready!(fut1.poll(cx)).into_future(); + state.set(State::Second { fut2 }); } - State::Second(fut2) => { - let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx)); - *state = State::Empty; + StateProj::Second { fut2 } => { + let v = ready!(fut2.poll(cx)); + state.set(State::Empty); return Poll::Ready(v); } - State::Empty => panic!("polled a completed future"), + StateProj::Empty => panic!("polled a completed future"), } } } diff --git a/src/net/addr.rs b/src/net/addr.rs index ea839500e..71988fb37 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -68,6 +68,9 @@ pub enum ToSocketAddrsFuture { Done, } +// The field of `Self::Resolving` is `Unpin`, and the field of `Self::Ready` is never pinned. +impl Unpin for ToSocketAddrsFuture {} + /// Wrap `std::io::Error` with additional message /// /// Keeps the original error kind and stores the original I/O error as `source`. @@ -84,7 +87,7 @@ impl> Future for ToSocketAddrsFuture { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; + let this = self.get_mut(); let state = mem::replace(this, ToSocketAddrsFuture::Done); match state { From 3bb121dc1e3a16bc1a71e26522c8967aaac9ac7c Mon Sep 17 00:00:00 2001 From: hhggit Date: Fri, 20 Mar 2020 11:43:04 +0800 Subject: [PATCH 162/272] reset timer after timeout was ready --- src/stream/stream/timeout.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 0e0ee912c..ec15728b8 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -18,6 +18,7 @@ pin_project! { stream: S, #[pin] delay: Timer, + duration: Duration, } } @@ -25,7 +26,7 @@ impl Timeout { pub(crate) fn new(stream: S, dur: Duration) -> Self { let delay = timer_after(dur); - Self { stream, delay } + Self { stream, delay, duration: dur } } } @@ -33,16 +34,20 @@ impl Stream for Timeout { type Item = Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.project(); + let mut this = self.project(); - match this.stream.poll_next(cx) { + let r = match this.stream.poll_next(cx) { Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => match this.delay.poll(cx) { + Poll::Pending => match this.delay.as_mut().poll(cx) { Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError { _private: () }))), - Poll::Pending => Poll::Pending, + Poll::Pending => return Poll::Pending, }, - } + }; + + *this.delay.as_mut() = timer_after(*this.duration); + + r } } From 8c0e319e949835fd373ead5b4dfc20e031a00f2a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 27 Jun 2020 16:53:52 +0200 Subject: [PATCH 163/272] feat: new channels - add new top level `channels` module (stable) based on `async-channel` - deprecate `sync::channel` --- Cargo.toml | 3 +++ src/channel.rs | 6 ++++++ src/lib.rs | 6 +++--- src/sync/channel.rs | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/channel.rs diff --git a/Cargo.toml b/Cargo.toml index eaa2d028b..5aaa2b5f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ std = [ "wasm-bindgen-futures", "futures-channel", "async-mutex", + "async-channel", ] alloc = [ "futures-core/alloc", @@ -74,10 +75,12 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } +async-channel = { version = "1.5.1", optional = true } # Devdepencency, but they are not allowed to be optional :/ surf = { version = "2.0.0", optional = true } + [target.'cfg(not(target_os = "unknown"))'.dependencies] async-global-executor = { version = "1.4.0", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } diff --git a/src/channel.rs b/src/channel.rs new file mode 100644 index 000000000..90adc1402 --- /dev/null +++ b/src/channel.rs @@ -0,0 +1,6 @@ +//! Channels + +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_channel::*; diff --git a/src/lib.rs b/src/lib.rs index 6f97bdcae..e985f40d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,15 +106,14 @@ //! [`io`], [`fs`], and [`net`] modules. //! //! The [`task`] module contains `async-std`'s task abstractions. [`sync`] -//! contains further primitive shared memory types, including [`channel`], -//! which contains the channel types for message passing. +//! contains further primitive shared memory types. [`channel`] contains the channel types for message passing. //! //! [files]: fs/struct.File.html //! [TCP]: net/struct.TcpStream.html //! [UDP]: net/struct.UdpSocket.html //! [`io`]: fs/struct.File.html //! [`sync`]: sync/index.html -//! [`channel`]: sync/fn.channel.html +//! [`channel`]: channel/index.html //! //! ## Timeouts, intervals, and delays //! @@ -300,6 +299,7 @@ cfg_std! { pub mod os; pub mod prelude; pub mod sync; + pub mod channel; } cfg_default! { diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 928cfc5de..528d8e0b3 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -60,6 +60,7 @@ use crate::sync::WakerSet; /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[deprecated = "new channel api at async_std::channel"] pub fn channel(cap: usize) -> (Sender, Receiver) { let channel = Arc::new(Channel::with_capacity(cap)); let s = Sender { @@ -102,6 +103,7 @@ pub fn channel(cap: usize) -> (Sender, Receiver) { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[deprecated = "new channel api at async_std::channel"] pub struct Sender { /// The inner channel. channel: Arc>, @@ -363,6 +365,7 @@ impl fmt::Debug for Sender { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[deprecated = "new channel api at async_std::channel"] pub struct Receiver { /// The inner channel. channel: Arc>, @@ -993,6 +996,7 @@ impl Drop for Channel { #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(PartialEq, Eq)] +#[deprecated = "new channel api at async_std::channel"] pub enum TrySendError { /// The channel is full but not disconnected. Full(T), @@ -1025,6 +1029,7 @@ impl Display for TrySendError { #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug, PartialEq, Eq)] +#[deprecated = "new channel api at async_std::channel"] pub enum TryRecvError { /// The channel is empty but not disconnected. Empty, @@ -1048,6 +1053,7 @@ impl Display for TryRecvError { #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug, PartialEq, Eq)] +#[deprecated = "new channel api at async_std::channel"] pub struct RecvError; impl Error for RecvError {} From 36366cd4d9742eb9851f82f06fd91bbb9a9dd147 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 1 Dec 2020 15:25:11 +0100 Subject: [PATCH 164/272] fix warnings --- src/sync/channel.rs | 2 ++ src/sync/mod.rs | 1 + tests/channel.rs | 1 + tests/stream.rs | 6 +++--- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 528d8e0b3..1f4dcdad9 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use std::cell::UnsafeCell; use std::error::Error; use std::fmt::{self, Debug, Display}; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 8b7fe3102..6fd9292f3 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -185,6 +185,7 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; + #[allow(deprecated)] pub use channel::{channel, Sender, Receiver, RecvError, TryRecvError, TrySendError}; pub use condvar::Condvar; diff --git a/tests/channel.rs b/tests/channel.rs index a218ea2ae..181a5d2ce 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -1,4 +1,5 @@ #![cfg(feature = "unstable")] +#![allow(deprecated)] use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; diff --git a/tests/stream.rs b/tests/stream.rs index 3a192339f..654735b2a 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -5,9 +5,9 @@ use std::task::{Context, Poll}; use pin_project_lite::pin_project; +use async_std::channel::bounded as channel; use async_std::prelude::*; use async_std::stream; -use async_std::sync::channel; use async_std::task; #[cfg(target_arch = "wasm32")] @@ -36,7 +36,7 @@ fn merging_delayed_streams_work() { task::block_on(async move { task::sleep(std::time::Duration::from_millis(500)).await; - sender.send(92).await; + sender.send(92).await.unwrap(); drop(sender); let xs = t.await; assert_eq!(xs, vec![92]) @@ -55,7 +55,7 @@ fn merging_delayed_streams_work() { task::block_on(async move { task::sleep(std::time::Duration::from_millis(500)).await; - sender.send(92).await; + sender.send(92).await.unwrap(); drop(sender); let xs = t.await; assert_eq!(xs, vec![92]) From da236ae39b7328f2185fba4e438c8f804aa81180 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 1 Dec 2020 15:48:21 +0100 Subject: [PATCH 165/272] more deprecation fixes --- src/sync/channel.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 1f4dcdad9..bb1b2ca32 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -34,6 +34,7 @@ use crate::sync::WakerSet; /// # Examples /// /// ``` +/// #![allow(deprecated)] /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # @@ -85,6 +86,7 @@ pub fn channel(cap: usize) -> (Sender, Receiver) { /// # Examples /// /// ``` +/// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -119,6 +121,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # @@ -208,6 +211,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -227,6 +231,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// use async_std::sync::channel; /// /// let (s, _) = channel::(5); @@ -241,6 +246,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -262,6 +268,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -283,6 +290,7 @@ impl Sender { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -343,6 +351,7 @@ impl fmt::Debug for Sender { /// # Examples /// /// ``` +/// #![allow(deprecated)] /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # @@ -386,6 +395,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # fn main() -> Result<(), async_std::sync::RecvError> { /// # async_std::task::block_on(async { /// # @@ -449,6 +459,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -471,6 +482,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// use async_std::sync::channel; /// /// let (_, r) = channel::(5); @@ -485,6 +497,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -506,6 +519,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; @@ -527,6 +541,7 @@ impl Receiver { /// # Examples /// /// ``` + /// #![allow(deprecated)] /// # async_std::task::block_on(async { /// # /// use async_std::sync::channel; From 1f6bb8b01af18f8180ef36c126f65c379e3b3c11 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 1 Dec 2020 15:51:38 +0100 Subject: [PATCH 166/272] feat: add process module Reexport based on async-process --- Cargo.toml | 4 +++- src/{process/mod.rs => process.rs} | 9 ++++----- 2 files changed, 7 insertions(+), 6 deletions(-) rename src/{process/mod.rs => process.rs} (72%) diff --git a/Cargo.toml b/Cargo.toml index eaa2d028b..9fdb7d09a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,8 @@ default = [ docs = ["attributes", "unstable", "default"] unstable = [ "std", - "async-io" + "async-io", + "async-process", ] attributes = ["async-attributes"] std = [ @@ -74,6 +75,7 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } +async-process = { version = "1.0.1", optional = true } # Devdepencency, but they are not allowed to be optional :/ surf = { version = "2.0.0", optional = true } diff --git a/src/process/mod.rs b/src/process.rs similarity index 72% rename from src/process/mod.rs rename to src/process.rs index 630c5b9b9..418d2bb36 100644 --- a/src/process/mod.rs +++ b/src/process.rs @@ -7,8 +7,7 @@ //! //! [`std::process`]: https://doc.rust-lang.org/std/process/index.html -// Re-export structs. -pub use std::process::{ExitStatus, Output}; - -// Re-export functions. -pub use std::process::{abort, exit, id}; +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::*; From 7b896c0bf4f09bde41e03576bdcdd6f924c3af8a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 1 Dec 2020 16:02:00 +0100 Subject: [PATCH 167/272] manual reexports --- src/lib.rs | 2 +- src/os/unix/mod.rs | 5 +++++ src/os/windows/mod.rs | 5 +++++ src/process.rs | 31 +++++++++++++++++++++++++++++-- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f97bdcae..dd8fb597d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -313,7 +313,7 @@ cfg_default! { cfg_unstable! { pub mod pin; - #[cfg(not(target_os = "unknown"))] + #[cfg(all(not(target_os = "unknown"), feature = "std"))] pub mod process; mod unit; diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index c389d95a5..36a97967c 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -8,3 +8,8 @@ cfg_default! { pub mod fs; pub mod net; } + +#[cfg(all(feature = "unstable", feature = "std"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::unix as process; diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 67bf0372a..11389fb79 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -8,3 +8,8 @@ cfg_unstable! { #[cfg(feature = "default")] pub mod fs; } + +#[cfg(all(feature = "unstable", feature = "std"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::windows as process; diff --git a/src/process.rs b/src/process.rs index 418d2bb36..09b8390ee 100644 --- a/src/process.rs +++ b/src/process.rs @@ -7,7 +7,34 @@ //! //! [`std::process`]: https://doc.rust-lang.org/std/process/index.html -#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] -pub use async_process::*; +pub use async_process::ExitStatus; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Output; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Stdio; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Child; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStderr; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStdin; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStdout; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Command; From 151025fa38c98eb519053f7d151054e8f7b14f91 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 1 Dec 2020 16:10:51 +0100 Subject: [PATCH 168/272] fixup --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9fdb7d09a..e6492cdf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,6 @@ once_cell = { version = "1.3.1", optional = true } pin-project-lite = { version = "0.2.0", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } -async-process = { version = "1.0.1", optional = true } # Devdepencency, but they are not allowed to be optional :/ surf = { version = "2.0.0", optional = true } @@ -85,6 +84,7 @@ async-global-executor = { version = "1.4.0", optional = true, features = ["async async-io = { version = "1.0.1", optional = true } blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } +async-process = { version = "1.0.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] gloo-timers = { version = "0.2.1", features = ["futures"], optional = true } From 92f5038ed658ac75e7788cadacccaac7dda4430b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 8 Dec 2020 22:08:44 +0100 Subject: [PATCH 169/272] attempt to fix docs builds --- src/os/windows/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 11389fb79..8a1aeadd9 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -7,9 +7,6 @@ cfg_std! { cfg_unstable! { #[cfg(feature = "default")] pub mod fs; + #[cfg(feature = "std")] + pub use async_process::windows as process; } - -#[cfg(all(feature = "unstable", feature = "std"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] -pub use async_process::windows as process; From f8f1eacc9aa8b32a0987b1dd436fd6974d57dc4b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 8 Dec 2020 22:40:31 +0100 Subject: [PATCH 170/272] Attempt 2 at fixing docs on windows --- src/os/windows/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 8a1aeadd9..0d45a52e4 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -8,5 +8,6 @@ cfg_unstable! { #[cfg(feature = "default")] pub mod fs; #[cfg(feature = "std")] + #[cfg(windows)] pub use async_process::windows as process; } From 34e9ff3cd2aab8971a5e0338b2afbcb1725f8b7d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 8 Dec 2020 23:04:03 +0100 Subject: [PATCH 171/272] Restore sync process exports --- src/process.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/process.rs b/src/process.rs index 09b8390ee..8ba803da5 100644 --- a/src/process.rs +++ b/src/process.rs @@ -38,3 +38,6 @@ pub use async_process::ChildStdout; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub use async_process::Command; + +// Re-export functions. +pub use std::process::{abort, exit, id}; From c738d73bd7a6ac465580135d59556528f01747d7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 4 Dec 2020 19:05:44 +0100 Subject: [PATCH 172/272] v1.8.0 Update CHANGELOG.md --- CHANGELOG.md | 22 ++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0cd2f83c..e31829df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.8.0] - 2020-12-04 + +This patch introduces `async_std::channel`, a new submodule for our async channels implementation. `channels` have been one of async-std's most requested features, and have existed as "unstable" for the past year. We've been cautious about stabilizing channels, and this caution turned out to be warranted: we realized our channels could hang indefinitely under certain circumstances, and people ended up expressing a need for unbounded channels. + +So today we're introducing the new `async_std::channel` submodule which exports the `async-channel` crate, and we're marking the older unstable `async_std::sync::channel` API as "deprecated". This release includes both APIs, but we intend to stabilize `async_std::channel` and remove the older API in January. This should give dependent projects a month to upgrade, though we can extend that if it proves to be too short. + +The rationale for adding a new top-level `channel` submodule, rather than extending `sync` is that the `std::sync` and `async_std::sync` submodule are a bit of a mess, and the libs team [has been talking about splitting `std::sync` up]([https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478](https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478)) into separate modules. The stdlib has to guarantee it'll forever be backwards compatible, but `async-std` does not (we fully expect a 2.0 once we have async closures & traits). So we're experimenting with this change before `std` does, with the expectation that this change can serve as a data point when the libs team decides how to proceed in std. + +### Added + +- `async_std::channel` as "unstable" #915 +- `async_std::process` as "unstable" #916 + +### Fixed + +- Fixed mentions of the `tokio03` flags in the docs #909 +- Fixed a double drop issue in `StreamExt::cycle` #903 + +### Internal + +- updated `pin-project` to `v0.2.0` + # [1.7.0] - 2020-10-30 This patch adds a feature to enable compatibility with the new `tokio` 0.3.0 diff --git a/Cargo.toml b/Cargo.toml index 32a788827..93f783ccc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.7.0" +version = "1.8.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 01949b505b3ff6176f4c3ffaf78bc52fa1eea02b Mon Sep 17 00:00:00 2001 From: Wesley Merkel Date: Fri, 11 Dec 2020 14:05:05 -0600 Subject: [PATCH 173/272] Fix link in typo in src/task/mod.rs --- src/task/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index ca0b92a02..440f6ddc6 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -103,7 +103,7 @@ //! the desired task name to [`Builder::name`]. To retrieve the task name from within the //! task, use [`Task::name`]. //! -//! [`Arc`]: ../gsync/struct.Arc.html +//! [`Arc`]: ../sync/struct.Arc.html //! [`spawn`]: fn.spawn.html //! [`JoinHandle`]: struct.JoinHandle.html //! [`JoinHandle::task`]: struct.JoinHandle.html#method.task From 8823c460fc71e185e14af62670c872dea5f0c895 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 23 Dec 2020 22:12:16 +0100 Subject: [PATCH 174/272] rand: update to 0.8 Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 4 ++-- tests/channel.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93f783ccc..1ca61fb8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,10 +99,10 @@ wasm-bindgen-test = "0.3.10" [dev-dependencies] femme = "2.1.1" -rand = "0.7.3" +rand = "0.8.0" tempfile = "3.1.0" futures = "0.3.4" -rand_xorshift = "0.2.0" +rand_xorshift = "0.3.0" [[test]] name = "stream" diff --git a/tests/channel.rs b/tests/channel.rs index 181a5d2ce..f9eacb6db 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -345,8 +345,8 @@ fn drops() { for _ in 0..RUNS { let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0); task::block_on(async move { - let steps = rng.gen_range(0, 10_000); - let additional = rng.gen_range(0, 50); + let steps = rng.gen_range(0..10_000); + let additional = rng.gen_range(0..50); DROPS.store(0, Ordering::SeqCst); let (s, r) = channel::(50); From dbbf311344243a3fcabbc8ba2faf26c739740750 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 23 Dec 2020 22:22:10 +0100 Subject: [PATCH 175/272] try to fix wasm Signed-off-by: Marc-Antoine Perennou --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1ca61fb8a..63f095726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ futures-channel = { version = "0.3.4", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.10" +getrandom = { version = "0.2.0", features = ["js"] } [dev-dependencies] femme = "2.1.1" From 47b22fff5626b0951edc81f991c5001a1c0d674d Mon Sep 17 00:00:00 2001 From: surechen Date: Wed, 30 Dec 2020 17:14:18 +0800 Subject: [PATCH 176/272] edit Small typo for Stream --- src/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 0bfd4e865..b3c7ff7a3 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -255,7 +255,7 @@ //! //! # Infinity //! -//! Streams do not have to be finite. As an example, an repeat stream is +//! Streams do not have to be finite. As an example, a repeat stream is //! an infinite stream: //! //! ``` From ffd46f75cac401b6c2d6ab444d8ebff11d1d04ca Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 31 Dec 2020 18:49:53 +0900 Subject: [PATCH 177/272] Replace deprecated compare_and_swap with compare_exchange --- src/sync/rwlock.rs | 13 ++++++++++--- src/task/task_local.rs | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 08d8ed849..89c043f4f 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -1,10 +1,10 @@ use std::cell::UnsafeCell; use std::fmt; +use std::future::Future; use std::isize; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::process; -use std::future::Future; use std::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::WakerSet; @@ -301,7 +301,11 @@ impl RwLock { /// # }) /// ``` pub fn try_write(&self) -> Option> { - if self.state.compare_and_swap(0, WRITE_LOCK, Ordering::SeqCst) == 0 { + if self + .state + .compare_exchange(0, WRITE_LOCK, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { Some(RwLockWriteGuard(self)) } else { None @@ -318,7 +322,10 @@ impl RwLock { /// let lock = RwLock::new(10); /// assert_eq!(lock.into_inner(), 10); /// ``` - pub fn into_inner(self) -> T where T: Sized { + pub fn into_inner(self) -> T + where + T: Sized, + { self.value.into_inner() } diff --git a/src/task/task_local.rs b/src/task/task_local.rs index 4e2ba8387..1661c0bb9 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -124,9 +124,9 @@ impl LocalKey { std::process::abort(); } - match key.compare_and_swap(0, counter, Ordering::AcqRel) { - 0 => counter, - k => k, + match key.compare_exchange(0, counter, Ordering::AcqRel, Ordering::Acquire) { + Ok(_) => counter, + Err(k) => k, } } From 7eaf577b785b7436c3ae66c69e5c790e369e7564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Wed, 6 Jan 2021 12:47:26 -0500 Subject: [PATCH 178/272] Fix a typo for [sic] FuturesExt trait The trait that is being referred to here is called `futures::future::FutureExt`. --- docs/src/overview/std-and-library-futures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md index c321d2119..7f2b98d6a 100644 --- a/docs/src/overview/std-and-library-futures.md +++ b/docs/src/overview/std-and-library-futures.md @@ -8,9 +8,9 @@ Rust has two kinds of types commonly referred to as `Future`: The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`. -It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. +It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FutureExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. -In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures` crate by adding it to your `Cargo.toml` and importing `FuturesExt`. +In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures` crate by adding it to your `Cargo.toml` and importing `FutureExt`. ## Interfaces and Stability From 4a3f963810ddd1dfd1df1eae5c341c89dd7af088 Mon Sep 17 00:00:00 2001 From: Koxiaet Date: Wed, 13 Jan 2021 10:10:43 +0000 Subject: [PATCH 179/272] feat: use async-lock for RwLock and Barrier --- Cargo.toml | 4 +- src/sync/barrier.rs | 229 --------------------- src/sync/mod.rs | 16 +- src/sync/rwlock.rs | 463 ------------------------------------------ src/sync/waker_set.rs | 15 -- 5 files changed, 9 insertions(+), 718 deletions(-) delete mode 100644 src/sync/barrier.rs delete mode 100644 src/sync/rwlock.rs diff --git a/Cargo.toml b/Cargo.toml index 63f095726..ef861ed40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,8 +52,8 @@ std = [ "slab", "wasm-bindgen-futures", "futures-channel", - "async-mutex", "async-channel", + "async-lock", ] alloc = [ "futures-core/alloc", @@ -64,7 +64,7 @@ tokio03 = ["async-global-executor/tokio03"] [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-mutex = { version = "1.1.3", optional = true } +async-lock = { version = "2.3.0", optional = true } crossbeam-utils = { version = "0.8.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-io = { version = "0.3.4", optional = true } diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs deleted file mode 100644 index f492ebe64..000000000 --- a/src/sync/barrier.rs +++ /dev/null @@ -1,229 +0,0 @@ -use crate::sync::{Condvar,Mutex}; - -/// A barrier enables multiple tasks to synchronize the beginning -/// of some computation. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::{Arc, Barrier}; -/// use async_std::task; -/// -/// let mut handles = Vec::with_capacity(10); -/// let barrier = Arc::new(Barrier::new(10)); -/// for _ in 0..10 { -/// let c = barrier.clone(); -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// handles.push(task::spawn(async move { -/// println!("before wait"); -/// c.wait().await; -/// println!("after wait"); -/// })); -/// } -/// // Wait for the other futures to finish. -/// for handle in handles { -/// handle.await; -/// } -/// # }); -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct Barrier { - state: Mutex, - cvar: Condvar, - num_tasks: usize, -} - -// The inner state of a double barrier -#[derive(Debug)] -struct BarrierState { - count: usize, - generation_id: usize, -} - -/// A `BarrierWaitResult` is returned by `wait` when all threads in the `Barrier` have rendezvoused. -/// -/// [`wait`]: struct.Barrier.html#method.wait -/// [`Barrier`]: struct.Barrier.html -/// -/// # Examples -/// -/// ``` -/// use async_std::sync::Barrier; -/// -/// let barrier = Barrier::new(1); -/// let barrier_wait_result = barrier.wait(); -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug, Clone)] -pub struct BarrierWaitResult(bool); - -impl Barrier { - /// Creates a new barrier that can block a given number of tasks. - /// - /// A barrier will block `n`-1 tasks which call [`wait`] and then wake up - /// all tasks at once when the `n`th task calls [`wait`]. - /// - /// [`wait`]: #method.wait - /// - /// # Examples - /// - /// ``` - /// use std::sync::Barrier; - /// - /// let barrier = Barrier::new(10); - /// ``` - pub fn new(n: usize) -> Barrier { - Barrier { - state: Mutex::new(BarrierState { - count: 0, - generation_id: 1, - }), - cvar: Condvar::new(), - num_tasks: n, - } - } - - /// Blocks the current task until all tasks have rendezvoused here. - /// - /// Barriers are re-usable after all tasks have rendezvoused once, and can - /// be used continuously. - /// - /// A single (arbitrary) task will receive a [`BarrierWaitResult`] that - /// returns `true` from [`is_leader`] when returning from this function, and - /// all other tasks will receive a result that will return `false` from - /// [`is_leader`]. - /// - /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html - /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::{Arc, Barrier}; - /// use async_std::task; - /// - /// let mut handles = Vec::with_capacity(10); - /// let barrier = Arc::new(Barrier::new(10)); - /// for _ in 0..10 { - /// let c = barrier.clone(); - /// // The same messages will be printed together. - /// // You will NOT see any interleaving. - /// handles.push(task::spawn(async move { - /// println!("before wait"); - /// c.wait().await; - /// println!("after wait"); - /// })); - /// } - /// // Wait for the other futures to finish. - /// for handle in handles { - /// handle.await; - /// } - /// # }); - /// ``` - pub async fn wait(&self) -> BarrierWaitResult { - let mut state = self.state.lock().await; - let local_gen = state.generation_id; - state.count += 1; - - if state.count < self.num_tasks { - while local_gen == state.generation_id && state.count < self.num_tasks { - state = self.cvar.wait(state).await; - } - - BarrierWaitResult(false) - } else { - state.count = 0; - state.generation_id = state.generation_id.wrapping_add(1); - self.cvar.notify_all(); - BarrierWaitResult(true) - } - } -} - -impl BarrierWaitResult { - /// Returns `true` if this task from [`wait`] is the "leader task". - /// - /// Only one task will have `true` returned from their result, all other - /// tasks will have `false` returned. - /// - /// [`wait`]: struct.Barrier.html#method.wait - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::Barrier; - /// - /// let barrier = Barrier::new(1); - /// let barrier_wait_result = barrier.wait().await; - /// println!("{:?}", barrier_wait_result.is_leader()); - /// # }); - /// ``` - pub fn is_leader(&self) -> bool { - self.0 - } -} - -#[cfg(all(test, not(target_os = "unknown")))] -mod test { - use futures::channel::mpsc::unbounded; - use futures::sink::SinkExt; - use futures::stream::StreamExt; - - use crate::sync::{Arc, Barrier}; - use crate::task; - - #[test] - fn test_barrier() { - // NOTE(dignifiedquire): Based on the test in std, I was seeing some - // race conditions, so running it in a loop to make sure things are - // solid. - - for _ in 0..1_000 { - task::block_on(async move { - const N: usize = 10; - - let barrier = Arc::new(Barrier::new(N)); - let (tx, mut rx) = unbounded(); - - for _ in 0..N - 1 { - let c = barrier.clone(); - let mut tx = tx.clone(); - task::spawn(async move { - let res = c.wait().await; - - tx.send(res.is_leader()).await.unwrap(); - }); - } - - // At this point, all spawned threads should be blocked, - // so we shouldn't get anything from the port - let res = rx.try_next(); - assert!(match res { - Err(_err) => true, - _ => false, - }); - - let mut leader_found = barrier.wait().await.is_leader(); - - // Now, the barrier is cleared and we should get data. - for _ in 0..N - 1 { - if rx.next().await.unwrap() { - assert!(!leader_found); - leader_found = true; - } - } - assert!(leader_found); - }); - } - } -} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 6fd9292f3..4c1fca11e 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -177,22 +177,20 @@ pub use std::sync::{Arc, Weak}; #[doc(inline)] -pub use async_mutex::{Mutex, MutexGuard}; +pub use async_lock::{Mutex, MutexGuard, MutexGuardArc}; -pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; - -mod rwlock; +#[doc(inline)] +pub use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard}; cfg_unstable! { - pub use barrier::{Barrier, BarrierWaitResult}; + pub use async_lock::{Barrier, BarrierWaitResult}; #[allow(deprecated)] pub use channel::{channel, Sender, Receiver, RecvError, TryRecvError, TrySendError}; pub use condvar::Condvar; + pub(crate) use waker_set::WakerSet; - mod barrier; mod condvar; mod channel; -} -pub(crate) mod waker_set; -pub(crate) use waker_set::WakerSet; + pub(crate) mod waker_set; +} diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs deleted file mode 100644 index 89c043f4f..000000000 --- a/src/sync/rwlock.rs +++ /dev/null @@ -1,463 +0,0 @@ -use std::cell::UnsafeCell; -use std::fmt; -use std::future::Future; -use std::isize; -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::process; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use crate::sync::WakerSet; -use crate::task::{Context, Poll}; - -/// Set if a write lock is held. -#[allow(clippy::identity_op)] -const WRITE_LOCK: usize = 1 << 0; - -/// The value of a single blocked read contributing to the read count. -const ONE_READ: usize = 1 << 1; - -/// The bits in which the read count is stored. -const READ_COUNT_MASK: usize = !(ONE_READ - 1); - -/// A reader-writer lock for protecting shared data. -/// -/// This type is an async version of [`std::sync::RwLock`]. -/// -/// [`std::sync::RwLock`]: https://doc.rust-lang.org/std/sync/struct.RwLock.html -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::RwLock; -/// -/// let lock = RwLock::new(5); -/// -/// // Multiple read locks can be held at a time. -/// let r1 = lock.read().await; -/// let r2 = lock.read().await; -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// drop((r1, r2)); -/// -/// // Only one write locks can be held at a time. -/// let mut w = lock.write().await; -/// *w += 1; -/// assert_eq!(*w, 6); -/// # -/// # }) -/// ``` -pub struct RwLock { - state: AtomicUsize, - read_wakers: WakerSet, - write_wakers: WakerSet, - value: UnsafeCell, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - /// Creates a new reader-writer lock. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(0); - /// ``` - pub fn new(t: T) -> RwLock { - RwLock { - state: AtomicUsize::new(0), - read_wakers: WakerSet::new(), - write_wakers: WakerSet::new(), - value: UnsafeCell::new(t), - } - } -} - -impl RwLock { - /// Acquires a read lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_read().is_some()); - /// # - /// # }) - /// ``` - pub async fn read(&self) -> RwLockReadGuard<'_, T> { - pub struct ReadFuture<'a, T: ?Sized> { - lock: &'a RwLock, - opt_key: Option, - } - - impl<'a, T: ?Sized> Future for ReadFuture<'a, T> { - type Output = RwLockReadGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // If the current task is in the set, remove it. - if let Some(key) = self.opt_key.take() { - self.lock.read_wakers.remove(key); - } - - // Try acquiring a read lock. - match self.lock.try_read() { - Some(guard) => return Poll::Ready(guard), - None => { - // Insert this lock operation. - self.opt_key = Some(self.lock.read_wakers.insert(cx)); - - // If the lock is still acquired for writing, return. - if self.lock.state.load(Ordering::SeqCst) & WRITE_LOCK != 0 { - return Poll::Pending; - } - } - } - } - } - } - - impl Drop for ReadFuture<'_, T> { - fn drop(&mut self) { - // If the current task is still in the set, that means it is being cancelled now. - if let Some(key) = self.opt_key { - self.lock.read_wakers.cancel(key); - - // If there are no active readers, notify a blocked writer if none were - // notified already. - if self.lock.state.load(Ordering::SeqCst) & READ_COUNT_MASK == 0 { - self.lock.write_wakers.notify_any(); - } - } - } - } - - ReadFuture { - lock: self, - opt_key: None, - } - .await - } - - /// Attempts to acquire a read lock. - /// - /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_read().is_some()); - /// # - /// # }) - /// ``` - pub fn try_read(&self) -> Option> { - let mut state = self.state.load(Ordering::SeqCst); - - loop { - // If a write lock is currently held, then a read lock cannot be acquired. - if state & WRITE_LOCK != 0 { - return None; - } - - // Make sure the number of readers doesn't overflow. - if state > isize::MAX as usize { - process::abort(); - } - - // Increment the number of active reads. - match self.state.compare_exchange_weak( - state, - state + ONE_READ, - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(_) => return Some(RwLockReadGuard(self)), - Err(s) => state = s, - } - } - } - - /// Acquires a write lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let mut n = lock.write().await; - /// *n = 2; - /// - /// assert!(lock.try_read().is_none()); - /// # - /// # }) - /// ``` - pub async fn write(&self) -> RwLockWriteGuard<'_, T> { - pub struct WriteFuture<'a, T: ?Sized> { - lock: &'a RwLock, - opt_key: Option, - } - - impl<'a, T: ?Sized> Future for WriteFuture<'a, T> { - type Output = RwLockWriteGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // If the current task is in the set, remove it. - if let Some(key) = self.opt_key.take() { - self.lock.write_wakers.remove(key); - } - - // Try acquiring a write lock. - match self.lock.try_write() { - Some(guard) => return Poll::Ready(guard), - None => { - // Insert this lock operation. - self.opt_key = Some(self.lock.write_wakers.insert(cx)); - - // If the lock is still acquired for reading or writing, return. - if self.lock.state.load(Ordering::SeqCst) != 0 { - return Poll::Pending; - } - } - } - } - } - } - - impl Drop for WriteFuture<'_, T> { - fn drop(&mut self) { - // If the current task is still in the set, that means it is being cancelled now. - if let Some(key) = self.opt_key { - if !self.lock.write_wakers.cancel(key) { - // If no other blocked reader was notified, notify all readers. - self.lock.read_wakers.notify_all(); - } - } - } - } - - WriteFuture { - lock: self, - opt_key: None, - } - .await - } - - /// Attempts to acquire a write lock. - /// - /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_write().is_none()); - /// # - /// # }) - /// ``` - pub fn try_write(&self) -> Option> { - if self - .state - .compare_exchange(0, WRITE_LOCK, Ordering::SeqCst, Ordering::SeqCst) - .is_ok() - { - Some(RwLockWriteGuard(self)) - } else { - None - } - } - - /// Consumes the lock, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(10); - /// assert_eq!(lock.into_inner(), 10); - /// ``` - pub fn into_inner(self) -> T - where - T: Sized, - { - self.value.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the lock mutably, no actual locking takes place -- the mutable - /// borrow statically guarantees no locks exist. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let mut lock = RwLock::new(0); - /// *lock.get_mut() = 10; - /// assert_eq!(*lock.write().await, 10); - /// # - /// # }) - /// ``` - pub fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } - } -} - -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - struct Locked; - impl fmt::Debug for Locked { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - match self.try_read() { - None => f.debug_struct("RwLock").field("data", &Locked).finish(), - Some(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), - } - } -} - -impl From for RwLock { - fn from(val: T) -> RwLock { - RwLock::new(val) - } -} - -impl Default for RwLock { - fn default() -> RwLock { - RwLock::new(Default::default()) - } -} - -/// A guard that releases the read lock when dropped. -pub struct RwLockReadGuard<'a, T: ?Sized>(&'a RwLock); - -unsafe impl Send for RwLockReadGuard<'_, T> {} -unsafe impl Sync for RwLockReadGuard<'_, T> {} - -impl Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - let state = self.0.state.fetch_sub(ONE_READ, Ordering::SeqCst); - - // If this was the last reader, notify a blocked writer if none were notified already. - if state & READ_COUNT_MASK == ONE_READ { - self.0.write_wakers.notify_any(); - } - } -} - -impl fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -/// A guard that releases the write lock when dropped. -pub struct RwLockWriteGuard<'a, T: ?Sized>(&'a RwLock); - -unsafe impl Send for RwLockWriteGuard<'_, T> {} -unsafe impl Sync for RwLockWriteGuard<'_, T> {} - -impl Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.0.state.store(0, Ordering::SeqCst); - - // Notify all blocked readers. - if !self.0.read_wakers.notify_all() { - // If there were no blocked readers, notify a blocked writer if none were notified - // already. - self.0.write_wakers.notify_any(); - } - } -} - -impl fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -impl DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } - } -} diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 881304bac..e38df861e 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -127,21 +127,6 @@ impl WakerSet { false } - /// Notifies a blocked operation if none have been notified already. - /// - /// Returns `true` if an operation was notified. - #[inline] - pub fn notify_any(&self) -> bool { - // Use `SeqCst` ordering to synchronize with `Lock::drop()`. - let flag = self.flag.load(Ordering::SeqCst); - - if flag & NOTIFIED == 0 && flag & NOTIFIABLE != 0 { - self.notify(Notify::Any) - } else { - false - } - } - /// Notifies one additional blocked operation. /// /// Returns `true` if an operation was notified. From ac19c660c571f1bf35d7b3fa34d43ac914763b0b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Wed, 13 Jan 2021 11:11:28 +0100 Subject: [PATCH 180/272] Update async-global-executor and add tokio feature for tokio 1.0 Co-authored-by: Yoshua Wuyts --- CHANGELOG.md | 4 ++++ Cargo.toml | 5 ++--- src/lib.rs | 9 +++++++++ src/rt/mod.rs | 2 +- src/task/spawn_blocking.rs | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e31829df8..c16bbf645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +## Added + +- Add `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) + # [1.8.0] - 2020-12-04 This patch introduces `async_std::channel`, a new submodule for our async channels implementation. `channels` have been one of async-std's most requested features, and have existed as "unstable" for the past year. We've been cautious about stabilizing channels, and this caution turned out to be warranted: we realized our channels could hang indefinitely under certain circumstances, and people ended up expressing a need for unbounded channels. diff --git a/Cargo.toml b/Cargo.toml index ef861ed40..9afb0e2af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ default = [ "std", "async-global-executor", "async-io", - "blocking", "futures-lite", "kv-log-macro", "log", @@ -59,6 +58,7 @@ alloc = [ "futures-core/alloc", "pin-project-lite", ] +tokio1 = ["async-global-executor/tokio"] tokio02 = ["async-global-executor/tokio02"] tokio03 = ["async-global-executor/tokio03"] @@ -83,9 +83,8 @@ surf = { version = "2.0.0", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] -async-global-executor = { version = "1.4.0", optional = true, features = ["async-io"] } +async-global-executor = { version = "2.0.0", optional = true, features = ["async-io"] } async-io = { version = "1.0.1", optional = true } -blocking = { version = "1.0.0", optional = true } futures-lite = { version = "1.0.0", optional = true } async-process = { version = "1.0.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 22495d367..9a1c00c10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,6 +213,15 @@ //! features = ["attributes"] //! ``` //! +//! Compatibility with the `tokio` 1.0 runtime is also simultaneously possible +//! using the `tokio1` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["tokio1"] +//! ``` +//! //! Compatibility with the `tokio` 0.2 runtime is possible using the `tokio02` //! Cargo feature: //! diff --git a/src/rt/mod.rs b/src/rt/mod.rs index da78d9f89..80f1c4e6b 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -12,7 +12,7 @@ pub static RUNTIME: Lazy = Lazy::new(|| { // Create an executor thread pool. let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string()); - async_global_executor::init_with_config(async_global_executor::GlobalExecutorConfig::default().with_env_var("ASYNC_STD_THREAD_COUNT").with_thread_name(thread_name)); + async_global_executor::init_with_config(async_global_executor::GlobalExecutorConfig::default().with_env_var("ASYNC_STD_THREAD_COUNT").with_thread_name_fn(move || thread_name.clone())); Runtime {} }); diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 8d6f3a51a..3c57a751b 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -35,5 +35,5 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - task::spawn(blocking::unblock(f)) + task::spawn(async_global_executor::spawn_blocking(f)) } From 684ab185feaccb756a62628311fa3dd915683261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Rubio?= Date: Wed, 13 Jan 2021 11:19:58 +0100 Subject: [PATCH 181/272] docs: update cargo-edit link in the installation section The project `cargo-add` has been deprecated in favor of `cargo-edit`: https://github.com/withoutboats/cargo-add --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e1b593d6..39876bf7b 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ using Rust's familiar stdlib API. ## Installation -With [cargo add][cargo-add] installed run: +With [cargo-edit](https://github.com/killercup/cargo-edit) installed run: ```sh $ cargo add async-std From 8c5238743b7f2deed0ed05ec314ac44d973f715b Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 13 Jan 2021 11:20:22 +0100 Subject: [PATCH 182/272] remove deprecated sync::channel --- CHANGELOG.md | 4 + src/sync/channel.rs | 1082 ----------------------------------------- src/sync/mod.rs | 3 - src/sync/waker_set.rs | 10 - tests/channel.rs | 53 +- 5 files changed, 30 insertions(+), 1122 deletions(-) delete mode 100644 src/sync/channel.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c16bbf645..62638056f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview - Add `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) +## Removed + +- Removed deprecated `sync::channel` + # [1.8.0] - 2020-12-04 This patch introduces `async_std::channel`, a new submodule for our async channels implementation. `channels` have been one of async-std's most requested features, and have existed as "unstable" for the past year. We've been cautious about stabilizing channels, and this caution turned out to be warranted: we realized our channels could hang indefinitely under certain circumstances, and people ended up expressing a need for unbounded channels. diff --git a/src/sync/channel.rs b/src/sync/channel.rs deleted file mode 100644 index bb1b2ca32..000000000 --- a/src/sync/channel.rs +++ /dev/null @@ -1,1082 +0,0 @@ -#![allow(deprecated)] - -use std::cell::UnsafeCell; -use std::error::Error; -use std::fmt::{self, Debug, Display}; -use std::future::Future; -use std::isize; -use std::marker::PhantomData; -use std::mem; -use std::pin::Pin; -use std::process; -use std::ptr; -use std::sync::atomic::{self, AtomicUsize, Ordering}; -use std::sync::Arc; -use std::task::{Context, Poll}; - -use crossbeam_utils::Backoff; - -use crate::stream::Stream; -use crate::sync::WakerSet; - -/// Creates a bounded multi-producer multi-consumer channel. -/// -/// This channel has a buffer that can hold at most `cap` messages at a time. -/// -/// Senders and receivers can be cloned. When all senders associated with a channel get dropped, it -/// becomes closed. Receive operations on a closed and empty channel return [RecvError] instead of -/// trying to await a message when using [Receiver::recv] or `None` when used as a [Stream]. -/// -/// # Panics -/// -/// If `cap` is zero, this function will panic. -/// -/// # Examples -/// -/// ``` -/// #![allow(deprecated)] -/// # fn main() -> Result<(), async_std::sync::RecvError> { -/// # async_std::task::block_on(async { -/// # -/// use std::time::Duration; -/// -/// use async_std::sync::channel; -/// use async_std::task; -/// -/// let (s, r) = channel(1); -/// -/// // This call returns immediately because there is enough space in the channel. -/// s.send(1usize).await; -/// -/// task::spawn(async move { -/// // This call will have to wait because the channel is full. -/// // It will be able to complete only after the first message is received. -/// s.send(2).await; -/// }); -/// -/// task::sleep(Duration::from_secs(1)).await; -/// assert_eq!(r.recv().await?, 1); -/// assert_eq!(r.recv().await?, 2); -/// # Ok(()) -/// # -/// # }) } -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[deprecated = "new channel api at async_std::channel"] -pub fn channel(cap: usize) -> (Sender, Receiver) { - let channel = Arc::new(Channel::with_capacity(cap)); - let s = Sender { - channel: channel.clone(), - }; - let r = Receiver { - channel, - opt_key: None, - }; - (s, r) -} - -/// The sending side of a channel. -/// -/// This struct is created by the [`channel`] function. See its -/// documentation for more. -/// -/// [`channel`]: fn.channel.html -/// -/// # Examples -/// -/// ``` -/// #![allow(deprecated)] -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::channel; -/// use async_std::task; -/// -/// let (s1, r) = channel(100); -/// let s2 = s1.clone(); -/// -/// task::spawn(async move { s1.send(1).await }); -/// task::spawn(async move { s2.send(2).await }); -/// -/// let msg1 = r.recv().await.unwrap(); -/// let msg2 = r.recv().await.unwrap(); -/// -/// assert_eq!(msg1 + msg2, 3); -/// # -/// # }) -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[deprecated = "new channel api at async_std::channel"] -pub struct Sender { - /// The inner channel. - channel: Arc>, -} - -impl Sender { - /// Sends a message into the channel. - /// - /// If the channel is full, this method will wait until there is space in the channel. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # fn main() -> Result<(), async_std::sync::RecvError> { - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// use async_std::task; - /// - /// let (s, r) = channel(1); - /// - /// task::spawn(async move { - /// s.send(1).await; - /// s.send(2).await; - /// }); - /// - /// assert_eq!(r.recv().await?, 1); - /// assert_eq!(r.recv().await?, 2); - /// assert!(r.recv().await.is_err()); - /// # - /// # Ok(()) - /// # }) } - /// ``` - pub async fn send(&self, msg: T) { - struct SendFuture<'a, T> { - channel: &'a Channel, - msg: Option, - opt_key: Option, - } - - impl Unpin for SendFuture<'_, T> {} - - impl Future for SendFuture<'_, T> { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - let msg = self.msg.take().unwrap(); - - // If the current task is in the set, remove it. - if let Some(key) = self.opt_key.take() { - self.channel.send_wakers.remove(key); - } - - // Try sending the message. - match self.channel.try_send(msg) { - Ok(()) => return Poll::Ready(()), - Err(TrySendError::Disconnected(msg)) => { - self.msg = Some(msg); - return Poll::Pending; - } - Err(TrySendError::Full(msg)) => { - self.msg = Some(msg); - - // Insert this send operation. - self.opt_key = Some(self.channel.send_wakers.insert(cx)); - - // If the channel is still full and not disconnected, return. - if self.channel.is_full() && !self.channel.is_disconnected() { - return Poll::Pending; - } - } - } - } - } - } - - impl Drop for SendFuture<'_, T> { - fn drop(&mut self) { - // If the current task is still in the set, that means it is being cancelled now. - // Wake up another task instead. - if let Some(key) = self.opt_key { - self.channel.send_wakers.cancel(key); - } - } - } - - SendFuture { - channel: &self.channel, - msg: Some(msg), - opt_key: None, - } - .await - } - - /// Attempts to send a message into the channel. - /// - /// If the channel is full, this method will return an error. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// assert!(s.try_send(1).is_ok()); - /// assert!(s.try_send(2).is_err()); - /// # - /// # }) - /// ``` - pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { - self.channel.try_send(msg) - } - - /// Returns the channel capacity. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// use async_std::sync::channel; - /// - /// let (s, _) = channel::(5); - /// assert_eq!(s.capacity(), 5); - /// ``` - pub fn capacity(&self) -> usize { - self.channel.cap - } - - /// Returns `true` if the channel is empty. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// - /// assert!(s.is_empty()); - /// s.send(0).await; - /// assert!(!s.is_empty()); - /// # - /// # }) - /// ``` - pub fn is_empty(&self) -> bool { - self.channel.is_empty() - } - - /// Returns `true` if the channel is full. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// - /// assert!(!s.is_full()); - /// s.send(0).await; - /// assert!(s.is_full()); - /// # - /// # }) - /// ``` - pub fn is_full(&self) -> bool { - self.channel.is_full() - } - - /// Returns the number of messages in the channel. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(2); - /// assert_eq!(s.len(), 0); - /// - /// s.send(1).await; - /// s.send(2).await; - /// assert_eq!(s.len(), 2); - /// # - /// # }) - /// ``` - pub fn len(&self) -> usize { - self.channel.len() - } -} - -impl Drop for Sender { - fn drop(&mut self) { - // Decrement the sender count and disconnect the channel if it drops down to zero. - if self.channel.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 { - self.channel.disconnect(); - } - } -} - -impl Clone for Sender { - fn clone(&self) -> Sender { - let count = self.channel.sender_count.fetch_add(1, Ordering::Relaxed); - - // Make sure the count never overflows, even if lots of sender clones are leaked. - if count > isize::MAX as usize { - process::abort(); - } - - Sender { - channel: self.channel.clone(), - } - } -} - -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Sender { .. }") - } -} - -/// The receiving side of a channel. -/// -/// This type receives messages by calling `recv`. But it also implements the [`Stream`] trait, -/// which means it can act as an asynchronous iterator. This struct is created by the [`channel`] -/// function. See its documentation for more. -/// -/// [`channel`]: fn.channel.html -/// [`Stream`]: ../stream/trait.Stream.html -/// -/// # Examples -/// -/// ``` -/// #![allow(deprecated)] -/// # fn main() -> Result<(), async_std::sync::RecvError> { -/// # async_std::task::block_on(async { -/// # -/// use std::time::Duration; -/// -/// use async_std::sync::channel; -/// use async_std::task; -/// -/// let (s, r) = channel(100); -/// -/// task::spawn(async move { -/// s.send(1usize).await; -/// task::sleep(Duration::from_secs(1)).await; -/// s.send(2).await; -/// }); -/// -/// assert_eq!(r.recv().await?, 1); // Received immediately. -/// assert_eq!(r.recv().await?, 2); // Received after 1 second. -/// # -/// # Ok(()) -/// # }) } -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[deprecated = "new channel api at async_std::channel"] -pub struct Receiver { - /// The inner channel. - channel: Arc>, - - /// The key for this receiver in the `channel.stream_wakers` set. - opt_key: Option, -} - -impl Receiver { - /// Receives a message from the channel. - /// - /// If the channel is empty and still has senders, this method - /// will wait until a message is sent into it. Once all senders - /// have been dropped it will return [RecvError]. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # fn main() -> Result<(), async_std::sync::RecvError> { - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// use async_std::task; - /// - /// let (s, r) = channel(1); - /// - /// task::spawn(async move { - /// s.send(1usize).await; - /// s.send(2).await; - /// // Then we drop the sender - /// }); - /// - /// assert_eq!(r.recv().await?, 1); - /// assert_eq!(r.recv().await?, 2); - /// assert!(r.recv().await.is_err()); - /// # - /// # Ok(()) - /// # }) } - /// ``` - pub async fn recv(&self) -> Result { - struct RecvFuture<'a, T> { - channel: &'a Channel, - opt_key: Option, - } - - impl Future for RecvFuture<'_, T> { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - poll_recv( - &self.channel, - &self.channel.recv_wakers, - &mut self.opt_key, - cx, - ) - } - } - - impl Drop for RecvFuture<'_, T> { - fn drop(&mut self) { - // If the current task is still in the set, that means it is being cancelled now. - if let Some(key) = self.opt_key { - self.channel.recv_wakers.cancel(key); - } - } - } - - RecvFuture { - channel: &self.channel, - opt_key: None, - } - .await - } - - /// Attempts to receive a message from the channel. - /// - /// If the channel is empty, this method will return an error. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// - /// s.send(1u8).await; - /// - /// assert!(r.try_recv().is_ok()); - /// assert!(r.try_recv().is_err()); - /// # - /// # }) - /// ``` - pub fn try_recv(&self) -> Result { - self.channel.try_recv() - } - - /// Returns the channel capacity. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// use async_std::sync::channel; - /// - /// let (_, r) = channel::(5); - /// assert_eq!(r.capacity(), 5); - /// ``` - pub fn capacity(&self) -> usize { - self.channel.cap - } - - /// Returns `true` if the channel is empty. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// - /// assert!(r.is_empty()); - /// s.send(0).await; - /// assert!(!r.is_empty()); - /// # - /// # }) - /// ``` - pub fn is_empty(&self) -> bool { - self.channel.is_empty() - } - - /// Returns `true` if the channel is full. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(1); - /// - /// assert!(!r.is_full()); - /// s.send(0).await; - /// assert!(r.is_full()); - /// # - /// # }) - /// ``` - pub fn is_full(&self) -> bool { - self.channel.is_full() - } - - /// Returns the number of messages in the channel. - /// - /// # Examples - /// - /// ``` - /// #![allow(deprecated)] - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::channel; - /// - /// let (s, r) = channel(2); - /// assert_eq!(r.len(), 0); - /// - /// s.send(1).await; - /// s.send(2).await; - /// assert_eq!(r.len(), 2); - /// # - /// # }) - /// ``` - pub fn len(&self) -> usize { - self.channel.len() - } -} - -impl Drop for Receiver { - fn drop(&mut self) { - // If the current task is still in the stream set, that means it is being cancelled now. - if let Some(key) = self.opt_key { - self.channel.stream_wakers.cancel(key); - } - - // Decrement the receiver count and disconnect the channel if it drops down to zero. - if self.channel.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 { - self.channel.disconnect(); - } - } -} - -impl Clone for Receiver { - fn clone(&self) -> Receiver { - let count = self.channel.receiver_count.fetch_add(1, Ordering::Relaxed); - - // Make sure the count never overflows, even if lots of receiver clones are leaked. - if count > isize::MAX as usize { - process::abort(); - } - - Receiver { - channel: self.channel.clone(), - opt_key: None, - } - } -} - -impl Stream for Receiver { - type Item = T; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = &mut *self; - let res = futures_core::ready!(poll_recv( - &this.channel, - &this.channel.stream_wakers, - &mut this.opt_key, - cx, - )); - Poll::Ready(res.ok()) - } -} - -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Receiver { .. }") - } -} - -/// Polls a receive operation on a channel. -/// -/// If the receive operation is blocked, the current task will be inserted into `wakers` and its -/// associated key will then be stored in `opt_key`. -fn poll_recv( - channel: &Channel, - wakers: &WakerSet, - opt_key: &mut Option, - cx: &mut Context<'_>, -) -> Poll> { - loop { - // If the current task is in the set, remove it. - if let Some(key) = opt_key.take() { - wakers.remove(key); - } - - // Try receiving a message. - match channel.try_recv() { - Ok(msg) => return Poll::Ready(Ok(msg)), - Err(TryRecvError::Disconnected) => return Poll::Ready(Err(RecvError {})), - Err(TryRecvError::Empty) => { - // Insert this receive operation. - *opt_key = Some(wakers.insert(cx)); - - // If the channel is still empty and not disconnected, return. - if channel.is_empty() && !channel.is_disconnected() { - return Poll::Pending; - } - } - } - } -} - -/// A slot in a channel. -struct Slot { - /// The current stamp. - stamp: AtomicUsize, - - /// The message in this slot. - msg: UnsafeCell, -} - -/// Bounded channel based on a preallocated array. -struct Channel { - /// The head of the channel. - /// - /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but - /// packed into a single `usize`. The lower bits represent the index, while the upper bits - /// represent the lap. The mark bit in the head is always zero. - /// - /// Messages are popped from the head of the channel. - head: AtomicUsize, - - /// The tail of the channel. - /// - /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but - /// packed into a single `usize`. The lower bits represent the index, while the upper bits - /// represent the lap. The mark bit indicates that the channel is disconnected. - /// - /// Messages are pushed into the tail of the channel. - tail: AtomicUsize, - - /// The buffer holding slots. - buffer: *mut Slot, - - /// The channel capacity. - cap: usize, - - /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. - one_lap: usize, - - /// If this bit is set in the tail, that means either all senders were dropped or all receivers - /// were dropped. - mark_bit: usize, - - /// Send operations waiting while the channel is full. - send_wakers: WakerSet, - - /// Receive operations waiting while the channel is empty and not disconnected. - recv_wakers: WakerSet, - - /// Streams waiting while the channel is empty and not disconnected. - stream_wakers: WakerSet, - - /// The number of currently active `Sender`s. - sender_count: AtomicUsize, - - /// The number of currently active `Receivers`s. - receiver_count: AtomicUsize, - - /// Indicates that dropping a `Channel` may drop values of type `T`. - _marker: PhantomData, -} - -unsafe impl Send for Channel {} -unsafe impl Sync for Channel {} -impl Unpin for Channel {} - -impl Channel { - /// Creates a bounded channel of capacity `cap`. - fn with_capacity(cap: usize) -> Self { - assert!(cap > 0, "capacity must be positive"); - - // Compute constants `mark_bit` and `one_lap`. - let mark_bit = (cap + 1).next_power_of_two(); - let one_lap = mark_bit * 2; - - // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. - let head = 0; - // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. - let tail = 0; - - // Allocate a buffer of `cap` slots. - let buffer = { - let mut v = Vec::>::with_capacity(cap); - let ptr = v.as_mut_ptr(); - mem::forget(v); - ptr - }; - - // Initialize stamps in the slots. - for i in 0..cap { - unsafe { - // Set the stamp to `{ lap: 0, mark: 0, index: i }`. - let slot = buffer.add(i); - ptr::write(&mut (*slot).stamp, AtomicUsize::new(i)); - } - } - - Channel { - buffer, - cap, - one_lap, - mark_bit, - head: AtomicUsize::new(head), - tail: AtomicUsize::new(tail), - send_wakers: WakerSet::new(), - recv_wakers: WakerSet::new(), - stream_wakers: WakerSet::new(), - sender_count: AtomicUsize::new(1), - receiver_count: AtomicUsize::new(1), - _marker: PhantomData, - } - } - - /// Attempts to send a message. - fn try_send(&self, msg: T) -> Result<(), TrySendError> { - let backoff = Backoff::new(); - let mut tail = self.tail.load(Ordering::Relaxed); - - loop { - // Extract mark bit from the tail and unset it. - // - // If the mark bit was set (which means all receivers have been dropped), we will still - // send the message into the channel if there is enough capacity. The message will get - // dropped when the channel is dropped (which means when all senders are also dropped). - let mark_bit = tail & self.mark_bit; - tail ^= mark_bit; - - // Deconstruct the tail. - let index = tail & (self.mark_bit - 1); - let lap = tail & !(self.one_lap - 1); - - // Inspect the corresponding slot. - let slot = unsafe { &*self.buffer.add(index) }; - let stamp = slot.stamp.load(Ordering::Acquire); - - // If the tail and the stamp match, we may attempt to push. - if tail == stamp { - let new_tail = if index + 1 < self.cap { - // Same lap, incremented index. - // Set to `{ lap: lap, mark: 0, index: index + 1 }`. - tail + 1 - } else { - // One lap forward, index wraps around to zero. - // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. - lap.wrapping_add(self.one_lap) - }; - - // Try moving the tail. - match self.tail.compare_exchange_weak( - tail | mark_bit, - new_tail | mark_bit, - Ordering::SeqCst, - Ordering::Relaxed, - ) { - Ok(_) => { - // Write the message into the slot and update the stamp. - unsafe { slot.msg.get().write(msg) }; - let stamp = tail + 1; - slot.stamp.store(stamp, Ordering::Release); - - // Wake a blocked receive operation. - self.recv_wakers.notify_one(); - - // Wake all blocked streams. - self.stream_wakers.notify_all(); - - return Ok(()); - } - Err(t) => { - tail = t; - backoff.spin(); - } - } - } else if stamp.wrapping_add(self.one_lap) == tail + 1 { - atomic::fence(Ordering::SeqCst); - let head = self.head.load(Ordering::Relaxed); - - // If the head lags one lap behind the tail as well... - if head.wrapping_add(self.one_lap) == tail { - // ...then the channel is full. - - // Check if the channel is disconnected. - if mark_bit != 0 { - return Err(TrySendError::Disconnected(msg)); - } else { - return Err(TrySendError::Full(msg)); - } - } - - backoff.spin(); - tail = self.tail.load(Ordering::Relaxed); - } else { - // Snooze because we need to wait for the stamp to get updated. - backoff.snooze(); - tail = self.tail.load(Ordering::Relaxed); - } - } - } - - /// Attempts to receive a message. - fn try_recv(&self) -> Result { - let backoff = Backoff::new(); - let mut head = self.head.load(Ordering::Relaxed); - - loop { - // Deconstruct the head. - let index = head & (self.mark_bit - 1); - let lap = head & !(self.one_lap - 1); - - // Inspect the corresponding slot. - let slot = unsafe { &*self.buffer.add(index) }; - let stamp = slot.stamp.load(Ordering::Acquire); - - // If the the stamp is ahead of the head by 1, we may attempt to pop. - if head + 1 == stamp { - let new = if index + 1 < self.cap { - // Same lap, incremented index. - // Set to `{ lap: lap, mark: 0, index: index + 1 }`. - head + 1 - } else { - // One lap forward, index wraps around to zero. - // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. - lap.wrapping_add(self.one_lap) - }; - - // Try moving the head. - match self.head.compare_exchange_weak( - head, - new, - Ordering::SeqCst, - Ordering::Relaxed, - ) { - Ok(_) => { - // Read the message from the slot and update the stamp. - let msg = unsafe { slot.msg.get().read() }; - let stamp = head.wrapping_add(self.one_lap); - slot.stamp.store(stamp, Ordering::Release); - - // Wake a blocked send operation. - self.send_wakers.notify_one(); - - return Ok(msg); - } - Err(h) => { - head = h; - backoff.spin(); - } - } - } else if stamp == head { - atomic::fence(Ordering::SeqCst); - let tail = self.tail.load(Ordering::Relaxed); - - // If the tail equals the head, that means the channel is empty. - if (tail & !self.mark_bit) == head { - // If the channel is disconnected... - if tail & self.mark_bit != 0 { - return Err(TryRecvError::Disconnected); - } else { - // Otherwise, the receive operation is not ready. - return Err(TryRecvError::Empty); - } - } - - backoff.spin(); - head = self.head.load(Ordering::Relaxed); - } else { - // Snooze because we need to wait for the stamp to get updated. - backoff.snooze(); - head = self.head.load(Ordering::Relaxed); - } - } - } - - /// Returns the current number of messages inside the channel. - fn len(&self) -> usize { - loop { - // Load the tail, then load the head. - let tail = self.tail.load(Ordering::SeqCst); - let head = self.head.load(Ordering::SeqCst); - - // If the tail didn't change, we've got consistent values to work with. - if self.tail.load(Ordering::SeqCst) == tail { - let hix = head & (self.mark_bit - 1); - let tix = tail & (self.mark_bit - 1); - - return if hix < tix { - tix - hix - } else if hix > tix { - self.cap - hix + tix - } else if (tail & !self.mark_bit) == head { - 0 - } else { - self.cap - }; - } - } - } - - /// Returns `true` if the channel is disconnected. - pub fn is_disconnected(&self) -> bool { - self.tail.load(Ordering::SeqCst) & self.mark_bit != 0 - } - - /// Returns `true` if the channel is empty. - fn is_empty(&self) -> bool { - let head = self.head.load(Ordering::SeqCst); - let tail = self.tail.load(Ordering::SeqCst); - - // Is the tail equal to the head? - // - // Note: If the head changes just before we load the tail, that means there was a moment - // when the channel was not empty, so it is safe to just return `false`. - (tail & !self.mark_bit) == head - } - - /// Returns `true` if the channel is full. - fn is_full(&self) -> bool { - let tail = self.tail.load(Ordering::SeqCst); - let head = self.head.load(Ordering::SeqCst); - - // Is the head lagging one lap behind tail? - // - // Note: If the tail changes just before we load the head, that means there was a moment - // when the channel was not full, so it is safe to just return `false`. - head.wrapping_add(self.one_lap) == tail & !self.mark_bit - } - - /// Disconnects the channel and wakes up all blocked operations. - fn disconnect(&self) { - let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); - - if tail & self.mark_bit == 0 { - // Notify everyone blocked on this channel. - self.send_wakers.notify_all(); - self.recv_wakers.notify_all(); - self.stream_wakers.notify_all(); - } - } -} - -impl Drop for Channel { - fn drop(&mut self) { - // Get the index of the head. - let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1); - - // Loop over all slots that hold a message and drop them. - for i in 0..self.len() { - // Compute the index of the next slot holding a message. - let index = if hix + i < self.cap { - hix + i - } else { - hix + i - self.cap - }; - - unsafe { - self.buffer.add(index).drop_in_place(); - } - } - - // Finally, deallocate the buffer, but don't run any destructors. - unsafe { - Vec::from_raw_parts(self.buffer, 0, self.cap); - } - } -} - -/// An error returned from the `try_send` method. -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(PartialEq, Eq)] -#[deprecated = "new channel api at async_std::channel"] -pub enum TrySendError { - /// The channel is full but not disconnected. - Full(T), - - /// The channel is full and disconnected. - Disconnected(T), -} - -impl Error for TrySendError {} - -impl Debug for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Full(_) => Debug::fmt("Full", f), - Self::Disconnected(_) => Debug::fmt("Disconnected", f), - } - } -} - -impl Display for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Full(_) => Display::fmt("The channel is full.", f), - Self::Disconnected(_) => Display::fmt("The channel is full and disconnected.", f), - } - } -} - -/// An error returned from the `try_recv` method. -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug, PartialEq, Eq)] -#[deprecated = "new channel api at async_std::channel"] -pub enum TryRecvError { - /// The channel is empty but not disconnected. - Empty, - - /// The channel is empty and disconnected. - Disconnected, -} - -impl Error for TryRecvError {} - -impl Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Empty => Display::fmt("The channel is empty.", f), - Self::Disconnected => Display::fmt("The channel is empty and disconnected.", f), - } - } -} - -/// An error returned from the `recv` method. -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug, PartialEq, Eq)] -#[deprecated = "new channel api at async_std::channel"] -pub struct RecvError; - -impl Error for RecvError {} - -impl Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt("The channel is empty.", f) - } -} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 4c1fca11e..864e781d5 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -184,13 +184,10 @@ pub use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockW cfg_unstable! { pub use async_lock::{Barrier, BarrierWaitResult}; - #[allow(deprecated)] - pub use channel::{channel, Sender, Receiver, RecvError, TryRecvError, TrySendError}; pub use condvar::Condvar; pub(crate) use waker_set::WakerSet; mod condvar; - mod channel; pub(crate) mod waker_set; } diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index e38df861e..243b3e33e 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -70,16 +70,6 @@ impl WakerSet { key } - /// Removes the waker of an operation. - #[cold] - pub fn remove(&self, key: usize) { - let mut inner = self.lock(); - - if inner.entries.remove(key).is_some() { - inner.notifiable -= 1; - } - } - /// If the waker for this key is still waiting for a notification, then update /// the waker for the entry, and return false. If the waker has been notified, /// treat the entry as completed and return true. diff --git a/tests/channel.rs b/tests/channel.rs index f9eacb6db..f30b7e7fd 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -1,11 +1,10 @@ #![cfg(feature = "unstable")] -#![allow(deprecated)] use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use async_std::sync::channel; +use async_std::channel::bounded as channel; use async_std::task; use rand::{Rng, SeedableRng}; @@ -27,10 +26,10 @@ fn smoke() { task::block_on(async { let (s, r) = channel(1); - s.send(7).await; + s.send(7).await.unwrap(); assert_eq!(r.recv().await.unwrap(), 7); - s.send(8).await; + s.send(8).await.unwrap(); assert_eq!(r.recv().await.unwrap(), 8); drop(s); @@ -40,7 +39,7 @@ fn smoke() { task::block_on(async { let (s, r) = channel(10); drop(r); - s.send(1).await; + assert!(s.send(1).await.is_err()); }); } @@ -49,8 +48,8 @@ fn smoke() { fn capacity() { for i in 1..10 { let (s, r) = channel::<()>(i); - assert_eq!(s.capacity(), i); - assert_eq!(r.capacity(), i); + assert_eq!(s.capacity().unwrap(), i); + assert_eq!(r.capacity().unwrap(), i); } } @@ -68,7 +67,7 @@ fn len_empty_full() { assert_eq!(r.is_empty(), true); assert_eq!(r.is_full(), false); - s.send(()).await; + s.send(()).await.unwrap(); assert_eq!(s.len(), 1); assert_eq!(s.is_empty(), false); @@ -77,7 +76,7 @@ fn len_empty_full() { assert_eq!(r.is_empty(), false); assert_eq!(r.is_full(), false); - s.send(()).await; + s.send(()).await.unwrap(); assert_eq!(s.len(), 2); assert_eq!(s.is_empty(), false); @@ -113,9 +112,9 @@ fn recv() { }); task::sleep(ms(1500)).await; - s.send(7).await; - s.send(8).await; - s.send(9).await; + s.send(7).await.unwrap(); + s.send(8).await.unwrap(); + s.send(9).await.unwrap(); }) } @@ -126,13 +125,13 @@ fn send() { let (s, r) = channel(1); spawn(async move { - s.send(7).await; + s.send(7).await.unwrap(); task::sleep(ms(1000)).await; - s.send(8).await; + s.send(8).await.unwrap(); task::sleep(ms(1000)).await; - s.send(9).await; + s.send(9).await.unwrap(); task::sleep(ms(1000)).await; - s.send(10).await; + s.send(10).await.unwrap(); }); task::sleep(ms(1500)).await; @@ -148,9 +147,9 @@ fn recv_after_disconnect() { task::block_on(async { let (s, r) = channel(100); - s.send(1).await; - s.send(2).await; - s.send(3).await; + s.send(1).await.unwrap(); + s.send(2).await.unwrap(); + s.send(3).await.unwrap(); drop(s); @@ -175,7 +174,7 @@ fn len() { for _ in 0..CAP / 10 { for i in 0..50 { - s.send(i).await; + s.send(i).await.unwrap(); assert_eq!(s.len(), i + 1); } @@ -189,7 +188,7 @@ fn len() { assert_eq!(r.len(), 0); for i in 0..CAP { - s.send(i).await; + s.send(i).await.unwrap(); assert_eq!(s.len(), i + 1); } @@ -212,7 +211,7 @@ fn len() { }); for i in 0..COUNT { - s.send(i).await; + s.send(i).await.unwrap(); let len = s.len(); assert!(len <= CAP); } @@ -257,7 +256,7 @@ fn spsc() { }); for i in 0..COUNT { - s.send(i).await; + s.send(i).await.unwrap(); } drop(s); @@ -293,7 +292,7 @@ fn mpmc() { let s = s.clone(); tasks.push(spawn(async move { for i in 0..COUNT { - s.send(i).await; + s.send(i).await.unwrap(); } })); } @@ -318,7 +317,7 @@ fn oneshot() { let (s, r) = channel(1); let c1 = spawn(async move { r.recv().await.unwrap() }); - let c2 = spawn(async move { s.send(0).await }); + let c2 = spawn(async move { s.send(0).await.unwrap() }); c1.await; c2.await; @@ -361,13 +360,13 @@ fn drops() { }); for _ in 0..steps { - s.send(DropCounter).await; + s.send(DropCounter).await.unwrap(); } child.await; for _ in 0..additional { - s.send(DropCounter).await; + s.send(DropCounter).await.unwrap(); } assert_eq!(DROPS.load(Ordering::SeqCst), steps); From 8274995e70a63b2fb28e5b19ca09ebebd54d9e29 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Fri, 15 Jan 2021 09:44:18 +0100 Subject: [PATCH 183/272] stabilize new channels Signed-off-by: Marc-Antoine Perennou --- CHANGELOG.md | 2 ++ src/channel.rs | 2 -- tests/channel.rs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62638056f..125687f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +The new `async_std::channel` submodule, introduced in 1.8.0, has been stabilized. You no longer need the `unstable` feature to use it. + ## Added - Add `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) diff --git a/src/channel.rs b/src/channel.rs index 90adc1402..729388928 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,6 +1,4 @@ //! Channels -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub use async_channel::*; diff --git a/tests/channel.rs b/tests/channel.rs index f30b7e7fd..2aa271319 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "unstable")] - use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; From 8fcb24339ab62f8330369a3462af2e251494395c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Jan 2021 16:47:18 +0100 Subject: [PATCH 184/272] Add 1.9.0 release notes --- CHANGELOG.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 125687f28..80bbc9799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,78 @@ All notable changes to async-std will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). -## [Unreleased] +# [Unreleased] -The new `async_std::channel` submodule, introduced in 1.8.0, has been stabilized. You no longer need the `unstable` feature to use it. +## Added +## Removed +## Changed + +# [1.9.0] - 2021-01-15 + +This patch stabilizes the `async_std::channel` submodule, removes the +deprecated `sync::channel` types, and introduces the `tokio1` feature. + +## New Channels + +As part of our `1.8.0` release last month we introduced the new +`async_std::channel` submodule and deprecated the unstable +`async_std::sync::channel` types. You can read our full motiviation for this +change in the last patch notes. But the short version is that the old +channels had some fundamental problems, and the `sync` submodule is a bit of +a mess. + +This release of `async-std` promotes `async_std::channel` to stable, and +fully removes the `async_std::sync::channel` types. In practice many +libraries have already been upgraded to the new channels in the past month, +and this will enable much of the ecosystem to switch off "unstable" versions +of `async-std`. + +```rust +use async_std::channel; + +let (sender, receiver) = channel::unbounded(); + +assert_eq!(sender.send("Hello").await, Ok(())); +assert_eq!(receiver.recv().await, Ok("Hello")); +``` + +## Tokio 1.0 compat + +The Tokio project recently released version 1.0 of their runtime, and the +async-std team would like to congratulate the Tokio team on achieving this +milestone. + +This release of `async-std` adds the `tokio1` feature flag, enabling Tokio's +TLS constructors to be initialized within the `async-std` runtime. This is in +addition to the `tokio02` and `tokio03` feature flags which we were already +exposing. + +In terms of stability it's worth noting that we will continue to provide +support for the `tokio02`, `tokio03`, and `tokio1` on the current major +release line of `async-std`. These flags are part of our public API, and +removing compat support for older Tokio versions is considered a breaking +change. ## Added -- Add `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) +- Added the `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) +- Stabilized the `async_std::channel` submodule ([#934](https://github.com/async-rs/async-std/pull/934)) ## Removed -- Removed deprecated `sync::channel` +- Removed deprecated `sync::channel` ([#933](https://github.com/async-rs/async-std/pull/933)) + +## Fixed + +- Fixed a typo for [sic] `FuturesExt` trait ([#930](https://github.com/async-rs/async-std/pull/930)) +- Update the link to `cargo-edit` in the installation section of the docs ([#932](https://github.com/async-rs/async-std/pull/932)) +- Fixed a small typo for stream ([#926](https://github.com/async-rs/async-std/pull/926)) + +## Internal + +- Updated `rand` to 0.8 ([#923](https://github.com/async-rs/async-std/pull/923)) +- Migrated `RwLock` and `Barrier` to use the `async-lock` crate internally ([#925](https://github.com/async-rs/async-std/pull/925)) +- Replaced uses of deprecated the `compare_and_swap` method with `compare_exchange` ([#927](https://github.com/async-rs/async-std/pull/927)) # [1.8.0] - 2020-12-04 From b210ee36289dea779c1619fb5026fb1dcb0b50a0 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Jan 2021 17:24:54 +0100 Subject: [PATCH 185/272] bump Cargo.toml to 1.9.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9afb0e2af..9d7ed23a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.8.0" +version = "1.9.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 09e99843e448de0860e1fe6d40d0d32958835073 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 15 Jan 2021 09:05:21 -0800 Subject: [PATCH 186/272] Implement Clone for File. Implement `Clone` for `File` so that `File`s can be passed into closures for use in `spawn_blocking`. `File`'s contents are already wrapped in `Arc`s, so the implementation of `clone` is straightforward. This also aligns with `TcpStream` which already implements `Clone` using its internal `Arc`. --- src/fs/file.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/fs/file.rs b/src/fs/file.rs index 56d292b97..e8cad7ad7 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -58,6 +58,7 @@ use crate::utils::Context as _; /// # /// # Ok(()) }) } /// ``` +#[derive(Clone)] pub struct File { /// A reference to the inner file. file: Arc, @@ -470,6 +471,13 @@ struct Lock(Arc>); unsafe impl Send for Lock {} unsafe impl Sync for Lock {} +impl Clone for Lock { + #[inline] + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + /// The state of a lock. struct LockState { /// Set to `true` when locked. @@ -878,4 +886,21 @@ mod tests { File::open(file!()).await.unwrap(); }); } + + #[test] + fn async_file_clone() { + crate::task::block_on(async move { + let file = File::open(file!()).await.unwrap(); + let mut clone = file.clone(); + let len = crate::task::spawn_blocking(move || { + let mut buf = Vec::new(); + crate::task::block_on(async move { + clone.read_to_end(&mut buf).await.unwrap(); + drop(clone); + buf.len() + }) + }).await; + assert_eq!(len as u64, file.metadata().await.unwrap().len()); + }); + } } From a46464deab149387bf4002ff35cf74ce480243a3 Mon Sep 17 00:00:00 2001 From: Theo Bogusta <3322313+theo3@users.noreply.github.com> Date: Tue, 19 Jan 2021 14:28:35 -0500 Subject: [PATCH 187/272] Fix vectored IO for TcpStream Implements `Write::poll_write_vectored` and `Read::poll_read_vectored` on `TcpStream` using the vectored IO methods on the underlying stream. Previously, the trait's default implementation was used, which just called `poll_write` and `poll_read` once for each `IoSlice`. --- src/net/tcp/stream.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 2e14806fb..c2a6277ba 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -307,6 +307,14 @@ impl Read for &TcpStream { ) -> Poll> { Pin::new(&mut &*self.watcher).poll_read(cx, buf) } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + Pin::new(&mut &*self.watcher).poll_read_vectored(cx, bufs) + } } impl Write for TcpStream { @@ -344,6 +352,14 @@ impl Write for &TcpStream { Pin::new(&mut &*self.watcher).poll_write(cx, buf) } + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut &*self.watcher).poll_write_vectored(cx, bufs) + } + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut &*self.watcher).poll_flush(cx) } From e11a7ecf3626c4c7bc4efda08e325e95ea4bd788 Mon Sep 17 00:00:00 2001 From: Lucas Riutzel Date: Sun, 24 Jan 2021 21:18:14 +0000 Subject: [PATCH 188/272] Fix typo in DoubleEndedStream docs --- src/stream/double_ended_stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index a177865b6..0cd705418 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -152,7 +152,7 @@ pub trait DoubleEndedStream: Stream { } #[doc = r#" - Returns the the frist element from the right that matches the predicate. + Returns the first element from the right that matches the predicate. # Examples From b05fa450c7f2814962f47b2e45f5319c99de58e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 29 Jan 2021 15:55:37 +0100 Subject: [PATCH 189/272] Docs: fix link to io --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9a1c00c10..ebbb3965a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ //! [files]: fs/struct.File.html //! [TCP]: net/struct.TcpStream.html //! [UDP]: net/struct.UdpSocket.html -//! [`io`]: fs/struct.File.html +//! [`io`]: io/index.html //! [`sync`]: sync/index.html //! [`channel`]: channel/index.html //! From 7fecd0d710d3bd7786df78ee2bc10278ac576008 Mon Sep 17 00:00:00 2001 From: Martin Glagla Date: Tue, 2 Feb 2021 19:25:28 +0100 Subject: [PATCH 190/272] add task::try_current --- src/task/current.rs | 24 ++++++++++++++++++++++-- src/task/mod.rs | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/task/current.rs b/src/task/current.rs index e4624e15f..ad354d629 100644 --- a/src/task/current.rs +++ b/src/task/current.rs @@ -23,6 +23,26 @@ use crate::task::{Task, TaskLocalsWrapper}; /// # }) /// ``` pub fn current() -> Task { - TaskLocalsWrapper::get_current(|t| t.task().clone()) - .expect("`task::current()` called outside the context of a task") + try_current().expect("`task::current()` called outside the context of a task") } + +/// Returns a handle to the current task if called within the context of a task created by [`block_on`], +/// [`spawn`], or [`Builder::spawn`], otherwise returns `None`. +/// +/// [`block_on`]: fn.block_on.html +/// [`spawn`]: fn.spawn.html +/// [`Builder::spawn`]: struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// use async_std::task; +/// +/// match task::try_current() { +/// Some(t) => println!("The name of this task is {:?}", t.name()), +/// None => println!("Not inside a task!"), +/// } +/// ``` +pub fn try_current() -> Option { + TaskLocalsWrapper::get_current(|t| t.task().clone()) +} \ No newline at end of file diff --git a/src/task/mod.rs b/src/task/mod.rs index 440f6ddc6..fe574ec68 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -133,7 +133,7 @@ cfg_std! { cfg_default! { pub use block_on::block_on; pub use builder::Builder; - pub use current::current; + pub use current::{current, try_current}; pub use task::Task; pub use task_id::TaskId; pub use join_handle::JoinHandle; From fe310f6b1cae602db0e829760fa342c3e560a4a8 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 16 Feb 2021 22:14:10 +0100 Subject: [PATCH 191/272] io: export write::* We weren't exporting WriteExt. We already do that with read::* Signed-off-by: Marc-Antoine Perennou --- src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index a673636ff..e20ed800b 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -285,7 +285,7 @@ cfg_std! { pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; - pub use write::Write; + pub use write::*; pub mod prelude; From 5bc34cb6ba61fd4c748c68c6e251606f14c36659 Mon Sep 17 00:00:00 2001 From: Rolf Karp Date: Sat, 13 Mar 2021 16:22:33 +0100 Subject: [PATCH 192/272] Fix WriteFmtFuture not taking into account already written bytes (#964) --- src/io/write/write_fmt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index d20c41d8a..318b1c37f 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -11,7 +11,7 @@ pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> { pub(crate) writer: &'a mut T, pub(crate) res: Option>>, pub(crate) buffer: Option>, - pub(crate) amt: u64, + pub(crate) amt: usize, } impl Future for WriteFmtFuture<'_, T> { @@ -37,15 +37,15 @@ impl Future for WriteFmtFuture<'_, T> { // Copy the data from the buffer into the writer until it's done. loop { - if *amt == buffer.len() as u64 { + if *amt == buffer.len() { futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; return Poll::Ready(Ok(())); } - let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buffer))?; + let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &buffer[*amt..]))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } - *amt += i as u64; + *amt += i; } } } From c4e181cfe1d9c86c25a439cde9433b36aa8fe463 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 31 Mar 2021 11:20:29 -0700 Subject: [PATCH 193/272] Change Incoming impls to only do one allocation This modifies net::tcp::Incoming and os::net::unix::Incoming to only do one allocation, rather than an allocation for each connection. --- src/net/tcp/listener.rs | 27 ++++++--------------------- src/os/unix/net/listener.rs | 24 +++++------------------- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index cfefc7d24..0825cd92c 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,6 +1,6 @@ use std::fmt; -use std::future::Future; use std::net::SocketAddr; +use std::net::TcpStream as StdTcpStream; use std::pin::Pin; use async_io::Async; @@ -148,8 +148,7 @@ impl TcpListener { /// ``` pub fn incoming(&self) -> Incoming<'_> { Incoming { - listener: self, - accept: None, + incoming: Box::pin(self.watcher.incoming()), } } @@ -187,35 +186,21 @@ impl TcpListener { /// [`TcpListener`]: struct.TcpListener.html /// [`std::net::Incoming`]: https://doc.rust-lang.org/std/net/struct.Incoming.html pub struct Incoming<'a> { - listener: &'a TcpListener, - accept: Option< - Pin> + Send + Sync + 'a>>, - >, + incoming: Pin>> + Send + Sync + 'a>>, } impl Stream for Incoming<'_> { type Item = io::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.accept.is_none() { - self.accept = Some(Box::pin(self.listener.accept())); - } - - if let Some(f) = &mut self.accept { - let res = ready!(f.as_mut().poll(cx)); - self.accept = None; - return Poll::Ready(Some(res.map(|(stream, _)| stream))); - } - } + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| TcpStream { watcher: Arc::new(stream) }))) } } impl fmt::Debug for Incoming<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Incoming") - .field("listener", self.listener) - .finish() + write!(f, "Incoming {{ ... }}") } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 3573d7d34..e86502b59 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -1,8 +1,8 @@ //! Unix-specific networking extensions. use std::fmt; -use std::future::Future; use std::os::unix::net::UnixListener as StdUnixListener; +use std::os::unix::net::UnixStream as StdUnixStream; use std::pin::Pin; use async_io::Async; @@ -129,8 +129,7 @@ impl UnixListener { /// ``` pub fn incoming(&self) -> Incoming<'_> { Incoming { - listener: self, - accept: None, + incoming: Box::pin(self.watcher.incoming()), } } @@ -178,34 +177,21 @@ impl fmt::Debug for UnixListener { /// [`incoming`]: struct.UnixListener.html#method.incoming /// [`UnixListener`]: struct.UnixListener.html pub struct Incoming<'a> { - listener: &'a UnixListener, - accept: Option< - Pin> + Send + Sync + 'a>>, - >, + incoming: Pin>> + Send + Sync + 'a>>, } impl Stream for Incoming<'_> { type Item = io::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.accept.is_none() { - self.accept = Some(Box::pin(self.listener.accept())); - } - - if let Some(f) = &mut self.accept { - let res = ready!(f.as_mut().poll(cx)); - self.accept = None; - return Poll::Ready(Some(res.map(|(stream, _)| stream))); - } - } + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| UnixStream { watcher: Arc::new(stream) }))) } } impl fmt::Debug for Incoming<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Incoming") - .field("listener", self.listener) .finish() } } From a410082a7f042ef1325c4cb09c1f5e7e1633ef84 Mon Sep 17 00:00:00 2001 From: Max Davitt Date: Fri, 28 May 2021 12:59:41 -0400 Subject: [PATCH 194/272] Fix typo in Tasks book page --- docs/src/concepts/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index 2db8cb0c9..c3dbbe202 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -2,7 +2,7 @@ Now that we know what Futures are, we want to run them! -In `async-std`, the [`tasks`][tasks] module is responsible for this. The simplest way is using the `block_on` function: +In `async-std`, the [`task`][tasks] module is responsible for this. The simplest way is using the `block_on` function: ```rust,edition2018 # extern crate async_std; From 871d2220b80cd297050c17388d782a9d7b647a69 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 4 Mar 2021 08:59:58 -0800 Subject: [PATCH 195/272] Fix stdin.rs comments to say "read" instead of "write". This just fixes a few comments that appear to have been copied and pasted from stdout.rs. --- src/io/stdin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index bf92bb04c..c969574e5 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -16,7 +16,7 @@ use crate::utils::Context as _; /// ### Note: Windows Portability Consideration /// /// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return /// an error. /// /// # Examples @@ -49,7 +49,7 @@ pub fn stdin() -> Stdin { /// ### Note: Windows Portability Consideration /// /// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return /// an error. /// /// [`stdin`]: fn.stdin.html @@ -79,7 +79,7 @@ struct Inner { /// The line buffer. line: String, - /// The write buffer. + /// The read buffer. buf: Vec, /// The result of the last asynchronous operation on the stdin. From 9c031375c889192cad528a9a09ae5d8048bb8422 Mon Sep 17 00:00:00 2001 From: surechen Date: Tue, 24 Aug 2021 19:38:21 +0800 Subject: [PATCH 196/272] docs: add description for fuse() in handling_disconnection Ref #88 --- docs/src/tutorial/handling_disconnection.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 87a6ac660..b6e53641c 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -90,12 +90,12 @@ async fn connection_writer_loop( let mut shutdown = shutdown.fuse(); loop { // 2 select! { - msg = messages.next().fuse() => match msg { + msg = messages.next().fuse() => match msg { // 3 Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, void = shutdown.next().fuse() => match void { - Some(void) => match void {}, // 3 + Some(void) => match void {}, // 4 None => break, } } @@ -106,7 +106,8 @@ async fn connection_writer_loop( 1. We add shutdown channel as an argument. 2. Because of `select`, we can't use a `while let` loop, so we desugar it further into a `loop`. -3. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`. +3. Function fuse() is used to turn any `Stream` into a `FusedStream`. This is used for fusing a stream such that poll_next will never again be called once it has finished. +4. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`. Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. To not lose these messages completely, we'll return the messages channel back to the broker. From 194c1eda2151ec205d6fa7669ca6c4381deef285 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Wed, 25 Aug 2021 10:43:01 -0700 Subject: [PATCH 197/272] 1.10.0 # [1.10.0] - 2021-08-25 This release comes with an assortment of small features and fixes. ## Added - `File` now implements `Clone` so that `File`s can be passed into closures for use in `spawn_blocking`. - `File`'s contents are already wrapped in `Arc`s, so the implementation of `Clone` is straightforward. - `task::try_current()` which returns a handle to the current task if called within the context of a task created by async-std. - `async_std::io` now re-exports `WriteExt` directly. ## Fixed - `write!` now takes already written bytes into account on `File`. ## Internal - `TcpStream` now properly makes use of vectored IO. - The `net::*::Incoming` implementations now do less allocation. ## Docs - Several docs improvements / fixes. --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80bbc9799..fa3c8c3c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,26 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## Removed ## Changed +# [1.10.0] - 2021-08-25 + +This release comes with an assortment of small features and fixes. + +## Added +- `File` now implements `Clone` so that `File`s can be passed into closures for use in `spawn_blocking`. + - `File`'s contents are already wrapped in `Arc`s, so the implementation of `Clone` is straightforward. +- `task::try_current()` which returns a handle to the current task if called within the context of a task created by async-std. +- `async_std::io` now re-exports `WriteExt` directly. + +## Fixed +- `write!` now takes already written bytes into account on `File`. + +## Internal +- `TcpStream` now properly makes use of vectored IO. +- The `net::*::Incoming` implementations now do less allocation. + +## Docs +- Several docs improvements / fixes. + # [1.9.0] - 2021-01-15 This patch stabilizes the `async_std::channel` submodule, removes the From 5640a7ff1f8ca5ff157f26a9ccb47a0b95d22fb4 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 26 Aug 2021 15:33:35 +0200 Subject: [PATCH 198/272] release v1.10.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9d7ed23a0..bc9078cd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.9.0" +version = "1.10.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 3a26fb32dcb1775afa93b34e3c79f5ffe9c035d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Mon, 30 Aug 2021 17:51:40 +0300 Subject: [PATCH 199/272] doc: update docs to fit the move of channels from the sync module fixes #983 --- src/channel.rs | 4 ++++ src/sync/mod.rs | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/channel.rs b/src/channel.rs index 729388928..8eb30a42d 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,4 +1,8 @@ //! Channels +//! +//! Multi-producer, multi-consumer queues, used for message-based +//! communication. Can provide a lightweight inter-task synchronisation +//! mechanism, at the cost of some extra memory. #[doc(inline)] pub use async_channel::*; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 864e781d5..35203a6ea 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -129,11 +129,6 @@ //! to reach a point in the program, before continuing execution all //! together. //! -//! - [`channel`]: Multi-producer, multi-consumer queues, used for -//! message-based communication. Can provide a lightweight -//! inter-task synchronisation mechanism, at the cost of some -//! extra memory. -//! //! - [`Mutex`]: Mutual exclusion mechanism, which ensures that at //! most one task at a time is able to access some data. //! @@ -142,6 +137,9 @@ //! writer at a time. In some cases, this can be more efficient than //! a mutex. //! +//! If you're looking for channels, check out +//! [`async_std::channel`][crate::channel]. +//! //! [`Arc`]: struct.Arc.html //! [`Barrier`]: struct.Barrier.html //! [`channel`]: fn.channel.html From da654e998da3a2b43e5fbd375bb44f2189c8df33 Mon Sep 17 00:00:00 2001 From: kokihonda Date: Mon, 20 Sep 2021 14:33:09 +0900 Subject: [PATCH 200/272] fix wrong link. --- src/task/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index ca0b92a02..440f6ddc6 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -103,7 +103,7 @@ //! the desired task name to [`Builder::name`]. To retrieve the task name from within the //! task, use [`Task::name`]. //! -//! [`Arc`]: ../gsync/struct.Arc.html +//! [`Arc`]: ../sync/struct.Arc.html //! [`spawn`]: fn.spawn.html //! [`JoinHandle`]: struct.JoinHandle.html //! [`JoinHandle::task`]: struct.JoinHandle.html#method.task From dd2749ca358f0be2f32881d5590d5e333fe189c8 Mon Sep 17 00:00:00 2001 From: kokihonda Date: Tue, 21 Sep 2021 10:26:16 +0900 Subject: [PATCH 201/272] Remove the numbering of the remaining previous chapters --- docs/src/tutorial/all_together.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 8bb01e943..b8174d300 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -124,8 +124,8 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { Entry::Occupied(..) => (), Entry::Vacant(entry) => { let (client_sender, client_receiver) = mpsc::unbounded(); - entry.insert(client_sender); // 4 - spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5 + entry.insert(client_sender); + spawn_and_log_error(connection_writer_loop(client_receiver, stream)); } } } From 6f61c9dc7e15f96ce0520d89775f2ef329e17174 Mon Sep 17 00:00:00 2001 From: Jasper van Herpt Date: Wed, 13 Oct 2021 21:49:07 +0200 Subject: [PATCH 202/272] Match error message from async File::create std File::create --- src/fs/file.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index e8cad7ad7..aae201b6d 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -154,7 +154,6 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::create(&path) - .context(|| format!("could not create `{}`", path.display())) }) .await?; Ok(File::new(file, true)) @@ -903,4 +902,15 @@ mod tests { assert_eq!(len as u64, file.metadata().await.unwrap().len()); }); } + + #[test] + fn async_file_create_error () { + let file_name = Path::new("/tmp/does_not_exist/test"); + let expect = std::fs::File::create(file_name).unwrap_err(); + + crate::task::block_on(async move { + let actual = File::create(file_name).await.unwrap_err(); + assert_eq!(format!("{}", expect), format!("{}", actual)); + }) + } } From d3133c04be9dc4f8e465a90cffe44064dec491ca Mon Sep 17 00:00:00 2001 From: piegames Date: Mon, 18 Oct 2021 11:38:36 +0200 Subject: [PATCH 203/272] Add TcpListener::into_incoming --- src/net/tcp/listener.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 0825cd92c..a9f4d52b2 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -150,6 +150,45 @@ impl TcpListener { Incoming { incoming: Box::pin(self.watcher.incoming()), } + } + + /// Turn this into a stream over the connections being received on this + /// listener. + /// + /// The returned stream is infinite and will also not yield + /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to + /// calling [`TcpListener::accept`] in a loop. + /// + /// ## Examples + /// + /// Merge the incoming connections of multiple sockets into one [`Stream`]: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::TcpListener; + /// + /// // Our server listens on multiple ports for some reason + /// let listeners = vec![ + /// TcpListener::bind("[::0]:8080").await?, + /// TcpListener::bind("[::0]:12345").await?, + /// TcpListener::bind("[::0]:5678").await?, + /// ]; + /// // Iterate over all incoming connections + /// let incoming = futures::stream::select_all( + /// listeners.into_iter() + /// .map(TcpListener::into_incoming) + /// .map(Box::pin) + /// ); + /// # + /// # Ok(()) }) } + /// ``` + #[cfg(feature = "unstable")] + pub fn into_incoming(self) -> impl Stream> + Send { + futures_lite::stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) } /// Returns the local address that this listener is bound to. From 5e8d1e6e83b99754444173095246b620af4aa06b Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Sun, 7 Nov 2021 21:12:01 +0100 Subject: [PATCH 204/272] stream: correct iterators in doc examples Correct the iterators used in examples to produce the described output. --- src/stream/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index b3c7ff7a3..f7f2727a1 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -164,7 +164,7 @@ //! # //! # use async_std::prelude::*; //! # use async_std::stream; -//! let mut values = stream::repeat(1u8).take(5); +//! let mut values = stream::from_iter(1u8..6); //! //! while let Some(x) = values.next().await { //! println!("{}", x); @@ -183,7 +183,8 @@ //! //! Unlike `std::iter::IntoIterator`, `IntoStream` does not have compiler //! support yet. This means that automatic conversions like with `for` loops -//! doesn't occur yet, and `into_stream` will always have to be called manually. +//! doesn't occur yet, and `into_stream` or `from_iter` as above will always +//! have to be called manually. //! //! [`IntoStream`]: trait.IntoStream.html //! [`into_stream`]: trait.IntoStream.html#tymethod.into_stream @@ -271,7 +272,7 @@ //! # //! # use async_std::prelude::*; //! # use async_std::stream; -//! let numbers = stream::repeat(1u8); +//! let numbers = stream::from_iter(0u8..); //! let mut five_numbers = numbers.take(5); //! //! while let Some(number) = five_numbers.next().await { From 22e4bbdf73d4fea2e0454376bbe354e8449a42ab Mon Sep 17 00:00:00 2001 From: dasman <75807342+Dastan-glitch@users.noreply.github.com> Date: Fri, 25 Feb 2022 05:47:21 -0500 Subject: [PATCH 205/272] Fix a typo in future/mod.rs --- src/future/future/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 24f3fb599..356514509 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -361,7 +361,7 @@ extension_trait! { #[doc = r#" Waits for both the future and a timeout, if the timeout completes before - the future, it returns an TimeoutError. + the future, it returns a TimeoutError. # Example ``` From 21fb4ac0fb8341f490b094253d4c3649623d07e2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 10 Mar 2022 17:09:33 +1100 Subject: [PATCH 206/272] Remove two useless rules from `extension_trait!`. They never run because they are subsumed by the two rules immediately above. --- src/utils.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index d80524446..a54d25252 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -309,14 +309,6 @@ macro_rules! extension_trait { extension_trait!(@ext ($($head)* -> $f) $($tail)*); }; - // Parse the return type in an extension method. - (@doc ($($head:tt)*) -> impl Future + $lt:lifetime [$f:ty] $($tail:tt)*) => { - extension_trait!(@doc ($($head)* -> borrowed::ImplFuture<$lt, $out>) $($tail)*); - }; - (@ext ($($head:tt)*) -> impl Future + $lt:lifetime [$f:ty] $($tail:tt)*) => { - extension_trait!(@ext ($($head)* -> $f) $($tail)*); - }; - // Parse a token. (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => { extension_trait!(@doc ($($head)* $token) $($tail)*); From db7c1946c89e119ea37966803735cdcc0601058e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 10 Mar 2022 17:10:12 +1100 Subject: [PATCH 207/272] Move the `extension_trait!` accumulator to the end of the rules. That way, when the `-> impl Future` rules fail (which is most of the time), the cost of reparsing the accumulated tokens is avoided. --- src/utils.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index a54d25252..6ae49115d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -281,7 +281,7 @@ macro_rules! extension_trait { #[cfg(feature = "docs")] #[doc = $doc] pub trait $name { - extension_trait!(@doc () $($body_base)* $($body_ext)*); + extension_trait!(@doc [$($body_base)* $($body_ext)*] -> []); } // When not rendering docs, re-export the base trait from the futures crate. @@ -291,7 +291,7 @@ macro_rules! extension_trait { // The extension trait that adds methods to any type implementing the base trait. #[doc = $doc_ext] pub trait $ext: $name { - extension_trait!(@ext () $($body_ext)*); + extension_trait!(@ext [$($body_ext)*] -> []); } // Blanket implementation of the extension trait for any type implementing the base trait. @@ -302,24 +302,24 @@ macro_rules! extension_trait { }; // Parse the return type in an extension method. - (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { - extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); + (@doc [-> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { + extension_trait!(@doc [$($tail)*] -> [$($accum)* -> owned::ImplFuture<$out>]); }; - (@ext ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { - extension_trait!(@ext ($($head)* -> $f) $($tail)*); + (@ext [-> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*] -> [$($accum:tt)*]) => { + extension_trait!(@ext [$($tail)*] -> [$($accum)* -> $f]); }; // Parse a token. - (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => { - extension_trait!(@doc ($($head)* $token) $($tail)*); + (@doc [$token:tt $($tail:tt)*] -> [$($accum:tt)*]) => { + extension_trait!(@doc [$($tail)*] -> [$($accum)* $token]); }; - (@ext ($($head:tt)*) $token:tt $($tail:tt)*) => { - extension_trait!(@ext ($($head)* $token) $($tail)*); + (@ext [$token:tt $($tail:tt)*] -> [$($accum:tt)*]) => { + extension_trait!(@ext [$($tail)*] -> [$($accum)* $token]); }; // Handle the end of the token list. - (@doc ($($head:tt)*)) => { $($head)* }; - (@ext ($($head:tt)*)) => { $($head)* }; + (@doc [] -> [$($accum:tt)*]) => { $($accum)* }; + (@ext [] -> [$($accum:tt)*]) => { $($accum)* }; // Parse imports at the beginning of the macro. ($import:item $($tail:tt)*) => { From e19ab626a1a7cef88c11f18a750624bce050f47f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 11 Mar 2022 09:31:01 +1100 Subject: [PATCH 208/272] Remove unused parameter from `extension_trait!` rules. Two of the rules have `(+ $lt:lifetime)?` that is not used on the RHS and serves no useful purpose. This commit removes it. --- src/io/buf_read/mod.rs | 4 ++-- src/io/read/mod.rs | 10 +++++----- src/io/seek/mod.rs | 2 +- src/io/write/mod.rs | 10 +++++----- src/stream/stream/mod.rs | 24 ++++++++++++------------ src/utils.rs | 4 ++-- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 7a0ecc606..3ee6e30ba 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -124,7 +124,7 @@ extension_trait! { &'a mut self, byte: u8, buf: &'a mut Vec, - ) -> impl Future + 'a [ReadUntilFuture<'a, Self>] + ) -> impl Future [ReadUntilFuture<'a, Self>] where Self: Unpin, { @@ -177,7 +177,7 @@ extension_trait! { fn read_line<'a>( &'a mut self, buf: &'a mut String, - ) -> impl Future> + 'a [ReadLineFuture<'a, Self>] + ) -> impl Future> [ReadLineFuture<'a, Self>] where Self: Unpin, { diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 388237c80..c8f4e28b9 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -112,7 +112,7 @@ extension_trait! { fn read<'a>( &'a mut self, buf: &'a mut [u8], - ) -> impl Future> + 'a [ReadFuture<'a, Self>] + ) -> impl Future> [ReadFuture<'a, Self>] where Self: Unpin { @@ -134,7 +134,7 @@ extension_trait! { fn read_vectored<'a>( &'a mut self, bufs: &'a mut [IoSliceMut<'a>], - ) -> impl Future> + 'a [ReadVectoredFuture<'a, Self>] + ) -> impl Future> [ReadVectoredFuture<'a, Self>] where Self: Unpin, { @@ -171,7 +171,7 @@ extension_trait! { fn read_to_end<'a>( &'a mut self, buf: &'a mut Vec, - ) -> impl Future> + 'a [ReadToEndFuture<'a, Self>] + ) -> impl Future> [ReadToEndFuture<'a, Self>] where Self: Unpin, { @@ -210,7 +210,7 @@ extension_trait! { fn read_to_string<'a>( &'a mut self, buf: &'a mut String, - ) -> impl Future> + 'a [ReadToStringFuture<'a, Self>] + ) -> impl Future> [ReadToStringFuture<'a, Self>] where Self: Unpin, { @@ -265,7 +265,7 @@ extension_trait! { fn read_exact<'a>( &'a mut self, buf: &'a mut [u8], - ) -> impl Future> + 'a [ReadExactFuture<'a, Self>] + ) -> impl Future> [ReadExactFuture<'a, Self>] where Self: Unpin, { diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index f565ca46b..3b5036c80 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -76,7 +76,7 @@ extension_trait! { fn seek( &mut self, pos: SeekFrom, - ) -> impl Future> + '_ [SeekFuture<'_, Self>] + ) -> impl Future> [SeekFuture<'_, Self>] where Self: Unpin, { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 0ed91dda6..3dee9feb6 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -110,7 +110,7 @@ extension_trait! { fn write<'a>( &'a mut self, buf: &'a [u8], - ) -> impl Future> + 'a [WriteFuture<'a, Self>] + ) -> impl Future> [WriteFuture<'a, Self>] where Self: Unpin, { @@ -136,7 +136,7 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn flush(&mut self) -> impl Future> + '_ [FlushFuture<'_, Self>] + fn flush(&mut self) -> impl Future> [FlushFuture<'_, Self>] where Self: Unpin, { @@ -158,7 +158,7 @@ extension_trait! { fn write_vectored<'a>( &'a mut self, bufs: &'a [IoSlice<'a>], - ) -> impl Future> + 'a [WriteVectoredFuture<'a, Self>] + ) -> impl Future> [WriteVectoredFuture<'a, Self>] where Self: Unpin, { @@ -194,7 +194,7 @@ extension_trait! { fn write_all<'a>( &'a mut self, buf: &'a [u8], - ) -> impl Future> + 'a [WriteAllFuture<'a, Self>] + ) -> impl Future> [WriteAllFuture<'a, Self>] where Self: Unpin, { @@ -231,7 +231,7 @@ extension_trait! { fn write_fmt<'a>( &'a mut self, fmt: std::fmt::Arguments<'_>, - ) -> impl Future> + 'a [WriteFmtFuture<'a, Self>] + ) -> impl Future> [WriteFmtFuture<'a, Self>] where Self: Unpin, { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4e074a2f3..af7407a2d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -260,7 +260,7 @@ extension_trait! { # }) } ``` "#] - fn next(&mut self) -> impl Future> + '_ [NextFuture<'_, Self>] + fn next(&mut self) -> impl Future> [NextFuture<'_, Self>] where Self: Unpin, { @@ -1165,7 +1165,7 @@ extension_trait! { fn nth( &mut self, n: usize, - ) -> impl Future> + '_ [NthFuture<'_, Self>] + ) -> impl Future> [NthFuture<'_, Self>] where Self: Unpin + Sized, { @@ -1221,7 +1221,7 @@ extension_trait! { fn all( &mut self, f: F, - ) -> impl Future + '_ [AllFuture<'_, Self, F, Self::Item>] + ) -> impl Future [AllFuture<'_, Self, F, Self::Item>] where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -1270,7 +1270,7 @@ extension_trait! { fn find