From 064fdf020fa362349bf13ad89d5db2ef5e547412 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 12 Oct 2019 01:35:41 +0200 Subject: [PATCH 001/707] Stream::delay Signed-off-by: Yoshua Wuyts --- src/stream/stream/delay.rs | 44 ++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 32 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/stream/stream/delay.rs diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs new file mode 100644 index 000000000..f6de5f2d4 --- /dev/null +++ b/src/stream/stream/delay.rs @@ -0,0 +1,44 @@ +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Delay { + stream: S, + delay: futures_timer::Delay, + delay_done: bool, +} + +impl Delay { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_pinned!(delay: futures_timer::Delay); + pin_utils::unsafe_unpinned!(delay_done: bool); + + pub(super) fn new(stream: S, dur: Duration) -> Self { + Delay { + stream, + delay: futures_timer::Delay::new(dur), + delay_done: false, + } + } +} + +impl Stream for Delay +where + S: Stream, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if !self.delay_done { + futures_core::ready!(self.as_mut().delay().poll(cx)); + *self.as_mut().delay_done() = true; + } + + self.as_mut().stream().poll_next(cx) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8849605ca..26def735b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod chain; +mod delay; mod enumerate; mod filter; mod filter_map; @@ -61,6 +62,7 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; pub use fuse::Fuse; +pub use delay::Delay; pub use inspect::Inspect; pub use map::Map; pub use scan::Scan; @@ -340,6 +342,36 @@ extension_trait! { Enumerate::new(self) } + #[doc = r#" + Creates a stream that is delayed before it starts yielding items. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::future; + use std::time::Duration; + + let p1 = future::ready(1).delay(Duration::from_millis(200)); + let p1 = future::ready(2).delay(Duration::from_millis(100)); + let p1 = future::ready(3).delay(Duration::from_millis(300)); + + assert_eq!(future::join!(p1, p2, p3).await, (1, 2, 3)); + # + # }) } + ``` + "#] + #[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#" Takes a closure and creates a stream that calls that closure on every element of this stream. From 483ded0e1c2bc85a7bc79efcc1636f9729b8969a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 12 Oct 2019 01:38:53 +0200 Subject: [PATCH 002/707] fix example Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 26def735b..80e319691 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -351,14 +351,19 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use async_std::future; + use async_std::stream; use std::time::Duration; - let p1 = future::ready(1).delay(Duration::from_millis(200)); - let p1 = future::ready(2).delay(Duration::from_millis(100)); - let p1 = future::ready(3).delay(Duration::from_millis(300)); + let a = stream::once(1).delay(Duration::from_millis(200)); + let b = stream::once(2).delay(Duration::from_millis(100)); + let c = stream::once(3).delay(Duration::from_millis(300)); + + let s = stream::join!(a, b, c); - assert_eq!(future::join!(p1, p2, p3).await, (1, 2, 3)); + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, Some(2)); + assert_eq!(stream.next().await, Some(3)); + assert_eq!(stream.next().await, None); # # }) } ``` From f9741e7488eb8b0ba63fc50e8fa43732002bac7b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 15 Oct 2019 21:43:54 +0900 Subject: [PATCH 003/707] feat: Add Stderr::lock --- src/io/stderr.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5706aa2ed..282d5e973 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,3 +1,4 @@ +use lazy_static::lazy_static; use std::pin::Pin; use std::sync::Mutex; @@ -79,6 +80,35 @@ 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 std::io::Write; + /// + /// let stderr = io::stderr(); + /// let mut handle = stderr.lock().await; + /// + /// handle.write_all(b"hello world")?; + /// # + /// # Ok(()) }) } + /// ``` + pub async fn lock(&self) -> std::io::StderrLock<'static> { + lazy_static! { + static ref STDERR: std::io::Stderr = std::io::stderr(); + } + + STDERR.lock() + } +} + impl Write for Stderr { fn poll_write( mut self: Pin<&mut Self>, From 9b0980659321f9b90bff21e435335d37de0434a4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 15 Oct 2019 21:44:11 +0900 Subject: [PATCH 004/707] feat: Add Stdin::lock --- src/io/stdin.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 95a77b805..f6c4a25ec 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,3 +1,4 @@ +use lazy_static::lazy_static; use std::pin::Pin; use std::sync::Mutex; @@ -134,6 +135,35 @@ impl Stdin { }) .await } + + /// 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 and BufRead traits for accessing the underlying data. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io; + /// use std::io::Read; + /// + /// let mut buffer = String::new(); + /// + /// let stdin = io::stdin(); + /// let mut handle = stdin.lock().await; + /// + /// handle.read_to_string(&mut buffer)?; + /// # + /// # Ok(()) }) } + /// ``` + pub async fn lock(&self) -> std::io::StdinLock<'static> { + lazy_static! { + static ref STDIN: std::io::Stdin = std::io::stdin(); + } + + STDIN.lock() + } } impl Read for Stdin { From 94ef3dc2b2cf0f32155e9643efd3a98c270a10f4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 15 Oct 2019 21:44:23 +0900 Subject: [PATCH 005/707] feat: Add Stdout::lock --- src/io/stdout.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 7849f1ce9..318013813 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,3 +1,4 @@ +use lazy_static::lazy_static; use std::pin::Pin; use std::sync::Mutex; @@ -79,6 +80,35 @@ 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 std::io::Write; + /// + /// let stdout = io::stdout(); + /// let mut handle = stdout.lock().await; + /// + /// handle.write_all(b"hello world")?; + /// # + /// # Ok(()) }) } + /// ``` + pub async fn lock(&self) -> std::io::StdoutLock<'static> { + lazy_static! { + static ref STDOUT: std::io::Stdout = std::io::stdout(); + } + + STDOUT.lock() + } +} + impl Write for Stdout { fn poll_write( mut self: Pin<&mut Self>, From f00d32ee7d5c6afd1abc9db5a0e60081af7296ea Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 15:30:52 +0900 Subject: [PATCH 006/707] Add TimeoutStream struct --- src/stream/stream/timeout.rs | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/stream/stream/timeout.rs diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs new file mode 100644 index 000000000..7a8cf477b --- /dev/null +++ b/src/stream/stream/timeout.rs @@ -0,0 +1,57 @@ +use std::error::Error; +use std::fmt; +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[derive(Debug)] +pub struct TimeoutStream { + stream: S, + delay: Delay, +} + +impl TimeoutStream { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_pinned!(delay: Delay); + + pub fn new(stream: S, dur: Duration) -> TimeoutStream { + let delay = Delay::new(dur); + + TimeoutStream { stream, delay } + } +} + +impl Stream for TimeoutStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().stream().poll_next(cx) { + Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => match self.delay().poll(cx) { + Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError))), + Poll::Pending => Poll::Pending, + }, + } + } +} + +/// 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; + +impl Error for TimeoutError {} + +impl fmt::Display for TimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "stream has timed out".fmt(f) + } +} From 7a87dea085884eebbe7472be5b4658218b58cd32 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 15:31:07 +0900 Subject: [PATCH 007/707] feat: Add Stream::timeout --- src/stream/stream/mod.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d582d700e..c44917d10 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -104,13 +104,16 @@ cfg_if! { cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod merge; + mod timeout; use std::pin::Pin; + use std::time::Duration; use crate::future::Future; use crate::stream::FromStream; pub use merge::Merge; + pub use timeout::TimeoutStream; } } @@ -1044,6 +1047,40 @@ extension_trait! { Skip::new(self, n) } + #[doc=r#" + Await a stream or times out after a duration of time. + + If you want to await an I/O future consider using + [`io::timeout`](../io/fn.timeout.html) instead. + + # Examples + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use std::time::Duration; + + use async_std::stream; + use async_std::prelude::*; + + let mut s = stream::repeat(1).take(3).timeout(Duration::from_secs(1)); + + while let Some(v) = s.next().await { + assert_eq!(v, Ok(1)); + } + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> TimeoutStream + where + Self: Stream + Sized, + { + TimeoutStream::new(self, dur) + } + #[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. From 054f4fac740daaf5c2c6fcaccef9f374fc6c71ec Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 16:53:33 +0900 Subject: [PATCH 008/707] feat: Add future::delay --- src/future/delay.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 2 ++ 2 files changed, 63 insertions(+) create mode 100644 src/future/delay.rs diff --git a/src/future/delay.rs b/src/future/delay.rs new file mode 100644 index 000000000..723c7c199 --- /dev/null +++ b/src/future/delay.rs @@ -0,0 +1,61 @@ +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::future::Future; +use crate::task::{Context, Poll}; + +/// Creates a future that is delayed before it starts yielding items. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// use async_std::future; +/// use std::time::Duration; + +/// let a = future::delay(future::ready(1) ,Duration::from_millis(2000)); +/// dbg!(a.await); +/// # }) +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +pub fn delay(f: F, dur: Duration) -> DelayFuture +where + F: Future, +{ + DelayFuture::new(f, dur) +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct DelayFuture { + future: F, + delay: Delay, +} + +impl DelayFuture { + pin_utils::unsafe_pinned!(future: F); + pin_utils::unsafe_pinned!(delay: Delay); + + pub fn new(future: F, dur: Duration) -> DelayFuture { + let delay = Delay::new(dur); + + DelayFuture { future, delay } + } +} + +impl Future for DelayFuture { + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.as_mut().delay().poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => match self.future().poll(cx) { + Poll::Ready(v) => Poll::Ready(v), + Poll::Pending => Poll::Pending, + }, + } + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index cc3b7a5d5..d1a0f3150 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -65,7 +65,9 @@ mod timeout; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod into_future; + mod delay; pub use into_future::IntoFuture; + pub use delay::delay; } } From b251fc999a2faaeac6107af60f6dcf7ad077a5c3 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:18:05 +0900 Subject: [PATCH 009/707] Move delay method to FutureExt::delay --- src/future/future.rs | 22 ++++++++++++++++++++++ src/future/{ => future}/delay.rs | 22 ---------------------- src/future/mod.rs | 3 +-- 3 files changed, 23 insertions(+), 24 deletions(-) rename src/future/{ => future}/delay.rs (64%) diff --git a/src/future/future.rs b/src/future/future.rs index 556dc1acd..38f3d704c 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -105,6 +105,28 @@ extension_trait! { } pub trait FutureExt: std::future::Future { + /// Creates a future that is delayed before it starts yielding items. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use async_std::future; + /// use std::time::Duration; + /// use async_std::future::FutureExt; + /// + /// let a = future::ready(1).delay(Duration::from_millis(2000)); + /// dbg!(a.await); + /// # }) + /// ``` + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + fn delay(self, dur: Duration) -> DelayFuture + where + Self: Future + Sized + { + DelayFuture::new(self, dur) + } } impl Future for Box { diff --git a/src/future/delay.rs b/src/future/future/delay.rs similarity index 64% rename from src/future/delay.rs rename to src/future/future/delay.rs index 723c7c199..319b4ff8e 100644 --- a/src/future/delay.rs +++ b/src/future/future/delay.rs @@ -6,28 +6,6 @@ use futures_timer::Delay; use crate::future::Future; use crate::task::{Context, Poll}; -/// Creates a future that is delayed before it starts yielding items. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// use async_std::future; -/// use std::time::Duration; - -/// let a = future::delay(future::ready(1) ,Duration::from_millis(2000)); -/// dbg!(a.await); -/// # }) -/// ``` -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] -pub fn delay(f: F, dur: Duration) -> DelayFuture -where - F: Future, -{ - DelayFuture::new(f, dur) -} - #[doc(hidden)] #[derive(Debug)] pub struct DelayFuture { diff --git a/src/future/mod.rs b/src/future/mod.rs index d1a0f3150..6bfd6303c 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -51,6 +51,7 @@ pub use async_macros::{select, try_select}; use cfg_if::cfg_if; pub use future::Future; +pub use future::FutureExt; pub use pending::pending; pub use poll_fn::poll_fn; pub use ready::ready; @@ -65,9 +66,7 @@ mod timeout; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod into_future; - mod delay; pub use into_future::IntoFuture; - pub use delay::delay; } } From 358d2bc038f8894794ad71b6003938452859093b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:20:07 +0900 Subject: [PATCH 010/707] Add import crate --- src/future/future.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/future/future.rs b/src/future/future.rs index 38f3d704c..5254ac047 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -9,6 +9,15 @@ cfg_if::cfg_if! { } } +cfg_if::cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod delay; + + use std::time::Duration; + use delay::DelayFuture; + } +} + extension_trait! { #[doc = r#" A future represents an asynchronous computation. From 10f32ca817551565d6602911a79ad6a9736fde95 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:49:07 +0900 Subject: [PATCH 011/707] Fix TimeoutError --- src/stream/stream/timeout.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 7a8cf477b..f73ae8715 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -35,7 +35,7 @@ impl Stream for TimeoutStream { Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), Poll::Ready(None) => Poll::Ready(None), Poll::Pending => match self.delay().poll(cx) { - Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError))), + Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError { _private: () }))), Poll::Pending => Poll::Pending, }, } @@ -46,7 +46,9 @@ impl Stream for TimeoutStream { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(any(feature = "unstable", feature = "docs"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct TimeoutError; +pub struct TimeoutError { + _private: (), +} impl Error for TimeoutError {} From f1ed034600f17eb6ab2756e22e2fdc0f2944dd87 Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 16 Oct 2019 22:21:32 +0900 Subject: [PATCH 012/707] Update src/stream/stream/mod.rs Co-Authored-By: Yoshua Wuyts --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c44917d10..47b3f835a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -113,7 +113,7 @@ cfg_if! { use crate::stream::FromStream; pub use merge::Merge; - pub use timeout::TimeoutStream; + pub use timeout::{TimeoutError, TimeoutStream}; } } From 9d55fff81da0d4f76a9266df399fd8084406d4d2 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 22:38:28 +0900 Subject: [PATCH 013/707] fix export FutureExt --- src/future/future.rs | 2 +- src/future/mod.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/future/future.rs b/src/future/future.rs index 5254ac047..484977231 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -120,9 +120,9 @@ extension_trait! { /// /// ``` /// # async_std::task::block_on(async { + /// use async_std::prelude::*; /// use async_std::future; /// use std::time::Duration; - /// use async_std::future::FutureExt; /// /// let a = future::ready(1).delay(Duration::from_millis(2000)); /// dbg!(a.await); diff --git a/src/future/mod.rs b/src/future/mod.rs index 6bfd6303c..cc3b7a5d5 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -51,7 +51,6 @@ pub use async_macros::{select, try_select}; use cfg_if::cfg_if; pub use future::Future; -pub use future::FutureExt; pub use pending::pending; pub use poll_fn::poll_fn; pub use ready::ready; From 53fa132d136cb3a47f6aa3c5ba7249c3d6b401a6 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 22:45:18 +0900 Subject: [PATCH 014/707] fix type Declaration --- src/future/future.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/future.rs b/src/future/future.rs index 484977231..abf7c183f 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -130,7 +130,7 @@ extension_trait! { /// ``` #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(any(feature = "unstable", feature = "docs"))] - fn delay(self, dur: Duration) -> DelayFuture + fn delay(self, dur: Duration) -> impl Future [DelayFuture] where Self: Future + Sized { From c3f6f969c51de4717ae4ef6639bb7f1ad916a6e8 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 22:56:17 +0900 Subject: [PATCH 015/707] fix: Rename TimeoutStream to Timeout --- src/stream/stream/mod.rs | 6 +++--- src/stream/stream/timeout.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c44917d10..a881a7aec 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -113,7 +113,7 @@ cfg_if! { use crate::stream::FromStream; pub use merge::Merge; - pub use timeout::TimeoutStream; + pub use timeout::Timeout; } } @@ -1074,11 +1074,11 @@ extension_trait! { "#] #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> TimeoutStream + fn timeout(self, dur: Duration) -> Timeout where Self: Stream + Sized, { - TimeoutStream::new(self, dur) + Timeout::new(self, dur) } #[doc = r#" diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index f73ae8715..042dc12b0 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -11,23 +11,23 @@ use crate::task::{Context, Poll}; #[doc(hidden)] #[derive(Debug)] -pub struct TimeoutStream { +pub struct Timeout { stream: S, delay: Delay, } -impl TimeoutStream { +impl Timeout { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_pinned!(delay: Delay); - pub fn new(stream: S, dur: Duration) -> TimeoutStream { + pub fn new(stream: S, dur: Duration) -> Timeout { let delay = Delay::new(dur); - TimeoutStream { stream, delay } + Timeout { stream, delay } } } -impl Stream for TimeoutStream { +impl Stream for Timeout { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From 0a4073449beac09b0bb2492a3754b57171147d5f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 22:56:48 +0900 Subject: [PATCH 016/707] doc: Add Stream::Timeout doc --- src/stream/stream/timeout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 042dc12b0..7e0270e3b 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -9,7 +9,7 @@ use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] +/// A stream with timeout time set #[derive(Debug)] pub struct Timeout { stream: S, From a2393501c5edd4c0ef682dfdfe09f05e02bd5e5c Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 16 Oct 2019 18:43:34 +0200 Subject: [PATCH 017/707] Implemented StreamExt::throttle --- src/stream/stream/mod.rs | 10 +++++++ src/stream/stream/throttle.rs | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/stream/stream/throttle.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 764dc9782..8035769a7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -50,6 +50,7 @@ mod skip_while; mod step_by; mod take; mod take_while; +mod throttle; mod try_fold; mod try_for_each; mod zip; @@ -86,10 +87,12 @@ 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 zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; +use std::time::Duration; use cfg_if::cfg_if; @@ -288,6 +291,13 @@ extension_trait! { TakeWhile::new(self, predicate) } + fn throttle(self, d: Duration) -> Throttle + where + Self: Sized, + { + Throttle::new(self, d) + } + #[doc = r#" Creates a stream that yields each `step`th element. diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs new file mode 100644 index 000000000..c37c972c9 --- /dev/null +++ b/src/stream/stream/throttle.rs @@ -0,0 +1,56 @@ +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that only yields one element once every `duration`, and drops all others. +/// #[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Throttle { + stream: S, + duration: Duration, + delay: Option, +} + +impl Unpin for Throttle {} + +impl Throttle { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(duration: Duration); + pin_utils::unsafe_pinned!(delay: Option); + + pub(super) fn new(stream: S, duration: Duration) -> Self { + Throttle { + stream, + duration, + delay: None, + } + } +} + +impl Stream for Throttle { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().stream().poll_next(cx) { + Poll::Ready(v) => match self.as_mut().delay().as_pin_mut() { + None => { + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(v) + } + Some(d) => match d.poll(cx) { + Poll::Ready(_) => { + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + }, + }, + Poll::Pending => Poll::Pending, + } + } +} From a5a00d7b1465b728592ae0cff55fbefba853d528 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 16:29:23 +0900 Subject: [PATCH 018/707] feat: Add StdinLock struct --- src/io/stdin.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index f6c4a25ec..b4ccffbce 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -47,6 +47,11 @@ pub fn stdin() -> Stdin { #[derive(Debug)] pub struct Stdin(Mutex); +#[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. @@ -157,12 +162,14 @@ impl Stdin { /// # /// # Ok(()) }) } /// ``` - pub async fn lock(&self) -> std::io::StdinLock<'static> { + pub async fn lock(&self) -> StdinLock<'static> { lazy_static! { static ref STDIN: std::io::Stdin = std::io::stdin(); } - STDIN.lock() + blocking::spawn(async { + StdinLock(STDIN.lock()) + }).await } } @@ -248,3 +255,13 @@ cfg_if! { } } } + +impl Read for StdinLock<'_> { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &mut [u8], + ) -> Poll> { + unimplemented!() + } +} From 70e84762643ec0877f03efc2e01514221f9bd116 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 16:32:14 +0900 Subject: [PATCH 019/707] fix StdinLock doc test --- src/io/stdin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index b4ccffbce..4201a3a01 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -151,14 +151,14 @@ impl Stdin { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; - /// use std::io::Read; + /// use crate::async_std::prelude::*; /// /// let mut buffer = String::new(); /// /// let stdin = io::stdin(); /// let mut handle = stdin.lock().await; /// - /// handle.read_to_string(&mut buffer)?; + /// handle.read_to_string(&mut buffer).await?; /// # /// # Ok(()) }) } /// ``` From f2bf01223c51702532e48ba6f8629d4202028a6b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 16:34:39 +0900 Subject: [PATCH 020/707] $cargo fmt --- src/io/stdin.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 4201a3a01..daba03e86 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -167,9 +167,7 @@ impl Stdin { static ref STDIN: std::io::Stdin = std::io::stdin(); } - blocking::spawn(async { - StdinLock(STDIN.lock()) - }).await + blocking::spawn(async { StdinLock(STDIN.lock()) }).await } } From ec98b41c85bb8c2891755fc6b86366fb85413a5e Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 23:56:01 +0900 Subject: [PATCH 021/707] feat: Add FlattenCompat struct --- src/lib.rs | 1 + src/stream/stream/flatten.rs | 24 ++++++++++++++++++++++++ src/stream/stream/mod.rs | 1 + 3 files changed, 26 insertions(+) create mode 100644 src/stream/stream/flatten.rs diff --git a/src/lib.rs b/src/lib.rs index e138c87d4..afbad31e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "1024"] +#![feature(associated_type_bounds)] use cfg_if::cfg_if; diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs new file mode 100644 index 000000000..fffda4dd0 --- /dev/null +++ b/src/stream/stream/flatten.rs @@ -0,0 +1,24 @@ +use std::pin::Pin; + +use crate::stream::{IntoStream, Stream}; +use crate::task::{Context, Poll}; + +/// Real logic of both `Flatten` and `FlatMap` which simply delegate to +/// this type. +#[derive(Clone, Debug)] +struct FlattenCompat { + stream: S, + frontiter: Option, +} +impl FlattenCompat { + pin_utils::unsafe_unpinned!(stream: S); + pin_utils::unsafe_unpinned!(frontiter: Option); + + /// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. + pub fn new(stream: S) -> FlattenCompat { + FlattenCompat { + stream, + frontiter: None, + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 764dc9782..7047b033a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -106,6 +106,7 @@ cfg_if! { cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod merge; + mod flatten; use std::pin::Pin; From 8bef2e9e95e8a7c4a90bb334afdbc7d2f67122b0 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 21:28:38 +0200 Subject: [PATCH 022/707] Don't flush files if they weren't written to --- src/fs/file.rs | 35 ++++++++++++++++++++--------------- src/fs/open_options.rs | 5 ++++- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 3129e96a0..745a58481 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -66,6 +66,23 @@ pub struct File { } impl File { + /// Creates an async file handle. + pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File { + let file = Arc::new(file); + + File { + file: file.clone(), + lock: Lock::new(State { + file, + mode: Mode::Idle, + cache: Vec::new(), + is_flushed, + last_read_err: None, + last_write_err: None, + }), + } + } + /// Opens a file in read-only mode. /// /// See the [`OpenOptions::open`] function for more options. @@ -96,7 +113,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = blocking::spawn(move || std::fs::File::open(&path)).await?; - Ok(file.into()) + Ok(File::new(file, true)) } /// Opens a file in write-only mode. @@ -131,7 +148,7 @@ impl File { pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = blocking::spawn(move || std::fs::File::create(&path)).await?; - Ok(file.into()) + Ok(File::new(file, true)) } /// Synchronizes OS-internal buffered contents and metadata to disk. @@ -383,19 +400,7 @@ impl Seek for &File { impl From for File { fn from(file: std::fs::File) -> File { - let file = Arc::new(file); - - File { - file: file.clone(), - lock: Lock::new(State { - file, - mode: Mode::Idle, - cache: Vec::new(), - is_flushed: false, - last_read_err: None, - last_write_err: None, - }), - } + File::new(file, false) } } diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index a2eb9e76b..7f7007347 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -284,7 +284,10 @@ impl OpenOptions { pub fn open>(&self, path: P) -> impl Future> { let path = path.as_ref().to_owned(); let options = self.0.clone(); - async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await } + async move { + let file = blocking::spawn(move || options.open(path)).await?; + Ok(File::new(file, true)) + } } } From bb1416420d047638d2cc8be21920206c3053870a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 23:56:32 +0900 Subject: [PATCH 023/707] feat: Add Stream trait for FlattenCompat --- src/stream/stream/flatten.rs | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index fffda4dd0..7265d17f8 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -22,3 +22,52 @@ impl FlattenCompat { } } } + +impl Stream for FlattenCompat +where + S: Stream> + std::marker::Unpin, + U: Stream + std::marker::Unpin, +{ + type Item = U::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(ref mut inner) = self.as_mut().frontiter() { + if let item @ Some(_) = futures_core::ready!(Pin::new(inner).poll_next(cx)) { + return Poll::Ready(item); + } + } + + match futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)) { + None => return Poll::Ready(None), + Some(inner) => *self.as_mut().frontiter() = Some(inner.into_stream()), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::FlattenCompat; + + use crate::prelude::*; + use crate::task; + + use std::collections::VecDeque; + + #[test] + fn test_poll_next() -> std::io::Result<()> { + let inner1: VecDeque = vec![1, 2, 3].into_iter().collect(); + let inner2: VecDeque = vec![4, 5, 6].into_iter().collect(); + + let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); + + task::block_on(async move { + let flat = FlattenCompat::new(s); + let v: Vec = flat.collect().await; + + assert_eq!(v, vec![1, 2, 3, 4, 5, 6]); + Ok(()) + }) + } +} From 2dee2897509f0ac5c1a8e26d768bfdf3cbe54099 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 10:43:21 +0900 Subject: [PATCH 024/707] Add FlatMap struct --- src/stream/stream/flatten.rs | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 7265d17f8..e922e94e4 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,8 +1,44 @@ use std::pin::Pin; +use crate::prelude::*; +use crate::stream::stream::map::Map; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; +#[allow(missing_debug_implementations)] +pub struct FlatMap { + inner: FlattenCompat, U>, +} + +impl FlatMap +where + S: Stream, + U: IntoStream, + F: FnMut(S::Item) -> U, +{ + pin_utils::unsafe_pinned!(inner: FlattenCompat, U>); + + pub fn new(stream: S, f: F) -> FlatMap { + FlatMap { + inner: FlattenCompat::new(stream.map(f)), + } + } +} + +impl Stream for FlatMap +where + S: Stream> + std::marker::Unpin, + S::Item: std::marker::Unpin, + U: Stream + std::marker::Unpin, + F: FnMut(S::Item) -> U + std::marker::Unpin, +{ + type Item = U::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().inner().poll_next(cx) + } +} + /// Real logic of both `Flatten` and `FlatMap` which simply delegate to /// this type. #[derive(Clone, Debug)] @@ -10,6 +46,7 @@ struct FlattenCompat { stream: S, frontiter: Option, } + impl FlattenCompat { pin_utils::unsafe_unpinned!(stream: S); pin_utils::unsafe_unpinned!(frontiter: Option); From 2187a2a31d5f9c72e8ee32d2beae3c129bd8e80f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 10:43:36 +0900 Subject: [PATCH 025/707] feat: Add Stream::flat_map --- src/stream/stream/mod.rs | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7047b033a..2f1a89f95 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -30,6 +30,7 @@ mod filter; mod filter_map; mod find; mod find_map; +mod flatten; mod fold; mod for_each; mod fuse; @@ -77,6 +78,7 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; +pub use flatten::FlatMap; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; @@ -93,6 +95,7 @@ use std::marker::PhantomData; use cfg_if::cfg_if; +use crate::stream::IntoStream; use crate::utils::extension_trait; cfg_if! { @@ -106,7 +109,6 @@ cfg_if! { cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod merge; - mod flatten; use std::pin::Pin; @@ -496,7 +498,6 @@ extension_trait! { # # }) } ``` - "#] fn last( self, @@ -570,6 +571,42 @@ extension_trait! { Filter::new(self, predicate) } + #[doc= r#" + Creates an stream that works like map, but flattens nested structure. + + # Examples + + Basic usage: + + ``` + # async_std::task::block_on(async { + + use std::collections::VecDeque; + use async_std::prelude::*; + use async_std::stream::IntoStream; + + let inner1: VecDeque = vec![1,2,3].into_iter().collect(); + let inner2: VecDeque = vec![4,5,6].into_iter().collect(); + + let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); + + let flat= s.flat_map(|s| s.into_stream() ); + let v: Vec = flat.collect().await; + + assert_eq!(v, vec![1,2,3,4,5,6]); + + # }); + ``` + "#] + fn flat_map(self, f: F) -> FlatMap + where + Self: Sized, + U: IntoStream, + F: FnMut(Self::Item) -> U, + { + FlatMap::new(self, f) + } + #[doc = r#" Both filters and maps a stream. From cd862083a5db3d04c4ff2cbbaa3eb96e50f04ccd Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 12:19:38 +0900 Subject: [PATCH 026/707] Add Flatten struct --- src/stream/stream/flatten.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index e922e94e4..06e9ec2e2 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -39,6 +39,21 @@ where } } +pub struct Flatten +where + S::Item: IntoStream, +{ + inner: FlattenCompat::IntoStream>, +} + +impl> Flatten { + pin_utils::unsafe_pinned!(inner: FlattenCompat::IntoStream>); + + pub fn new(stream: S) -> Flatten { + Flatten { inner: FlattenCompat::new(stream) } + } +} + /// Real logic of both `Flatten` and `FlatMap` which simply delegate to /// this type. #[derive(Clone, Debug)] From 8138afbfadd0b7f2640a28d9c0af0f59a59e3967 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 12:20:02 +0900 Subject: [PATCH 027/707] feat: Add Stream trait for Flatten --- src/stream/stream/flatten.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 06e9ec2e2..5c7616722 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -54,6 +54,19 @@ impl> Flatten { } } +impl Stream for Flatten +where + S: Stream> + std::marker::Unpin, + U: Stream + std::marker::Unpin, +{ + type Item = U::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().inner().poll_next(cx) + } +} + + /// Real logic of both `Flatten` and `FlatMap` which simply delegate to /// this type. #[derive(Clone, Debug)] From 176359afae6adf0f4202fde938f0857c9759c9d9 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 12:20:28 +0900 Subject: [PATCH 028/707] Add Stream::flatten --- src/stream/stream/mod.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2f1a89f95..63d1d8cc2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -78,7 +78,7 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; -pub use flatten::FlatMap; +pub use flatten::{FlatMap, Flatten}; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; @@ -590,8 +590,7 @@ extension_trait! { let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); - let flat= s.flat_map(|s| s.into_stream() ); - let v: Vec = flat.collect().await; + let v :Vec<_> = s.flat_map(|s| s.into_stream()).collect().await; assert_eq!(v, vec![1,2,3,4,5,6]); @@ -607,6 +606,37 @@ extension_trait! { FlatMap::new(self, f) } + #[doc = r#" + Creates an stream that flattens nested structure. + + # Examples + + Basic usage: + + ``` + # async_std::task::block_on(async { + + use std::collections::VecDeque; + use async_std::prelude::*; + + let inner1: VecDeque = vec![1,2,3].into_iter().collect(); + let inner2: VecDeque = vec![4,5,6].into_iter().collect(); + let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); + + let v: Vec<_> = s.flatten().collect().await; + + assert_eq!(v, vec![1,2,3,4,5,6]); + + # }); + "#] + fn flatten(self) -> Flatten + where + Self: Sized, + Self::Item: IntoStream, + { + Flatten::new(self) + } + #[doc = r#" Both filters and maps a stream. From 410d16eaf6f5933bc5516a709c27134f227111e4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 18 Oct 2019 13:20:44 +0900 Subject: [PATCH 029/707] Add docs + To unstable feature --- src/stream/stream/flatten.rs | 11 +++++++++++ src/stream/stream/mod.rs | 11 +++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 5c7616722..b9700876b 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -5,6 +5,11 @@ use crate::stream::stream::map::Map; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; +/// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its +/// documentation for more. +/// +/// [`flat_map`]: trait.Stream.html#method.flat_map +/// [`Stream`]: trait.Stream.html #[allow(missing_debug_implementations)] pub struct FlatMap { inner: FlattenCompat, U>, @@ -39,6 +44,12 @@ where } } +/// This `struct` is created by the [`flatten`] method on [`Stream`]. See its +/// documentation for more. +/// +/// [`flatten`]: trait.Stream.html#method.flatten +/// [`Stream`]: trait.Stream.html +#[allow(missing_debug_implementations)] pub struct Flatten where S::Item: IntoStream, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 6802de904..8ad8cdc03 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -30,7 +30,6 @@ mod filter; mod filter_map; mod find; mod find_map; -mod flatten; mod fold; mod for_each; mod fuse; @@ -78,7 +77,6 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; -pub use flatten::{FlatMap, Flatten}; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; @@ -93,17 +91,18 @@ pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use crate::stream::IntoStream; - cfg_unstable! { use std::pin::Pin; use crate::future::Future; use crate::stream::FromStream; + use crate::stream::into_stream::IntoStream; pub use merge::Merge; + pub use flatten::{FlatMap, Flatten}; mod merge; + mod flatten; } extension_trait! { @@ -589,6 +588,8 @@ extension_trait! { # }); ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flat_map(self, f: F) -> FlatMap where Self: Sized, @@ -621,6 +622,8 @@ extension_trait! { # }); "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flatten(self) -> Flatten where Self: Sized, From a9a7bdc29039951c39290741f1e512afa4f70831 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Fri, 18 Oct 2019 07:23:52 +0200 Subject: [PATCH 030/707] add stream::count --- src/stream/stream/count.rs | 41 ++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 29 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/stream/stream/count.rs diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs new file mode 100644 index 000000000..fcd75f6a5 --- /dev/null +++ b/src/stream/stream/count.rs @@ -0,0 +1,41 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct CountFuture { + stream: S, + count: usize, +} + +impl CountFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(count: usize); + + pub(crate) fn new(stream: S) -> Self { + CountFuture { stream, count: 0 } + } +} + +impl Future for CountFuture +where + S: Sized + Stream, +{ + type Output = usize; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(_) => { + cx.waker().wake_by_ref(); + *self.as_mut().count() += 1; + Poll::Pending + } + None => Poll::Ready(self.count), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b2..b9d4bc86a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -25,6 +25,7 @@ mod all; mod any; mod chain; mod cmp; +mod count; mod enumerate; mod filter; mod filter_map; @@ -57,6 +58,7 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; +use count::CountFuture; use enumerate::Enumerate; use filter_map::FilterMap; use find::FindFuture; @@ -1392,6 +1394,33 @@ extension_trait! { CmpFuture::new(self, other) } + #[doc = r#" + Counts the number of elements in the stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + let s1 = VecDeque::from(vec![0]); + let s2 = VecDeque::from(vec![1, 2, 3]); + + assert_eq!(s1.count().await, 1); + assert_eq!(s2.count().await, 3); + # + # }) } + ``` + "#] + fn count(self) -> impl Future [CountFuture] + where + Self: Sized + Stream, + { + CountFuture::new(self) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than or equal to those of another. From 97094b2a1c78e20ed01bbc1a27776506907c49a9 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Sun, 20 Oct 2019 22:03:51 +0200 Subject: [PATCH 031/707] remove Sized constraint --- src/stream/stream/count.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index fcd75f6a5..b6d53ca84 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -22,7 +22,7 @@ impl CountFuture { impl Future for CountFuture where - S: Sized + Stream, + S: Stream, { type Output = usize; From 0d4a907335beec5472f03c28c204e039fcc5e6fb Mon Sep 17 00:00:00 2001 From: Sunjay Varma Date: Sun, 20 Oct 2019 19:18:37 -0400 Subject: [PATCH 032/707] Added Extend + FromStream for PathBuf --- src/path/pathbuf.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 64744e140..5d81f1c5b 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,6 +1,12 @@ use std::ffi::{OsStr, OsString}; +#[cfg(feature = "unstable")] +use std::pin::Pin; use crate::path::Path; +#[cfg(feature = "unstable")] +use crate::prelude::*; +#[cfg(feature = "unstable")] +use crate::stream::{Extend, FromStream, IntoStream}; /// This struct is an async version of [`std::path::PathBuf`]. /// @@ -206,7 +212,7 @@ impl From for PathBuf { impl Into for PathBuf { fn into(self) -> std::path::PathBuf { - self.inner.into() + self.inner } } @@ -233,3 +239,44 @@ impl AsRef for PathBuf { self.inner.as_ref() } } + +#[cfg(feature = "unstable")] +impl> Extend

for PathBuf { + fn stream_extend<'a, S: IntoStream>( + &'a mut self, + stream: S, + ) -> Pin + 'a>> + where + P: 'a, + ::IntoStream: 'a, + { + let stream = stream.into_stream(); + + //TODO: This can be added back in once this issue is resolved: + // https://github.com/rust-lang/rust/issues/58234 + //self.reserve(stream.size_hint().0); + + Box::pin(stream.for_each(move |item| self.push(item.as_ref()))) + } +} + +#[cfg(feature = "unstable")] +impl<'b, P: AsRef + 'b> FromStream

for PathBuf { + #[inline] + fn from_stream<'a, S: IntoStream>( + stream: S, + ) -> Pin + 'a>> + where + ::IntoStream: 'a, + { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + let mut out = Self::new(); + out.stream_extend(stream).await; + out + }) + } +} From 88558eae6ebffaf77e999551b9f12b1cc883946d Mon Sep 17 00:00:00 2001 From: Andre Zanellato Date: Mon, 21 Oct 2019 19:47:14 -0300 Subject: [PATCH 033/707] Typos and sentence structure fixes --- docs/src/concepts/futures.md | 10 +++------- docs/src/concepts/tasks.md | 2 +- docs/src/overview/async-std.md | 2 +- docs/src/overview/stability-guarantees.md | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index 67db720ca..7d9cc6360 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie ## An easy view of computation -While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: - -- computation is a sequence of composable operations -- they can branch based on a decision -- they either run to succession and yield a result, or they can yield an error +While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error ## Deferring computation @@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system ## Conclusion -Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling. +Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling. A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple. -Next, we will introduce you to `tasks`, which we need to actually *run* Futures. +Next, we will introduce you to `tasks`, which we will use to actually *run* Futures. [^1]: Two parties reading while it is guaranteed that no one is writing is always safe. diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index d4037a3bb..2142cac46 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread` ## Blocking -`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: +`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: ```rust,edition2018 # extern crate async_std; diff --git a/docs/src/overview/async-std.md b/docs/src/overview/async-std.md index 2b59ffb03..0086599f1 100644 --- a/docs/src/overview/async-std.md +++ b/docs/src/overview/async-std.md @@ -4,4 +4,4 @@ `async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`. -[organization]: https://github.com/async-rs/async-std +[organization]: https://github.com/async-rs diff --git a/docs/src/overview/stability-guarantees.md b/docs/src/overview/stability-guarantees.md index 84bb68d7d..d84b64ab5 100644 --- a/docs/src/overview/stability-guarantees.md +++ b/docs/src/overview/stability-guarantees.md @@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte ## Security fixes -Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice. +Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 months_ of ahead. ## Credits From faad4c8c263181ade0fddf105fb875bb411959f8 Mon Sep 17 00:00:00 2001 From: Andre Zanellato Date: Mon, 21 Oct 2019 19:50:57 -0300 Subject: [PATCH 034/707] Sentence structure on notice --- docs/src/overview/stability-guarantees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/overview/stability-guarantees.md b/docs/src/overview/stability-guarantees.md index d84b64ab5..8c14e20fd 100644 --- a/docs/src/overview/stability-guarantees.md +++ b/docs/src/overview/stability-guarantees.md @@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte ## Security fixes -Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 months_ of ahead. +Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead. ## Credits From e26eb7a719015f3d8fa6804d2159ec0845093226 Mon Sep 17 00:00:00 2001 From: Kyle Tomsic Date: Sun, 20 Oct 2019 10:05:55 -0400 Subject: [PATCH 035/707] Add `Stream::sum()` and `Stream::product()` implementations These are the stream equivalents to `std::iter::Iterator::sum()` and `std::iter::Iterator::product()`. Note that this changeset tweaks the `Stream::Sum` and `Stream::Product` traits a little: rather than returning a generic future `F`, they return a pinned, boxed, `Future` trait object now. This is in line with other traits that return a future, e.g. `FromStream`. --- src/option/mod.rs | 5 +++ src/option/product.rs | 66 +++++++++++++++++++++++++++++ src/option/sum.rs | 63 ++++++++++++++++++++++++++++ src/result/mod.rs | 5 +++ src/result/product.rs | 64 ++++++++++++++++++++++++++++ src/result/sum.rs | 64 ++++++++++++++++++++++++++++ src/stream/product.rs | 64 ++++++++++++++++++++++++++-- src/stream/stream/mod.rs | 90 ++++++++++++++++++++++++++++++++++++++++ src/stream/sum.rs | 62 +++++++++++++++++++++++++-- 9 files changed, 476 insertions(+), 7 deletions(-) create mode 100644 src/option/product.rs create mode 100644 src/option/sum.rs create mode 100644 src/result/product.rs create mode 100644 src/result/sum.rs diff --git a/src/option/mod.rs b/src/option/mod.rs index afb29adc9..76f096b3f 100644 --- a/src/option/mod.rs +++ b/src/option/mod.rs @@ -7,3 +7,8 @@ mod from_stream; #[doc(inline)] pub use std::option::Option; + +cfg_unstable! { + mod product; + mod sum; +} diff --git a/src/option/product.rs b/src/option/product.rs new file mode 100644 index 000000000..8b66bc693 --- /dev/null +++ b/src/option/product.rs @@ -0,0 +1,66 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Product}; + +impl Product> for Option +where + T: Product, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is a `None`, no further + elements are taken, and the `None` is returned. Should no `None` occur, + the product of all elements is returned. + + # Examples + + This multiplies every integer in a vector, rejecting the product if a negative element is + encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + use async_std::prelude::*; + + let v: VecDeque<_> = vec![1, 2, 4].into_iter().collect(); + let prod: Option = v.map(|x| + if x < 0 { + None + } else { + Some(x) + }).product().await; + assert_eq!(prod, Some(8)); + # + # }) } + ``` + "#] + fn product<'a, S>(stream: S) -> Pin> + 'a>> + where S: Stream> + 'a + { + Box::pin(async move { + pin_utils::pin_mut!(stream); + + // Using `scan` here because it is able to stop the stream early + // if a failure occurs + let mut found_none = false; + let out = >::product(stream + .scan((), |_, elem| { + match elem { + Some(elem) => Some(elem), + None => { + found_none = true; + // Stop processing the stream on error + None + } + } + })).await; + + if found_none { + None + } else { + Some(out) + } + }) + } +} diff --git a/src/option/sum.rs b/src/option/sum.rs new file mode 100644 index 000000000..25dc92093 --- /dev/null +++ b/src/option/sum.rs @@ -0,0 +1,63 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Sum}; + +impl Sum> for Option +where + T: Sum, +{ + #[doc = r#" + Takes each element in the `Iterator`: if it is a `None`, no further + elements are taken, and the `None` is returned. Should no `None` occur, + the sum of all elements is returned. + + # Examples + + This sums up the position of the character 'a' in a vector of strings, + if a word did not have the character 'a' the operation returns `None`: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + use async_std::prelude::*; + + let words: VecDeque<_> = vec!["have", "a", "great", "day"] + .into_iter() + .collect(); + let total: Option = words.map(|w| w.find('a')).sum().await; + assert_eq!(total, Some(5)); + # + # }) } + ``` + "#] + fn sum<'a, S>(stream: S) -> Pin> + 'a>> + where S: Stream> + 'a + { + Box::pin(async move { + pin_utils::pin_mut!(stream); + + // Using `scan` here because it is able to stop the stream early + // if a failure occurs + let mut found_none = false; + let out = >::sum(stream + .scan((), |_, elem| { + match elem { + Some(elem) => Some(elem), + None => { + found_none = true; + // Stop processing the stream on error + None + } + } + })).await; + + if found_none { + None + } else { + Some(out) + } + }) + } +} diff --git a/src/result/mod.rs b/src/result/mod.rs index 908f9c4dc..cae0ebd93 100644 --- a/src/result/mod.rs +++ b/src/result/mod.rs @@ -7,3 +7,8 @@ mod from_stream; #[doc(inline)] pub use std::result::Result; + +cfg_unstable! { + mod product; + mod sum; +} diff --git a/src/result/product.rs b/src/result/product.rs new file mode 100644 index 000000000..17afa94b2 --- /dev/null +++ b/src/result/product.rs @@ -0,0 +1,64 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Product}; + +impl Product> for Result +where + T: Product, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is an `Err`, no further + elements are taken, and the `Err` is returned. Should no `Err` occur, + the product of all elements is returned. + + # Examples + + This multiplies every integer in a vector, rejecting the product if a negative element is + encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + use async_std::prelude::*; + + let v: VecDeque<_> = vec![1, 2, 4].into_iter().collect(); + let res: Result = v.map(|x| + if x < 0 { + Err("Negative element found") + } else { + Ok(x) + }).product().await; + assert_eq!(res, Ok(8)); + # + # }) } + ``` + "#] + fn product<'a, S>(stream: S) -> Pin> + 'a>> + where S: Stream> + 'a + { + Box::pin(async move { + pin_utils::pin_mut!(stream); + + // Using `scan` here because it is able to stop the stream early + // if a failure occurs + let mut found_error = None; + let out = >::product(stream + .scan((), |_, elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None + } + } + })).await; + match found_error { + Some(err) => Err(err), + None => Ok(out) + } + }) + } +} diff --git a/src/result/sum.rs b/src/result/sum.rs new file mode 100644 index 000000000..caca4f65b --- /dev/null +++ b/src/result/sum.rs @@ -0,0 +1,64 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Sum}; + +impl Sum> for Result +where + T: Sum, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is an `Err`, no further + elements are taken, and the `Err` is returned. Should no `Err` occur, + the sum of all elements is returned. + + # Examples + + This sums up every integer in a vector, rejecting the sum if a negative + element is encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + use async_std::prelude::*; + + let v: VecDeque<_> = vec![1, 2].into_iter().collect(); + let res: Result = v.map(|x| + if x < 0 { + Err("Negative element found") + } else { + Ok(x) + }).sum().await; + assert_eq!(res, Ok(3)); + # + # }) } + ``` + "#] + fn sum<'a, S>(stream: S) -> Pin> + 'a>> + where S: Stream> + 'a + { + Box::pin(async move { + pin_utils::pin_mut!(stream); + + // Using `scan` here because it is able to stop the stream early + // if a failure occurs + let mut found_error = None; + let out = >::sum(stream + .scan((), |_, elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None + } + } + })).await; + match found_error { + Some(err) => Err(err), + None => Ok(out) + } + }) + } +} diff --git a/src/stream/product.rs b/src/stream/product.rs index 5799990d7..71b14c704 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,7 +1,9 @@ +use std::pin::Pin; + use crate::future::Future; use crate::stream::Stream; -/// Trait to represent types that can be created by productming up a stream. +/// Trait to represent types that can be created by multiplying the elements of a stream. /// /// This trait is used to implement the [`product`] method on streams. Types which /// implement the trait can be generated by the [`product`] method. Like @@ -16,8 +18,62 @@ use crate::stream::Stream; pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. - fn product(stream: S) -> F + fn product<'a, S>(stream: S) -> Pin + 'a>> where - S: Stream, - F: Future; + S: Stream + 'a; +} + +use core::ops::Mul; +use core::num::Wrapping; +use crate::stream::stream::StreamExt; + +macro_rules! integer_product { + (@impls $one: expr, $($a:ty)*) => ($( + impl Product for $a { + fn product<'a, S>(stream: S) -> Pin+ 'a>> + where + S: Stream + 'a, + { + Box::pin(async move { stream.fold($one, Mul::mul).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($one, Mul::mul).await } ) + } + } + )*); + ($($a:ty)*) => ( + integer_product!(@impls 1, $($a)*); + integer_product!(@impls 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>)*); + ); +} + +integer_product!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_product!{ f32 f64 } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b2..f2d3c6ebc 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -96,6 +96,7 @@ cfg_unstable! { use crate::future::Future; use crate::stream::FromStream; + use crate::stream::{Product, Sum}; pub use merge::Merge; @@ -1536,6 +1537,95 @@ extension_trait! { { LtFuture::new(self, other) } + + #[doc = r#" + Sums the elements of an iterator. + + Takes each element, adds them together, and returns the result. + + An empty iterator returns the zero value of the type. + + # Panics + + When calling `sum()` and a primitive integer type is being returned, this + method will panic if the computation overflows and debug assertions are + enabled. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + use async_std::prelude::*; + + let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); + 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, + ) -> impl Future + 'a [Pin + 'a>>] + where + Self: Sized + Stream + 'a, + S: Sum, + { + Sum::sum(self) + } + + #[doc = r#" + Iterates over the entire iterator, multiplying all the elements + + An empty iterator returns the one value of the type. + + # Panics + + When calling `product()` and a primitive integer type is being returned, + method will panic if the computation overflows and debug assertions are + enabled. + + # Examples + + This example calculates the factorial of n (i.e. the product of the numbers from 1 to + n, inclusive): + + ``` + # fn main() { async_std::task::block_on(async { + # + async fn factorial(n: u32) -> u32 { + use std::collections::VecDeque; + use async_std::prelude::*; + + let s: VecDeque<_> = (1..=n).collect(); + 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, + ) -> impl Future + 'a [Pin + 'a>>] + where + Self: Sized + Stream + 'a, + P: Product, + { + Product::product(self) + } } impl Stream for Box { diff --git a/src/stream/sum.rs b/src/stream/sum.rs index a87ade1a4..dadbc3471 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,3 +1,5 @@ +use std::pin::Pin; + use crate::future::Future; use crate::stream::Stream; @@ -16,8 +18,62 @@ use crate::stream::Stream; pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. - fn sum(stream: S) -> F + fn sum<'a, S>(stream: S) -> Pin + 'a>> where - S: Stream, - F: Future; + S: Stream + 'a; +} + +use core::ops::Add; +use core::num::Wrapping; +use crate::stream::stream::StreamExt; + +macro_rules! integer_sum { + (@impls $zero: expr, $($a:ty)*) => ($( + impl Sum for $a { + fn sum<'a, S>(stream: S) -> Pin+ 'a>> + where + S: Stream + 'a, + { + Box::pin(async move { stream.fold($zero, Add::add).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($zero, Add::add).await } ) + } + } + )*); + ($($a:ty)*) => ( + integer_sum!(@impls 0, $($a)*); + integer_sum!(@impls 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>)*); + ); +} + +integer_sum!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_sum!{ f32 f64 } From 4e5828e64669516a28acb63f1fc11aaa2cbbefc5 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 16:46:11 +0800 Subject: [PATCH 036/707] add stream::max_by method --- src/stream/stream/max_by.rs | 56 +++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 41 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/stream/stream/max_by.rs diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs new file mode 100644 index 000000000..d25a869d0 --- /dev/null +++ b/src/stream/stream/max_by.rs @@ -0,0 +1,56 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct MaxByFuture { + stream: S, + compare: F, + max: Option, +} + +impl MaxByFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(compare: F); + pin_utils::unsafe_unpinned!(max: Option); + + pub(super) fn new(stream: S, compare: F) -> Self { + MaxByFuture { + stream, + compare, + max: None, + } + } +} + +impl Future for MaxByFuture +where + S: Stream + Unpin + Sized, + S::Item: Copy, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match self.as_mut().max().take() { + None => *self.as_mut().max() = Some(new), + Some(old) => match (&mut self.as_mut().compare())(&new, &old) { + Ordering::Greater => *self.as_mut().max() = Some(new), + _ => *self.as_mut().max() = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(self.max), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b2..2ac4d70be 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -40,6 +40,7 @@ mod last; mod le; mod lt; mod map; +mod max_by; mod min_by; mod next; mod nth; @@ -68,6 +69,7 @@ use gt::GtFuture; use last::LastFuture; use le::LeFuture; use lt::LtFuture; +use max_by::MaxByFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; @@ -639,6 +641,45 @@ extension_trait! { MinByFuture::new(self, compare) } + #[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. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + + let max = s.clone().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, Some(3)); + + let max = s.max_by(|x, y| y.cmp(x)).await; + assert_eq!(max, Some(1)); + + let max = VecDeque::::new().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by( + self, + compare: F, + ) -> impl Future> [MaxByFuture] + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MaxByFuture::new(self, compare) + } + #[doc = r#" Returns the nth element of the stream. From 944e43d4bf1e228b9e9243c79a3a600d97855308 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 18:35:02 +0900 Subject: [PATCH 037/707] =?UTF-8?q?Remove=20Pin=20API=20related=20unsafe?= =?UTF-8?q?=20code=20by=20using=20pin-project-lite=20cra=E2=80=A6=20(#381)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/future/timeout.rs | 25 ++-- src/io/buf_read/lines.rs | 63 +++++----- src/io/buf_read/split.rs | 59 ++++----- src/io/buf_reader.rs | 141 +++++++++++---------- src/io/buf_writer.rs | 195 ++++++++++++++++-------------- src/io/copy.rs | 43 +++---- src/io/read/chain.rs | 89 +++++++------- src/io/read/take.rs | 54 +++++---- src/io/timeout.rs | 37 +++--- src/stream/from_fn.rs | 52 ++++---- src/stream/interval.rs | 8 +- src/stream/once.rs | 24 ++-- src/stream/repeat_with.rs | 51 ++++---- src/stream/stream/chain.rs | 32 ++--- src/stream/stream/cmp.rs | 58 ++++----- src/stream/stream/enumerate.rs | 29 +++-- src/stream/stream/filter.rs | 27 +++-- src/stream/stream/filter_map.rs | 29 +++-- src/stream/stream/fold.rs | 36 +++--- src/stream/stream/for_each.rs | 27 +++-- src/stream/stream/fuse.rs | 33 +++-- src/stream/stream/ge.rs | 27 +++-- src/stream/stream/gt.rs | 23 ++-- src/stream/stream/inspect.rs | 27 +++-- src/stream/stream/last.rs | 27 +++-- src/stream/stream/le.rs | 23 ++-- src/stream/stream/lt.rs | 23 ++-- src/stream/stream/map.rs | 29 +++-- src/stream/stream/merge.rs | 40 +++--- src/stream/stream/min_by.rs | 38 +++--- src/stream/stream/partial_cmp.rs | 58 ++++----- src/stream/stream/scan.rs | 27 +++-- src/stream/stream/skip.rs | 27 +++-- src/stream/stream/skip_while.rs | 29 +++-- src/stream/stream/step_by.rs | 32 ++--- src/stream/stream/take.rs | 33 +++-- src/stream/stream/take_while.rs | 27 +++-- src/stream/stream/try_fold.rs | 38 +++--- src/stream/stream/try_for_each.rs | 29 +++-- src/stream/stream/zip.rs | 35 +++--- src/task/block_on.rs | 14 +-- 42 files changed, 894 insertions(+), 825 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2741a19af..e353386db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ pin-utils = "0.1.0-alpha.4" slab = "0.4.2" kv-log-macro = "1.0.4" broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } +pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" diff --git a/src/future/timeout.rs b/src/future/timeout.rs index a8338fbaa..c745d7322 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::future::Future; use crate::task::{Context, Poll}; @@ -39,24 +40,24 @@ where f.await } -/// A future that times out after a duration of time. -struct TimeoutFuture { - future: F, - delay: Delay, -} - -impl TimeoutFuture { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(delay: Delay); +pin_project! { + /// A future that times out after a duration of time. + struct TimeoutFuture { + #[pin] + future: F, + #[pin] + delay: Delay, + } } impl Future for TimeoutFuture { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Ready(v) => Poll::Ready(Ok(v)), - Poll::Pending => match self.delay().poll(cx) { + Poll::Pending => match this.delay.poll(cx) { Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Pending => Poll::Pending, }, diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 6cb4a0769..c60529cd7 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -2,50 +2,55 @@ use std::mem; use std::pin::Pin; use std::str; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream of lines in a byte stream. -/// -/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Lines`]. -/// -/// [`lines`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html -#[derive(Debug)] -pub struct Lines { - pub(crate) reader: R, - pub(crate) buf: String, - pub(crate) bytes: Vec, - pub(crate) read: usize, +pin_project! { + /// A stream of lines in a byte stream. + /// + /// This stream is created by the [`lines`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Lines`]. + /// + /// [`lines`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html + #[derive(Debug)] + pub struct Lines { + #[pin] + pub(crate) reader: R, + pub(crate) buf: String, + pub(crate) bytes: Vec, + pub(crate) read: usize, + } } impl Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - bytes, - read, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::ready!(read_line_internal( + this.reader, + cx, + this.buf, + this.bytes, + this.read + ))?; + if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); + if this.buf.ends_with('\n') { + this.buf.pop(); + if this.buf.ends_with('\r') { + this.buf.pop(); } } - Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) + Poll::Ready(Some(Ok(mem::replace(this.buf, String::new())))) } } diff --git a/src/io/buf_read/split.rs b/src/io/buf_read/split.rs index aa3b6fb6c..229a99b3a 100644 --- a/src/io/buf_read/split.rs +++ b/src/io/buf_read/split.rs @@ -1,46 +1,51 @@ use std::mem; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. -/// -/// This stream is created by the [`split`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Split`]. -/// -/// [`split`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html -#[derive(Debug)] -pub struct Split { - pub(crate) reader: R, - pub(crate) buf: Vec, - pub(crate) read: usize, - pub(crate) delim: u8, +pin_project! { + /// A stream over the contents of an instance of [`BufRead`] split on a particular byte. + /// + /// This stream is created by the [`split`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Split`]. + /// + /// [`split`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html + #[derive(Debug)] + pub struct Split { + #[pin] + pub(crate) reader: R, + pub(crate) buf: Vec, + pub(crate) read: usize, + pub(crate) delim: u8, + } } impl Stream for Split { type Item = io::Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - read, - delim, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::ready!(read_until_internal( + this.reader, + cx, + *this.delim, + this.buf, + this.read + ))?; + if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } - if buf[buf.len() - 1] == *delim { - buf.pop(); + if this.buf[this.buf.len() - 1] == *this.delim { + this.buf.pop(); } - Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) + Poll::Ready(Some(Ok(mem::replace(this.buf, vec![])))) } } diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 13cb4cc56..1d00b526c 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,51 +2,56 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// Adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` -/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer -/// of the incoming byte stream. -/// -/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to -/// the same file or network socket. It does not help when reading very large amounts at once, or -/// reading just one or a few times. It also provides no advantage when reading from a source that -/// is already in memory, like a `Vec`. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating -/// multiple instances of a `BufReader` on the same stream can cause data loss. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`Read`]: trait.Read.html -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// -/// # 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 line = String::new(); -/// file.read_line(&mut line).await?; -/// # -/// # Ok(()) }) } -/// ``` -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, +pin_project! { + /// Adds buffering to any reader. + /// + /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` + /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer + /// of the incoming byte stream. + /// + /// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to + /// the same file or network socket. It does not help when reading very large amounts at once, or + /// reading just one or a few times. It also provides no advantage when reading from a source that + /// is already in memory, like a `Vec`. + /// + /// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating + /// multiple instances of a `BufReader` on the same stream can cause data loss. + /// + /// This type is an async version of [`std::io::BufReader`]. + /// + /// [`Read`]: trait.Read.html + /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// + /// # 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 line = String::new(); + /// file.read_line(&mut line).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub struct BufReader { + #[pin] + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, + } } impl BufReader { @@ -95,10 +100,6 @@ impl BufReader { } impl BufReader { - pin_utils::unsafe_pinned!(inner: R); - pin_utils::unsafe_unpinned!(pos: usize); - pin_utils::unsafe_unpinned!(cap: usize); - /// Gets a reference to the underlying reader. /// /// It is inadvisable to directly read from the underlying reader. @@ -141,6 +142,13 @@ impl BufReader { &mut self.inner } + /// Gets a pinned mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { + self.project().inner + } + /// Returns a reference to the internal buffer. /// /// This function will not attempt to fill the buffer if it is empty. @@ -185,9 +193,10 @@ impl BufReader { /// Invalidates all data in the internal buffer. #[inline] - fn discard_buffer(mut self: Pin<&mut Self>) { - *self.as_mut().pos() = 0; - *self.cap() = 0; + fn discard_buffer(self: Pin<&mut Self>) { + let this = self.project(); + *this.pos = 0; + *this.cap = 0; } } @@ -201,7 +210,7 @@ impl Read for BufReader { // (larger than our internal buffer), bypass our internal buffer // entirely. if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); + let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -218,7 +227,8 @@ impl Read for BufReader { ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.pos == self.cap && total_len >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + let res = + futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -234,28 +244,23 @@ impl BufRead for BufReader { self: Pin<&'a mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let Self { - inner, - buf, - cap, - pos, - } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + let mut this = self.project(); // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. - if *pos >= *cap { - debug_assert!(*pos == *cap); - *cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; - *pos = 0; + if *this.pos >= *this.cap { + debug_assert!(*this.pos == *this.cap); + *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?; + *this.pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&this.buf[*this.pos..*this.cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - *self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + *this.pos = cmp::min(*this.pos + amt, *this.cap); } } @@ -305,24 +310,26 @@ impl Seek for BufReader { if let Some(offset) = n.checked_sub(remainder) { result = futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(offset)) )?; } else { // seek backwards by our remainder, and then by the offset futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(-remainder)) )?; self.as_mut().discard_buffer(); result = futures_core::ready!( - self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) + self.as_mut() + .get_pin_mut() + .poll_seek(cx, SeekFrom::Current(n)) )?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index f12aacbc7..6327ca71e 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -2,6 +2,7 @@ use std::fmt; use std::pin::Pin; use futures_core::ready; +use pin_project_lite::pin_project; use crate::io::write::WriteExt; use crate::io::{self, Seek, SeekFrom, Write}; @@ -9,88 +10,88 @@ use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A -/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying -/// writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a `Vec`. -/// -/// When the `BufWriter` is dropped, the contents of its buffer will be written -/// out. However, any errors that happen in the process of flushing the buffer -/// when the writer is dropped will be ignored. Code that wishes to handle such -/// errors must manually call [`flush`] before the writer is dropped. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// -/// # Examples -/// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::net::TcpStream; -/// use async_std::prelude::*; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; -/// -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; -/// } -/// # -/// # Ok(()) }) } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::io::BufWriter; -/// use async_std::net::TcpStream; -/// use async_std::prelude::*; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; -/// }; -/// # -/// # Ok(()) }) } -/// ``` -/// -/// 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. -/// -/// [`Write`]: trait.Write.html -/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write -/// [`TcpStream`]: ../net/struct.TcpStream.html -/// [`flush`]: trait.Write.html#tymethod.flush -pub struct BufWriter { - inner: W, - buf: Vec, - written: usize, +pin_project! { + /// Wraps a writer and buffers its output. + /// + /// It can be excessively inefficient to work directly with something that + /// implements [`Write`]. For example, every call to + /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A + /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying + /// writer in large, infrequent batches. + /// + /// `BufWriter` can improve the speed of programs that make *small* and + /// *repeated* write calls to the same file or network socket. It does not + /// help when writing very large amounts at once, or writing just one or a few + /// times. It also provides no advantage when writing to a destination that is + /// in memory, like a `Vec`. + /// + /// When the `BufWriter` is dropped, the contents of its buffer will be written + /// out. However, any errors that happen in the process of flushing the buffer + /// when the writer is dropped will be ignored. Code that wishes to handle such + /// errors must manually call [`flush`] before the writer is dropped. + /// + /// This type is an async version of [`std::io::BufReader`]. + /// + /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// + /// # Examples + /// + /// Let's write the numbers one through ten to a [`TcpStream`]: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; + /// + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// } + /// # + /// # Ok(()) }) } + /// ``` + /// + /// Because we're not buffering, we write each one in turn, incurring the + /// overhead of a system call per byte written. We can fix this with a + /// `BufWriter`: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::io::BufWriter; + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// }; + /// # + /// # Ok(()) }) } + /// ``` + /// + /// 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. + /// + /// [`Write`]: trait.Write.html + /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write + /// [`TcpStream`]: ../net/struct.TcpStream.html + /// [`flush`]: trait.Write.html#tymethod.flush + pub struct BufWriter { + #[pin] + inner: W, + buf: Vec, + written: usize, + } } #[derive(Debug)] pub struct IntoInnerError(W, std::io::Error); impl BufWriter { - pin_utils::unsafe_pinned!(inner: W); - pin_utils::unsafe_unpinned!(buf: Vec); - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. /// @@ -178,6 +179,13 @@ impl BufWriter { &mut self.inner } + /// Gets a pinned mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { + self.project().inner + } + /// Consumes BufWriter, returning the underlying writer /// /// This method will not write leftover data, it will be lost. @@ -234,16 +242,15 @@ impl BufWriter { /// /// [`LineWriter`]: struct.LineWriter.html fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - inner, - buf, - written, - } = unsafe { Pin::get_unchecked_mut(self) }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; - let len = buf.len(); + let mut this = self.project(); + let len = this.buf.len(); let mut ret = Ok(()); - while *written < len { - match inner.as_mut().poll_write(cx, &buf[*written..]) { + while *this.written < len { + match this + .inner + .as_mut() + .poll_write(cx, &this.buf[*this.written..]) + { Poll::Ready(Ok(0)) => { ret = Err(io::Error::new( io::ErrorKind::WriteZero, @@ -251,7 +258,7 @@ impl BufWriter { )); break; } - Poll::Ready(Ok(n)) => *written += n, + Poll::Ready(Ok(n)) => *this.written += n, Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {} Poll::Ready(Err(e)) => { ret = Err(e); @@ -260,10 +267,10 @@ impl BufWriter { Poll::Pending => return Poll::Pending, } } - if *written > 0 { - buf.drain(..*written); + if *this.written > 0 { + this.buf.drain(..*this.written); } - *written = 0; + *this.written = 0; Poll::Ready(ret) } } @@ -278,20 +285,20 @@ impl Write for BufWriter { ready!(self.as_mut().poll_flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.get_pin_mut().poll_write(cx, buf) } else { - Pin::new(&mut *self.buf()).poll_write(cx, buf) + Pin::new(&mut *self.project().buf).poll_write(cx, buf) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_flush(cx) + self.get_pin_mut().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_close(cx) + self.get_pin_mut().poll_close(cx) } } @@ -314,6 +321,6 @@ impl Seek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.get_pin_mut().poll_seek(cx, pos) } } diff --git a/src/io/copy.rs b/src/io/copy.rs index 3840d2af9..098df8d70 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; @@ -46,47 +48,38 @@ where R: Read + Unpin + ?Sized, W: Write + Unpin + ?Sized, { - pub struct CopyFuture<'a, R, W: ?Sized> { - reader: R, - writer: &'a mut W, - amt: u64, - } - - impl CopyFuture<'_, R, W> { - fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { - unsafe { - let this = self.get_unchecked_mut(); - ( - Pin::new_unchecked(&mut this.reader), - Pin::new(&mut *this.writer), - &mut this.amt, - ) - } + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, } } - impl Future for CopyFuture<'_, R, W> + impl Future for CopyFuture where R: BufRead, - W: Write + Unpin + ?Sized, + W: Write + Unpin, { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut reader, mut writer, amt) = self.project(); + let mut this = self.project(); loop { - let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; if buffer.is_empty() { - futures_core::ready!(writer.as_mut().poll_flush(cx))?; - return Poll::Ready(Ok(*amt)); + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); } - let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } - *amt += i as u64; - reader.as_mut().consume(i); + *this.amt += i as u64; + this.reader.as_mut().consume(i); } } } diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 09517ccad..335cac255 100644 --- a/src/io/read/chain.rs +++ b/src/io/read/chain.rs @@ -1,20 +1,25 @@ -use crate::io::IoSliceMut; use std::fmt; use std::pin::Pin; -use crate::io::{self, BufRead, Read}; +use pin_project_lite::pin_project; + +use crate::io::{self, BufRead, IoSliceMut, Read}; use crate::task::{Context, Poll}; -/// Adaptor to chain together two readers. -/// -/// This struct is generally created by calling [`chain`] on a reader. -/// Please see the documentation of [`chain`] for more details. -/// -/// [`chain`]: trait.Read.html#method.chain -pub struct Chain { - pub(crate) first: T, - pub(crate) second: U, - pub(crate) done_first: bool, +pin_project! { + /// Adaptor to chain together two readers. + /// + /// This struct is generally created by calling [`chain`] on a reader. + /// Please see the documentation of [`chain`] for more details. + /// + /// [`chain`]: trait.Read.html#method.chain + pub struct Chain { + #[pin] + pub(crate) first: T, + #[pin] + pub(crate) second: U, + pub(crate) done_first: bool, + } } impl Chain { @@ -98,76 +103,64 @@ impl fmt::Debug for Chain { } } -impl Read for Chain { +impl Read for Chain { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - let rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read(cx, buf)) { - Ok(0) if !buf.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read(cx, buf)) { + Ok(0) if !buf.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.poll_read(cx, buf) + this.second.poll_read(cx, buf) } fn poll_read_vectored( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - if !self.done_first { - let rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) { - Ok(0) if !bufs.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) { + Ok(0) if !bufs.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.poll_read_vectored(cx, bufs) + this.second.poll_read_vectored(cx, bufs) } } -impl BufRead for Chain { +impl BufRead for Chain { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - - if !*done_first { - let first = unsafe { Pin::new_unchecked(first) }; - match futures_core::ready!(first.poll_fill_buf(cx)) { + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_fill_buf(cx)) { Ok(buf) if buf.is_empty() => { - *done_first = true; + *this.done_first = true; } Ok(buf) => return Poll::Ready(Ok(buf)), Err(err) => return Poll::Ready(Err(err)), } } - let second = unsafe { Pin::new_unchecked(second) }; - second.poll_fill_buf(cx) + this.second.poll_fill_buf(cx) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - let rd = Pin::new(&mut self.first); - rd.consume(amt) + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + if !*this.done_first { + this.first.consume(amt) } else { - let rd = Pin::new(&mut self.second); - rd.consume(amt) + this.second.consume(amt) } } } diff --git a/src/io/read/take.rs b/src/io/read/take.rs index def4e2405..09b02c2fa 100644 --- a/src/io/read/take.rs +++ b/src/io/read/take.rs @@ -1,19 +1,24 @@ use std::cmp; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read}; use crate::task::{Context, Poll}; -/// Reader adaptor which limits the bytes read from an underlying reader. -/// -/// This struct is generally created by calling [`take`] on a reader. -/// Please see the documentation of [`take`] for more details. -/// -/// [`take`]: trait.Read.html#method.take -#[derive(Debug)] -pub struct Take { - pub(crate) inner: T, - pub(crate) limit: u64, +pin_project! { + /// Reader adaptor which limits the bytes read from an underlying reader. + /// + /// This struct is generally created by calling [`take`] on a reader. + /// Please see the documentation of [`take`] for more details. + /// + /// [`take`]: trait.Read.html#method.take + #[derive(Debug)] + pub struct Take { + #[pin] + pub(crate) inner: T, + pub(crate) limit: u64, + } } impl Take { @@ -152,15 +157,15 @@ impl Take { } } -impl Read for Take { +impl Read for Take { /// Attempt to read from the `AsyncRead` into `buf`. fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let Self { inner, limit } = &mut *self; - take_read_internal(Pin::new(inner), cx, buf, limit) + let this = self.project(); + take_read_internal(this.inner, cx, buf, this.limit) } } @@ -186,31 +191,30 @@ pub fn take_read_internal( } } -impl BufRead for Take { +impl BufRead for Take { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, limit } = unsafe { self.get_unchecked_mut() }; - let inner = unsafe { Pin::new_unchecked(inner) }; + let this = self.project(); - if *limit == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(&[])); } - match futures_core::ready!(inner.poll_fill_buf(cx)) { + match futures_core::ready!(this.inner.poll_fill_buf(cx)) { Ok(buf) => { - let cap = cmp::min(buf.len() as u64, *limit) as usize; + let cap = cmp::min(buf.len() as u64, *this.limit) as usize; Poll::Ready(Ok(&buf[..cap])) } Err(e) => Poll::Ready(Err(e)), } } - fn consume(mut self: Pin<&mut Self>, amt: usize) { + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; + let amt = cmp::min(amt as u64, *this.limit) as usize; + *this.limit -= amt as u64; - let rd = Pin::new(&mut self.inner); - rd.consume(amt); + this.inner.consume(amt); } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 9fcc15efc..8ef844d9f 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -3,7 +3,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use futures_timer::Delay; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; use crate::future::Future; use crate::io; @@ -43,22 +43,18 @@ where .await } -/// Future returned by the `FutureExt::timeout` method. -#[derive(Debug)] -pub struct Timeout -where - F: Future>, -{ - future: F, - timeout: Delay, -} - -impl Timeout -where - F: Future>, -{ - unsafe_pinned!(future: F); - unsafe_pinned!(timeout: Delay); +pin_project! { + /// Future returned by the `FutureExt::timeout` method. + #[derive(Debug)] + pub struct Timeout + where + F: Future>, + { + #[pin] + future: F, + #[pin] + timeout: Delay, + } } impl Future for Timeout @@ -67,13 +63,14 @@ where { type Output = io::Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Pending => {} other => return other, } - if self.timeout().poll(cx).is_ready() { + if this.timeout.poll(cx).is_ready() { let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); Poll::Ready(err) } else { diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index c1cb97af4..f53f3e5bd 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields elements by calling a closure. -/// -/// This stream is constructed by [`from_fn`] function. -/// -/// [`from_fn`]: fn.from_fn.html -#[derive(Debug)] -pub struct FromFn { - f: F, - future: Option, - __t: PhantomData, +pin_project! { + /// A stream that yields elements by calling a closure. + /// + /// This stream is constructed by [`from_fn`] function. + /// + /// [`from_fn`]: fn.from_fn.html + #[derive(Debug)] + pub struct FromFn { + f: F, + #[pin] + future: Option, + __t: PhantomData, + } } /// Creates a new stream where to produce each new element a provided closure is called. @@ -68,11 +73,6 @@ where } } -impl FromFn { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for FromFn where F: FnMut() -> Fut, @@ -80,20 +80,18 @@ where { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - match &self.future { - Some(_) => { - let next = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); + if this.future.is_some() { + let next = + futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); + this.future.set(None); - return Poll::Ready(next); - } - None => { - let fut = (self.as_mut().f())(); - self.as_mut().future().set(Some(fut)); - } + return Poll::Ready(next); + } else { + let fut = (this.f)(); + this.future.set(Some(fut)); } } } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 043d30745..2f7fe9e3a 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -4,8 +4,6 @@ use std::time::{Duration, Instant}; use futures_core::future::Future; use futures_core::stream::Stream; -use pin_utils::unsafe_pinned; - use futures_timer::Delay; /// Creates a new stream that yields at a set interval. @@ -62,15 +60,11 @@ pub struct Interval { interval: Duration, } -impl Interval { - unsafe_pinned!(delay: Delay); -} - impl Stream for Interval { type Item = (); fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if Pin::new(&mut *self).delay().poll(cx).is_pending() { + if Pin::new(&mut self.delay).poll(cx).is_pending() { return Poll::Pending; } let when = Instant::now(); diff --git a/src/stream/once.rs b/src/stream/once.rs index be875e41a..ae90d639a 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -24,20 +26,22 @@ pub fn once(t: T) -> Once { Once { value: Some(t) } } -/// A stream that yields a single item. -/// -/// This stream is constructed by the [`once`] function. -/// -/// [`once`]: fn.once.html -#[derive(Debug)] -pub struct Once { - value: Option, +pin_project! { + /// A stream that yields a single item. + /// + /// This stream is constructed by the [`once`] function. + /// + /// [`once`]: fn.once.html + #[derive(Debug)] + pub struct Once { + value: Option, + } } impl Stream for Once { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(self.value.take()) + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.project().value.take()) } } diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index f38b323d1..fda30fedb 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that repeats elements of type `T` endlessly by applying a provided closure. -/// -/// This stream is constructed by the [`repeat_with`] function. -/// -/// [`repeat_with`]: fn.repeat_with.html -#[derive(Debug)] -pub struct RepeatWith { - f: F, - future: Option, - __a: PhantomData, +pin_project! { + /// A stream that repeats elements of type `T` endlessly by applying a provided closure. + /// + /// This stream is constructed by the [`repeat_with`] function. + /// + /// [`repeat_with`]: fn.repeat_with.html + #[derive(Debug)] + pub struct RepeatWith { + f: F, + #[pin] + future: Option, + __a: PhantomData, + } } /// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. @@ -69,11 +74,6 @@ where } } -impl RepeatWith { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for RepeatWith where F: FnMut() -> Fut, @@ -81,22 +81,19 @@ where { type Item = A; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - match &self.future { - Some(_) => { - let res = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + if this.future.is_some() { + let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); + this.future.set(None); - return Poll::Ready(Some(res)); - } - None => { - let fut = (self.as_mut().f())(); + return Poll::Ready(Some(res)); + } else { + let fut = (this.f)(); - self.as_mut().future().set(Some(fut)); - } + this.future.set(Some(fut)); } } } diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 2693382ee..df3161501 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,20 +1,23 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::prelude::*; use crate::task::{Context, Poll}; -/// Chains two streams one after another. -#[derive(Debug)] -pub struct Chain { - first: Fuse, - second: Fuse, +pin_project! { + /// Chains two streams one after another. + #[derive(Debug)] + pub struct Chain { + #[pin] + first: Fuse, + #[pin] + second: Fuse, + } } impl Chain { - pin_utils::unsafe_pinned!(first: Fuse); - pin_utils::unsafe_pinned!(second: Fuse); - pub(super) fn new(first: S, second: U) -> Self { Chain { first: first.fuse(), @@ -26,22 +29,23 @@ impl Chain { impl> Stream for Chain { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if !self.first.done { - let next = futures_core::ready!(self.as_mut().first().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if !this.first.done { + let next = futures_core::ready!(this.first.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if !self.second.done { - let next = futures_core::ready!(self.as_mut().second().poll_next(cx)); + if !this.second.done { + let next = futures_core::ready!(this.second.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if self.first.done && self.second.done { + if this.first.done && this.second.done { return Poll::Ready(None); } diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index fc7161ad8..df08e9db9 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another using `Ord`. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct CmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another using `Ord`. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct CmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl CmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { CmpFuture { l: l.fuse(), @@ -42,11 +43,12 @@ where { type Output = Ordering; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Ordering::Equal); @@ -57,30 +59,30 @@ where } // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + 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 { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.take().unwrap(); + let r_value = this.r_cache.take().unwrap(); let result = l_value.cmp(&r_value); if let Ordering::Equal = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index 7d5a3d68e..2a3afa877 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,19 +1,21 @@ -use crate::task::{Context, Poll}; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; +use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Enumerate { - stream: S, - i: usize, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Enumerate { + #[pin] + stream: S, + i: usize, + } } impl Enumerate { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(i: usize); - pub(super) fn new(stream: S) -> Self { Enumerate { stream, i: 0 } } @@ -25,13 +27,14 @@ where { type Item = (usize, S::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(v) => { - let ret = (self.i, v); - *self.as_mut().i() += 1; + let ret = (*this.i, v); + *this.i += 1; Poll::Ready(Some(ret)) } None => Poll::Ready(None), diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 8ed282cec..eb4153f76 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to filter elements of another stream with a predicate. -#[derive(Debug)] -pub struct Filter { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream to filter elements of another stream with a predicate. + #[derive(Debug)] + pub struct Filter { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl Filter { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { Filter { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), Some(_) => { cx.waker().wake_by_ref(); Poll::Pending diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 756efff18..6a4593f97 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -2,21 +2,23 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; +use pin_project_lite::pin_project; + use crate::stream::Stream; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FilterMap { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FilterMap { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl FilterMap { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { FilterMap { stream, @@ -34,10 +36,11 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) => match (self.as_mut().f())(v) { + Some(v) => match (this.f)(v) { Some(b) => Poll::Ready(Some(b)), None => { cx.waker().wake_by_ref(); diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 18ddcd815..5b0eb124b 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + __t: PhantomData, + } } impl FoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - pub(super) fn new(stream: S, init: B, f: F) -> Self { FoldFuture { stream, @@ -36,17 +37,18 @@ where { type Output = B; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); - *self.as_mut().acc() = Some(new); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + *this.acc = Some(new); } - None => return Poll::Ready(self.as_mut().acc().take().unwrap()), + None => return Poll::Ready(this.acc.take().unwrap()), } } } diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 0406a5075..4696529be 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ForEachFuture { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct ForEachFuture { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl ForEachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { ForEachFuture { stream, @@ -33,12 +35,13 @@ where { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => (self.as_mut().f())(v), + Some(v) => (this.f)(v), None => return Poll::Ready(()), } } diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index ff5bdab1f..11629700f 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,33 +1,32 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A `Stream` that is permanently closed once a single call to `poll` results in -/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. -#[derive(Clone, Debug)] -pub struct Fuse { - pub(crate) stream: S, - pub(crate) done: bool, -} - -impl Unpin for Fuse {} - -impl Fuse { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(done: bool); +pin_project! { + /// A `Stream` that is permanently closed once a single call to `poll` results in + /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. + #[derive(Clone, Debug)] + pub struct Fuse { + #[pin] + pub(crate) stream: S, + pub(crate) done: bool, + } } impl Stream for Fuse { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.done { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if *this.done { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); if next.is_none() { - *self.as_mut().done() = true; + *this.done = true; } Poll::Ready(next) } diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index eb9786b5f..3dc6031c5 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { GeFuture { partial_cmp: l.partial_cmp(r), @@ -30,14 +33,14 @@ where impl Future for GeFuture where - L: Stream + Sized, - R: Stream + Sized, + L: Stream, + R: Stream, L::Item: PartialOrd, { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 6c480a25a..513ca764a 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { GtFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) => Poll::Ready(true), diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index e63b58492..5de60fb39 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that does something with each element of another stream. -#[derive(Debug)] -pub struct Inspect { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + /// A stream that does something with each element of another stream. + #[derive(Debug)] + pub struct Inspect { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl Inspect { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { Inspect { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); Poll::Ready(next.and_then(|x| { - (self.as_mut().f())(&x); + (this.f)(&x); Some(x) })) } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index c58dd66a9..eba01e5c2 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,20 +1,22 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LastFuture { - stream: S, - last: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LastFuture { + #[pin] + stream: S, + last: Option, + } } impl LastFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(last: Option); - pub(crate) fn new(stream: S) -> Self { LastFuture { stream, last: None } } @@ -27,16 +29,17 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - *self.as_mut().last() = Some(new); + *this.last = Some(new); Poll::Pending } - None => Poll::Ready(self.last), + None => Poll::Ready(*this.last), } } } diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 37b62d831..af7270056 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// Determines if the elements of this `Stream` are lexicographically -/// less or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + /// Determines if the elements of this `Stream` are lexicographically + /// less or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { LeFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index b774d7b43..524f26893 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// less than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // less than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { LtFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) => Poll::Ready(true), diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 4bc2e366a..a1fafc30f 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Map { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Map { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl Map { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { Map { stream, @@ -34,8 +36,9 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - Poll::Ready(next.map(self.as_mut().f())) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.map(this.f)) } } diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 9889dc70a..3ccc223d6 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -2,22 +2,25 @@ use std::pin::Pin; use std::task::{Context, Poll}; use futures_core::Stream; +use pin_project_lite::pin_project; -/// A stream that merges two other streams into a single stream. -/// -/// This stream is returned by [`Stream::merge`]. -/// -/// [`Stream::merge`]: trait.Stream.html#method.merge -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct Merge { - left: L, - right: R, +pin_project! { + /// A stream that merges two other streams into a single stream. + /// + /// This stream is returned by [`Stream::merge`]. + /// + /// [`Stream::merge`]: trait.Stream.html#method.merge + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[derive(Debug)] + pub struct Merge { + #[pin] + left: L, + #[pin] + right: R, + } } -impl Unpin for Merge {} - impl Merge { pub(crate) fn new(left: L, right: R) -> Self { Self { left, right } @@ -26,19 +29,20 @@ impl Merge { impl Stream for Merge where - L: Stream + Unpin, - R: Stream + Unpin, + L: Stream, + R: Stream, { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Poll::Ready(Some(item)) = Pin::new(&mut self.left).poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if let Poll::Ready(Some(item)) = this.left.poll_next(cx) { // The first stream made progress. The Merge needs to be polled // again to check the progress of the second stream. cx.waker().wake_by_ref(); Poll::Ready(Some(item)) } else { - Pin::new(&mut self.right).poll_next(cx) + this.right.poll_next(cx) } } } diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index a68cf3130..ab12aa05b 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,23 +1,24 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct MinByFuture { - stream: S, - compare: F, - min: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinByFuture { + #[pin] + stream: S, + compare: F, + min: Option, + } } impl MinByFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(compare: F); - pin_utils::unsafe_unpinned!(min: Option); - pub(super) fn new(stream: S, compare: F) -> Self { MinByFuture { stream, @@ -35,22 +36,23 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - match self.as_mut().min().take() { - None => *self.as_mut().min() = Some(new), - Some(old) => match (&mut self.as_mut().compare())(&new, &old) { - Ordering::Less => *self.as_mut().min() = Some(new), - _ => *self.as_mut().min() = Some(old), + match this.min.take() { + None => *this.min = Some(new), + Some(old) => match (this.compare)(&new, &old) { + Ordering::Less => *this.min = Some(new), + _ => *this.min = Some(old), }, } Poll::Pending } - None => Poll::Ready(self.min), + None => Poll::Ready(*this.min), } } } diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index fac2705dc..e30c6ea8d 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct PartialCmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct PartialCmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl PartialCmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { PartialCmpFuture { l: l.fuse(), @@ -42,12 +43,13 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { // Short circuit logic // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Some(Ordering::Equal)); @@ -58,30 +60,30 @@ where } // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + 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 { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.as_mut().take().unwrap(); + let r_value = this.r_cache.as_mut().take().unwrap(); let result = l_value.partial_cmp(&r_value); if let Some(Ordering::Equal) = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 78975161b..c4771d851 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,13 +1,18 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to maintain state while polling another stream. -#[derive(Debug)] -pub struct Scan { - stream: S, - state_f: (St, F), +pin_project! { + /// A stream to maintain state while polling another stream. + #[derive(Debug)] + pub struct Scan { + #[pin] + stream: S, + state_f: (St, F), + } } impl Scan { @@ -17,13 +22,8 @@ impl Scan { state_f: (initial_state, f), } } - - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(state_f: (St, F)); } -impl Unpin for Scan {} - impl Stream for Scan where S: Stream, @@ -31,11 +31,12 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let poll_result = self.as_mut().stream().poll_next(cx); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let poll_result = this.stream.as_mut().poll_next(cx); poll_result.map(|item| { item.and_then(|item| { - let (state, f) = self.as_mut().state_f(); + let (state, f) = this.state_f; f(state, item) }) }) diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index 8a2d966d8..6562b99fd 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -1,19 +1,21 @@ use std::pin::Pin; use std::task::{Context, Poll}; +use pin_project_lite::pin_project; + use crate::stream::Stream; -/// A stream to skip first n elements of another stream. -#[derive(Debug)] -pub struct Skip { - stream: S, - n: usize, +pin_project! { + /// A stream to skip first n elements of another stream. + #[derive(Debug)] + pub struct Skip { + #[pin] + stream: S, + n: usize, + } } impl Skip { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(n: usize); - pub(crate) fn new(stream: S, n: usize) -> Self { Skip { stream, n } } @@ -25,14 +27,15 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.n { + Some(v) => match *this.n { 0 => return Poll::Ready(Some(v)), - _ => *self.as_mut().n() -= 1, + _ => *this.n -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index b1a8d9eaa..0499df23c 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to skip elements of another stream based on a predicate. -#[derive(Debug)] -pub struct SkipWhile { - stream: S, - predicate: Option

, - __t: PhantomData, +pin_project! { + /// A stream to skip elements of another stream based on a predicate. + #[derive(Debug)] + pub struct SkipWhile { + #[pin] + stream: S, + predicate: Option

, + __t: PhantomData, + } } impl SkipWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: Option

); - pub(crate) fn new(stream: S, predicate: P) -> Self { SkipWhile { stream, @@ -32,15 +34,16 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.as_mut().predicate() { + Some(v) => match this.predicate { Some(p) => { if !p(&v) { - *self.as_mut().predicate() = None; + *this.predicate = None; return Poll::Ready(Some(v)); } } diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index f84feecd3..ab9e45b65 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -1,21 +1,22 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that steps a given amount of elements of another stream. -#[derive(Debug)] -pub struct StepBy { - stream: S, - step: usize, - i: usize, +pin_project! { + /// A stream that steps a given amount of elements of another stream. + #[derive(Debug)] + pub struct StepBy { + #[pin] + stream: S, + step: usize, + i: usize, + } } impl StepBy { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(step: usize); - pin_utils::unsafe_unpinned!(i: usize); - pub(crate) fn new(stream: S, step: usize) -> Self { StepBy { stream, @@ -31,17 +32,18 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.i { + Some(v) => match this.i { 0 => { - *self.as_mut().i() = self.step; + *this.i = *this.step; return Poll::Ready(Some(v)); } - _ => *self.as_mut().i() -= 1, + _ => *this.i -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 81d48d23e..835bc447a 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,33 +1,32 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields the first `n` items of another stream. -#[derive(Clone, Debug)] -pub struct Take { - pub(crate) stream: S, - pub(crate) remaining: usize, -} - -impl Unpin for Take {} - -impl Take { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(remaining: usize); +pin_project! { + /// A stream that yields the first `n` items of another stream. + #[derive(Clone, Debug)] + pub struct Take { + #[pin] + pub(crate) stream: S, + pub(crate) remaining: usize, + } } impl Stream for Take { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.remaining == 0 { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if *this.remaining == 0 { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + Some(_) => *this.remaining -= 1, + None => *this.remaining = 0, } Poll::Ready(next) } diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 6f3cc8f2d..bf89458b7 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields elements based on a predicate. -#[derive(Debug)] -pub struct TakeWhile { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream that yields elements based on a predicate. + #[derive(Debug)] + pub struct TakeWhile { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl TakeWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { TakeWhile { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), Some(_) => { cx.waker().wake_by_ref(); Poll::Pending diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index 212b05891..80392e108 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct TryFoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + __t: PhantomData, + } } impl TryFoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - pub(super) fn new(stream: S, init: T, f: F) -> Self { TryFoldFuture { stream, @@ -36,23 +37,22 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); match new { - Ok(o) => { - *self.as_mut().acc() = Some(o); - } + Ok(o) => *this.acc = Some(o), Err(e) => return Poll::Ready(Err(e)), } } - None => return Poll::Ready(Ok(self.as_mut().acc().take().unwrap())), + None => return Poll::Ready(Ok(this.acc.take().unwrap())), } } } diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index ae3d5ea5b..578b854c1 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,23 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct TryForEeachFuture { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryForEeachFuture { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl TryForEeachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { TryForEeachFuture { stream, @@ -36,14 +38,15 @@ where { type Output = Result<(), E>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match item { None => return Poll::Ready(Ok(())), Some(v) => { - let res = (self.as_mut().f())(v); + let res = (this.f)(v); if let Err(e) = res { return Poll::Ready(Err(e)); } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 4c66aefd0..9b7c299b3 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -1,14 +1,20 @@ use std::fmt; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// An iterator that iterates two other iterators simultaneously. -pub struct Zip { - item_slot: Option, - first: A, - second: B, +pin_project! { + /// An iterator that iterates two other iterators simultaneously. + pub struct Zip { + item_slot: Option, + #[pin] + first: A, + #[pin] + second: B, + } } impl fmt::Debug for Zip { @@ -20,8 +26,6 @@ impl fmt::Debug for Zip { } } -impl Unpin for Zip {} - impl Zip { pub(crate) fn new(first: A, second: B) -> Self { Zip { @@ -30,25 +34,22 @@ impl Zip { second, } } - - pin_utils::unsafe_unpinned!(item_slot: Option); - pin_utils::unsafe_pinned!(first: A); - pin_utils::unsafe_pinned!(second: B); } impl Stream for Zip { type Item = (A::Item, B::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.as_mut().item_slot().is_none() { - match self.as_mut().first().poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if this.item_slot.is_none() { + match this.first.poll_next(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(None) => return Poll::Ready(None), - Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item), + Poll::Ready(Some(item)) => *this.item_slot = Some(item), } } - let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx)); - let first_item = self.as_mut().item_slot().take().unwrap(); + let second_item = futures_core::ready!(this.second.poll_next(cx)); + let first_item = this.item_slot.take().unwrap(); Poll::Ready(second_item.map(|second_item| (first_item, second_item))) } } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 032bf02d5..b0adc3879 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -7,6 +7,7 @@ use std::task::{RawWaker, RawWakerVTable}; use std::thread; use crossbeam_utils::sync::Parker; +use pin_project_lite::pin_project; use super::task; use super::task_local; @@ -100,19 +101,18 @@ where } } -struct CatchUnwindFuture { - future: F, -} - -impl CatchUnwindFuture { - pin_utils::unsafe_pinned!(future: F); +pin_project! { + struct CatchUnwindFuture { + #[pin] + future: F, + } } impl Future for CatchUnwindFuture { type Output = thread::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + panic::catch_unwind(AssertUnwindSafe(|| self.project().future.poll(cx)))?.map(Ok) } } From 020eb85093d714b25015980ed0aeeb148ae25ec3 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 18:59:06 +0800 Subject: [PATCH 038/707] add stream::min_by_key method --- src/stream/stream/min_by_key.rs | 58 +++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 38 +++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/stream/stream/min_by_key.rs diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs new file mode 100644 index 000000000..2cc34a0d5 --- /dev/null +++ b/src/stream/stream/min_by_key.rs @@ -0,0 +1,58 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct MinByKeyFuture { + stream: S, + min: Option, + key_by: K, +} + +impl MinByKeyFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(min: Option); + pin_utils::unsafe_unpinned!(key_by: K); + + pub(super) fn new(stream: S, key_by: K) -> Self { + MinByKeyFuture { + stream, + min: None, + key_by, + } + } +} + +impl Future for MinByKeyFuture +where + S: Stream + Unpin + Sized, + K: FnMut(&S::Item) -> S::Item, + S::Item: Ord + Copy, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(new) => { + let new = self.as_mut().key_by()(&new); + cx.waker().wake_by_ref(); + match self.as_mut().min().take() { + None => *self.as_mut().min() = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Less => *self.as_mut().min() = Some(new), + _ => *self.as_mut().min() = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(self.min), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b2..56dda48a1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -53,6 +53,7 @@ mod take_while; mod try_fold; mod try_for_each; mod zip; +mod min_by_key; use all::AllFuture; use any::AnyFuture; @@ -74,6 +75,7 @@ use nth::NthFuture; use partial_cmp::PartialCmpFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEeachFuture; +use min_by_key::MinByKeyFuture; pub use chain::Chain; pub use filter::Filter; @@ -600,6 +602,42 @@ extension_trait! { FilterMap::new(self, f) } + #[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. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![1, 2, -3].into_iter().collect(); + + let min = s.clone().min_by_key(|x| x.abs()).await; + assert_eq!(min, Some(1)); + + let min = VecDeque::::new().min_by_key(|x| x.abs()).await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by_key( + self, + key_by: K, + ) -> impl Future> [MinByKeyFuture] + where + Self: Sized, + K: FnMut(&Self::Item) -> Self::Item, + { + MinByKeyFuture::new(self, key_by) + } + #[doc = r#" Returns the element that gives the minimum value with respect to the specified comparison function. If several elements are equally minimum, From d6f940110b2411ea06f2ed8b27f7d1b4e57bafe7 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 19:04:04 +0800 Subject: [PATCH 039/707] update doc --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2ac4d70be..cccd8b275 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -642,8 +642,8 @@ extension_trait! { } #[doc = r#" - Returns the element that gives the minimum value with respect to the - specified comparison function. If several elements are equally minimum, + 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. # Examples From f5a0a0ba86e93b0d1dd7f4b091fef814b5e30849 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 19:17:24 +0800 Subject: [PATCH 040/707] fmt --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 56dda48a1..b5011c27c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -41,6 +41,7 @@ mod le; mod lt; mod map; mod min_by; +mod min_by_key; mod next; mod nth; mod partial_cmp; @@ -53,7 +54,6 @@ mod take_while; mod try_fold; mod try_for_each; mod zip; -mod min_by_key; use all::AllFuture; use any::AnyFuture; @@ -70,12 +70,12 @@ use last::LastFuture; use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; +use min_by_key::MinByKeyFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEeachFuture; -use min_by_key::MinByKeyFuture; pub use chain::Chain; pub use filter::Filter; From 2abf5ca891b72bcab0afa3adcdc2b99ba2f59f85 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 20:20:59 +0900 Subject: [PATCH 041/707] Deny warnings on CI (#378) * Deny warnings on CI * Fix some clippy warnings --- .github/workflows/ci.yml | 7 +++++++ src/io/timeout.rs | 2 +- src/io/write/write_fmt.rs | 4 ++-- src/net/addr.rs | 2 +- src/path/path.rs | 2 +- src/path/pathbuf.rs | 4 ++-- src/stream/exact_size_stream.rs | 3 +-- src/stream/extend.rs | 4 ++-- src/stream/from_fn.rs | 5 ++--- src/stream/repeat_with.rs | 8 ++++---- src/sync/barrier.rs | 9 +++------ src/task/yield_now.rs | 4 ++-- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c622a59bf..565134dc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ jobs: build_and_test: name: Build and test runs-on: ${{ matrix.os }} + env: + RUSTFLAGS: -Dwarnings strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] @@ -46,6 +48,8 @@ jobs: check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest + env: + RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@master @@ -77,6 +81,9 @@ jobs: clippy_check: name: Clippy check runs-on: ubuntu-latest + # TODO: There is a lot of warnings + # env: + # RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@v1 - id: component diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 8ef844d9f..ec3668ea5 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -71,7 +71,7 @@ where } if this.timeout.poll(cx).is_ready() { - let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); + let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out")); Poll::Ready(err) } else { Poll::Pending diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index a1149cde3..ad3e94ade 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -32,7 +32,7 @@ impl Future for WriteFmtFuture<'_, T> { buffer, .. } = &mut *self; - let mut buffer = buffer.as_mut().unwrap(); + let buffer = buffer.as_mut().unwrap(); // Copy the data from the buffer into the writer until it's done. loop { @@ -40,7 +40,7 @@ impl Future for WriteFmtFuture<'_, T> { 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, &mut buffer))?; + let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } diff --git a/src/net/addr.rs b/src/net/addr.rs index 1dada64ce..519b1846e 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -210,7 +210,7 @@ impl ToSocketAddrs for str { impl Future, ToSocketAddrsFuture ) { - if let Some(addr) = self.parse().ok() { + if let Ok(addr) = self.parse() { return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); } diff --git a/src/path/path.rs b/src/path/path.rs index aa15ab225..22dab769d 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -799,7 +799,7 @@ impl AsRef for String { impl AsRef for std::path::PathBuf { fn as_ref(&self) -> &Path { - Path::new(self.into()) + Path::new(self) } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 64744e140..38018c9db 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -5,7 +5,7 @@ use crate::path::Path; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -206,7 +206,7 @@ impl From for PathBuf { impl Into for PathBuf { fn into(self) -> std::path::PathBuf { - self.inner.into() + self.inner } } diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 7d2e7cb96..32a1eb315 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -59,7 +59,7 @@ pub use crate::stream::Stream; /// # } /// # } /// # } -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// impl ExactSizeStream for Counter { /// // We can easily calculate the remaining number of iterations. @@ -74,7 +74,6 @@ pub use crate::stream::Stream; /// /// assert_eq!(5, counter.len()); /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/extend.rs b/src/stream/extend.rs index d9e148164..350418d87 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -14,7 +14,7 @@ use crate::stream::IntoStream; /// ## Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream::{self, Extend}; @@ -25,7 +25,7 @@ use crate::stream::IntoStream; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index f53f3e5bd..7fee8926d 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ pin_project! { /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::sync::Mutex; @@ -58,8 +58,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(3)); /// assert_eq!(s.next().await, None); /// # -/// # }) } -/// +/// # }) /// ``` pub fn from_fn(f: F) -> FromFn where diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index fda30fedb..15d4aa122 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -29,7 +29,7 @@ pin_project! { /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -42,13 +42,13 @@ pin_project! { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); -/// # }) } +/// # }) /// ``` /// /// Going finite: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -60,7 +60,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, None); -/// # }) } +/// # }) /// ``` pub fn repeat_with(repeater: F) -> RepeatWith where diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 080eff8c4..0163e3fc5 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -8,7 +8,7 @@ use crate::sync::Mutex; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -30,7 +30,6 @@ use crate::sync::Mutex; /// handle.await; /// } /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -120,7 +119,7 @@ impl Barrier { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -142,7 +141,6 @@ impl Barrier { /// handle.await; /// } /// # }); - /// # } /// ``` pub async fn wait(&self) -> BarrierWaitResult { let mut lock = self.state.lock().await; @@ -190,7 +188,7 @@ impl BarrierWaitResult { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::Barrier; /// @@ -198,7 +196,6 @@ impl BarrierWaitResult { /// let barrier_wait_result = barrier.wait().await; /// println!("{:?}", barrier_wait_result.is_leader()); /// # }); - /// # } /// ``` pub fn is_leader(&self) -> bool { self.0 diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 2a4788d74..c11408ab5 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -18,13 +18,13 @@ use std::pin::Pin; /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::task; /// /// task::yield_now().await; /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] From b2fe91385b08cbcd3046294decce9136df904d8c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 23 Oct 2019 17:02:03 +0100 Subject: [PATCH 042/707] Add channel behind unstable feature flag (#380) * Add channel behind unstable feature flag * Don't check tests without unstable feature flag * Fix typos * Remove useless attribute --- .github/workflows/ci.yml | 2 +- Cargo.toml | 1 + src/sync/channel.rs | 1132 ++++++++++++++++++++++++++++++++++++++ src/sync/mod.rs | 3 + tests/channel.rs | 350 ++++++++++++ 5 files changed, 1487 insertions(+), 1 deletion(-) create mode 100644 src/sync/channel.rs create mode 100644 tests/channel.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 565134dc1..d47eabce3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --benches --bins --examples --tests + args: --all --bins --examples - name: check unstable uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index e353386db..7aaba6874 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" +rand = "0.7.2" # surf = "1.0.2" tempdir = "0.3.7" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } diff --git a/src/sync/channel.rs b/src/sync/channel.rs new file mode 100644 index 000000000..6751417af --- /dev/null +++ b/src/sync/channel.rs @@ -0,0 +1,1132 @@ +use std::cell::UnsafeCell; +use std::fmt; +use std::future::Future; +use std::isize; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::pin::Pin; +use std::process; +use std::ptr; +use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll, Waker}; + +use crossbeam_utils::{Backoff, CachePadded}; +use futures_core::stream::Stream; +use slab::Slab; + +/// 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 `None` instead of +/// blocking. +/// +/// # Panics +/// +/// If `cap` is zero, this function will panic. +/// +/// # Examples +/// +/// ``` +/// # 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(1).await; +/// +/// task::spawn(async move { +/// // This call blocks the current task 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, Some(1)); +/// assert_eq!(r.recv().await, Some(2)); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +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. +/// +/// # Examples +/// +/// ``` +/// # 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)))] +pub struct Sender { + /// The inner channel. + channel: Arc>, +} + +impl Sender { + /// Sends a message into the channel. + /// + /// If the channel is full, this method will block the current task until the send operation + /// can proceed. + /// + /// # Examples + /// + /// ``` + /// # 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, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn send(&self, msg: T) { + struct SendFuture<'a, T> { + sender: &'a Sender, + 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 { + let msg = self.msg.take().unwrap(); + + // Try sending the message. + let poll = match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + Err(PushError::Full(msg)) => { + // Register the current task. + match self.opt_key { + None => self.opt_key = Some(self.sender.channel.sends.register(cx)), + Some(key) => self.sender.channel.sends.reregister(key, cx), + } + + // Try sending the message again. + match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) | Err(PushError::Full(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key.take() { + // `true` means the send operation is completed. + self.sender.channel.sends.unregister(key, true); + } + } + + poll + } + } + + impl Drop for SendFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the send operation is cancelled. + self.sender.channel.sends.unregister(key, false); + } + } + } + + SendFuture { + sender: self, + msg: Some(msg), + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// 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 + /// + /// ``` + /// # 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 + /// + /// ``` + /// # 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 + /// + /// ``` + /// # 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 implements the [`Stream`] trait, which means it can act as an asynchronous iterator. +/// +/// [`Stream`]: ../stream/trait.Stream.html +/// +/// # Examples +/// +/// ``` +/// # 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(1).await; +/// task::sleep(Duration::from_secs(1)).await; +/// s.send(2).await; +/// }); +/// +/// assert_eq!(r.recv().await, Some(1)); // Received immediately. +/// assert_eq!(r.recv().await, Some(2)); // Received after 1 second. +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub struct Receiver { + /// The inner channel. + channel: Arc>, + + /// The registration key for this receiver in the `channel.streams` registry. + opt_key: Option, +} + +impl Receiver { + /// Receives a message from the channel. + /// + /// If the channel is empty and it still has senders, this method will block the current task + /// until the receive operation can proceed. If the channel is empty and there are no more + /// senders, this method returns `None`. + /// + /// # Examples + /// + /// ``` + /// # 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, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn recv(&self) -> Option { + struct RecvFuture<'a, T> { + channel: &'a Channel, + opt_key: Option, + } + + impl Future for RecvFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + poll_recv(&self.channel, &self.channel.recvs, &mut self.opt_key, cx) + } + } + + impl Drop for RecvFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the receive operation is cancelled. + self.channel.recvs.unregister(key, false); + } + } + } + + RecvFuture { + channel: &self.channel, + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// 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 + /// + /// ``` + /// # 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 + /// + /// ``` + /// # 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 + /// + /// ``` + /// # 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 was registered as blocked on this stream, unregister now. + if let Some(key) = self.opt_key { + // `false` means the last request for a stream item is cancelled. + self.channel.streams.unregister(key, false); + } + + // 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; + poll_recv(&this.channel, &this.channel.streams, &mut this.opt_key, cx) + } +} + +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 registered in `registry` and its +/// registration key will then be stored in `opt_key`. +fn poll_recv( + channel: &Channel, + registry: &Registry, + opt_key: &mut Option, + cx: &mut Context<'_>, +) -> Poll> { + // Try receiving a message. + let poll = match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => { + // Register the current task. + match *opt_key { + None => *opt_key = Some(registry.register(cx)), + Some(key) => registry.reregister(key, cx), + } + + // Try receiving a message again. + match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => Poll::Pending, + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = opt_key.take() { + // `true` means the receive operation is completed. + registry.unregister(key, true); + } + } + + poll +} + +/// 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: CachePadded, + + /// 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: CachePadded, + + /// 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. + sends: Registry, + + /// Receive operations waiting while the channel is empty and not disconnected. + recvs: Registry, + + /// Streams waiting while the channel is empty and not disconnected. + streams: Registry, + + /// 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: CachePadded::new(AtomicUsize::new(head)), + tail: CachePadded::new(AtomicUsize::new(tail)), + sends: Registry::new(), + recvs: Registry::new(), + streams: Registry::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + _marker: PhantomData, + } + } + + /// Attempts to push a message. + fn push(&self, msg: T) -> Result<(), PushError> { + let backoff = Backoff::new(); + let mut tail = self.tail.load(Ordering::Relaxed); + + loop { + // 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, + new_tail, + 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.recvs.notify_one(); + + // Wake all blocked streams. + self.streams.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 tail & self.mark_bit != 0 { + return Err(PushError::Disconnected(msg)); + } else { + return Err(PushError::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 pop a message. + fn pop(&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.sends.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(PopError::Disconnected); + } else { + // Otherwise, the receive operation is not ready. + return Err(PopError::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 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.sends.notify_all(); + self.recvs.notify_all(); + self.streams.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 `push()` method. +enum PushError { + /// The channel is full but not disconnected. + Full(T), + + /// The channel is full and disconnected. + Disconnected(T), +} + +/// An error returned from the `pop()` method. +enum PopError { + /// The channel is empty but not disconnected. + Empty, + + /// The channel is empty and disconnected. + Disconnected, +} + +/// A list of blocked channel operations. +struct Blocked { + /// A list of registered channel operations. + /// + /// Each entry has a waker associated with the task that is executing the operation. If the + /// waker is set to `None`, that means the task has been woken up but hasn't removed itself + /// from the registry yet. + entries: Slab>, + + /// The number of wakers in the entry list. + waker_count: usize, +} + +/// A registry of blocked channel operations. +/// +/// Blocked operations register themselves in a registry. Successful operations on the opposite +/// side of the channel wake blocked operations in the registry. +struct Registry { + /// A list of blocked channel operations. + blocked: Spinlock, + + /// Set to `true` if there are no wakers in the registry. + /// + /// Note that this either means there are no entries in the registry, or that all entries have + /// been notified. + is_empty: AtomicBool, +} + +impl Registry { + /// Creates a new registry. + fn new() -> Registry { + Registry { + blocked: Spinlock::new(Blocked { + entries: Slab::new(), + waker_count: 0, + }), + is_empty: AtomicBool::new(true), + } + } + + /// Registers a blocked channel operation and returns a key associated with it. + fn register(&self, cx: &Context<'_>) -> usize { + let mut blocked = self.blocked.lock(); + + // Insert a new entry into the list of blocked tasks. + let w = cx.waker().clone(); + let key = blocked.entries.insert(Some(w)); + + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + + key + } + + /// Re-registers a blocked channel operation by filling in its waker. + fn reregister(&self, key: usize, cx: &Context<'_>) { + let mut blocked = self.blocked.lock(); + + let was_none = blocked.entries[key].is_none(); + let w = cx.waker().clone(); + blocked.entries[key] = Some(w); + + if was_none { + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + } + } + + /// Unregisters a channel operation. + /// + /// If `completed` is `true`, the operation will be removed from the registry. If `completed` + /// is `false`, that means the operation was cancelled so another one will be notified. + fn unregister(&self, key: usize, completed: bool) { + let mut blocked = self.blocked.lock(); + let mut removed = false; + + match blocked.entries.remove(key) { + Some(_) => removed = true, + None => { + if !completed { + // This operation was cancelled. Notify another one. + if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { + if let Some(w) = opt_waker.take() { + w.wake(); + removed = true; + } + } + } + } + } + + if removed { + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + + /// Notifies one blocked channel operation. + #[inline] + fn notify_one(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + } + } + + /// Notifies all blocked channel operations. + #[inline] + fn notify_all(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + for (_, opt_waker) in blocked.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + } + } + + blocked.waker_count = 0; + self.is_empty.store(true, Ordering::SeqCst); + } + } +} + +/// A simple spinlock. +struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, +} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } +} + +/// A guard holding a spinlock locked. +struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +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/sync/mod.rs b/src/sync/mod.rs index be74d8f7b..3ad2776ca 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -40,5 +40,8 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; + pub use channel::{channel, Sender, Receiver}; + mod barrier; + mod channel; } diff --git a/tests/channel.rs b/tests/channel.rs new file mode 100644 index 000000000..91622b0d0 --- /dev/null +++ b/tests/channel.rs @@ -0,0 +1,350 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use async_std::sync::channel; +use async_std::task; +use rand::{thread_rng, Rng}; + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + task::block_on(async { + let (s, r) = channel(1); + + s.send(7).await; + assert_eq!(r.recv().await, Some(7)); + + s.send(8).await; + assert_eq!(r.recv().await, Some(8)); + + drop(s); + assert_eq!(r.recv().await, None); + }) +} + +#[test] +fn capacity() { + for i in 1..10 { + let (s, r) = channel::<()>(i); + assert_eq!(s.capacity(), i); + assert_eq!(r.capacity(), i); + } +} + +#[test] +fn len_empty_full() { + task::block_on(async { + let (s, r) = channel(2); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + s.send(()).await; + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + s.send(()).await; + + assert_eq!(s.len(), 2); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), true); + assert_eq!(r.len(), 2); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), true); + + r.recv().await; + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + }) +} + +#[test] +fn recv() { + task::block_on(async { + let (s, r) = channel(100); + + task::spawn(async move { + assert_eq!(r.recv().await, Some(7)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(8)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(9)); + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1500)).await; + s.send(7).await; + s.send(8).await; + s.send(9).await; + }) +} + +#[test] +fn send() { + task::block_on(async { + let (s, r) = channel(1); + + task::spawn(async move { + s.send(7).await; + task::sleep(ms(1000)).await; + s.send(8).await; + task::sleep(ms(1000)).await; + s.send(9).await; + task::sleep(ms(1000)).await; + s.send(10).await; + }); + + 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)); + }) +} + +#[test] +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; + + 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); + }) +} + +#[test] +fn len() { + const COUNT: usize = 25_000; + const CAP: usize = 1000; + + task::block_on(async { + let (s, r) = channel(CAP); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for _ in 0..CAP / 10 { + for i in 0..50 { + s.send(i).await; + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + r.recv().await; + assert_eq!(r.len(), 50 - i - 1); + } + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..CAP { + s.send(i).await; + assert_eq!(s.len(), i + 1); + } + + for _ in 0..CAP { + r.recv().await.unwrap(); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + let child = task::spawn({ + let r = r.clone(); + async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + let len = r.len(); + assert!(len <= CAP); + } + } + }); + + for i in 0..COUNT { + s.send(i).await; + let len = s.len(); + assert!(len <= CAP); + } + + child.await; + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + }) +} + +#[test] +fn disconnect_wakes_receiver() { + task::block_on(async { + let (s, r) = channel::<()>(1); + + let child = task::spawn(async move { + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1000)).await; + drop(s); + + child.await; + }) +} + +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + task::block_on(async { + let (s, r) = channel(3); + + let child = task::spawn(async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + } + assert_eq!(r.recv().await, None); + }); + + for i in 0..COUNT { + s.send(i).await; + } + drop(s); + + child.await; + }) +} + +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const TASKS: usize = 4; + + task::block_on(async { + let (s, r) = channel::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = Arc::new(v); + + let mut tasks = Vec::new(); + + for _ in 0..TASKS { + let r = r.clone(); + let v = v.clone(); + tasks.push(task::spawn(async move { + for _ in 0..COUNT { + let n = r.recv().await.unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + })); + } + + for _ in 0..TASKS { + let s = s.clone(); + tasks.push(task::spawn(async move { + for i in 0..COUNT { + s.send(i).await; + } + })); + } + + for t in tasks { + t.await; + } + + for c in v.iter() { + assert_eq!(c.load(Ordering::SeqCst), TASKS); + } + }); +} + +#[test] +fn oneshot() { + const COUNT: usize = 10_000; + + task::block_on(async { + 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 }); + + c1.await; + c2.await; + } + }) +} + +#[test] +fn drops() { + const RUNS: usize = 100; + + static DROPS: AtomicUsize = AtomicUsize::new(0); + + #[derive(Debug, PartialEq)] + struct DropCounter; + + impl Drop for DropCounter { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut rng = thread_rng(); + + for _ in 0..RUNS { + task::block_on(async { + 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 r = r.clone(); + async move { + for _ in 0..steps { + r.recv().await.unwrap(); + } + } + }); + + for _ in 0..steps { + s.send(DropCounter).await; + } + + child.await; + + for _ in 0..additional { + s.send(DropCounter).await; + } + + assert_eq!(DROPS.load(Ordering::SeqCst), steps); + drop(s); + drop(r); + assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); + }) + } +} From 1c843a8124d599c19930da286363fc29b135d644 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 23 Oct 2019 22:27:51 +0200 Subject: [PATCH 043/707] Re-implemented Throttle to keep last value in memory --- src/stream/stream/mod.rs | 2 +- src/stream/stream/throttle.rs | 43 ++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 95b044495..3584be3df 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -281,7 +281,7 @@ extension_trait! { TakeWhile::new(self, predicate) } - fn throttle(self, d: Duration) -> Throttle + fn throttle(self, d: Duration) -> Throttle where Self: Sized, { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index c37c972c9..8e96fea9f 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -10,47 +10,58 @@ use crate::task::{Context, Poll}; /// A stream that only yields one element once every `duration`, and drops all others. /// #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct Throttle { +pub struct Throttle { stream: S, duration: Duration, delay: Option, + last: Option, } -impl Unpin for Throttle {} +impl Unpin for Throttle {} -impl Throttle { +impl Throttle { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(duration: Duration); pin_utils::unsafe_pinned!(delay: Option); + pin_utils::unsafe_unpinned!(last: Option); pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, duration, delay: None, + last: None, } } } -impl Stream for Throttle { +impl Stream for Throttle { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(v) => match self.as_mut().delay().as_pin_mut() { - None => { + if let Some(d) = self.as_mut().delay().as_pin_mut() { + if d.poll(cx).is_ready() { + if let Some(v) = self.as_mut().last().take() { + // Sets last to None. *self.as_mut().delay() = Some(Delay::new(self.duration)); - Poll::Ready(v) + return Poll::Ready(Some(v)); } - Some(d) => match d.poll(cx) { - Poll::Ready(_) => { - *self.as_mut().delay() = Some(Delay::new(self.duration)); - Poll::Ready(v) - } - Poll::Pending => Poll::Pending, - }, - }, + } + } + + match self.as_mut().stream().poll_next(cx) { Poll::Pending => Poll::Pending, + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some(v)) => { + if self.as_mut().delay().is_some() { + *self.as_mut().last() = Some(v); + cx.waker().wake_by_ref(); // Continue driving even though emitting Pending + return Poll::Pending; + } + + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(Some(v)) + } } } } From 1fd05a157f70c99157079672584d3f1aa1626bd8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 23 Oct 2019 22:34:39 +0200 Subject: [PATCH 044/707] Reset delay to prevent poll after ready --- src/stream/stream/throttle.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 8e96fea9f..2a0cc5630 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -45,6 +45,8 @@ impl Stream for Throttle { // Sets last to None. *self.as_mut().delay() = Some(Delay::new(self.duration)); return Poll::Ready(Some(v)); + } else { + *self.as_mut().delay() = None; } } } From d97b3dfdf3a159608023aea7a197b522514d817a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 24 Oct 2019 08:29:05 +0900 Subject: [PATCH 045/707] fix: Remove Pin API related unsafe code --- src/future/future/delay.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index 319b4ff8e..53a4d75aa 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -2,21 +2,23 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::future::Future; use crate::task::{Context, Poll}; +pin_project! { #[doc(hidden)] #[derive(Debug)] -pub struct DelayFuture { - future: F, - delay: Delay, + pub struct DelayFuture { + #[pin] + future: F, + #[pin] + delay: Delay, + } } impl DelayFuture { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(delay: Delay); - pub fn new(future: F, dur: Duration) -> DelayFuture { let delay = Delay::new(dur); @@ -27,10 +29,12 @@ impl DelayFuture { impl Future for DelayFuture { type Output = F::Output; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().delay().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + match this.delay.poll(cx) { Poll::Pending => Poll::Pending, - Poll::Ready(_) => match self.future().poll(cx) { + Poll::Ready(_) => match this.future.poll(cx) { Poll::Ready(v) => Poll::Ready(v), Poll::Pending => Poll::Pending, }, From feeb3c10df6725b35f67bc9c8cc73e4a68e46e4d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 24 Oct 2019 08:41:01 +0900 Subject: [PATCH 046/707] fix: Remove Pin API related unsafe code --- src/stream/stream/timeout.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 7e0270e3b..3c14811fb 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -4,22 +4,24 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream with timeout time set -#[derive(Debug)] -pub struct Timeout { - stream: S, - delay: Delay, +pin_project! { + /// A stream with timeout time set + #[derive(Debug)] + pub struct Timeout { + #[pin] + stream: S, + #[pin] + delay: Delay, + } } impl Timeout { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_pinned!(delay: Delay); - pub fn new(stream: S, dur: Duration) -> Timeout { let delay = Delay::new(dur); @@ -30,11 +32,13 @@ impl Timeout { impl Stream for Timeout { type Item = Result; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.as_mut().stream().poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + match this.stream.poll_next(cx) { Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => match self.delay().poll(cx) { + Poll::Pending => match this.delay.poll(cx) { Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError { _private: () }))), Poll::Pending => Poll::Pending, }, From 271b6f4a1c6a8096ba68682430a984001de19d56 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 25 Oct 2019 23:58:08 +0900 Subject: [PATCH 047/707] fix: Using pin_project! --- src/stream/stream/flatten.rs | 85 +++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index b9700876b..d44fab0e5 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,18 +1,22 @@ use std::pin::Pin; +use pin_project_lite::pin_project; use crate::prelude::*; use crate::stream::stream::map::Map; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; -/// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its -/// documentation for more. -/// -/// [`flat_map`]: trait.Stream.html#method.flat_map -/// [`Stream`]: trait.Stream.html -#[allow(missing_debug_implementations)] -pub struct FlatMap { - inner: FlattenCompat, U>, +pin_project! { + /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`flat_map`]: trait.Stream.html#method.flat_map + /// [`Stream`]: trait.Stream.html + #[allow(missing_debug_implementations)] + pub struct FlatMap { + #[pin] + inner: FlattenCompat, U>, + } } impl FlatMap @@ -21,7 +25,6 @@ where U: IntoStream, F: FnMut(S::Item) -> U, { - pin_utils::unsafe_pinned!(inner: FlattenCompat, U>); pub fn new(stream: S, f: F) -> FlatMap { FlatMap { @@ -33,33 +36,33 @@ where impl Stream for FlatMap where S: Stream> + std::marker::Unpin, - S::Item: std::marker::Unpin, U: Stream + std::marker::Unpin, - F: FnMut(S::Item) -> U + std::marker::Unpin, + F: FnMut(S::Item) -> U, { type Item = U::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.as_mut().inner().poll_next(cx) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_next(cx) } } -/// This `struct` is created by the [`flatten`] method on [`Stream`]. See its -/// documentation for more. -/// -/// [`flatten`]: trait.Stream.html#method.flatten -/// [`Stream`]: trait.Stream.html -#[allow(missing_debug_implementations)] -pub struct Flatten -where - S::Item: IntoStream, -{ - inner: FlattenCompat::IntoStream>, +pin_project!{ + /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`flatten`]: trait.Stream.html#method.flatten + /// [`Stream`]: trait.Stream.html + #[allow(missing_debug_implementations)] + pub struct Flatten + where + S::Item: IntoStream, + { + #[pin] + inner: FlattenCompat::IntoStream>, + } } impl> Flatten { - pin_utils::unsafe_pinned!(inner: FlattenCompat::IntoStream>); - pub fn new(stream: S) -> Flatten { Flatten { inner: FlattenCompat::new(stream) } } @@ -72,24 +75,23 @@ where { type Item = U::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.as_mut().inner().poll_next(cx) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_next(cx) } } -/// Real logic of both `Flatten` and `FlatMap` which simply delegate to -/// this type. -#[derive(Clone, Debug)] -struct FlattenCompat { - stream: S, - frontiter: Option, +pin_project! { + /// Real logic of both `Flatten` and `FlatMap` which simply delegate to + /// this type. + #[derive(Clone, Debug)] + struct FlattenCompat { + stream: S, + frontiter: Option, + } } impl FlattenCompat { - pin_utils::unsafe_unpinned!(stream: S); - pin_utils::unsafe_unpinned!(frontiter: Option); - /// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. pub fn new(stream: S) -> FlattenCompat { FlattenCompat { @@ -106,17 +108,18 @@ where { type Item = U::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - if let Some(ref mut inner) = self.as_mut().frontiter() { + if let Some(inner) = this.frontiter { if let item @ Some(_) = futures_core::ready!(Pin::new(inner).poll_next(cx)) { return Poll::Ready(item); } } - match futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)) { + match futures_core::ready!(Pin::new(&mut this.stream).poll_next(cx)) { None => return Poll::Ready(None), - Some(inner) => *self.as_mut().frontiter() = Some(inner.into_stream()), + Some(inner) => *this.frontiter = Some(inner.into_stream()), } } } From 00e7e58bf3194c8a9e0b912061cdf0741a1834be Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 26 Oct 2019 00:26:53 +0900 Subject: [PATCH 048/707] fix type def --- src/stream/stream/flatten.rs | 23 ++++++++++------------- src/stream/stream/mod.rs | 12 ++++-------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index d44fab0e5..bc1bee343 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -13,27 +13,27 @@ pin_project! { /// [`flat_map`]: trait.Stream.html#method.flat_map /// [`Stream`]: trait.Stream.html #[allow(missing_debug_implementations)] - pub struct FlatMap { + pub struct FlatMap { #[pin] - inner: FlattenCompat, U>, + inner: FlattenCompat, U>, } } -impl FlatMap +impl FlatMap where S: Stream, U: IntoStream, F: FnMut(S::Item) -> U, { - pub fn new(stream: S, f: F) -> FlatMap { + pub fn new(stream: S, f: F) -> FlatMap { FlatMap { inner: FlattenCompat::new(stream.map(f)), } } } -impl Stream for FlatMap +impl Stream for FlatMap where S: Stream> + std::marker::Unpin, U: Stream + std::marker::Unpin, @@ -53,22 +53,19 @@ pin_project!{ /// [`flatten`]: trait.Stream.html#method.flatten /// [`Stream`]: trait.Stream.html #[allow(missing_debug_implementations)] - pub struct Flatten - where - S::Item: IntoStream, - { + pub struct Flatten { #[pin] - inner: FlattenCompat::IntoStream>, + inner: FlattenCompat } } -impl> Flatten { - pub fn new(stream: S) -> Flatten { +impl> Flatten { + pub fn new(stream: S) -> Flatten { Flatten { inner: FlattenCompat::new(stream) } } } -impl Stream for Flatten +impl Stream for Flatten::IntoStream> where S: Stream> + std::marker::Unpin, U: Stream + std::marker::Unpin, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ad8cdc03..c994a8ff2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -30,6 +30,7 @@ mod filter; mod filter_map; mod find; mod find_map; +mod flatten; mod fold; mod for_each; mod fuse; @@ -77,6 +78,7 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; +pub use flatten::{FlatMap, Flatten}; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; @@ -99,10 +101,8 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; pub use merge::Merge; - pub use flatten::{FlatMap, Flatten}; mod merge; - mod flatten; } extension_trait! { @@ -588,9 +588,7 @@ extension_trait! { # }); ``` "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flat_map(self, f: F) -> FlatMap + fn flat_map(self, f: F) -> FlatMap where Self: Sized, U: IntoStream, @@ -622,9 +620,7 @@ extension_trait! { # }); "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self: Sized, Self::Item: IntoStream, From 001368d3dfa4f9f0962c52c357a776e9e66e8780 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 26 Oct 2019 00:29:10 +0900 Subject: [PATCH 049/707] $cargo fmt --- src/stream/stream/flatten.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index bc1bee343..fc3592ee1 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use pin_project_lite::pin_project; +use std::pin::Pin; use crate::prelude::*; use crate::stream::stream::map::Map; @@ -25,7 +25,6 @@ where U: IntoStream, F: FnMut(S::Item) -> U, { - pub fn new(stream: S, f: F) -> FlatMap { FlatMap { inner: FlattenCompat::new(stream.map(f)), @@ -46,7 +45,7 @@ where } } -pin_project!{ +pin_project! { /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its /// documentation for more. /// @@ -61,7 +60,9 @@ pin_project!{ impl> Flatten { pub fn new(stream: S) -> Flatten { - Flatten { inner: FlattenCompat::new(stream) } + Flatten { + inner: FlattenCompat::new(stream), + } } } @@ -77,7 +78,6 @@ where } } - pin_project! { /// Real logic of both `Flatten` and `FlatMap` which simply delegate to /// this type. From 0c5abee284eae2136ab1b795d086e69761b8a913 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 26 Oct 2019 00:36:04 +0900 Subject: [PATCH 050/707] to unstable stream::flat_map, stream::flatten --- src/stream/stream/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c994a8ff2..596dc4190 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -30,7 +30,6 @@ mod filter; mod filter_map; mod find; mod find_map; -mod flatten; mod fold; mod for_each; mod fuse; @@ -78,7 +77,6 @@ use try_for_each::TryForEeachFuture; pub use chain::Chain; pub use filter::Filter; -pub use flatten::{FlatMap, Flatten}; pub use fuse::Fuse; pub use inspect::Inspect; pub use map::Map; @@ -101,8 +99,10 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; pub use merge::Merge; + pub use flatten::{FlatMap, Flatten}; mod merge; + mod flatten; } extension_trait! { @@ -588,6 +588,8 @@ extension_trait! { # }); ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flat_map(self, f: F) -> FlatMap where Self: Sized, @@ -620,6 +622,8 @@ extension_trait! { # }); "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn flatten(self) -> Flatten where Self: Sized, From b66ffa670eb91356f49cc0ceb56a3fd95df3489a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 26 Oct 2019 00:42:27 +0900 Subject: [PATCH 051/707] update recursion_limit --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7f04e0821..1e33c8fca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ #![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 = "1024"] +#![recursion_limit = "2048"] #![feature(associated_type_bounds)] #[macro_use] From 7ce721f562e17db4eb7953ec5612c5edff8675d6 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 01:42:19 +0900 Subject: [PATCH 052/707] Update src/lib.rs Co-Authored-By: Taiki Endo --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1e33c8fca..22481b53a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,6 @@ #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "2048"] -#![feature(associated_type_bounds)] #[macro_use] mod utils; From b7b5df13aa7843f42fe01c31fa8758727646dbd8 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 01:42:39 +0900 Subject: [PATCH 053/707] Update src/stream/stream/flatten.rs Co-Authored-By: Taiki Endo --- 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 fc3592ee1..56277330f 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -108,7 +108,7 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); loop { - if let Some(inner) = this.frontiter { + if let Some(inner) = this.frontiter.as_mut().as_pin_mut() { if let item @ Some(_) = futures_core::ready!(Pin::new(inner).poll_next(cx)) { return Poll::Ready(item); } From 6168952d6f262ee04d4fc06533db2665e207edb3 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 01:42:57 +0900 Subject: [PATCH 054/707] Update src/stream/stream/flatten.rs Co-Authored-By: Taiki Endo --- 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 56277330f..b244f035d 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -109,7 +109,7 @@ where let mut this = self.project(); loop { if let Some(inner) = this.frontiter.as_mut().as_pin_mut() { - if let item @ Some(_) = futures_core::ready!(Pin::new(inner).poll_next(cx)) { + if let item @ Some(_) = futures_core::ready!(inner.poll_next(cx)) { return Poll::Ready(item); } } From bf3508ffb254db07a187293e8cdef7df651bce6e Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 01:43:07 +0900 Subject: [PATCH 055/707] Update src/stream/stream/flatten.rs Co-Authored-By: Taiki Endo --- 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 b244f035d..1b21746e7 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -114,7 +114,7 @@ where } } - match futures_core::ready!(Pin::new(&mut this.stream).poll_next(cx)) { + match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { None => return Poll::Ready(None), Some(inner) => *this.frontiter = Some(inner.into_stream()), } From 8932cecec75b78058de240d2a4c72d90b07759b3 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 01:43:14 +0900 Subject: [PATCH 056/707] Update src/stream/stream/flatten.rs Co-Authored-By: Taiki Endo --- 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 1b21746e7..593308556 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -116,7 +116,7 @@ where match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { None => return Poll::Ready(None), - Some(inner) => *this.frontiter = Some(inner.into_stream()), + Some(inner) => this.frontiter.set(Some(inner.into_stream())), } } } From 61b7a09c70d78329131dedc528baea5d6cda7942 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 26 Oct 2019 01:44:48 +0900 Subject: [PATCH 057/707] Fix type declaration --- src/stream/stream/flatten.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 593308556..3bf6fe84b 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -34,8 +34,9 @@ where impl Stream for FlatMap where - S: Stream> + std::marker::Unpin, - U: Stream + std::marker::Unpin, + S: Stream, + S::Item: IntoStream, + U: Stream, F: FnMut(S::Item) -> U, { type Item = U::Item; @@ -58,7 +59,11 @@ pin_project! { } } -impl> Flatten { +impl Flatten +where + S: Stream, + S::Item: IntoStream, +{ pub fn new(stream: S) -> Flatten { Flatten { inner: FlattenCompat::new(stream), @@ -68,8 +73,9 @@ impl> Flatten { impl Stream for Flatten::IntoStream> where - S: Stream> + std::marker::Unpin, - U: Stream + std::marker::Unpin, + S: Stream, + S::Item: IntoStream, + U: Stream, { type Item = U::Item; @@ -83,7 +89,9 @@ pin_project! { /// this type. #[derive(Clone, Debug)] struct FlattenCompat { + #[pin] stream: S, + #[pin] frontiter: Option, } } @@ -100,8 +108,9 @@ impl FlattenCompat { impl Stream for FlattenCompat where - S: Stream> + std::marker::Unpin, - U: Stream + std::marker::Unpin, + S: Stream, + S::Item: IntoStream, + U: Stream, { type Item = U::Item; From 81e3cab00db42c2084fba9734a53e7074048bfd9 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 06:14:57 +0900 Subject: [PATCH 058/707] Change homepage link (#389) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7aaba6874..ad8873037 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = [ edition = "2018" license = "Apache-2.0/MIT" repository = "https://github.com/async-rs/async-std" -homepage = "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"] From 37a7eadf17c55d0cb524080d34c2f30d848ecd7e Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sat, 26 Oct 2019 11:52:41 +0800 Subject: [PATCH 059/707] use pin_project_lite --- src/stream/stream/max_by.rs | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index d25a869d0..d3d640b58 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -1,23 +1,24 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct MaxByFuture { - stream: S, - compare: F, - max: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxByFuture { + #[pin] + stream: S, + compare: F, + max: Option, + } } impl MaxByFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(compare: F); - pin_utils::unsafe_unpinned!(max: Option); - pub(super) fn new(stream: S, compare: F) -> Self { MaxByFuture { stream, @@ -35,22 +36,23 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - match self.as_mut().max().take() { - None => *self.as_mut().max() = Some(new), - Some(old) => match (&mut self.as_mut().compare())(&new, &old) { - Ordering::Greater => *self.as_mut().max() = Some(new), - _ => *self.as_mut().max() = Some(old), + match this.max.take() { + None => *this.max = Some(new), + Some(old) => match (this.compare)(&new, &old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), }, } Poll::Pending } - None => Poll::Ready(self.max), + None => Poll::Ready(*this.max), } } } From 006fc7e9de2fca94dfa807ef477667a9695aa05d Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 27 Oct 2019 00:17:42 +0800 Subject: [PATCH 060/707] Update src/stream/stream/max_by.rs Co-Authored-By: Taiki Endo --- src/stream/stream/max_by.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index d3d640b58..a41f49a7a 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -30,7 +30,7 @@ impl MaxByFuture { impl Future for MaxByFuture where - S: Stream + Unpin + Sized, + S: Stream, S::Item: Copy, F: FnMut(&S::Item, &S::Item) -> Ordering, { From a8d3d1483f29060b15ee4df80852a6a67603569f Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 27 Oct 2019 00:17:50 +0800 Subject: [PATCH 061/707] Update src/stream/stream/max_by.rs Co-Authored-By: Taiki Endo --- src/stream/stream/max_by.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index a41f49a7a..6cd9e5606 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -31,7 +31,6 @@ impl MaxByFuture { impl Future for MaxByFuture where S: Stream, - S::Item: Copy, F: FnMut(&S::Item, &S::Item) -> Ordering, { type Output = Option; From b57849e1cb6a2d473c0a1a62ff807ef237b99ff0 Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 27 Oct 2019 00:18:01 +0800 Subject: [PATCH 062/707] Update src/stream/stream/max_by.rs Co-Authored-By: Taiki Endo --- src/stream/stream/max_by.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index 6cd9e5606..a626b284a 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -51,7 +51,7 @@ where } Poll::Pending } - None => Poll::Ready(*this.max), + None => Poll::Ready(this.max.take()), } } } From 5a4fdeb1cd6ce25192298e01fa5a5792279570cd Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 27 Oct 2019 00:18:18 +0800 Subject: [PATCH 063/707] Update src/stream/stream/min_by_key.rs Co-Authored-By: Taiki Endo --- src/stream/stream/min_by_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index cac60733d..d6dda2f71 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -30,7 +30,7 @@ impl MinByKeyFuture { impl Future for MinByKeyFuture where - S: Stream + Unpin + Sized, + S: Stream, K: FnMut(&S::Item) -> S::Item, S::Item: Ord + Copy, { From fb78ed18124780164a9cc4b1986103649dec6979 Mon Sep 17 00:00:00 2001 From: yjh Date: Sun, 27 Oct 2019 00:19:49 +0800 Subject: [PATCH 064/707] Update src/stream/stream/min_by_key.rs Co-Authored-By: Taiki Endo --- src/stream/stream/min_by_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index d6dda2f71..a482c632d 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -32,7 +32,7 @@ impl Future for MinByKeyFuture where S: Stream, K: FnMut(&S::Item) -> S::Item, - S::Item: Ord + Copy, + S::Item: Ord, { type Output = Option; From 7cfec4e8cec531792330e4caeb72a9145ea7a941 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sun, 27 Oct 2019 00:26:19 +0800 Subject: [PATCH 065/707] use take and remove Copy --- src/stream/stream/min_by_key.rs | 2 +- src/stream/stream/mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index a482c632d..6557f2296 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -54,7 +54,7 @@ where } Poll::Pending } - None => Poll::Ready(*this.min), + None => Poll::Ready(this.min.take()), } } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b5011c27c..ca43e7d0e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -633,6 +633,7 @@ extension_trait! { ) -> impl Future> [MinByKeyFuture] where Self: Sized, + Self::Item: Ord, K: FnMut(&Self::Item) -> Self::Item, { MinByKeyFuture::new(self, key_by) From 610c66e774d1f70292f73111810feab1e990a055 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 27 Oct 2019 02:22:26 +0900 Subject: [PATCH 066/707] Remove usage of actions-rs/clippy-check --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d47eabce3..c79630ad2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,6 +95,5 @@ jobs: toolchain: ${{ steps.component.outputs.toolchain }} override: true - run: rustup component add clippy - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} + - name: clippy + run: cargo clippy --all --features unstable From 6549b66ad2e9ab0a6ea89dc1855b844de357f704 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 27 Oct 2019 03:28:20 +0900 Subject: [PATCH 067/707] run clippy check on beta & address clippy warnings --- .github/workflows/ci.yml | 23 +++++++---------------- src/lib.rs | 1 + src/stream/exact_size_stream.rs | 1 + src/task/task.rs | 2 ++ 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c79630ad2..653834a77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,13 @@ on: - staging - trying +env: + RUSTFLAGS: -Dwarnings + jobs: build_and_test: name: Build and test runs-on: ${{ matrix.os }} - env: - RUSTFLAGS: -Dwarnings strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] @@ -48,8 +49,6 @@ jobs: check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest - env: - RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@master @@ -81,19 +80,11 @@ jobs: clippy_check: name: Clippy check runs-on: ubuntu-latest - # TODO: There is a lot of warnings - # env: - # RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@v1 - - id: component - uses: actions-rs/components-nightly@v1 - with: - component: clippy - - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ steps.component.outputs.toolchain }} - override: true - - run: rustup component add clippy + - 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 diff --git a/src/lib.rs b/src/lib.rs index 7f888a14a..ad5aa8fab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,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(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 32a1eb315..8b6ba97da 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -75,6 +75,7 @@ pub use crate::stream::Stream; /// assert_eq!(5, counter.len()); /// # }); /// ``` +#[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 { diff --git a/src/task/task.rs b/src/task/task.rs index ca3cac142..3d8e10804 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -167,6 +167,7 @@ impl Tag { } pub fn task(&self) -> &Task { + #[allow(clippy::transmute_ptr_to_ptr)] unsafe { let raw = self.raw_metadata.load(Ordering::Acquire); @@ -189,6 +190,7 @@ impl Tag { } } + #[allow(clippy::transmute_ptr_to_ptr)] mem::transmute::<&AtomicUsize, &Option>(&self.raw_metadata) .as_ref() .unwrap() From 6608d39c59f508ae09a32410df14c2e623b9cf44 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Sat, 26 Oct 2019 21:58:34 +0200 Subject: [PATCH 068/707] remove Stream trait bound --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b9d4bc86a..9e0c1ef15 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1416,7 +1416,7 @@ extension_trait! { "#] fn count(self) -> impl Future [CountFuture] where - Self: Sized + Stream, + Self: Sized, { CountFuture::new(self) } From 13a08b0d54f5c983883ce3efca60278d38a8b6d7 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 27 Oct 2019 12:35:14 +0900 Subject: [PATCH 069/707] Narrow the disclosure range of FlatMap::new Co-Authored-By: Taiki Endo --- 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 3bf6fe84b..3efb35231 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -25,7 +25,7 @@ where U: IntoStream, F: FnMut(S::Item) -> U, { - pub fn new(stream: S, f: F) -> FlatMap { + pub(super) fn new(stream: S, f: F) -> FlatMap { FlatMap { inner: FlattenCompat::new(stream.map(f)), } From 37f14b0195e63838f3e4485ba14bb8e54cc59789 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 27 Oct 2019 12:35:32 +0900 Subject: [PATCH 070/707] Narrow the disclosure range of Flatten::new Co-Authored-By: Taiki Endo --- 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 3efb35231..d81e79fbc 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -64,7 +64,7 @@ where S: Stream, S::Item: IntoStream, { - pub fn new(stream: S) -> Flatten { + pub(super) fn new(stream: S) -> Flatten { Flatten { inner: FlattenCompat::new(stream), } From a42ae2f3d925499ea8732a017baf7f68f146cf27 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 27 Oct 2019 12:35:51 +0900 Subject: [PATCH 071/707] Narrow the disclosure range of FlattenCompat::new Co-Authored-By: Taiki Endo --- 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 d81e79fbc..2533f3794 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -98,7 +98,7 @@ pin_project! { impl FlattenCompat { /// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. - pub fn new(stream: S) -> FlattenCompat { + fn new(stream: S) -> FlattenCompat { FlattenCompat { stream, frontiter: None, From c9d958d30974daec880edb90af1f5a85191df8e7 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 27 Oct 2019 18:46:24 +0900 Subject: [PATCH 072/707] $cargo fix -Z unstable-options --clippy --features unstable --- examples/a-chat/main.rs | 2 +- examples/a-chat/server.rs | 2 +- tests/rwlock.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/a-chat/main.rs b/examples/a-chat/main.rs index ced7cac24..89e5e2b62 100644 --- a/examples/a-chat/main.rs +++ b/examples/a-chat/main.rs @@ -8,6 +8,6 @@ fn main() -> Result<()> { match (args.nth(1).as_ref().map(String::as_str), args.next()) { (Some("client"), None) => client::main(), (Some("server"), None) => server::main(), - _ => Err("Usage: a-chat [client|server]")?, + _ => Err("Usage: a-chat [client|server]".into()), } } diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs index 77ebfd1e3..e049a490e 100644 --- a/examples/a-chat/server.rs +++ b/examples/a-chat/server.rs @@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result let mut lines = reader.lines(); let name = match lines.next().await { - None => Err("peer disconnected immediately")?, + None => return Err("peer disconnected immediately".into()), Some(line) => line?, }; let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::(); diff --git a/tests/rwlock.rs b/tests/rwlock.rs index ff25e862d..370dcb9fc 100644 --- a/tests/rwlock.rs +++ b/tests/rwlock.rs @@ -13,7 +13,7 @@ use futures::channel::mpsc; /// Generates a random number in `0..n`. pub fn random(n: u32) -> u32 { thread_local! { - static RNG: Cell> = Cell::new(Wrapping(1406868647)); + static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); } RNG.with(|rng| { From 7c293d37f7ffc2127f9dbc48c668a3eaa8204eba Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 27 Oct 2019 18:47:09 +0900 Subject: [PATCH 073/707] fix clippy::comparison_chain --- src/stream/interval.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 2f7fe9e3a..016f71a28 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -111,6 +111,7 @@ fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant { #[cfg(test)] mod test { use super::next_interval; + use std::cmp::Ordering; use std::time::{Duration, Instant}; struct Timeline(Instant); @@ -134,12 +135,10 @@ mod test { // 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 { - if a == b { - true - } else if a > b { - a - b < Duration::from_millis(1) - } else { - b - a < Duration::from_millis(1) + match a.cmp(&b) { + Ordering::Equal => true, + Ordering::Greater => a - b < Duration::from_millis(1), + Ordering::Less => b - a < Duration::from_millis(1), } } From 7fe2a1bbce08b1741aa47432d50e25032a0e041c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 27 Oct 2019 18:47:22 +0900 Subject: [PATCH 074/707] fix clippy::cognitive_complexity --- tests/buf_writer.rs | 1 + tests/channel.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index cb2368aac..5df90e08c 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -4,6 +4,7 @@ use async_std::task; #[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); diff --git a/tests/channel.rs b/tests/channel.rs index 91622b0d0..0c40f5a73 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -37,6 +37,7 @@ fn capacity() { #[test] fn len_empty_full() { + #![allow(clippy::cognitive_complexity)] task::block_on(async { let (s, r) = channel(2); From fe49f2618fc30c6bf7dd001935053687f6c18c3d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 27 Oct 2019 20:34:44 +0900 Subject: [PATCH 075/707] fix clippy::redundant_clone --- src/task/block_on.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index b0adc3879..c10303d79 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -154,6 +154,7 @@ where fn vtable() -> &'static RawWakerVTable { unsafe fn clone_raw(ptr: *const ()) -> RawWaker { + #![allow(clippy::redundant_clone)] let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); mem::forget(arc.clone()); RawWaker::new(ptr, vtable()) From 59615a655bc91bd48fc365015a85f73e3a7b9083 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 27 Oct 2019 21:48:23 +0900 Subject: [PATCH 076/707] feat: Add StderrLock and StdoutLock struct --- src/io/stderr.rs | 27 +++++++++++++++++++++++++-- src/io/stdin.rs | 2 +- src/io/stdout.rs | 27 +++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 8193eeb7d..1ea33616e 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -44,6 +44,11 @@ pub fn stderr() -> Stderr { #[derive(Debug)] pub struct Stderr(Mutex); +#[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. @@ -98,12 +103,12 @@ impl Stderr { /// # /// # Ok(()) }) } /// ``` - pub async fn lock(&self) -> std::io::StderrLock<'static> { + pub async fn lock(&self) -> StderrLock<'static> { lazy_static! { static ref STDERR: std::io::Stderr = std::io::stderr(); } - STDERR.lock() + blocking::spawn(move || StderrLock(STDERR.lock())).await } } @@ -209,3 +214,21 @@ cfg_windows! { } } } + +impl Write for StderrLock<'_> { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + unimplemented!() + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + unimplemented!() + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + unimplemented!() + } +} diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 93d91700e..f869cbf64 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -165,7 +165,7 @@ impl Stdin { static ref STDIN: std::io::Stdin = std::io::stdin(); } - blocking::spawn(move || { StdinLock(STDIN.lock()) }).await + blocking::spawn(move || StdinLock(STDIN.lock())).await } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index cbe14b8bf..360bfe86a 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -44,6 +44,11 @@ pub fn stdout() -> Stdout { #[derive(Debug)] pub struct Stdout(Mutex); +#[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. @@ -98,12 +103,12 @@ impl Stdout { /// # /// # Ok(()) }) } /// ``` - pub async fn lock(&self) -> std::io::StdoutLock<'static> { + pub async fn lock(&self) -> StdoutLock<'static> { lazy_static! { static ref STDOUT: std::io::Stdout = std::io::stdout(); } - STDOUT.lock() + blocking::spawn(move || StdoutLock(STDOUT.lock())).await } } @@ -209,3 +214,21 @@ cfg_windows! { } } } + +impl Write for StdoutLock<'_> { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + unimplemented!() + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + unimplemented!() + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + unimplemented!() + } +} From a3a740c14ae75464c0170c326dc6eeddf63a331c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 27 Oct 2019 22:21:27 +0100 Subject: [PATCH 077/707] backlink all docs Signed-off-by: Yoshua Wuyts --- src/stream/empty.rs | 5 +++++ src/stream/from_fn.rs | 3 ++- src/stream/interval.rs | 4 ++++ src/stream/once.rs | 3 ++- src/stream/repeat.rs | 5 +++-- src/stream/repeat_with.rs | 3 ++- src/stream/stream/chain.rs | 6 ++++++ src/stream/stream/filter.rs | 6 ++++++ src/stream/stream/fuse.rs | 6 ++++++ src/stream/stream/inspect.rs | 6 ++++++ src/stream/stream/merge.rs | 6 ++++-- src/stream/stream/scan.rs | 6 ++++++ src/stream/stream/skip.rs | 6 ++++++ src/stream/stream/skip_while.rs | 6 ++++++ src/stream/stream/step_by.rs | 6 ++++++ src/stream/stream/take.rs | 6 ++++++ src/stream/stream/take_while.rs | 6 ++++++ src/stream/stream/zip.rs | 6 ++++++ 18 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/stream/empty.rs b/src/stream/empty.rs index ceb91fead..490907071 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -6,6 +6,11 @@ use crate::task::{Context, Poll}; /// Creates a stream that doesn't yield any items. /// +/// This `struct` is created by the [`empty`] function. See its +/// documentation for more. +/// +/// [`empty`]: fn.empty.html +/// /// # Examples /// /// ``` diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 7fee8926d..5260d8788 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -10,7 +10,8 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that yields elements by calling a closure. /// - /// This stream is constructed by [`from_fn`] function. + /// This stream is created by the [`from_fn`] function. See its + /// documentation for more. /// /// [`from_fn`]: fn.from_fn.html #[derive(Debug)] diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 2f7fe9e3a..c3b82945a 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -52,6 +52,10 @@ pub fn interval(dur: Duration) -> Interval { /// A stream representing notifications at fixed interval /// +/// This stream is created by the [`interval`] function. See its +/// documentation for more. +/// +/// [`interval`]: fn.interval.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] diff --git a/src/stream/once.rs b/src/stream/once.rs index ae90d639a..d993c1603 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -29,7 +29,8 @@ pub fn once(t: T) -> Once { pin_project! { /// A stream that yields a single item. /// - /// This stream is constructed by the [`once`] function. + /// This stream is created by the [`once`] function. See its + /// documentation for more. /// /// [`once`]: fn.once.html #[derive(Debug)] diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index 75fd69735..abccb431c 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -29,9 +29,10 @@ where /// A stream that yields the same item repeatedly. /// -/// This stream is constructed by the [`repeat`] function. +/// This stream is created by the [`repeat`] function. See its +/// documentation for more. /// -/// [`repeat`]: fn.repeat.html +/// [`repeat`]: fn.once.html #[derive(Debug)] pub struct Repeat { item: T, diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index 15d4aa122..de53bc9da 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -10,7 +10,8 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that repeats elements of type `T` endlessly by applying a provided closure. /// - /// This stream is constructed by the [`repeat_with`] function. + /// This stream is created by the [`repeat_with`] function. See its + /// documentation for more. /// /// [`repeat_with`]: fn.repeat_with.html #[derive(Debug)] diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index df3161501..5e0eeb48c 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// Chains two streams one after another. + /// + /// This `struct` is created by the [`chain`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`chain`]: trait.Stream.html#method.chain + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct Chain { #[pin] diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index eb4153f76..a2562e771 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream to filter elements of another stream with a predicate. + /// + /// This `struct` is created by the [`filter`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`filter`]: trait.Stream.html#method.filter + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct Filter { #[pin] diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 11629700f..39af9cb0f 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A `Stream` that is permanently closed once a single call to `poll` results in /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. + /// + /// This `struct` is created by the [`fuse`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`fuse`]: trait.Stream.html#method.fuse + /// [`Stream`]: trait.Stream.html #[derive(Clone, Debug)] pub struct Fuse { #[pin] diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index 5de60fb39..ba60b0cec 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that does something with each element of another stream. + /// + /// This `struct` is created by the [`inspect`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`inspect`]: trait.Stream.html#method.inspect + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct Inspect { #[pin] diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 3ccc223d6..d926ec4fe 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -7,9 +7,11 @@ use pin_project_lite::pin_project; pin_project! { /// A stream that merges two other streams into a single stream. /// - /// This stream is returned by [`Stream::merge`]. + /// This `struct` is created by the [`merge`] method on [`Stream`]. See its + /// documentation for more. /// - /// [`Stream::merge`]: trait.Stream.html#method.merge + /// [`merge`]: trait.Stream.html#method.merge + /// [`Stream`]: trait.Stream.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index c4771d851..385edf8ec 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -7,6 +7,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream to maintain state while polling another stream. + /// + /// This `struct` is created by the [`scan`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`scan`]: trait.Stream.html#method.scan + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct Scan { #[pin] diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index 6562b99fd..cc2ba905b 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -7,6 +7,12 @@ use crate::stream::Stream; pin_project! { /// A stream to skip first n elements of another stream. + /// + /// This `struct` is created by the [`skip`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`skip`]: trait.Stream.html#method.skip + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct Skip { #[pin] diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 0499df23c..6435d81c6 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream to skip elements of another stream based on a predicate. + /// + /// This `struct` is created by the [`skip_while`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`skip_while`]: trait.Stream.html#method.skip_while + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct SkipWhile { #[pin] diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index ab9e45b65..130209829 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -7,6 +7,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that steps a given amount of elements of another stream. + /// + /// This `struct` is created by the [`step_by`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`step_by`]: trait.Stream.html#method.step_by + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct StepBy { #[pin] diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 835bc447a..e680b42ba 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -7,6 +7,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that yields the first `n` items of another stream. + /// + /// This `struct` is created by the [`take`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`take`]: trait.Stream.html#method.take + /// [`Stream`]: trait.Stream.html #[derive(Clone, Debug)] pub struct Take { #[pin] diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index bf89458b7..35978b47c 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that yields elements based on a predicate. + /// + /// This `struct` is created by the [`take_while`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`take_while`]: trait.Stream.html#method.take_while + /// [`Stream`]: trait.Stream.html #[derive(Debug)] pub struct TakeWhile { #[pin] diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 9b7c299b3..27681f375 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -8,6 +8,12 @@ use crate::task::{Context, Poll}; pin_project! { /// An iterator that iterates two other iterators simultaneously. + /// + /// This `struct` is created by the [`zip`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`zip`]: trait.Stream.html#method.zip + /// [`Stream`]: trait.Stream.html pub struct Zip { item_slot: Option, #[pin] From 4475a229d66ab1b7b862019e59421ad0dad456b1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 27 Oct 2019 22:40:49 +0100 Subject: [PATCH 078/707] backlink io docs Signed-off-by: Yoshua Wuyts --- src/io/empty.rs | 5 +++-- src/io/repeat.rs | 3 ++- src/io/sink.rs | 3 ++- src/io/stderr.rs | 16 +++++++++++++--- src/io/stdin.rs | 16 +++++++++++++--- src/io/stdout.rs | 16 +++++++++++++--- src/stream/repeat.rs | 2 +- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/io/empty.rs b/src/io/empty.rs index d8d768e02..904426757 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -28,9 +28,10 @@ pub fn empty() -> Empty { /// A reader that contains no data. /// -/// This reader is constructed by the [`sink`] function. +/// This reader is created by the [`empty`] function. See its +/// documentation for more. /// -/// [`sink`]: fn.sink.html +/// [`empty`]: fn.empty.html pub struct Empty { _private: (), } diff --git a/src/io/repeat.rs b/src/io/repeat.rs index a82e21be4..563681793 100644 --- a/src/io/repeat.rs +++ b/src/io/repeat.rs @@ -29,7 +29,8 @@ pub fn repeat(byte: u8) -> Repeat { /// A reader which yields one byte over and over and over and over and over and... /// -/// This reader is constructed by the [`repeat`] function. +/// This reader is created by the [`repeat`] function. See its +/// documentation for more. /// /// [`repeat`]: fn.repeat.html pub struct Repeat { diff --git a/src/io/sink.rs b/src/io/sink.rs index faa763c6a..86aeb0aec 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -25,7 +25,8 @@ pub fn sink() -> Sink { /// A writer that consumes and drops all data. /// -/// This writer is constructed by the [`sink`] function. +/// This writer is constructed by the [`sink`] function. See its documentation +/// for more. /// /// [`sink`]: fn.sink.html pub struct Sink { diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 1ec28b299..0a8c47004 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -11,6 +11,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`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 @@ -34,12 +40,16 @@ pub fn stderr() -> Stderr { /// A handle to the standard error of the current process. /// -/// Created by the [`stderr`] function. +/// This writer is created by the [`stderr`] function. See its documentation for +/// more. +/// +/// ### Note: Windows Portability Consideration /// -/// This type is an async version of [`std::io::Stderr`]. +/// 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 -/// [`std::io::Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html #[derive(Debug)] pub struct Stderr(Mutex); diff --git a/src/io/stdin.rs b/src/io/stdin.rs index dd3991fdc..22b9cf346 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -11,6 +11,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`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 @@ -35,12 +41,16 @@ pub fn stdin() -> Stdin { /// A handle to the standard input of the current process. /// -/// Created by the [`stdin`] function. +/// This reader is created by the [`stdin`] function. See its documentation for +/// more. +/// +/// ### Note: Windows Portability Consideration /// -/// This type is an async version of [`std::io::Stdin`]. +/// 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 -/// [`std::io::Stdin`]: https://doc.rust-lang.org/std/io/struct.Stdin.html #[derive(Debug)] pub struct Stdin(Mutex); diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 7945bfdd1..1e9340fcb 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -11,6 +11,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`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 @@ -34,12 +40,16 @@ pub fn stdout() -> Stdout { /// A handle to the standard output of the current process. /// -/// Created by the [`stdout`] function. +/// This writer is created by the [`stdout`] function. See its documentation +/// for more. +/// +/// ### Note: Windows Portability Consideration /// -/// This type is an async version of [`std::io::Stdout`]. +/// 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 -/// [`std::io::Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html #[derive(Debug)] pub struct Stdout(Mutex); diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index abccb431c..aaaff0c67 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -32,7 +32,7 @@ where /// This stream is created by the [`repeat`] function. See its /// documentation for more. /// -/// [`repeat`]: fn.once.html +/// [`repeat`]: fn.repeat.html #[derive(Debug)] pub struct Repeat { item: T, From 4c4604d63ec1305ae10481e956a5309b33c3f483 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 00:08:32 +0100 Subject: [PATCH 079/707] add stream mod docs Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 297 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 288 insertions(+), 9 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index e796510dc..a95e9185d 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -2,24 +2,303 @@ //! //! This module is an async version of [`std::iter`]. //! -//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html +//! If you've found yourself with an asynchronous collection of some kind, +//! and needed to perform an operation on the elements of said collection, +//! you'll quickly run into 'streams'. Streams are heavily used in idiomatic +//! asynchronous Rust code, so it's worth becoming familiar with them. +//! +//! Before explaining more, let's talk about how this module is structured: +//! +//! # Organization +//! +//! This module is largely organized by type: +//! +//! * [Traits] are the core portion: these traits define what kind of streams +//! exist and what you can do with them. The methods of these traits are worth +//! putting some extra study time into. +//! * [Functions] provide some helpful ways to create some basic streams. +//! * [Structs] are often the return types of the various methods on this +//! module's traits. You'll usually want to look at the method that creates +//! the `struct`, rather than the `struct` itself. For more detail about why, +//! see '[Implementing Stream](#implementing-stream)'. +//! +//! [Traits]: #traits +//! [Functions]: #functions +//! [Structs]: #structs +//! +//! That's it! Let's dig into streams. +//! +//! # Stream +//! +//! The heart and soul of this module is the [`Stream`] trait. The core of +//! [`Stream`] looks like this: +//! +//! ``` +//! # use async_std::task::{Context, Poll}; +//! # use std::pin::Pin; +//! trait Stream { +//! type Item; +//! fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; +//! } +//! ``` +//! +//! A stream has a method, [`next`], which when called, returns an +//! [`Poll`]<[`Option`]`>`. [`next`] will return `Ready(Some(Item))` +//! as long as there are elements, and once they've all been exhausted, will +//! return `None` to indicate that iteration is finished. If we're waiting on +//! something asynchronous to resolve `Pending` is returned. +//! +//! Individual streams may choose to resume iteration, and so calling +//! [`next`] again may or may not eventually start returning `Ready(Some(Item))` +//! again at some point. +//! +//! [`Stream`]'s full definition includes a number of other methods as well, +//! but they are default methods, built on top of [`next`], and so you get +//! them for free. +//! +//! Streams are also composable, and it's common to chain them together to do +//! more complex forms of processing. See the [Adapters](#adapters) section +//! below for more details. +//! +//! [`Poll`]: ../task/enum.Poll.html +//! [`Stream`]: trait.Stream.html +//! [`next`]: trait.Stream.html#tymethod.next +//! [`Option`]: ../../std/option/enum.Option.html +//! +//! # The three forms of streaming +//! +//! There are three common methods which can create streams from a collection: +//! +//! * `stream()`, which iterates over `&T`. +//! * `stream_mut()`, which iterates over `&mut T`. +//! * `into_stream()`, which iterates over `T`. +//! +//! Various things in async-std may implement one or more of the +//! three, where appropriate. +//! +//! # Implementing Stream +//! +//! Creating a stream of your own involves two steps: creating a `struct` to +//! hold the stream's state, and then `impl`ementing [`Stream`] for that +//! `struct`. This is why there are so many `struct`s in this module: there is +//! one for each stream and iterator adapter. //! -//! # Examples +//! Let's make a stream named `Counter` which counts from `1` to `5`: //! //! ``` -//! # async_std::task::block_on(async { +//! # use async_std::prelude::*; +//! # use async_std::task::{Context, Poll}; +//! # use std::pin::Pin; +//! // First, the struct: +//! +//! /// A stream which counts from one to five +//! struct Counter { +//! count: usize, +//! } +//! +//! // we want our count to start at one, so let's add a new() method to help. +//! // This isn't strictly necessary, but is convenient. Note that we start +//! // `count` at zero, we'll see why in `next()`'s implementation below. +//! impl Counter { +//! fn new() -> Counter { +//! Counter { count: 0 } +//! } +//! } +//! +//! // Then, we implement `Stream` for our `Counter`: +//! +//! impl Stream for Counter { +//! // we will be counting with usize +//! type Item = usize; +//! +//! // poll_next() is the only required method +//! fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { +//! // Increment our count. This is why we started at zero. +//! self.count += 1; +//! +//! // Check to see if we've finished counting or not. +//! if self.count < 6 { +//! Poll::Ready(Some(self.count)) +//! } else { +//! Poll::Ready(None) +//! } +//! } +//! } +//! +//! // And now we can use it! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut counter = Counter::new(); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); //! # -//! use async_std::prelude::*; -//! use async_std::stream; +//! # Ok(()) }) } +//! ``` +//! +//! This will print `1` through `5`, each on their own line. //! -//! let mut s = stream::repeat(9).take(3); +//! Calling `next().await` this way gets repetitive. Rust has a construct which +//! can call `next()` on your stream, until it reaches `None`. Let's go over +//! that next. //! -//! while let Some(v) = s.next().await { -//! assert_eq!(v, 9); +//! # while let Loops and IntoStream +//! +//! Rust's `while let` loop syntax is actually sugar for streams. Here's a basic +//! example of `while let`: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let mut values = stream::repeat(1u8).take(5); +//! +//! while let Some(x) = values.next().await { +//! println!("{}", x); //! } //! # -//! # }) +//! # Ok(()) }) } //! ``` +//! +//! This will print the numbers one through five, each on their own line. But +//! you'll notice something here: we never called anything on our vector to +//! produce a stream. What gives? +//! +//! There's a trait in the standard library for converting something into an +//! stream: [`IntoStream`]. This trait has one method, [`into_stream], +//! which converts the thing implementing [`IntoStream`] into a stream. +//! +//! 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. +//! +//! [`IntoStream`]: trait.IntoStream.html +//! [`into_stream`]: trait.IntoStream.html#tymethod.into_stream +//! +//! # Adapters +//! +//! Functions which take an [`Stream`] and return another [`Stream`] are +//! often called 'stream adapters', as they're a form of the 'adapter +//! pattern'. +//! +//! Common stream adapters include [`map`], [`take`], and [`filter`]. +//! For more, see their documentation. +//! +//! [`map`]: trait.Stream.html#method.map +//! [`take`]: trait.Stream.html#method.take +//! [`filter`]: trait.Stream.html#method.filter +//! +//! # Laziness +//! +//! Streams (and stream [adapters](#adapters)) are *lazy*. This means that +//! just creating a stream doesn't _do_ a whole lot. Nothing really happens +//! until you call [`next`]. This is sometimes a source of confusion when +//! creating a stream solely for its side effects. For example, the [`map`] +//! method calls a closure on each element it iterates over: +//! +//! ``` +//! # #![allow(unused_must_use)] +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let v = stream::repeat(1u8).take(5); +//! v.map(|x| println!("{}", x)); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! This will not print any values, as we only created a stream, rather than +//! using it. The compiler will warn us about this kind of behavior: +//! +//! ```text +//! warning: unused result that must be used: streams are lazy and +//! do nothing unless consumed +//! ``` +//! +//! The idiomatic way to write a [`map`] for its side effects is to use a +//! `while let` loop instead: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let mut v = stream::repeat(1u8).take(5); +//! +//! while let Some(x) = &v.next().await { +//! println!("{}", x); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`map`]: trait.Stream.html#method.map +//! +//! The two most common ways to evaluate a stream are to use a `while let` loop +//! like this, or using the [`collect`] method to produce a new collection. +//! +//! [`collect`]: trait.Stream.html#method.collect +//! +//! # Infinity +//! +//! Streams do not have to be finite. As an example, an repeat stream is +//! an infinite stream: +//! +//! ``` +//! # use async_std::stream; +//! let numbers = stream::repeat(1u8); +//! ``` +//! +//! It is common to use the [`take`] stream adapter to turn an infinite +//! stream into a finite one: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let numbers = stream::repeat(1u8); +//! let mut five_numbers = numbers.take(5); +//! +//! while let Some(number) = five_numbers.next().await { +//! println!("{}", number); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! This will print the numbers `0` through `4`, each on their own line. +//! +//! Bear in mind that methods on infinite streams, even those for which a +//! result can be determined mathematically in finite time, may not terminate. +//! Specifically, methods such as [`min`], which in the general case require +//! traversing every element in the stream, are likely not to return +//! successfully for any infinite streams. +//! +//! ```ignore +//! let ones = async_std::stream::repeat(1); +//! let least = ones.min().await.unwrap(); // Oh no! An infinite loop! +//! // `ones.min()` causes an infinite loop, so we won't reach this point! +//! println!("The smallest number one is {}.", least); +//! ``` +//! +//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html +//! [`take`]: trait.Stream.html#method.take +//! [`min`]: trait.Stream.html#method.min pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; From 20abd5cebfd7baf15108949ffac446af9d88d2b4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 00:15:13 +0100 Subject: [PATCH 080/707] standardize net docs Signed-off-by: Yoshua Wuyts --- src/net/mod.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/net/mod.rs b/src/net/mod.rs index b3ae287fa..29e430902 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,14 +1,42 @@ //! Networking primitives for TCP/UDP communication. //! -//! For OS-specific networking primitives like Unix domain sockets, refer to the [`async_std::os`] -//! module. +//! This module provides networking functionality for the Transmission Control and User +//! Datagram Protocols, as well as types for IP and socket addresses. //! //! This module is an async version of [`std::net`]. //! +//! # Organization +//! +//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP +//! * [`UdpSocket`] provides functionality for communication over UDP +//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and +//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses +//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] +//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses +//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting +//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] +//! * Other types are return or parameter types for various methods in this module +//! +//! [`IpAddr`]: enum.IpAddr.html +//! [`Ipv4Addr`]: struct.Ipv4Addr.html +//! [`Ipv6Addr`]: struct.Ipv6Addr.html +//! [`SocketAddr`]: enum.SocketAddr.html +//! [`SocketAddrV4`]: struct.SocketAddrV4.html +//! [`SocketAddrV6`]: struct.SocketAddrV6.html +//! [`TcpListener`]: struct.TcpListener.html +//! [`TcpStream`]: struct.TcpStream.html +//! [`ToSocketAddrs`]: trait.ToSocketAddrs.html +//! [`UdpSocket`]: struct.UdpSocket.html +//! +//! # Platform-specific extensions +//! +//! APIs such as Unix domain sockets are available on certain platforms only. You can find +//! platform-specific extensions in the [`async_std::os`] module. +//! //! [`async_std::os`]: ../os/index.html //! [`std::net`]: https://doc.rust-lang.org/std/net/index.html //! -//! ## Examples +//! # Examples //! //! A simple UDP echo server: //! From 5f8e2cbd4a4917b0444447c1c73905bef9341c0d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 00:34:27 +0100 Subject: [PATCH 081/707] add mod level docs for sync Signed-off-by: Yoshua Wuyts --- src/sync/mod.rs | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 3ad2776ca..0fe73225a 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -4,6 +4,149 @@ //! //! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html //! +//! ## The need for synchronization +//! +//! Conceptually, a Rust program is a series of operations which will +//! be executed on a computer. The timeline of events happening in the +//! program is consistent with the order of the operations in the code. +//! +//! Consider the following code, operating on some global static variables: +//! +//! ```rust +//! static mut A: u32 = 0; +//! static mut B: u32 = 0; +//! static mut C: u32 = 0; +//! +//! fn main() { +//! unsafe { +//! A = 3; +//! B = 4; +//! A = A + B; +//! C = B; +//! println!("{} {} {}", A, B, C); +//! C = A; +//! } +//! } +//! ``` +//! +//! It appears as if some variables stored in memory are changed, an addition +//! is performed, result is stored in `A` and the variable `C` is +//! modified twice. +//! +//! When only a single thread is involved, the results are as expected: +//! the line `7 4 4` gets printed. +//! +//! As for what happens behind the scenes, when optimizations are enabled the +//! final generated machine code might look very different from the code: +//! +//! - The first store to `C` might be moved before the store to `A` or `B`, +//! _as if_ we had written `C = 4; A = 3; B = 4`. +//! +//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored +//! in a temporary location until it gets printed, with the global variable +//! never getting updated. +//! +//! - The final result could be determined just by looking at the code +//! at compile time, so [constant folding] might turn the whole +//! block into a simple `println!("7 4 4")`. +//! +//! The compiler is allowed to perform any combination of these +//! optimizations, as long as the final optimized code, when executed, +//! produces the same results as the one without optimizations. +//! +//! Due to the [concurrency] involved in modern computers, assumptions +//! about the program's execution order are often wrong. Access to +//! global variables can lead to nondeterministic results, **even if** +//! compiler optimizations are disabled, and it is **still possible** +//! to introduce synchronization bugs. +//! +//! Note that thanks to Rust's safety guarantees, accessing global (static) +//! variables requires `unsafe` code, assuming we don't use any of the +//! synchronization primitives in this module. +//! +//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding +//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science) +//! +//! ## Out-of-order execution +//! +//! Instructions can execute in a different order from the one we define, due to +//! various reasons: +//! +//! - The **compiler** reordering instructions: If the compiler can issue an +//! instruction at an earlier point, it will try to do so. For example, it +//! might hoist memory loads at the top of a code block, so that the CPU can +//! start [prefetching] the values from memory. +//! +//! In single-threaded scenarios, this can cause issues when writing +//! signal handlers or certain kinds of low-level code. +//! Use [compiler fences] to prevent this reordering. +//! +//! - A **single processor** executing instructions [out-of-order]: +//! Modern CPUs are capable of [superscalar] execution, +//! i.e., multiple instructions might be executing at the same time, +//! even though the machine code describes a sequential process. +//! +//! This kind of reordering is handled transparently by the CPU. +//! +//! - A **multiprocessor** system executing multiple hardware threads +//! 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. +//! - [atomic operations] to ensure simultaneous access to the same +//! 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 +//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution +//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor +//! [memory fences]: https://doc.rust-lang.org/std/sync/atomic/fn.fence.html +//! [atomic operations]: https://doc.rust-lang.org/std/sync/atomic/index.html +//! +//! ## Higher-level synchronization objects +//! +//! Most of the low-level synchronization primitives are quite error-prone and +//! inconvenient to use, which is why async-std also exposes some +//! higher-level synchronization objects. +//! +//! These abstractions can be built out of lower-level primitives. +//! For efficiency, the sync objects in async-std are usually +//! implemented with help from the scheduler, which is +//! able to reschedule the tasks while they are blocked on acquiring +//! a lock. +//! +//! The following is an overview of the available synchronization +//! objects: +//! +//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used +//! in multithreaded environments to prolong the lifetime of some +//! data until all the threads have finished using it. +//! +//! - [`Barrier`]: Ensures multiple threads will wait for each other +//! 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. +//! +//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows +//! multiple readers at the same time, while allowing only one +//! writer at a time. In some cases, this can be more efficient than +//! a mutex. +//! +//! [`Arc`]: crate::sync::Arc +//! [`Barrier`]: crate::sync::Barrier +//! [`Condvar`]: crate::sync::Condvar +//! [`channel`]: fn.channel.html +//! [`Mutex`]: crate::sync::Mutex +//! [`Once`]: crate::sync::Once +//! [`RwLock`]: crate::sync::RwLock +//! //! # Examples //! //! Spawn a task that updates an integer protected by a mutex: From 613895d6be615430beb322c766b940387bd2992c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Mon, 28 Oct 2019 13:58:54 +0900 Subject: [PATCH 082/707] doc: fix documantation text --- src/future/future.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/future.rs b/src/future/future.rs index e8075f1bf..fe685176a 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -107,7 +107,7 @@ extension_trait! { } pub trait FutureExt: std::future::Future { - /// Creates a future that is delayed before it starts yielding items. + /// Returns a Future that delays execution for a specified time. /// /// # Examples /// From 434638661027d596b200da305fecda0b3ff48190 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 12:42:23 +0100 Subject: [PATCH 083/707] fix doc recursion limit Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ad5aa8fab..b659c39e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ #![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 = "1024"] +#![recursion_limit = "2048"] #[macro_use] mod utils; From b3ae6f2b03216ca88eca503d2834f0b1e2c9ce7f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 13:00:25 +0100 Subject: [PATCH 084/707] update Stream::fuse docs Signed-off-by: Yoshua Wuyts --- src/stream/stream/fuse.rs | 3 +-- src/stream/stream/mod.rs | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 39af9cb0f..6297bef7d 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -6,8 +6,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - /// A `Stream` that is permanently closed once a single call to `poll` results in - /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. + /// A stream that yields `None` forever after the underlying stream yields `None` once. /// /// This `struct` is created by the [`fuse`] method on [`Stream`]. See its /// documentation for more. diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2b237de62..f8640c8ad 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -501,9 +501,11 @@ extension_trait! { } #[doc = r#" - Transforms this `Stream` into a "fused" `Stream` such that after the first time - `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return - `Poll::Ready(None)`. + 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 From c7dc147f739d3be5917b254cf85fd0733bd4f014 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 29 Oct 2019 09:27:35 +0900 Subject: [PATCH 085/707] fix indent --- src/future/future/delay.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index 53a4d75aa..d672541ee 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -8,8 +8,8 @@ use crate::future::Future; use crate::task::{Context, Poll}; pin_project! { -#[doc(hidden)] -#[derive(Debug)] + #[doc(hidden)] + #[derive(Debug)] pub struct DelayFuture { #[pin] future: F, From 688976203e0ff6f564647f2f21b89fea7fafae88 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 29 Oct 2019 09:52:50 +0900 Subject: [PATCH 086/707] fix: Split FlattenCompat logic to Flatten and FlatMap --- src/stream/stream/flat_map.rs | 62 +++++++++++++++++++ src/stream/stream/flatten.rs | 112 +++------------------------------- src/stream/stream/mod.rs | 4 +- 3 files changed, 72 insertions(+), 106 deletions(-) create mode 100644 src/stream/stream/flat_map.rs diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs new file mode 100644 index 000000000..ed3268ea9 --- /dev/null +++ b/src/stream/stream/flat_map.rs @@ -0,0 +1,62 @@ +use pin_project_lite::pin_project; +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::stream::map::Map; +use crate::stream::{IntoStream, Stream}; +use crate::task::{Context, Poll}; + +pin_project! { + /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`flat_map`]: trait.Stream.html#method.flat_map + /// [`Stream`]: trait.Stream.html + #[allow(missing_debug_implementations)] + pub struct FlatMap { + #[pin] + stream: Map, + #[pin] + inner_stream: Option, + } +} + +impl FlatMap +where + S: Stream, + U: IntoStream, + F: FnMut(S::Item) -> U, +{ + pub(super) fn new(stream: S, f: F) -> FlatMap { + FlatMap { + stream: stream.map(f), + inner_stream: None, + } + } +} + +impl Stream for FlatMap +where + S: Stream, + S::Item: IntoStream, + U: Stream, + F: FnMut(S::Item) -> U, +{ + type Item = U::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + 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); + } + } + + 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())), + } + } + } +} diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 2533f3794..2ea0673ef 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -6,46 +6,6 @@ use crate::stream::stream::map::Map; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; -pin_project! { - /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its - /// documentation for more. - /// - /// [`flat_map`]: trait.Stream.html#method.flat_map - /// [`Stream`]: trait.Stream.html - #[allow(missing_debug_implementations)] - pub struct FlatMap { - #[pin] - inner: FlattenCompat, U>, - } -} - -impl FlatMap -where - S: Stream, - U: IntoStream, - F: FnMut(S::Item) -> U, -{ - pub(super) fn new(stream: S, f: F) -> FlatMap { - FlatMap { - inner: FlattenCompat::new(stream.map(f)), - } - } -} - -impl Stream for FlatMap -where - S: Stream, - S::Item: IntoStream, - U: Stream, - F: FnMut(S::Item) -> U, -{ - type Item = U::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().inner.poll_next(cx) - } -} - pin_project! { /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its /// documentation for more. @@ -55,7 +15,9 @@ pin_project! { #[allow(missing_debug_implementations)] pub struct Flatten { #[pin] - inner: FlattenCompat + stream: S, + #[pin] + inner_stream: Option, } } @@ -66,47 +28,13 @@ where { pub(super) fn new(stream: S) -> Flatten { Flatten { - inner: FlattenCompat::new(stream), - } - } -} - -impl Stream for Flatten::IntoStream> -where - S: Stream, - S::Item: IntoStream, - U: Stream, -{ - type Item = U::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().inner.poll_next(cx) - } -} - -pin_project! { - /// Real logic of both `Flatten` and `FlatMap` which simply delegate to - /// this type. - #[derive(Clone, Debug)] - struct FlattenCompat { - #[pin] - stream: S, - #[pin] - frontiter: Option, - } -} - -impl FlattenCompat { - /// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. - fn new(stream: S) -> FlattenCompat { - FlattenCompat { stream, - frontiter: None, + inner_stream: None, } } } -impl Stream for FlattenCompat +impl Stream for Flatten::IntoStream> where S: Stream, S::Item: IntoStream, @@ -117,7 +45,7 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); loop { - if let Some(inner) = this.frontiter.as_mut().as_pin_mut() { + 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); } @@ -125,34 +53,8 @@ where match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { None => return Poll::Ready(None), - Some(inner) => this.frontiter.set(Some(inner.into_stream())), + Some(inner) => this.inner_stream.set(Some(inner.into_stream())), } } } } - -#[cfg(test)] -mod tests { - use super::FlattenCompat; - - use crate::prelude::*; - use crate::task; - - use std::collections::VecDeque; - - #[test] - fn test_poll_next() -> std::io::Result<()> { - let inner1: VecDeque = vec![1, 2, 3].into_iter().collect(); - let inner2: VecDeque = vec![4, 5, 6].into_iter().collect(); - - let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); - - task::block_on(async move { - let flat = FlattenCompat::new(s); - let v: Vec = flat.collect().await; - - assert_eq!(v, vec![1, 2, 3, 4, 5, 6]); - Ok(()) - }) - } -} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 596dc4190..29e68fc4e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -99,10 +99,12 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; pub use merge::Merge; - pub use flatten::{FlatMap, Flatten}; + pub use flatten::Flatten; + pub use flat_map::FlatMap; mod merge; mod flatten; + mod flat_map; } extension_trait! { From ae7adf2c366e388a59ef3417dc7726892dfcf14c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 29 Oct 2019 10:01:41 +0900 Subject: [PATCH 087/707] fix: Remove unused import --- 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 2ea0673ef..f00498649 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,8 +1,8 @@ use pin_project_lite::pin_project; use std::pin::Pin; -use crate::prelude::*; -use crate::stream::stream::map::Map; + + use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; From 1554b0440743a7551ed3c23a88586a5832dca592 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 29 Oct 2019 10:12:22 +0900 Subject: [PATCH 088/707] $cargo fmt --- src/stream/stream/flatten.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index f00498649..5e791cda3 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,8 +1,6 @@ use pin_project_lite::pin_project; use std::pin::Pin; - - use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; From eb081b1948edf525ce459ef560eb4b13f8d49600 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 29 Oct 2019 10:23:54 +0100 Subject: [PATCH 089/707] Apply suggestions from code review Co-Authored-By: Florian Gilcher --- src/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index a95e9185d..6db0dbe92 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -156,7 +156,7 @@ //! //! # while let Loops and IntoStream //! -//! Rust's `while let` loop syntax is actually sugar for streams. Here's a basic +//! Rust's `while let` loop syntax is an idiomatic way to iterate over streams. Here's a basic //! example of `while let`: //! //! ``` @@ -191,7 +191,7 @@ //! # Adapters //! //! Functions which take an [`Stream`] and return another [`Stream`] are -//! often called 'stream adapters', as they're a form of the 'adapter +//! often called 'stream adapters', as they are a form of the 'adapter //! pattern'. //! //! Common stream adapters include [`map`], [`take`], and [`filter`]. From 3a06a1211b0f8787854d32e3cf5eb0d8fdd769c8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 29 Oct 2019 10:56:33 +0100 Subject: [PATCH 090/707] Add feedback from review Signed-off-by: Yoshua Wuyts --- src/sync/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 0fe73225a..d10e6bdf2 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -6,6 +6,9 @@ //! //! ## The need for synchronization //! +//! async-std's sync primitives are scheduler-aware, making it possible to +//! `.await` their operations - for example the locking of a [`Mutex`]. +//! //! Conceptually, a Rust program is a series of operations which will //! be executed on a computer. The timeline of events happening in the //! program is consistent with the order of the operations in the code. From b3d1fa9c98363c5dbc180e78407f4b29027f5fc4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 28 Oct 2019 11:33:40 +0100 Subject: [PATCH 091/707] v0.99.11 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e735a1b..19af02edb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [0.99.11] - 2019-10-29 + +This patch introduces `async_std::sync::channel`, a novel asynchronous port of +the ultra-fast Crossbeam channels. This has been one of the most anticipated +features for async-std, and we're excited to be providing a first version of +this! + +In addition to channels, this patch has the regular list of new methods, types, +and doc fixes. + +## Examples + +__Send and receive items from a channel__ +```rust +// Create a bounded channel with a max-size of 1 +let (s, r) = channel(1); + +// This call returns immediately because there is enough space in the channel. +s.send(1).await; + +task::spawn(async move { + // This call blocks the current task because the channel is full. + // It will be able to complete only after the first message is received. + s.send(2).await; +}); + +// Receive items from the channel +task::sleep(Duration::from_secs(1)).await; +assert_eq!(r.recv().await, Some(1)); +assert_eq!(r.recv().await, Some(2)); +``` + +## Added +- Added `sync::channel` as "unstable". +- Added doc links from instantiated structs to the methods that create them. +- Implemented `Extend` + `FromStream` for `PathBuf`. + +## Changed +- Fixed an issue with `block_on` so it works even when nested. +- Fixed issues with our Clippy check on CI. +- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase. +- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs). +- Updated the module-level documentation for `stream` and `sync`. +- Various typos and grammar fixes. + +## Removed +Nothing was removed in this release. + # [0.99.10] - 2019-10-16 This patch stabilizes several core concurrency macros, introduces async versions @@ -281,7 +329,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.11...HEAD +[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 [0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10 [0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8 diff --git a/Cargo.toml b/Cargo.toml index ad8873037..ab74dd015 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.10" +version = "0.99.11" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From b10930207cde0afc6821521d7b1bdd2374b5398d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 29 Oct 2019 00:44:07 +0100 Subject: [PATCH 092/707] more Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19af02edb..c34fa24e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,9 @@ assert_eq!(r.recv().await, Some(2)); - Added `sync::channel` as "unstable". - Added doc links from instantiated structs to the methods that create them. - Implemented `Extend` + `FromStream` for `PathBuf`. +- Added `Stream::sum` as "unstable" +- Added `Stream::product` as "unstable" +- Added `Stream::timeout` as "unstable" ## Changed - Fixed an issue with `block_on` so it works even when nested. @@ -51,6 +54,7 @@ assert_eq!(r.recv().await, Some(2)); - Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs). - Updated the module-level documentation for `stream` and `sync`. - Various typos and grammar fixes. +- Removed redundant file flushes, improving the performance of `File` operations ## Removed Nothing was removed in this release. From 2adaaa9d3f1fadad44ec58be8af72cd0d839054f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 29 Oct 2019 02:24:14 +0100 Subject: [PATCH 093/707] more updates Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c34fa24e9..eda60387a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,12 +40,17 @@ assert_eq!(r.recv().await, Some(2)); ``` ## Added +- Added `Future::delay` as "unstable" +- Added `Stream::flat_map` as "unstable" +- Added `Stream::flatten` as "unstable" +- Added `Stream::product` as "unstable" +- Added `Stream::sum` as "unstable" +- Added `Stream::min_by_key` +- Added `Stream::max_by` +- Added `Stream::timeout` as "unstable" - Added `sync::channel` as "unstable". - Added doc links from instantiated structs to the methods that create them. - Implemented `Extend` + `FromStream` for `PathBuf`. -- Added `Stream::sum` as "unstable" -- Added `Stream::product` as "unstable" -- Added `Stream::timeout` as "unstable" ## Changed - Fixed an issue with `block_on` so it works even when nested. From b942d0a40580e1df63ddcbf7505df0fe625c5a77 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Tue, 29 Oct 2019 21:44:56 +0800 Subject: [PATCH 094/707] add stream-min --- src/stream/stream/min.rs | 60 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 37 +++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/stream/stream/min.rs diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs new file mode 100644 index 000000000..1ab56065d --- /dev/null +++ b/src/stream/stream/min.rs @@ -0,0 +1,60 @@ +use std::marker::PhantomData; +use std::cmp::{Ordering, Ord}; +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinFuture { + #[pin] + stream: S, + _compare: PhantomData, + min: Option, + } +} + +impl MinFuture { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + _compare: PhantomData, + min: None, + } + } +} + +impl Future for MinFuture +where + S: Stream, + S::Item: Ord, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.min.take() { + None => *this.min = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Less => *this.min = Some(new), + _ => *this.min = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.min.take()), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e8190387e..27090a5be 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -41,6 +41,7 @@ mod le; mod lt; mod map; mod max_by; +mod min; mod min_by; mod min_by_key; mod next; @@ -71,6 +72,7 @@ use last::LastFuture; use le::LeFuture; use lt::LtFuture; use max_by::MaxByFuture; +use min::MinFuture; use min_by::MinByFuture; use min_by_key::MinByKeyFuture; use next::NextFuture; @@ -753,6 +755,41 @@ extension_trait! { self, compare: F, ) -> impl Future> [MinByFuture] + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MinByFuture::new(self, compare) + } + + #[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. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + + let min = s.clone().min().await; + assert_eq!(min, Some(1)); + + let min = VecDeque::::new().min().await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by( + self, + compare: F, + ) -> impl Future> [MinByFuture] where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, From 021862dcc88e6bdda67f010cd0d127e741efae1e Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Tue, 29 Oct 2019 21:49:30 +0800 Subject: [PATCH 095/707] fix min --- src/stream/stream/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 27090a5be..5c42989f7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -786,15 +786,14 @@ extension_trait! { # }) } ``` "#] - fn min_by( + fn min( self, - compare: F, - ) -> impl Future> [MinByFuture] + ) -> impl Future> [MinFuture] where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - MinByFuture::new(self, compare) + MinFuture::new(self) } #[doc = r#" From 2c91b30ee8613b3ac0319513996ce7b8c9ee8782 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 29 Oct 2019 23:02:18 +0900 Subject: [PATCH 096/707] feat: Add Read and Write trait to Lock struct --- src/io/mod.rs | 6 +++--- src/io/stderr.rs | 24 +++++++++++++++--------- src/io/stdin.rs | 15 +++++++++++---- src/io/stdout.rs | 24 +++++++++++++++--------- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 9a125b20b..c81d82f95 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -282,9 +282,9 @@ pub use read::Read; pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; -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; pub use write::Write; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 7cd95aa5d..4e727f210 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,4 +1,5 @@ use lazy_static::lazy_static; +use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -54,6 +55,11 @@ 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 #[derive(Debug)] pub struct StderrLock<'a>(std::io::StderrLock<'a>); @@ -104,12 +110,12 @@ impl Stderr { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; - /// use std::io::Write; + /// use async_std::prelude::*; /// /// let stderr = io::stderr(); /// let mut handle = stderr.lock().await; /// - /// handle.write_all(b"hello world")?; + /// handle.write_all(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` @@ -227,18 +233,18 @@ cfg_windows! { impl Write for StderrLock<'_> { fn poll_write( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, - _buf: &[u8], + buf: &[u8], ) -> Poll> { - unimplemented!() + Poll::Ready(self.0.write(buf)) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - unimplemented!() + 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> { - unimplemented!() + 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 82a0b00b1..9fb28bab8 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -55,6 +55,11 @@ 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 #[derive(Debug)] pub struct StdinLock<'a>(std::io::StdinLock<'a>); @@ -151,7 +156,7 @@ impl 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 and BufRead traits for accessing the underlying data. + /// 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 /// @@ -251,10 +256,12 @@ cfg_windows! { impl Read for StdinLock<'_> { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, - _buf: &mut [u8], + buf: &mut [u8], ) -> Poll> { - unimplemented!() + use std::io::Read as StdRead; + + Poll::Ready(self.0.read(buf)) } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 8d4ba273c..c314837bb 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,4 +1,5 @@ use lazy_static::lazy_static; +use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -54,6 +55,11 @@ 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 #[derive(Debug)] pub struct StdoutLock<'a>(std::io::StdoutLock<'a>); @@ -104,12 +110,12 @@ impl Stdout { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; - /// use std::io::Write; + /// use async_std::prelude::*; /// /// let stdout = io::stdout(); /// let mut handle = stdout.lock().await; /// - /// handle.write_all(b"hello world")?; + /// handle.write_all(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` @@ -227,18 +233,18 @@ cfg_windows! { impl Write for StdoutLock<'_> { fn poll_write( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, - _buf: &[u8], + buf: &[u8], ) -> Poll> { - unimplemented!() + Poll::Ready(self.0.write(buf)) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - unimplemented!() + 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> { - unimplemented!() + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) } } From 3620b2b6abcc42ef1955803805a2ffd322bd61ef Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 30 Oct 2019 09:17:12 +0900 Subject: [PATCH 097/707] fix: Add only rustfmt on Checking fmt and docs actions --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 653834a77..dd8ec8997 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,8 +59,10 @@ jobs: - uses: actions-rs/toolchain@v1 with: + profile: minimal toolchain: ${{ steps.component.outputs.toolchain }} override: true + components: rustfmt - name: setup run: | From 40c4e1a29d7946faa5052389517d1f01b0f2d7d9 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 30 Oct 2019 10:33:59 +0900 Subject: [PATCH 098/707] feat: Add stream::from_iter --- src/stream/from_iter.rs | 44 +++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 ++ 2 files changed, 46 insertions(+) create mode 100644 src/stream/from_iter.rs diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs new file mode 100644 index 000000000..8d3dba78b --- /dev/null +++ b/src/stream/from_iter.rs @@ -0,0 +1,44 @@ +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[derive(Debug)] + pub struct FromIter { + iter: I, + } +} + +/// # Examples +///``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut s = stream::from_iter(vec![0, 1, 2, 3]); +/// +/// assert_eq!(s.next().await, Some(0)); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(3)); +/// assert_eq!(s.next().await, None); +/// # +/// # }) +///```` +pub fn from_iter(iter: I) -> FromIter<::IntoIter> { + FromIter { + iter: iter.into_iter(), + } +} + +impl Stream for FromIter { + type Item = I::Item; + + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.iter.next()) + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 6db0dbe92..07eecf28c 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -302,6 +302,7 @@ pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; +pub use from_iter::{from_iter, FromIter}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; @@ -313,6 +314,7 @@ pub(crate) mod stream; mod empty; mod from_fn; +mod from_iter; mod once; mod repeat; mod repeat_with; From ff6a44fcd5e6b122ca42ae7563b7d155bcde6f66 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 30 Oct 2019 19:23:08 +0800 Subject: [PATCH 099/707] Use once_cell instead of lazy_static (#416) `once_cell` provides a neat way of initializing lazy singletons without macro. This PR use `sync::Lazy` to streamline same pattern proposed in related rust RFC. Resolve #406 --- Cargo.toml | 2 +- src/net/driver/mod.rs | 34 +++++++++++++++----------------- src/task/blocking.rs | 44 +++++++++++++++++++++--------------------- src/task/pool.rs | 40 ++++++++++++++++++-------------------- src/task/task_local.rs | 6 ++---- 5 files changed, 60 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab74dd015..dcf2c7d00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,12 +33,12 @@ crossbeam-utils = "0.6.6" futures-core-preview = "=0.3.0-alpha.19" futures-io-preview = "=0.3.0-alpha.19" futures-timer = "1.0.2" -lazy_static = "1.4.0" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" mio = "0.6.19" mio-uds = "0.6.7" num_cpus = "1.10.1" +once_cell = "1.2.0" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" kv-log-macro = "1.0.4" diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 806acdbe4..40e0abb29 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -1,8 +1,8 @@ use std::fmt; use std::sync::{Arc, Mutex}; -use lazy_static::lazy_static; use mio::{self, Evented}; +use once_cell::sync::Lazy; use slab::Slab; use crate::io; @@ -100,25 +100,23 @@ impl Reactor { // } } -lazy_static! { - /// The state of the global networking driver. - static ref REACTOR: Reactor = { - // 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-net-driver".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"); - }) +/// 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-net-driver".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"); + }) + .expect("cannot start a thread driving blocking tasks"); - Reactor::new().expect("cannot initialize reactor") - }; -} + 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<()> { diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 3216012a7..1f1a222a3 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -5,7 +5,7 @@ use std::thread; use std::time::Duration; use crossbeam_channel::{bounded, Receiver, Sender}; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use crate::task::task::{JoinHandle, Tag}; use crate::utils::abort_on_panic; @@ -19,30 +19,30 @@ struct Pool { receiver: Receiver>, } -lazy_static! { - static ref POOL: Pool = { - for _ in 0..2 { - thread::Builder::new() - .name("async-blocking-driver".to_string()) - .spawn(|| abort_on_panic(|| { +static POOL: Lazy = Lazy::new(|| { + for _ in 0..2 { + thread::Builder::new() + .name("async-blocking-driver".to_string()) + .spawn(|| { + abort_on_panic(|| { for task in &POOL.receiver { task.run(); } - })) - .expect("cannot start a thread driving blocking tasks"); - } - - // We want to use an unbuffered channel here to help - // us drive our dynamic control. In effect, the - // kernel's scheduler becomes the queue, reducing - // the number of buffers that work must flow through - // before being acted on by a core. This helps keep - // latency snappy in the overall async system by - // reducing bufferbloat. - let (sender, receiver) = bounded(0); - Pool { sender, receiver } - }; -} + }) + }) + .expect("cannot start a thread driving blocking tasks"); + } + + // We want to use an unbuffered channel here to help + // us drive our dynamic control. In effect, the + // kernel's scheduler becomes the queue, reducing + // the number of buffers that work must flow through + // before being acted on by a core. This helps keep + // latency snappy in the overall async system by + // reducing bufferbloat. + let (sender, receiver) = bounded(0); + Pool { sender, receiver } +}); // Create up to MAX_THREADS dynamic blocking task worker threads. // Dynamic threads will terminate themselves if they don't diff --git a/src/task/pool.rs b/src/task/pool.rs index bfaa17d48..3fd704708 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -3,7 +3,7 @@ use std::thread; use crossbeam_deque::{Injector, Stealer, Worker}; use kv_log_macro::trace; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use super::sleepers::Sleepers; use super::task; @@ -111,28 +111,26 @@ impl Pool { #[inline] pub(crate) fn get() -> &'static Pool { - lazy_static! { - static ref POOL: Pool = { - let num_threads = num_cpus::get().max(1); - let mut stealers = Vec::new(); + 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()); + // Spawn worker threads. + for _ in 0..num_threads { + let worker = Worker::new_fifo(); + stealers.push(worker.stealer()); - thread::Builder::new() - .name("async-task-driver".to_string()) - .spawn(|| abort_on_panic(|| worker::main_loop(worker))) - .expect("cannot start a thread driving tasks"); - } + thread::Builder::new() + .name("async-task-driver".to_string()) + .spawn(|| abort_on_panic(|| worker::main_loop(worker))) + .expect("cannot start a thread driving tasks"); + } - Pool { - injector: Injector::new(), - stealers, - sleepers: Sleepers::new(), - } - }; - } + Pool { + injector: Injector::new(), + stealers, + sleepers: Sleepers::new(), + } + }); &*POOL } diff --git a/src/task/task_local.rs b/src/task/task_local.rs index c72937f6f..e92f4f929 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -5,7 +5,7 @@ use std::future::Future; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use super::worker; use crate::utils::abort_on_panic; @@ -174,9 +174,7 @@ impl LocalKey { fn key(&self) -> usize { #[cold] fn init(key: &AtomicUsize) -> usize { - lazy_static! { - static ref COUNTER: Mutex = Mutex::new(1); - } + static COUNTER: Lazy> = Lazy::new(|| Mutex::new(1)); let mut counter = COUNTER.lock().unwrap(); let prev = key.compare_and_swap(0, *counter, Ordering::AcqRel); From 5fee91c0502cff2618649210928c50c116474d7a Mon Sep 17 00:00:00 2001 From: JayatiGoyal <44127709+JayatiGoyal@users.noreply.github.com> Date: Thu, 31 Oct 2019 00:36:42 +0530 Subject: [PATCH 100/707] corrected a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7aeaed86a..9af20a39f 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ syntax. ## Features - __Modern:__ Built from the ground up for `std::future` and `async/await` with - blazing fast compilation times. + blazing fast compilation time. - __Fast:__ Our robust allocator and threadpool designs provide ultra-high throughput with predictably low latency. - __Intuitive:__ Complete parity with the stdlib means you only need to learn From f5efaaa7ba82e6a0707a82ffa6cda499fdb6d694 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Thu, 31 Oct 2019 14:44:19 +0800 Subject: [PATCH 101/707] Add stream eq --- src/stream/stream/eq.rs | 61 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 38 +++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/stream/stream/eq.rs diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs new file mode 100644 index 000000000..42a37d844 --- /dev/null +++ b/src/stream/stream/eq.rs @@ -0,0 +1,61 @@ +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use super::fuse::Fuse; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct EqFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + } +} + +impl EqFuture +where + L::Item: PartialEq, +{ + pub(super) fn new(l: L, r: R) -> Self { + EqFuture { + l: l.fuse(), + r: r.fuse(), + } + } +} + +impl Future for EqFuture + where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx)); + let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx)); + + if this.l.done && this.r.done { + return Poll::Ready(true); + } + + match (l_val, r_val) { + (Some(l), Some(r)) if l != r => {return Poll::Ready(false);}, + _ => {}, + } + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e8190387e..07cd03a13 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -26,6 +26,7 @@ mod any; mod chain; mod cmp; mod enumerate; +mod eq; mod filter; mod filter_map; mod find; @@ -60,6 +61,7 @@ use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; use enumerate::Enumerate; +use eq::EqFuture; use filter_map::FilterMap; use find::FindFuture; use find_map::FindMapFuture; @@ -1622,6 +1624,42 @@ extension_trait! { GeFuture::new(self, other) } + #[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 std::collections::VecDeque; + + let single: VecDeque = vec![1].into_iter().collect(); + let single_eq: VecDeque = vec![10].into_iter().collect(); + let multi: VecDeque = vec![1,2].into_iter().collect(); + let multi_eq: VecDeque = vec![1,5].into_iter().collect(); + 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 + ) -> impl Future [EqFuture] + where + Self: Sized + Stream, + S: Sized + Stream, + ::Item: PartialEq, + { + EqFuture::new(self, other) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than those of another. From 17db7ffcd35e4c7e350adba6e3f39daddce52536 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Thu, 31 Oct 2019 18:05:51 +0800 Subject: [PATCH 102/707] Add stream ne --- src/stream/stream/mod.rs | 35 +++++++++++++++++++++++ src/stream/stream/ne.rs | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/stream/stream/ne.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e8190387e..b8fac7e58 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -43,6 +43,7 @@ mod map; mod max_by; mod min_by; mod min_by_key; +mod ne; mod next; mod nth; mod partial_cmp; @@ -73,6 +74,7 @@ use lt::LtFuture; use max_by::MaxByFuture; use min_by::MinByFuture; use min_by_key::MinByKeyFuture; +use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; @@ -1586,6 +1588,39 @@ extension_trait! { CmpFuture::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 std::collections::VecDeque; + let single: VecDeque = vec![1].into_iter().collect(); + let single_ne: VecDeque = vec![10].into_iter().collect(); + let multi: VecDeque = vec![1,2].into_iter().collect(); + let multi_ne: VecDeque = vec![1,5].into_iter().collect(); + 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 + ) -> impl Future [NeFuture] + where + Self: Sized + Stream, + S: Sized + Stream, + ::Item: PartialEq, + { + NeFuture::new(self, other) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than or equal to those of another. diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs new file mode 100644 index 000000000..2f17ed0e0 --- /dev/null +++ b/src/stream/stream/ne.rs @@ -0,0 +1,62 @@ +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use super::fuse::Fuse; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct NeFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + } +} + +impl NeFuture + where + L::Item: PartialEq, +{ + pub(super) fn new(l: L, r: R) -> Self { + Self { + l: l.fuse(), + r: r.fuse(), + } + } +} + +impl Future for NeFuture + where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx)); + let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx)); + + if this.l.done || this.r.done { + return Poll::Ready(false); + } + + match (l_val, r_val) { + (Some(l), Some(r)) if l == r => {continue;}, + _ => { return Poll::Ready(true); }, + } + + } + } +} \ No newline at end of file From 204da3339152596667ff6e6872afda73c997f517 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Thu, 31 Oct 2019 21:16:13 +0800 Subject: [PATCH 103/707] fmt code --- src/stream/stream/ne.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs index 2f17ed0e0..ffeaca815 100644 --- a/src/stream/stream/ne.rs +++ b/src/stream/stream/ne.rs @@ -22,8 +22,8 @@ pin_project! { } impl NeFuture - where - L::Item: PartialEq, +where + L::Item: PartialEq, { pub(super) fn new(l: L, r: R) -> Self { Self { @@ -34,10 +34,10 @@ impl NeFuture } impl Future for NeFuture - where - L: Stream + Sized, - R: Stream + Sized, - L::Item: PartialEq, +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, { type Output = bool; @@ -53,10 +53,13 @@ impl Future for NeFuture } match (l_val, r_val) { - (Some(l), Some(r)) if l == r => {continue;}, - _ => { return Poll::Ready(true); }, + (Some(l), Some(r)) if l == r => { + continue; + } + _ => { + return Poll::Ready(true); + } } - } } -} \ No newline at end of file +} From 1ab3d901e42fb7c2e9b44303c2b4cdf5116d68b9 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Thu, 31 Oct 2019 21:17:07 +0800 Subject: [PATCH 104/707] fmt code --- src/stream/stream/eq.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index 42a37d844..5343c1a0c 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -34,10 +34,10 @@ where } impl Future for EqFuture - where - L: Stream + Sized, - R: Stream + Sized, - L::Item: PartialEq, +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, { type Output = bool; @@ -53,8 +53,10 @@ impl Future for EqFuture } match (l_val, r_val) { - (Some(l), Some(r)) if l != r => {return Poll::Ready(false);}, - _ => {}, + (Some(l), Some(r)) if l != r => { + return Poll::Ready(false); + } + _ => {} } } } From 48c82a9668ec3f18246d7cb5066b72f1e5e3133d Mon Sep 17 00:00:00 2001 From: zhangguyu Date: Thu, 31 Oct 2019 22:33:17 +0800 Subject: [PATCH 105/707] Add stream position --- src/stream/stream/mod.rs | 41 ++++++++++++++++++++++++++++ src/stream/stream/position.rs | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/stream/stream/position.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e8190387e..c4abe32f1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -46,6 +46,7 @@ mod min_by_key; mod next; mod nth; mod partial_cmp; +mod position; mod scan; mod skip; mod skip_while; @@ -76,6 +77,7 @@ use min_by_key::MinByKeyFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEeachFuture; @@ -1548,6 +1550,45 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Searches for an element in a Stream that satisfies a predicate, returning + its index. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + 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

( + self, + predicate: P + ) -> impl Future> [PositionFuture] + where + Self: Sized + Stream, + P: FnMut(&Self::Item) -> bool, + { + PositionFuture::new(self, predicate) + } + #[doc = r#" Lexicographically compares the elements of this `Stream` with those of another using 'Ord'. diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs new file mode 100644 index 000000000..3cd5b84cd --- /dev/null +++ b/src/stream/stream/position.rs @@ -0,0 +1,51 @@ +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct PositionFuture { + #[pin] + stream: S, + predicate: P, + index:usize, + } +} + +impl PositionFuture { + pub(super) fn new(stream: S, predicate: P) -> Self { + PositionFuture { + stream, + predicate, + index: 0, + } + } +} + +impl Future for PositionFuture +where + S: Stream, + P: FnMut(&S::Item) -> bool, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(*this.index)), + Some(_) => { + cx.waker().wake_by_ref(); + *this.index += 1; + Poll::Pending + } + None => Poll::Ready(None), + } + } +} From c6c2bfa45601df7ead1a17778d3fc59e15eb3b8c Mon Sep 17 00:00:00 2001 From: Mark Hildreth Date: Thu, 31 Oct 2019 11:05:44 -0400 Subject: [PATCH 106/707] Added TCP smoke tests against std Listener and Stream --- tests/tcp.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/tcp.rs b/tests/tcp.rs index c8281d713..00fa3a045 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -49,3 +49,48 @@ fn incoming_read() -> io::Result<()> { Ok(()) }) } + +#[test] +fn smoke_std_stream_to_async_listener() -> io::Result<()> { + use std::io::Write; + + task::block_on(async { + let listener = TcpListener::bind("127.0.0.1:0").await?; + let addr = listener.local_addr()?; + + let mut std_stream = std::net::TcpStream::connect(&addr)?; + std_stream.write_all(THE_WINTERS_TALE)?; + + let mut buf = vec![0; 1024]; + let mut incoming = listener.incoming(); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], THE_WINTERS_TALE); + + Ok(()) + }) +} + +#[test] +fn smoke_async_stream_to_std_listener() -> io::Result<()> { + use std::io::Read; + + let std_listener = std::net::TcpListener::bind("127.0.0.1:0")?; + let addr = std_listener.local_addr()?; + + task::block_on(async move { + let mut stream = TcpStream::connect(&addr).await?; + stream.write_all(THE_WINTERS_TALE).await?; + io::Result::Ok(()) + })?; + + let mut buf = vec![0; 1024]; + let mut incoming = std_listener.incoming(); + let mut stream = incoming.next().unwrap()?; + + let n = stream.read(&mut buf).unwrap(); + assert_eq!(&buf[..n], THE_WINTERS_TALE); + + Ok(()) +} From 07d21e5eb37e6ddc2a1819dd0d27b83338f21299 Mon Sep 17 00:00:00 2001 From: zhangguyu Date: Thu, 31 Oct 2019 23:30:11 +0800 Subject: [PATCH 107/707] change trait bounds --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c4abe32f1..0469c7ae7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1583,7 +1583,7 @@ extension_trait! { predicate: P ) -> impl Future> [PositionFuture] where - Self: Sized + Stream, + Self: Sized, P: FnMut(&Self::Item) -> bool, { PositionFuture::new(self, predicate) From eeb44c86e9adfcf2fca7d85dbffaaf96b337efdc Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 1 Nov 2019 10:34:28 +0900 Subject: [PATCH 108/707] fix --- src/io/stderr.rs | 6 ++---- src/io/stdin.rs | 6 ++---- src/io/stdout.rs | 6 ++---- src/stream/stream/min.rs | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 4e727f210..7584dc1ba 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,4 +1,4 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -120,9 +120,7 @@ impl Stderr { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StderrLock<'static> { - lazy_static! { - static ref STDERR: std::io::Stderr = std::io::stderr(); - } + static STDERR: Lazy = Lazy::new(|| std::io::stderr()); blocking::spawn(move || StderrLock(STDERR.lock())).await } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 9fb28bab8..359f2a349 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,4 +1,4 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::pin::Pin; use std::sync::Mutex; @@ -176,9 +176,7 @@ impl Stdin { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StdinLock<'static> { - lazy_static! { - static ref STDIN: std::io::Stdin = std::io::stdin(); - } + static STDIN: Lazy = Lazy::new(|| std::io::stdin()); blocking::spawn(move || StdinLock(STDIN.lock())).await } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index c314837bb..ccfd85b23 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,4 +1,4 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -120,9 +120,7 @@ impl Stdout { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StdoutLock<'static> { - lazy_static! { - static ref STDOUT: std::io::Stdout = std::io::stdout(); - } + static STDOUT: Lazy = Lazy::new(|| std::io::stdout()); blocking::spawn(move || StdoutLock(STDOUT.lock())).await } diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs index 1ab56065d..b4a8c7c16 100644 --- a/src/stream/stream/min.rs +++ b/src/stream/stream/min.rs @@ -1,5 +1,5 @@ +use std::cmp::{Ord, Ordering}; use std::marker::PhantomData; -use std::cmp::{Ordering, Ord}; use std::pin::Pin; use pin_project_lite::pin_project; From caa23381f0e8471e2d5251bd2b71ae073f5076ba Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 1 Nov 2019 10:41:21 +0900 Subject: [PATCH 109/707] fix clippy warning --- src/io/stderr.rs | 2 +- src/io/stdin.rs | 2 +- src/io/stdout.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 7584dc1ba..334e50adc 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -120,7 +120,7 @@ impl Stderr { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StderrLock<'static> { - static STDERR: Lazy = Lazy::new(|| std::io::stderr()); + static STDERR: Lazy = Lazy::new(std::io::stderr); blocking::spawn(move || StderrLock(STDERR.lock())).await } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 359f2a349..8480c69da 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -176,7 +176,7 @@ impl Stdin { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StdinLock<'static> { - static STDIN: Lazy = Lazy::new(|| std::io::stdin()); + static STDIN: Lazy = Lazy::new(std::io::stdin); blocking::spawn(move || StdinLock(STDIN.lock())).await } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index ccfd85b23..aaa99ceb9 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -120,7 +120,7 @@ impl Stdout { /// # Ok(()) }) } /// ``` pub async fn lock(&self) -> StdoutLock<'static> { - static STDOUT: Lazy = Lazy::new(|| std::io::stdout()); + static STDOUT: Lazy = Lazy::new(std::io::stdout); blocking::spawn(move || StdoutLock(STDOUT.lock())).await } From 3dd59d7056936c6ec66b6f5579bbd8ed90746038 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Fri, 1 Nov 2019 02:45:33 +0100 Subject: [PATCH 110/707] Refactor the task module (#421) * Refactor the task module * Fix clippy warning * Simplify task-local entries * Reduce the amount of future wrapping * Cleanup * Simplify stealing --- Cargo.toml | 6 +- src/fs/canonicalize.rs | 4 +- src/fs/copy.rs | 4 +- src/fs/create_dir.rs | 4 +- src/fs/create_dir_all.rs | 4 +- src/fs/dir_builder.rs | 4 +- src/fs/dir_entry.rs | 6 +- src/fs/file.rs | 22 +- src/fs/hard_link.rs | 4 +- src/fs/metadata.rs | 4 +- src/fs/open_options.rs | 4 +- src/fs/read.rs | 4 +- src/fs/read_dir.rs | 6 +- src/fs/read_link.rs | 4 +- src/fs/read_to_string.rs | 4 +- src/fs/remove_dir.rs | 4 +- src/fs/remove_dir_all.rs | 4 +- src/fs/remove_file.rs | 4 +- src/fs/rename.rs | 4 +- src/fs/set_permissions.rs | 4 +- src/fs/symlink_metadata.rs | 4 +- src/fs/write.rs | 4 +- src/io/stderr.rs | 6 +- src/io/stdin.rs | 6 +- src/io/stdout.rs | 6 +- src/net/addr.rs | 6 +- src/net/driver/mod.rs | 2 +- src/net/tcp/stream.rs | 5 +- src/os/unix/fs.rs | 4 +- src/os/unix/net/datagram.rs | 4 +- src/os/unix/net/listener.rs | 4 +- src/os/unix/net/stream.rs | 4 +- src/task/block_on.rs | 177 +++++------- src/task/builder.rs | 58 +++- src/task/current.rs | 28 ++ src/task/executor/mod.rs | 13 + src/task/executor/pool.rs | 140 ++++++++++ src/task/{ => executor}/sleepers.rs | 0 src/task/join_handle.rs | 56 ++++ src/task/mod.rs | 68 ++--- src/task/pool.rs | 136 --------- src/task/spawn.rs | 31 +++ src/task/{blocking.rs => spawn_blocking.rs} | 101 ++++--- src/task/task.rs | 294 +++++++++----------- src/task/task_id.rs | 35 +++ src/task/task_local.rs | 116 ++++---- src/task/worker.rs | 110 -------- src/task/yield_now.rs | 4 +- src/utils.rs | 50 +++- tests/channel.rs | 2 + 50 files changed, 817 insertions(+), 761 deletions(-) create mode 100644 src/task/current.rs create mode 100644 src/task/executor/mod.rs create mode 100644 src/task/executor/pool.rs rename src/task/{ => executor}/sleepers.rs (100%) create mode 100644 src/task/join_handle.rs delete mode 100644 src/task/pool.rs create mode 100644 src/task/spawn.rs rename src/task/{blocking.rs => spawn_blocking.rs} (67%) create mode 100644 src/task/task_id.rs delete mode 100644 src/task/worker.rs diff --git a/Cargo.toml b/Cargo.toml index dcf2c7d00..63897053b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,23 +27,23 @@ unstable = ["broadcaster"] [dependencies] async-macros = "1.0.0" async-task = "1.0.0" +broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" crossbeam-utils = "0.6.6" futures-core-preview = "=0.3.0-alpha.19" futures-io-preview = "=0.3.0-alpha.19" futures-timer = "1.0.2" +kv-log-macro = "1.0.4" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" mio = "0.6.19" mio-uds = "0.6.7" num_cpus = "1.10.1" once_cell = "1.2.0" +pin-project-lite = "0.1" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" -kv-log-macro = "1.0.4" -broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } -pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 601d477cc..6eb6977db 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::{Path, PathBuf}; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Returns the canonical form of a path. /// @@ -32,5 +32,5 @@ use crate::task::blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await + spawn_blocking(move || std::fs::canonicalize(&path).map(Into::into)).await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 733fb64b3..170b66ece 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Copies the contents and permissions of a file to a new location. /// @@ -41,5 +41,5 @@ use crate::task::blocking; pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::copy(&from, &to)).await + spawn_blocking(move || std::fs::copy(&from, &to)).await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 740d303c6..03c24918c 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Creates a new directory. /// @@ -34,5 +34,5 @@ use crate::task::blocking; /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::create_dir(path)).await + spawn_blocking(move || std::fs::create_dir(path)).await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 76604de7f..152419430 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Creates a new directory and all of its parents if they are missing. /// @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::create_dir_all(path)).await + spawn_blocking(move || std::fs::create_dir_all(path)).await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index a55a9a922..9ee6b55ac 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -2,7 +2,7 @@ use std::future::Future; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// A builder for creating directories with configurable options. /// @@ -107,7 +107,7 @@ impl DirBuilder { } let path = path.as_ref().to_owned(); - async move { blocking::spawn(move || builder.create(path)).await } + async move { spawn_blocking(move || builder.create(path)).await } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 959e2adaa..527fab42e 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use crate::fs::{FileType, Metadata}; use crate::io; use crate::path::PathBuf; -use crate::task::blocking; +use crate::task::spawn_blocking; /// An entry in a directory. /// @@ -87,7 +87,7 @@ impl DirEntry { /// ``` pub async fn metadata(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(move || inner.metadata()).await + spawn_blocking(move || inner.metadata()).await } /// Reads the file type for this entry. @@ -125,7 +125,7 @@ impl DirEntry { /// ``` pub async fn file_type(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(move || inner.file_type()).await + spawn_blocking(move || inner.file_type()).await } /// Returns the bare name of this entry without the leading path. diff --git a/src/fs/file.rs b/src/fs/file.rs index 745a58481..8bc6c2cea 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, blocking, Context, Poll, Waker}; +use crate::task::{self, spawn_blocking, Context, Poll, Waker}; /// An open file on the filesystem. /// @@ -112,7 +112,7 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(move || std::fs::File::open(&path)).await?; + let file = spawn_blocking(move || std::fs::File::open(&path)).await?; Ok(File::new(file, true)) } @@ -147,7 +147,7 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(move || std::fs::File::create(&path)).await?; + let file = spawn_blocking(move || std::fs::File::create(&path)).await?; Ok(File::new(file, true)) } @@ -180,7 +180,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.sync_all()).await + spawn_blocking(move || state.file.sync_all()).await } /// Synchronizes OS-internal buffered contents to disk. @@ -216,7 +216,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.sync_data()).await + spawn_blocking(move || state.file.sync_data()).await } /// Truncates or extends the file. @@ -249,7 +249,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.set_len(size)).await + spawn_blocking(move || state.file.set_len(size)).await } /// Reads the file's metadata. @@ -268,7 +268,7 @@ impl File { /// ``` pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); - blocking::spawn(move || file.metadata()).await + spawn_blocking(move || file.metadata()).await } /// Changes the permissions on the file. @@ -297,7 +297,7 @@ impl File { /// ``` pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); - blocking::spawn(move || file.set_permissions(perm)).await + spawn_blocking(move || file.set_permissions(perm)).await } } @@ -692,7 +692,7 @@ impl LockGuard { self.register(cx); // Start a read operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { // Read some data from the file into the cache. let res = { let State { file, cache, .. } = &mut *self; @@ -801,7 +801,7 @@ impl LockGuard { self.register(cx); // Start a write operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { match (&*self.file).write_all(&self.cache) { Ok(_) => { // Switch to idle mode. @@ -834,7 +834,7 @@ impl LockGuard { self.register(cx); // Start a flush operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { match (&*self.file).flush() { Ok(()) => { // Mark the file as flushed. diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 8b09b5d1d..e6e56cd53 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Creates a hard link on the filesystem. /// @@ -32,5 +32,5 @@ use crate::task::blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::hard_link(&from, &to)).await + spawn_blocking(move || std::fs::hard_link(&from, &to)).await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 4afc55953..1383ec21f 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads metadata for a path. /// @@ -34,7 +34,7 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::metadata(path)).await + spawn_blocking(move || std::fs::metadata(path)).await } cfg_not_docs! { diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 7f7007347..91ad8cab5 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -3,7 +3,7 @@ use std::future::Future; use crate::fs::File; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// A builder for opening files with configurable options. /// @@ -285,7 +285,7 @@ impl OpenOptions { let path = path.as_ref().to_owned(); let options = self.0.clone(); async move { - let file = blocking::spawn(move || options.open(path)).await?; + let file = spawn_blocking(move || options.open(path)).await?; Ok(File::new(file, true)) } } diff --git a/src/fs/read.rs b/src/fs/read.rs index a0eb130ba..ab7d17566 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads the entire contents of a file as raw bytes. /// @@ -36,5 +36,5 @@ use crate::task::blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read(path)).await + spawn_blocking(move || std::fs::read(path)).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 6e478019d..fe12fa6d1 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -5,7 +5,7 @@ use crate::future::Future; use crate::io; use crate::path::Path; use crate::stream::Stream; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Returns a stream of entries in a directory. /// @@ -45,7 +45,7 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_dir(path)) + spawn_blocking(move || std::fs::read_dir(path)) .await .map(ReadDir::new) } @@ -91,7 +91,7 @@ impl Stream for ReadDir { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - self.0 = State::Busy(blocking::spawn(move || { + self.0 = State::Busy(spawn_blocking(move || { let next = inner.next(); (inner, next) })); diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index eaa7b6245..7ec18a45e 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::{Path, PathBuf}; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads a symbolic link and returns the path it points to. /// @@ -28,5 +28,5 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_link(path).map(Into::into)).await + spawn_blocking(move || std::fs::read_link(path).map(Into::into)).await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index 40c4b6b8b..d06aa614c 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads the entire contents of a file as a string. /// @@ -37,5 +37,5 @@ use crate::task::blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_to_string(path)).await + spawn_blocking(move || std::fs::read_to_string(path)).await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index d1fa7bf33..1a62db2e7 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Removes an empty directory. /// @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_dir(path)).await + spawn_blocking(move || std::fs::remove_dir(path)).await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 0a0fceb79..336674061 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Removes a directory and all of its contents. /// @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_dir_all(path)).await + spawn_blocking(move || std::fs::remove_dir_all(path)).await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 5bc0608dc..9a74ec111 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Removes a file. /// @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_file(path)).await + spawn_blocking(move || std::fs::remove_file(path)).await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index c2aa77b7f..ed7f39c9c 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Renames a file or directory to a new location. /// @@ -34,5 +34,5 @@ use crate::task::blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::rename(&from, &to)).await + spawn_blocking(move || std::fs::rename(&from, &to)).await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index d14ced944..60a6d6f16 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,7 +1,7 @@ use crate::fs::Permissions; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Changes the permissions of a file or directory. /// @@ -32,5 +32,5 @@ use crate::task::blocking; /// ``` pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::set_permissions(path, perm)).await + spawn_blocking(move || std::fs::set_permissions(path, perm)).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index bc5cce863..45be6d99d 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -1,7 +1,7 @@ use crate::fs::Metadata; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads metadata for a path without following symbolic links. /// @@ -34,5 +34,5 @@ use crate::task::blocking; /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::symlink_metadata(path)).await + spawn_blocking(move || std::fs::symlink_metadata(path)).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 3df56042f..4e5d20bb1 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Writes a slice of bytes as the new contents of a file. /// @@ -33,5 +33,5 @@ use crate::task::blocking; pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - blocking::spawn(move || std::fs::write(path, contents)).await + spawn_blocking(move || std::fs::write(path, contents)).await } diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 0a8c47004..76ca5239e 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use crate::future::Future; use crate::io::{self, Write}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Constructs a new handle to the standard error of the current process. /// @@ -124,7 +124,7 @@ impl Write for Stderr { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)) @@ -152,7 +152,7 @@ impl Write for Stderr { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 22b9cf346..c99a88dba 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use crate::future::{self, Future}; use crate::io::{self, Read}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Constructs a new handle to the standard input of the current process. /// @@ -127,7 +127,7 @@ impl Stdin { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)); @@ -180,7 +180,7 @@ impl Read for Stdin { } // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)) diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 1e9340fcb..5455466a6 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use crate::future::Future; use crate::io::{self, Write}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Constructs a new handle to the standard output of the current process. /// @@ -124,7 +124,7 @@ impl Write for Stdout { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)) @@ -152,7 +152,7 @@ impl Write for Stdout { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *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)) diff --git a/src/net/addr.rs b/src/net/addr.rs index 519b1846e..c17ff4985 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -5,7 +5,7 @@ use std::pin::Pin; use crate::future::Future; use crate::io; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; cfg_not_docs! { macro_rules! ret { @@ -194,7 +194,7 @@ impl ToSocketAddrs for (&str, u16) { } let host = host.to_string(); - let task = blocking::spawn(move || { + let task = spawn_blocking(move || { std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) }); ToSocketAddrsFuture::Resolving(task) @@ -215,7 +215,7 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = blocking::spawn(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + let task = spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 40e0abb29..7f33e8594 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -105,7 +105,7 @@ 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-net-driver".to_string()) + .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. diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 5988194f1..13a1752f2 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -6,8 +6,7 @@ use crate::future; use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; -use crate::task::blocking; -use crate::task::{Context, Poll}; +use crate::task::{spawn_blocking, Context, Poll}; /// A TCP stream between a local and a remote socket. /// @@ -74,7 +73,7 @@ impl TcpStream { let mut last_err = None; for addr in addrs.to_socket_addrs().await? { - let res = blocking::spawn(move || { + let res = spawn_blocking(move || { let std_stream = std::net::TcpStream::connect(addr)?; let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; Ok(TcpStream { diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index d3e85234d..498b3a97f 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -2,7 +2,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Creates a new symbolic link on the filesystem. /// @@ -26,7 +26,7 @@ use crate::task::blocking; pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref().to_owned(); let dst = dst.as_ref().to_owned(); - blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await + spawn_blocking(move || std::os::unix::fs::symlink(&src, &dst)).await } cfg_not_docs! { diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index c96afd50e..fc426b7cd 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -11,7 +11,7 @@ use crate::io; use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// A Unix datagram socket. /// @@ -67,7 +67,7 @@ impl UnixDatagram { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let socket = blocking::spawn(move || mio_uds::UnixDatagram::bind(path)).await?; + let socket = spawn_blocking(move || mio_uds::UnixDatagram::bind(path)).await?; Ok(UnixDatagram::new(socket)) } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index b6e6a2982..9bd86d381 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -13,7 +13,7 @@ use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; -use crate::task::{blocking, Context, Poll}; +use crate::task::{spawn_blocking, Context, Poll}; /// A Unix domain socket server, listening for connections. /// @@ -68,7 +68,7 @@ impl UnixListener { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let listener = blocking::spawn(move || mio_uds::UnixListener::bind(path)).await?; + let listener = spawn_blocking(move || mio_uds::UnixListener::bind(path)).await?; Ok(UnixListener { watcher: Watcher::new(listener), diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index b16f2a3ce..647edc96f 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -12,7 +12,7 @@ use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::{blocking, Context, Poll}; +use crate::task::{spawn_blocking, Context, Poll}; /// A Unix stream socket. /// @@ -58,7 +58,7 @@ impl UnixStream { pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || { + 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 { diff --git a/src/task/block_on.rs b/src/task/block_on.rs index c10303d79..54a415f53 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,21 +1,15 @@ -use std::cell::{Cell, UnsafeCell}; +use std::cell::Cell; use std::mem::{self, ManuallyDrop}; -use std::panic::{self, AssertUnwindSafe, UnwindSafe}; -use std::pin::Pin; use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; use std::thread; use crossbeam_utils::sync::Parker; -use pin_project_lite::pin_project; +use kv_log_macro::trace; +use log::log_enabled; -use super::task; -use super::task_local; -use super::worker; use crate::future::Future; -use crate::task::{Context, Poll, Waker}; - -use kv_log_macro::trace; +use crate::task::{Context, Poll, Task, Waker}; /// Spawns a task and blocks the current thread on its result. /// @@ -42,81 +36,43 @@ pub fn block_on(future: F) -> T where F: Future, { - unsafe { - // A place on the stack where the result will be stored. - let out = &mut UnsafeCell::new(None); - - // Wrap the future into one that stores the result into `out`. - let future = { - let out = out.get(); - - async move { - let future = CatchUnwindFuture { - future: AssertUnwindSafe(future), - }; - *out = Some(future.await); - } - }; - - // Create a tag for the task. - let tag = task::Tag::new(None); - - // Log this `block_on` operation. - let child_id = tag.task_id().as_u64(); - let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); + // Create a new task handle. + let task = Task::new(None); + // Log this `block_on` operation. + if log_enabled!(log::Level::Trace) { trace!("block_on", { - parent_id: parent_id, - child_id: child_id, + task_id: task.id().0, + parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); + } - // Wrap the future into one that drops task-local variables on exit. - let future = task_local::add_finalizer(future); - - let future = async move { - future.await; - trace!("block_on completed", { - parent_id: parent_id, - child_id: child_id, - }); - }; - - // Pin the future onto the stack. - pin_utils::pin_mut!(future); - - // Transmute the future into one that is futurestatic. - let future = mem::transmute::< - Pin<&'_ mut dyn Future>, - Pin<&'static mut dyn Future>, - >(future); - - // Block on the future and and wait for it to complete. - worker::set_tag(&tag, || block(future)); - - // Take out the result. - match (*out.get()).take().unwrap() { - Ok(v) => v, - Err(err) => panic::resume_unwind(err), + let future = async move { + // Drop task-locals on exit. + defer! { + Task::get_current(|t| unsafe { t.drop_locals() }); } - } -} -pin_project! { - struct CatchUnwindFuture { - #[pin] - future: F, - } -} + // Log completion on exit. + defer! { + if log_enabled!(log::Level::Trace) { + Task::get_current(|t| { + trace!("completed", { + task_id: t.id().0, + }); + }); + } + } -impl Future for CatchUnwindFuture { - type Output = thread::Result; + future.await + }; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - panic::catch_unwind(AssertUnwindSafe(|| self.project().future.poll(cx)))?.map(Ok) - } + // Run the future as a task. + unsafe { Task::set_current(&task, || run(future)) } } -fn block(f: F) -> T +/// Blocks the current thread on a future's result. +fn run(future: F) -> T where F: Future, { @@ -129,50 +85,59 @@ where static CACHE: Cell>> = Cell::new(None); } - pin_utils::pin_mut!(f); + // 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)); + 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 (); - let vt = vtable(); - let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) }; + // 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); + let mut step = 0; loop { - if let Poll::Ready(t) = f.as_mut().poll(cx) { + 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(); + + // Yield a few times or park the current thread. + if step < 3 { + thread::yield_now(); + step += 1; + } else { + arc_parker.park(); + step = 0; + } } }) } - -fn vtable() -> &'static RawWakerVTable { - unsafe fn clone_raw(ptr: *const ()) -> RawWaker { - #![allow(clippy::redundant_clone)] - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); - 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) -} diff --git a/src/task/builder.rs b/src/task/builder.rs index a43b42bcf..a61d7859c 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,7 +1,11 @@ -use super::pool; -use super::JoinHandle; +use kv_log_macro::trace; +use log::log_enabled; + use crate::future::Future; use crate::io; +use crate::task::executor; +use crate::task::{JoinHandle, Task}; +use crate::utils::abort_on_panic; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -11,11 +15,13 @@ pub struct Builder { impl Builder { /// Creates a new builder. + #[inline] pub fn new() -> Builder { Builder { name: None } } /// Configures the name of the task. + #[inline] pub fn name(mut self, name: String) -> Builder { self.name = Some(name); self @@ -27,6 +33,52 @@ impl Builder { F: Future + Send + 'static, T: Send + 'static, { - Ok(pool::get().spawn(future, self)) + // Create a new task handle. + 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), + }); + } + + let future = async move { + // Drop task-locals on exit. + defer! { + Task::get_current(|t| unsafe { t.drop_locals() }); + } + + // Log completion on exit. + defer! { + if log_enabled!(log::Level::Trace) { + Task::get_current(|t| { + trace!("completed", { + task_id: t.id().0, + }); + }); + } + } + + future.await + }; + + let schedule = move |t| executor::schedule(Runnable(t)); + let (task, handle) = async_task::spawn(future, schedule, task); + task.schedule(); + Ok(JoinHandle::new(handle)) + } +} + +/// A runnable task. +pub(crate) struct Runnable(async_task::Task); + +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())); + } } } diff --git a/src/task/current.rs b/src/task/current.rs new file mode 100644 index 000000000..0dc36991c --- /dev/null +++ b/src/task/current.rs @@ -0,0 +1,28 @@ +use crate::task::Task; + +/// Returns a handle to the current task. +/// +/// # Panics +/// +/// This function will panic if not called within the context of a task created by [`block_on`], +/// [`spawn`], or [`Builder::spawn`]. +/// +/// [`block_on`]: fn.block_on.html +/// [`spawn`]: fn.spawn.html +/// [`Builder::spawn`]: struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// println!("The name of this task is {:?}", task::current().name()); +/// # +/// # }) +/// ``` +pub fn current() -> Task { + Task::get_current(|t| t.clone()) + .expect("`task::current()` called outside the context of a task") +} diff --git a/src/task/executor/mod.rs b/src/task/executor/mod.rs new file mode 100644 index 000000000..2a6a696e1 --- /dev/null +++ b/src/task/executor/mod.rs @@ -0,0 +1,13 @@ +//! 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 new file mode 100644 index 000000000..1e743844a --- /dev/null +++ b/src/task/executor/pool.rs @@ -0,0 +1,140 @@ +use std::cell::UnsafeCell; +use std::iter; +use std::thread; +use std::time::Duration; + +use crossbeam_deque::{Injector, Stealer, Worker}; +use once_cell::sync::Lazy; + +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()); + + thread::Builder::new() + .name("async-std/executor".to_string()) + .spawn(|| abort_on_panic(|| main_loop(worker))) + .expect("cannot start a thread driving tasks"); + } + + Pool { + injector: Injector::new(), + stealers, + sleepers: Sleepers::new(), + } +}); + +thread_local! { + /// Local task queue associated with the current worker thread. + static QUEUE: UnsafeCell>> = UnsafeCell::new(None); +} + +/// Schedules a new runnable task for execution. +pub(crate) fn schedule(task: Runnable) { + QUEUE.with(|queue| { + let local = unsafe { (*queue.get()).as_ref() }; + + // If the current thread is a worker thread, push the task into its local task queue. + // Otherwise, push it into the global task queue. + match local { + None => POOL.injector.push(task), + Some(q) => q.push(task), + } + }); + + // Notify a sleeping worker that new work just came in. + POOL.sleepers.notify_one(); +} + +/// Main loop running a worker thread. +fn main_loop(local: Worker) { + // Initialize the local task queue. + QUEUE.with(|queue| unsafe { *queue.get() = Some(local) }); + + // The number of times the thread didn't find work in a row. + let mut step = 0; + + loop { + // Try to find a runnable task. + match find_runnable() { + Some(task) => { + // Found. Now run the task. + task.run(); + step = 0; + } + None => { + // Yield the current thread or put it to sleep. + match step { + 0..=2 => { + thread::yield_now(); + step += 1; + } + 3 => { + thread::sleep(Duration::from_micros(10)); + step += 1; + } + _ => { + POOL.sleepers.wait(); + step = 0; + } + } + } + } + } +} + +/// Find the next runnable task. +fn find_runnable() -> Option { + let pool = &*POOL; + + QUEUE.with(|queue| { + let local = unsafe { (*queue.get()).as_ref().unwrap() }; + + // Pop a task from the local queue, if not empty. + local.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(&local) + // 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 rotated = r.iter().chain(l.iter()); + rotated.map(|s| s.steal_batch_and_pop(&local)).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/sleepers.rs b/src/task/executor/sleepers.rs similarity index 100% rename from src/task/sleepers.rs rename to src/task/executor/sleepers.rs diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs new file mode 100644 index 000000000..9fefff2e6 --- /dev/null +++ b/src/task/join_handle.rs @@ -0,0 +1,56 @@ +use std::future::Future; +use std::pin::Pin; + +use crate::task::{Context, Poll, Task}; + +/// A handle that awaits the result of a task. +/// +/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer +/// a handle to the task and no way to `join` on it. +/// +/// Created when a task is [spawned]. +/// +/// [spawned]: fn.spawn.html +#[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 { + JoinHandle(inner) + } + + /// Returns a handle to the underlying task. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::task; + /// + /// let handle = task::spawn(async { + /// 1 + 2 + /// }); + /// println!("id = {}", handle.task().id()); + /// # + /// # }) + pub fn task(&self) -> &Task { + self.0.tag() + } +} + +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), + } + } +} diff --git a/src/task/mod.rs b/src/task/mod.rs index 24eae0819..72d559a76 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -126,63 +126,35 @@ pub use async_macros::ready; pub use block_on::block_on; pub use builder::Builder; -pub use pool::spawn; +pub use current::current; +pub use join_handle::JoinHandle; pub use sleep::sleep; -pub use task::{JoinHandle, Task, TaskId}; +pub use spawn::spawn; +pub use task::Task; +pub use task_id::TaskId; pub use task_local::{AccessError, LocalKey}; -pub use worker::current; + +#[cfg(any(feature = "unstable", test))] +pub use spawn_blocking::spawn_blocking; +#[cfg(not(any(feature = "unstable", test)))] +pub(crate) use spawn_blocking::spawn_blocking; + +use builder::Runnable; +use task_local::LocalsMap; mod block_on; mod builder; -mod pool; +mod current; +mod executor; +mod join_handle; mod sleep; -mod sleepers; +mod spawn; +mod spawn_blocking; mod task; +mod task_id; mod task_local; -mod worker; - -pub(crate) mod blocking; cfg_unstable! { - mod yield_now; pub use yield_now::yield_now; -} - -/// Spawns a blocking task. -/// -/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This -/// is useful to prevent long-running synchronous operations from blocking the main futures -/// executor. -/// -/// See also: [`task::block_on`], [`task::spawn`]. -/// -/// [`task::block_on`]: fn.block_on.html -/// [`task::spawn`]: fn.spawn.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// task::spawn_blocking(|| { -/// println!("long-running task here"); -/// }).await; -/// # -/// # }) -/// ``` -// Once this function stabilizes we should merge `blocking::spawn` into this so -// all code in our crate uses `task::blocking` too. -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[inline] -pub fn spawn_blocking(f: F) -> task::JoinHandle -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - blocking::spawn(f) + mod yield_now; } diff --git a/src/task/pool.rs b/src/task/pool.rs deleted file mode 100644 index 3fd704708..000000000 --- a/src/task/pool.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::iter; -use std::thread; - -use crossbeam_deque::{Injector, Stealer, Worker}; -use kv_log_macro::trace; -use once_cell::sync::Lazy; - -use super::sleepers::Sleepers; -use super::task; -use super::task_local; -use super::worker; -use super::{Builder, JoinHandle}; -use crate::future::Future; -use crate::utils::abort_on_panic; - -/// Spawns a task. -/// -/// 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(async { -/// 1 + 2 -/// }); -/// -/// assert_eq!(handle.await, 3); -/// # -/// # }) -/// ``` -pub fn spawn(future: F) -> JoinHandle -where - F: Future + Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(future).expect("cannot spawn future") -} - -pub(crate) struct Pool { - pub injector: Injector, - pub stealers: Vec>, - pub sleepers: Sleepers, -} - -impl Pool { - /// Spawn a future onto the pool. - pub fn spawn(&self, future: F, builder: Builder) -> JoinHandle - where - F: Future + Send + 'static, - T: Send + 'static, - { - let tag = task::Tag::new(builder.name); - - // Log this `spawn` operation. - let child_id = tag.task_id().as_u64(); - let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); - - trace!("spawn", { - parent_id: parent_id, - child_id: child_id, - }); - - // Wrap the future into one that drops task-local variables on exit. - let future = unsafe { task_local::add_finalizer(future) }; - - // Wrap the future into one that logs completion on exit. - let future = async move { - let res = future.await; - trace!("spawn completed", { - parent_id: parent_id, - child_id: child_id, - }); - res - }; - - let (task, handle) = async_task::spawn(future, worker::schedule, tag); - task.schedule(); - JoinHandle::new(handle) - } - - /// Find the next runnable task to run. - pub fn find_task(&self, local: &Worker) -> Option { - // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - iter::repeat_with(|| { - // Try stealing a batch of tasks from the injector queue. - self.injector - .steal_batch_and_pop(local) - // Or try stealing a bach of tasks from one of the other threads. - .or_else(|| { - self.stealers - .iter() - .map(|s| s.steal_batch_and_pop(local)) - .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()) - }) - } -} - -#[inline] -pub(crate) fn get() -> &'static Pool { - 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()); - - thread::Builder::new() - .name("async-task-driver".to_string()) - .spawn(|| abort_on_panic(|| worker::main_loop(worker))) - .expect("cannot start a thread driving tasks"); - } - - Pool { - injector: Injector::new(), - stealers, - sleepers: Sleepers::new(), - } - }); - &*POOL -} diff --git a/src/task/spawn.rs b/src/task/spawn.rs new file mode 100644 index 000000000..da2957b07 --- /dev/null +++ b/src/task/spawn.rs @@ -0,0 +1,31 @@ +use crate::future::Future; +use crate::task::{Builder, JoinHandle}; + +/// Spawns a task. +/// +/// 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(async { +/// 1 + 2 +/// }); +/// +/// assert_eq!(handle.await, 3); +/// # +/// # }) +/// ``` +pub fn spawn(future: F) -> JoinHandle +where + F: Future + Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(future).expect("cannot spawn task") +} diff --git a/src/task/blocking.rs b/src/task/spawn_blocking.rs similarity index 67% rename from src/task/blocking.rs rename to src/task/spawn_blocking.rs index 1f1a222a3..b6b5ea34b 100644 --- a/src/task/blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,5 +1,3 @@ -//! A thread pool for running blocking functions asynchronously. - use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; @@ -7,22 +5,63 @@ use std::time::Duration; use crossbeam_channel::{bounded, Receiver, Sender}; use once_cell::sync::Lazy; -use crate::task::task::{JoinHandle, Tag}; -use crate::utils::abort_on_panic; +use crate::task::{JoinHandle, Task}; +use crate::utils::{abort_on_panic, random}; + +type Runnable = async_task::Task; + +/// Spawns a blocking task. +/// +/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This +/// is useful to prevent long-running synchronous operations from blocking the main futures +/// executor. +/// +/// See also: [`task::block_on`], [`task::spawn`]. +/// +/// [`task::block_on`]: fn.block_on.html +/// [`task::spawn`]: fn.spawn.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # #[cfg(feature = "unstable")] +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// task::spawn_blocking(|| { +/// println!("long-running task here"); +/// }).await; +/// # +/// # }) +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[inline] +pub fn spawn_blocking(f: F) -> JoinHandle +where + F: FnOnce() -> T + Send + 'static, + T: Send + 'static, +{ + let (task, handle) = async_task::spawn(async { f() }, schedule, Task::new(None)); + task.schedule(); + JoinHandle::new(handle) +} const MAX_THREADS: u64 = 10_000; static DYNAMIC_THREAD_COUNT: AtomicU64 = AtomicU64::new(0); struct Pool { - sender: Sender>, - receiver: Receiver>, + sender: Sender, + receiver: Receiver, } static POOL: Lazy = Lazy::new(|| { for _ in 0..2 { thread::Builder::new() - .name("async-blocking-driver".to_string()) + .name("async-std/blocking".to_string()) .spawn(|| { abort_on_panic(|| { for task in &POOL.receiver { @@ -66,7 +105,7 @@ fn maybe_create_another_blocking_thread() { let rand_sleep_ms = u64::from(random(10_000)); thread::Builder::new() - .name("async-blocking-driver-dynamic".to_string()) + .name("async-std/blocking".to_string()) .spawn(move || { let wait_limit = Duration::from_millis(1000 + rand_sleep_ms); @@ -82,8 +121,8 @@ fn maybe_create_another_blocking_thread() { // Enqueues work, attempting to send to the threadpool in a // nonblocking way and spinning up another worker thread if // there is not a thread ready to accept the work. -fn schedule(t: async_task::Task) { - if let Err(err) = POOL.sender.try_send(t) { +pub(crate) fn schedule(task: Runnable) { + if let Err(err) = POOL.sender.try_send(task) { // We were not able to send to the channel without // blocking. Try to spin up another thread and then // retry sending while blocking. @@ -91,45 +130,3 @@ fn schedule(t: async_task::Task) { POOL.sender.send(err.into_inner()).unwrap(); } } - -/// Spawns a blocking task. -/// -/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. -pub(crate) fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - let tag = Tag::new(None); - let future = async move { f() }; - let (task, handle) = async_task::spawn(future, schedule, tag); - task.schedule(); - JoinHandle::new(handle) -} - -/// Generates a random number in `0..n`. -fn random(n: u32) -> u32 { - use std::cell::Cell; - use std::num::Wrapping; - - thread_local! { - static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); - } - - 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 - }) -} diff --git a/src/task/task.rs b/src/task/task.rs index 3d8e10804..bcec2e0e4 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -1,31 +1,74 @@ +use std::cell::Cell; use std::fmt; -use std::future::Future; -use std::i64; -use std::mem; -use std::num::NonZeroU64; -use std::pin::Pin; -use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; +use std::mem::ManuallyDrop; +use std::ptr; +use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::Arc; -use super::task_local; -use crate::task::{Context, Poll}; +use crate::task::{LocalsMap, TaskId}; +use crate::utils::abort_on_panic; + +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 { + /// 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(), + } + } +} /// A handle to a task. -#[derive(Clone)] -pub struct Task(Arc); +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 { - /// Returns a reference to task metadata. - pub(crate) fn metadata(&self) -> &Metadata { - &self.0 + /// 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 } } /// Gets the task's unique identifier. + #[inline] pub fn id(&self) -> TaskId { - self.metadata().task_id + self.inner().id } /// Returns the name of this task. @@ -34,178 +77,101 @@ impl Task { /// /// [`Builder::name`]: struct.Builder.html#method.name pub fn name(&self) -> Option<&str> { - self.metadata().name.as_ref().map(|s| s.as_str()) + self.inner().name.as_ref().map(|s| &**s) } -} - -impl fmt::Debug for Task { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Task").field("name", &self.name()).finish() - } -} -/// A handle that awaits the result of a task. -/// -/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer -/// a handle to the task and no way to `join` on it. -/// -/// Created when a task is [spawned]. -/// -/// [spawned]: fn.spawn.html -#[derive(Debug)] -pub struct JoinHandle(async_task::JoinHandle); - -unsafe impl Send for JoinHandle {} -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - pub(crate) fn new(inner: async_task::JoinHandle) -> JoinHandle { - JoinHandle(inner) + /// Returns the map holding task-local values. + pub(crate) fn locals(&self) -> &LocalsMap { + &self.inner().locals } - /// Returns a handle to the underlying task. + /// Drops all task-local values. /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::task; - /// - /// let handle = task::spawn(async { - /// 1 + 2 - /// }); - /// println!("id = {}", handle.task().id()); - /// # - /// # }) - pub fn task(&self) -> &Task { - self.0.tag().task() - } -} - -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!("task has panicked"), - Poll::Ready(Some(val)) => Poll::Ready(val), + /// 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(); + }); } } -} -/// A unique identifier for a task. -/// -/// # Examples -/// -/// ``` -/// # -/// use async_std::task; -/// -/// task::block_on(async { -/// println!("id = {:?}", task::current().id()); -/// }) -/// ``` -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct TaskId(NonZeroU64); - -impl TaskId { - pub(crate) fn new() -> TaskId { - static COUNTER: AtomicU64 = AtomicU64::new(1); - - let id = COUNTER.fetch_add(1, Ordering::Relaxed); - - if id > i64::MAX as u64 { - std::process::abort(); + /// 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)); + } + } } - unsafe { TaskId(NonZeroU64::new_unchecked(id)) } } - pub(crate) fn as_u64(self) -> u64 { - self.0.get() - } -} - -impl fmt::Display for TaskId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) + /// 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() + }) } -} - -pub(crate) type Runnable = async_task::Task; -pub(crate) struct Metadata { - pub task_id: TaskId, - pub name: Option, - pub local_map: task_local::Map, -} - -pub(crate) struct Tag { - task_id: TaskId, - raw_metadata: AtomicUsize, -} - -impl Tag { - pub fn new(name: Option) -> Tag { - let task_id = TaskId::new(); - - let opt_task = name.map(|name| { - Task(Arc::new(Metadata { - task_id, - name: Some(name), - local_map: task_local::Map::new(), - })) - }); - - Tag { - task_id, - raw_metadata: AtomicUsize::new(unsafe { - mem::transmute::, usize>(opt_task) - }), + /// 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, } } +} - pub fn task(&self) -> &Task { - #[allow(clippy::transmute_ptr_to_ptr)] - unsafe { - let raw = self.raw_metadata.load(Ordering::Acquire); - - if mem::transmute::<&usize, &Option>(&raw).is_none() { - let new = Some(Task(Arc::new(Metadata { - task_id: TaskId::new(), - name: None, - local_map: task_local::Map::new(), - }))); - - let new_raw = mem::transmute::, usize>(new); - - if self - .raw_metadata - .compare_exchange(raw, new_raw, Ordering::AcqRel, Ordering::Acquire) - .is_err() - { - let new = mem::transmute::>(new_raw); - drop(new); - } +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)); } - - #[allow(clippy::transmute_ptr_to_ptr)] - mem::transmute::<&AtomicUsize, &Option>(&self.raw_metadata) - .as_ref() - .unwrap() } } +} - pub fn task_id(&self) -> TaskId { - self.task_id +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), + } } } -impl Drop for Tag { - fn drop(&mut self) { - let raw = *self.raw_metadata.get_mut(); - let opt_task = unsafe { mem::transmute::>(raw) }; - drop(opt_task); +impl fmt::Debug for Task { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Task") + .field("id", &self.id()) + .field("name", &self.name()) + .finish() } } diff --git a/src/task/task_id.rs b/src/task/task_id.rs new file mode 100644 index 000000000..67eee154b --- /dev/null +++ b/src/task/task_id.rs @@ -0,0 +1,35 @@ +use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; + +/// A unique identifier for a task. +/// +/// # Examples +/// +/// ``` +/// use async_std::task; +/// +/// task::block_on(async { +/// println!("id = {:?}", task::current().id()); +/// }) +/// ``` +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +pub struct TaskId(pub(crate) u64); + +impl TaskId { + /// Generates a new `TaskId`. + pub(crate) fn generate() -> TaskId { + static COUNTER: AtomicU64 = AtomicU64::new(1); + + let id = COUNTER.fetch_add(1, Ordering::Relaxed); + if id > u64::max_value() / 2 { + std::process::abort(); + } + TaskId(id) + } +} + +impl fmt::Display for TaskId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/src/task/task_local.rs b/src/task/task_local.rs index e92f4f929..0869cab46 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -1,14 +1,9 @@ use std::cell::UnsafeCell; use std::error::Error; use std::fmt; -use std::future::Future; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Mutex; +use std::sync::atomic::{AtomicU32, Ordering}; -use once_cell::sync::Lazy; - -use super::worker; -use crate::utils::abort_on_panic; +use crate::task::Task; /// Declares task-local values. /// @@ -50,7 +45,7 @@ macro_rules! task_local { $crate::task::LocalKey { __init, - __key: ::std::sync::atomic::AtomicUsize::new(0), + __key: ::std::sync::atomic::AtomicU32::new(0), } }; ); @@ -71,7 +66,7 @@ pub struct LocalKey { pub __init: fn() -> T, #[doc(hidden)] - pub __key: AtomicUsize, + pub __key: AtomicU32, } impl LocalKey { @@ -154,14 +149,13 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - worker::get_task(|task| unsafe { + Task::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; - let map = &task.metadata().local_map; // Get the value in the map of task-locals, or initialize and insert one. - let value: *const dyn Send = map.get_or_insert(key, init); + let value: *const dyn Send = task.locals().get_or_insert(key, init); // Call the closure with the value passed as an argument. f(&*(value as *const T)) @@ -171,24 +165,26 @@ impl LocalKey { /// Returns the numeric key associated with this task-local. #[inline] - fn key(&self) -> usize { + fn key(&self) -> u32 { #[cold] - fn init(key: &AtomicUsize) -> usize { - static COUNTER: Lazy> = Lazy::new(|| Mutex::new(1)); + fn init(key: &AtomicU32) -> u32 { + static COUNTER: AtomicU32 = AtomicU32::new(1); - let mut counter = COUNTER.lock().unwrap(); - let prev = key.compare_and_swap(0, *counter, Ordering::AcqRel); + let counter = COUNTER.fetch_add(1, Ordering::Relaxed); + if counter > u32::max_value() / 2 { + std::process::abort(); + } - if prev == 0 { - *counter += 1; - *counter - 1 - } else { - prev + match key.compare_and_swap(0, counter, Ordering::AcqRel) { + 0 => counter, + k => k, } } - let key = self.__key.load(Ordering::Acquire); - if key == 0 { init(&self.__key) } else { key } + match self.__key.load(Ordering::Acquire) { + 0 => init(&self.__key), + k => k, + } } } @@ -214,51 +210,55 @@ impl fmt::Display for AccessError { impl Error for AccessError {} +/// A key-value entry in a map of task-locals. +struct Entry { + /// Key identifying the task-local variable. + key: u32, + + /// Value stored in this entry. + value: Box, +} + /// A map that holds task-locals. -pub(crate) struct Map { - /// A list of `(key, value)` entries sorted by the key. - entries: UnsafeCell)>>, +pub(crate) struct LocalsMap { + /// A list of key-value entries sorted by the key. + entries: UnsafeCell>>, } -impl Map { +impl LocalsMap { /// Creates an empty map of task-locals. - pub fn new() -> Map { - Map { - entries: UnsafeCell::new(Vec::new()), + pub fn new() -> LocalsMap { + LocalsMap { + entries: UnsafeCell::new(Some(Vec::new())), } } - /// Returns a thread-local value associated with `key` or inserts one constructed by `init`. + /// Returns a task-local value associated with `key` or inserts one constructed by `init`. #[inline] - pub fn get_or_insert(&self, key: usize, init: impl FnOnce() -> Box) -> &dyn Send { - let entries = unsafe { &mut *self.entries.get() }; - - let index = match entries.binary_search_by_key(&key, |e| e.0) { - Ok(i) => i, - Err(i) => { - entries.insert(i, (key, init())); - i + pub fn get_or_insert(&self, key: u32, init: impl FnOnce() -> Box) -> &dyn Send { + match unsafe { (*self.entries.get()).as_mut() } { + None => panic!("can't access task-locals while the task is being dropped"), + Some(entries) => { + let index = match entries.binary_search_by_key(&key, |e| e.key) { + Ok(i) => i, + Err(i) => { + let value = init(); + entries.insert(i, Entry { key, value }); + i + } + }; + &*entries[index].value } - }; - - &*entries[index].1 + } } /// Clears the map and drops all task-locals. - pub fn clear(&self) { - let entries = unsafe { &mut *self.entries.get() }; - entries.clear(); - } -} - -// Wrap the future into one that drops task-local variables on exit. -pub(crate) unsafe fn add_finalizer(f: impl Future) -> impl Future { - async move { - let res = f.await; - - // Abort on panic because thread-local variables behave the same way. - abort_on_panic(|| worker::get_task(|task| task.metadata().local_map.clear())); - - res + /// + /// This method is only safe to call at the end of the task. + pub unsafe fn clear(&self) { + // Since destructors may attempt to access task-locals, we musnt't hold a mutable reference + // to the `Vec` while dropping them. Instead, we first take the `Vec` out and then drop it. + let entries = (*self.entries.get()).take(); + drop(entries); } } diff --git a/src/task/worker.rs b/src/task/worker.rs deleted file mode 100644 index f7b08e16b..000000000 --- a/src/task/worker.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::cell::Cell; -use std::ptr; - -use crossbeam_deque::Worker; - -use super::pool; -use super::task; -use super::Task; -use crate::utils::abort_on_panic; - -/// Returns a handle to the current task. -/// -/// # Panics -/// -/// This function will panic if not called within the context of a task created by [`block_on`], -/// [`spawn`], or [`Builder::spawn`]. -/// -/// [`block_on`]: fn.block_on.html -/// [`spawn`]: fn.spawn.html -/// [`Builder::spawn`]: struct.Builder.html#method.spawn -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// println!("The name of this task is {:?}", task::current().name()); -/// # -/// # }) -/// ``` -pub fn current() -> Task { - get_task(|task| task.clone()).expect("`task::current()` called outside the context of a task") -} - -thread_local! { - static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut()); -} - -pub(crate) fn set_tag(tag: *const task::Tag, f: F) -> R -where - F: FnOnce() -> R, -{ - struct ResetTag<'a>(&'a Cell<*const task::Tag>); - - impl Drop for ResetTag<'_> { - fn drop(&mut self) { - self.0.set(ptr::null()); - } - } - - TAG.with(|t| { - t.set(tag); - let _guard = ResetTag(t); - - f() - }) -} - -pub(crate) fn get_task(f: F) -> Option -where - F: FnOnce(&Task) -> R, -{ - let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) }); - - match res { - Ok(Some(val)) => Some(val), - Ok(None) | Err(_) => None, - } -} - -thread_local! { - static IS_WORKER: Cell = Cell::new(false); - static QUEUE: Cell>> = Cell::new(None); -} - -pub(crate) fn is_worker() -> bool { - IS_WORKER.with(|is_worker| is_worker.get()) -} - -fn get_queue) -> T, T>(f: F) -> T { - QUEUE.with(|queue| { - let q = queue.take().unwrap(); - let ret = f(&q); - queue.set(Some(q)); - ret - }) -} - -pub(crate) fn schedule(task: task::Runnable) { - if is_worker() { - get_queue(|q| q.push(task)); - } else { - pool::get().injector.push(task); - } - pool::get().sleepers.notify_one(); -} - -pub(crate) fn main_loop(worker: Worker) { - IS_WORKER.with(|is_worker| is_worker.set(true)); - QUEUE.with(|queue| queue.set(Some(worker))); - - loop { - match get_queue(|q| pool::get().find_task(q)) { - Some(task) => set_tag(task.tag(), || abort_on_panic(|| task.run())), - None => pool::get().sleepers.wait(), - } - } -} diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index c11408ab5..5cd0ce5ba 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -1,8 +1,8 @@ +use std::pin::Pin; + use crate::future::Future; use crate::task::{Context, Poll}; -use std::pin::Pin; - /// Cooperatively gives up a timeslice to the task scheduler. /// /// Calling this function will move the currently executing future to the back diff --git a/src/utils.rs b/src/utils.rs index 60516d257..4f3ffe4f7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,4 @@ use std::mem; -use std::process; /// Calls a function and aborts if it panics. /// @@ -10,7 +9,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { impl Drop for Bomb { fn drop(&mut self) { - process::abort(); + std::process::abort(); } } @@ -20,6 +19,53 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } +/// Generates a random number in `0..n`. +pub fn random(n: u32) -> u32 { + use std::cell::Cell; + use std::num::Wrapping; + + thread_local! { + static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); + } + + 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 + }) +} + +/// Defers evaluation of a block of code until the end of the scope. +#[doc(hidden)] +macro_rules! defer { + ($($body:tt)*) => { + let _guard = { + pub struct Guard(Option); + + impl Drop for Guard { + fn drop(&mut self) { + (self.0).take().map(|f| f()); + } + } + + Guard(Some(|| { + let _ = { $($body)* }; + })) + }; + }; +} + /// Declares unstable items. #[doc(hidden)] macro_rules! cfg_unstable { diff --git a/tests/channel.rs b/tests/channel.rs index 0c40f5a73..f7938904d 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "unstable")] + use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; From 87de4e15982bbec890aa909600da5997c4f6edc7 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Fri, 1 Nov 2019 02:45:50 +0100 Subject: [PATCH 111/707] Add utility type WakerSet to the sync module (#390) * Add utility type Registry to the sync module * Remove unused import * Split unregister into complete and cancel * Refactoring and renaming * Split remove() into complete() and cancel() * Rename to WakerSet * Ignore clippy warning * Ignore another clippy warning * Use stronger SeqCst ordering --- benches/mutex.rs | 42 ++++++ benches/task.rs | 11 ++ src/sync/channel.rs | 336 ++++++++++-------------------------------- src/sync/mod.rs | 3 + src/sync/mutex.rs | 124 +++++----------- src/sync/rwlock.rs | 3 +- src/sync/waker_set.rs | 200 +++++++++++++++++++++++++ 7 files changed, 371 insertions(+), 348 deletions(-) create mode 100644 benches/mutex.rs create mode 100644 benches/task.rs create mode 100644 src/sync/waker_set.rs diff --git a/benches/mutex.rs b/benches/mutex.rs new file mode 100644 index 000000000..b159ba127 --- /dev/null +++ b/benches/mutex.rs @@ -0,0 +1,42 @@ +#![feature(test)] + +extern crate test; + +use std::sync::Arc; + +use async_std::sync::Mutex; +use async_std::task; +use test::Bencher; + +#[bench] +fn create(b: &mut Bencher) { + b.iter(|| Mutex::new(())); +} + +#[bench] +fn contention(b: &mut Bencher) { + b.iter(|| task::block_on(run(10, 1000))); +} + +#[bench] +fn no_contention(b: &mut Bencher) { + b.iter(|| task::block_on(run(1, 10000))); +} + +async fn run(task: usize, iter: usize) { + let m = Arc::new(Mutex::new(())); + let mut tasks = Vec::new(); + + for _ in 0..task { + let m = m.clone(); + tasks.push(task::spawn(async move { + for _ in 0..iter { + let _ = m.lock().await; + } + })); + } + + for t in tasks { + t.await; + } +} diff --git a/benches/task.rs b/benches/task.rs new file mode 100644 index 000000000..b31447130 --- /dev/null +++ b/benches/task.rs @@ -0,0 +1,11 @@ +#![feature(test)] + +extern crate test; + +use async_std::task; +use test::Bencher; + +#[bench] +fn block_on(b: &mut Bencher) { + b.iter(|| task::block_on(async {})); +} diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 6751417af..403bee742 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -4,17 +4,17 @@ use std::future::Future; use std::isize; use std::marker::PhantomData; use std::mem; -use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::process; use std::ptr; -use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::sync::Arc; -use std::task::{Context, Poll, Waker}; +use std::task::{Context, Poll}; use crossbeam_utils::{Backoff, CachePadded}; -use futures_core::stream::Stream; -use slab::Slab; + +use crate::stream::Stream; +use crate::sync::WakerSet; /// Creates a bounded multi-producer multi-consumer channel. /// @@ -128,7 +128,7 @@ impl Sender { /// ``` pub async fn send(&self, msg: T) { struct SendFuture<'a, T> { - sender: &'a Sender, + channel: &'a Channel, msg: Option, opt_key: Option, } @@ -142,23 +142,23 @@ impl Sender { let msg = self.msg.take().unwrap(); // Try sending the message. - let poll = match self.sender.channel.push(msg) { + let poll = match self.channel.try_send(msg) { Ok(()) => Poll::Ready(()), - Err(PushError::Disconnected(msg)) => { + Err(TrySendError::Disconnected(msg)) => { self.msg = Some(msg); Poll::Pending } - Err(PushError::Full(msg)) => { - // Register the current task. + Err(TrySendError::Full(msg)) => { + // Insert this send operation. match self.opt_key { - None => self.opt_key = Some(self.sender.channel.sends.register(cx)), - Some(key) => self.sender.channel.sends.reregister(key, cx), + None => self.opt_key = Some(self.channel.send_wakers.insert(cx)), + Some(key) => self.channel.send_wakers.update(key, cx), } // Try sending the message again. - match self.sender.channel.push(msg) { + match self.channel.try_send(msg) { Ok(()) => Poll::Ready(()), - Err(PushError::Disconnected(msg)) | Err(PushError::Full(msg)) => { + Err(TrySendError::Disconnected(msg)) | Err(TrySendError::Full(msg)) => { self.msg = Some(msg); Poll::Pending } @@ -167,10 +167,9 @@ impl Sender { }; if poll.is_ready() { - // If the current task was registered, unregister now. + // If the current task is in the set, remove it. if let Some(key) = self.opt_key.take() { - // `true` means the send operation is completed. - self.sender.channel.sends.unregister(key, true); + self.channel.send_wakers.complete(key); } } @@ -180,16 +179,16 @@ impl Sender { impl Drop for SendFuture<'_, T> { fn drop(&mut self) { - // If the current task was registered, unregister now. + // 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 { - // `false` means the send operation is cancelled. - self.sender.channel.sends.unregister(key, false); + self.channel.send_wakers.cancel(key); } } } SendFuture { - sender: self, + channel: &self.channel, msg: Some(msg), opt_key: None, } @@ -340,7 +339,7 @@ pub struct Receiver { /// The inner channel. channel: Arc>, - /// The registration key for this receiver in the `channel.streams` registry. + /// The key for this receiver in the `channel.stream_wakers` set. opt_key: Option, } @@ -382,16 +381,20 @@ impl Receiver { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - poll_recv(&self.channel, &self.channel.recvs, &mut self.opt_key, cx) + 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 was registered, unregister now. + // If the current task is still in the set, that means it is being cancelled now. if let Some(key) = self.opt_key { - // `false` means the receive operation is cancelled. - self.channel.recvs.unregister(key, false); + self.channel.recv_wakers.cancel(key); } } } @@ -484,10 +487,9 @@ impl Receiver { impl Drop for Receiver { fn drop(&mut self) { - // If the current task was registered as blocked on this stream, unregister now. + // If the current task is still in the stream set, that means it is being cancelled now. if let Some(key) = self.opt_key { - // `false` means the last request for a stream item is cancelled. - self.channel.streams.unregister(key, false); + self.channel.stream_wakers.cancel(key); } // Decrement the receiver count and disconnect the channel if it drops down to zero. @@ -518,7 +520,12 @@ impl Stream for Receiver { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = &mut *self; - poll_recv(&this.channel, &this.channel.streams, &mut this.opt_key, cx) + poll_recv( + &this.channel, + &this.channel.stream_wakers, + &mut this.opt_key, + cx, + ) } } @@ -530,39 +537,38 @@ impl fmt::Debug for Receiver { /// Polls a receive operation on a channel. /// -/// If the receive operation is blocked, the current task will be registered in `registry` and its -/// registration key will then be stored in `opt_key`. +/// 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, - registry: &Registry, + wakers: &WakerSet, opt_key: &mut Option, cx: &mut Context<'_>, ) -> Poll> { // Try receiving a message. - let poll = match channel.pop() { + let poll = match channel.try_recv() { Ok(msg) => Poll::Ready(Some(msg)), - Err(PopError::Disconnected) => Poll::Ready(None), - Err(PopError::Empty) => { - // Register the current task. + Err(TryRecvError::Disconnected) => Poll::Ready(None), + Err(TryRecvError::Empty) => { + // Insert this receive operation. match *opt_key { - None => *opt_key = Some(registry.register(cx)), - Some(key) => registry.reregister(key, cx), + None => *opt_key = Some(wakers.insert(cx)), + Some(key) => wakers.update(key, cx), } // Try receiving a message again. - match channel.pop() { + match channel.try_recv() { Ok(msg) => Poll::Ready(Some(msg)), - Err(PopError::Disconnected) => Poll::Ready(None), - Err(PopError::Empty) => Poll::Pending, + Err(TryRecvError::Disconnected) => Poll::Ready(None), + Err(TryRecvError::Empty) => Poll::Pending, } } }; if poll.is_ready() { - // If the current task was registered, unregister now. + // If the current task is in the set, remove it. if let Some(key) = opt_key.take() { - // `true` means the receive operation is completed. - registry.unregister(key, true); + wakers.complete(key); } } @@ -612,13 +618,13 @@ struct Channel { mark_bit: usize, /// Send operations waiting while the channel is full. - sends: Registry, + send_wakers: WakerSet, /// Receive operations waiting while the channel is empty and not disconnected. - recvs: Registry, + recv_wakers: WakerSet, /// Streams waiting while the channel is empty and not disconnected. - streams: Registry, + stream_wakers: WakerSet, /// The number of currently active `Sender`s. sender_count: AtomicUsize, @@ -672,17 +678,17 @@ impl Channel { mark_bit, head: CachePadded::new(AtomicUsize::new(head)), tail: CachePadded::new(AtomicUsize::new(tail)), - sends: Registry::new(), - recvs: Registry::new(), - streams: Registry::new(), + 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 push a message. - fn push(&self, msg: T) -> Result<(), PushError> { + /// 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); @@ -721,10 +727,10 @@ impl Channel { slot.stamp.store(stamp, Ordering::Release); // Wake a blocked receive operation. - self.recvs.notify_one(); + self.recv_wakers.notify_one(); // Wake all blocked streams. - self.streams.notify_all(); + self.stream_wakers.notify_all(); return Ok(()); } @@ -743,9 +749,9 @@ impl Channel { // Check if the channel is disconnected. if tail & self.mark_bit != 0 { - return Err(PushError::Disconnected(msg)); + return Err(TrySendError::Disconnected(msg)); } else { - return Err(PushError::Full(msg)); + return Err(TrySendError::Full(msg)); } } @@ -759,8 +765,8 @@ impl Channel { } } - /// Attempts to pop a message. - fn pop(&self) -> Result { + /// Attempts to receive a message. + fn try_recv(&self) -> Result { let backoff = Backoff::new(); let mut head = self.head.load(Ordering::Relaxed); @@ -799,7 +805,7 @@ impl Channel { slot.stamp.store(stamp, Ordering::Release); // Wake a blocked send operation. - self.sends.notify_one(); + self.send_wakers.notify_one(); return Ok(msg); } @@ -816,10 +822,10 @@ impl Channel { if (tail & !self.mark_bit) == head { // If the channel is disconnected... if tail & self.mark_bit != 0 { - return Err(PopError::Disconnected); + return Err(TryRecvError::Disconnected); } else { // Otherwise, the receive operation is not ready. - return Err(PopError::Empty); + return Err(TryRecvError::Empty); } } @@ -888,9 +894,9 @@ impl Channel { if tail & self.mark_bit == 0 { // Notify everyone blocked on this channel. - self.sends.notify_all(); - self.recvs.notify_all(); - self.streams.notify_all(); + self.send_wakers.notify_all(); + self.recv_wakers.notify_all(); + self.stream_wakers.notify_all(); } } } @@ -921,8 +927,8 @@ impl Drop for Channel { } } -/// An error returned from the `push()` method. -enum PushError { +/// An error returned from the `try_send()` method. +enum TrySendError { /// The channel is full but not disconnected. Full(T), @@ -930,203 +936,11 @@ enum PushError { Disconnected(T), } -/// An error returned from the `pop()` method. -enum PopError { +/// An error returned from the `try_recv()` method. +enum TryRecvError { /// The channel is empty but not disconnected. Empty, /// The channel is empty and disconnected. Disconnected, } - -/// A list of blocked channel operations. -struct Blocked { - /// A list of registered channel operations. - /// - /// Each entry has a waker associated with the task that is executing the operation. If the - /// waker is set to `None`, that means the task has been woken up but hasn't removed itself - /// from the registry yet. - entries: Slab>, - - /// The number of wakers in the entry list. - waker_count: usize, -} - -/// A registry of blocked channel operations. -/// -/// Blocked operations register themselves in a registry. Successful operations on the opposite -/// side of the channel wake blocked operations in the registry. -struct Registry { - /// A list of blocked channel operations. - blocked: Spinlock, - - /// Set to `true` if there are no wakers in the registry. - /// - /// Note that this either means there are no entries in the registry, or that all entries have - /// been notified. - is_empty: AtomicBool, -} - -impl Registry { - /// Creates a new registry. - fn new() -> Registry { - Registry { - blocked: Spinlock::new(Blocked { - entries: Slab::new(), - waker_count: 0, - }), - is_empty: AtomicBool::new(true), - } - } - - /// Registers a blocked channel operation and returns a key associated with it. - fn register(&self, cx: &Context<'_>) -> usize { - let mut blocked = self.blocked.lock(); - - // Insert a new entry into the list of blocked tasks. - let w = cx.waker().clone(); - let key = blocked.entries.insert(Some(w)); - - blocked.waker_count += 1; - if blocked.waker_count == 1 { - self.is_empty.store(false, Ordering::SeqCst); - } - - key - } - - /// Re-registers a blocked channel operation by filling in its waker. - fn reregister(&self, key: usize, cx: &Context<'_>) { - let mut blocked = self.blocked.lock(); - - let was_none = blocked.entries[key].is_none(); - let w = cx.waker().clone(); - blocked.entries[key] = Some(w); - - if was_none { - blocked.waker_count += 1; - if blocked.waker_count == 1 { - self.is_empty.store(false, Ordering::SeqCst); - } - } - } - - /// Unregisters a channel operation. - /// - /// If `completed` is `true`, the operation will be removed from the registry. If `completed` - /// is `false`, that means the operation was cancelled so another one will be notified. - fn unregister(&self, key: usize, completed: bool) { - let mut blocked = self.blocked.lock(); - let mut removed = false; - - match blocked.entries.remove(key) { - Some(_) => removed = true, - None => { - if !completed { - // This operation was cancelled. Notify another one. - if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - removed = true; - } - } - } - } - } - - if removed { - blocked.waker_count -= 1; - if blocked.waker_count == 0 { - self.is_empty.store(true, Ordering::SeqCst); - } - } - } - - /// Notifies one blocked channel operation. - #[inline] - fn notify_one(&self) { - if !self.is_empty.load(Ordering::SeqCst) { - let mut blocked = self.blocked.lock(); - - if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - - blocked.waker_count -= 1; - if blocked.waker_count == 0 { - self.is_empty.store(true, Ordering::SeqCst); - } - } - } - } - } - - /// Notifies all blocked channel operations. - #[inline] - fn notify_all(&self) { - if !self.is_empty.load(Ordering::SeqCst) { - let mut blocked = self.blocked.lock(); - - for (_, opt_waker) in blocked.entries.iter_mut() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - - blocked.waker_count = 0; - self.is_empty.store(true, Ordering::SeqCst); - } - } -} - -/// A simple spinlock. -struct Spinlock { - flag: AtomicBool, - value: UnsafeCell, -} - -impl Spinlock { - /// Returns a new spinlock initialized with `value`. - fn new(value: T) -> Spinlock { - Spinlock { - flag: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Locks the spinlock. - fn lock(&self) -> SpinlockGuard<'_, T> { - let backoff = Backoff::new(); - while self.flag.swap(true, Ordering::Acquire) { - backoff.snooze(); - } - SpinlockGuard { parent: self } - } -} - -/// A guard holding a spinlock locked. -struct SpinlockGuard<'a, T> { - parent: &'a Spinlock, -} - -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/sync/mod.rs b/src/sync/mod.rs index d10e6bdf2..ea991fe6a 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -191,3 +191,6 @@ cfg_unstable! { mod barrier; mod channel; } + +pub(crate) mod waker_set; +pub(crate) use waker_set::WakerSet; diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index cd7a3577f..fcd030d8f 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -2,18 +2,11 @@ use std::cell::UnsafeCell; use std::fmt; use std::ops::{Deref, DerefMut}; use std::pin::Pin; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use slab::Slab; +use std::sync::atomic::{AtomicBool, Ordering}; use crate::future::Future; -use crate::task::{Context, Poll, Waker}; - -/// Set if the mutex is locked. -const LOCK: usize = 1; - -/// Set if there are tasks blocked on the mutex. -const BLOCKED: usize = 1 << 1; +use crate::sync::WakerSet; +use crate::task::{Context, Poll}; /// A mutual exclusion primitive for protecting shared data. /// @@ -49,8 +42,8 @@ const BLOCKED: usize = 1 << 1; /// # }) /// ``` pub struct Mutex { - state: AtomicUsize, - blocked: std::sync::Mutex>>, + locked: AtomicBool, + wakers: WakerSet, value: UnsafeCell, } @@ -69,8 +62,8 @@ impl Mutex { /// ``` pub fn new(t: T) -> Mutex { Mutex { - state: AtomicUsize::new(0), - blocked: std::sync::Mutex::new(Slab::new()), + locked: AtomicBool::new(false), + wakers: WakerSet::new(), value: UnsafeCell::new(t), } } @@ -105,75 +98,46 @@ impl Mutex { pub struct LockFuture<'a, T> { mutex: &'a Mutex, opt_key: Option, - acquired: bool, } impl<'a, T> Future for LockFuture<'a, T> { type Output = MutexGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.mutex.try_lock() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + let poll = match self.mutex.try_lock() { + Some(guard) => Poll::Ready(guard), None => { - let mut blocked = self.mutex.blocked.lock().unwrap(); - - // Register the current task. + // Insert this lock operation. match self.opt_key { - None => { - // Insert a new entry into the list of blocked tasks. - let w = cx.waker().clone(); - let key = blocked.insert(Some(w)); - self.opt_key = Some(key); - - if blocked.len() == 1 { - self.mutex.state.fetch_or(BLOCKED, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked tasks. Just - // reset the waker if it was removed. - if blocked[key].is_none() { - let w = cx.waker().clone(); - blocked[key] = Some(w); - } - } + None => self.opt_key = Some(self.mutex.wakers.insert(cx)), + Some(key) => self.mutex.wakers.update(key, cx), } // Try locking again because it's possible the mutex got unlocked just - // before the current task was registered as a blocked task. + // before the current task was inserted into the waker set. match self.mutex.try_lock() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + Some(guard) => Poll::Ready(guard), None => Poll::Pending, } } + }; + + if poll.is_ready() { + // If the current task is in the set, remove it. + if let Some(key) = self.opt_key.take() { + self.mutex.wakers.complete(key); + } } + + poll } } 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 { - let mut blocked = self.mutex.blocked.lock().unwrap(); - let opt_waker = blocked.remove(key); - - if opt_waker.is_none() && !self.acquired { - // We were awoken but didn't acquire the lock. Wake up another task. - if let Some((_, opt_waker)) = blocked.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } - - if blocked.is_empty() { - self.mutex.state.fetch_and(!BLOCKED, Ordering::Relaxed); - } + self.mutex.wakers.cancel(key); } } } @@ -181,7 +145,6 @@ impl Mutex { LockFuture { mutex: self, opt_key: None, - acquired: false, } .await } @@ -220,7 +183,7 @@ impl Mutex { /// # }) /// ``` pub fn try_lock(&self) -> Option> { - if self.state.fetch_or(LOCK, Ordering::Acquire) & LOCK == 0 { + if !self.locked.swap(true, Ordering::SeqCst) { Some(MutexGuard(self)) } else { None @@ -266,18 +229,15 @@ impl Mutex { impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - f.debug_struct("Mutex") - .field("data", &LockedPlaceholder) - .finish() + 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(), } } @@ -303,19 +263,11 @@ unsafe impl Sync for MutexGuard<'_, T> {} impl Drop for MutexGuard<'_, T> { fn drop(&mut self) { - let state = self.0.state.fetch_and(!LOCK, Ordering::AcqRel); - - // If there are any blocked tasks, wake one of them up. - if state & BLOCKED != 0 { - let mut blocked = self.0.blocked.lock().unwrap(); + // Use `SeqCst` ordering to synchronize with `WakerSet::insert()` and `WakerSet::update()`. + self.0.locked.store(false, Ordering::SeqCst); - if let Some((_, opt_waker)) = blocked.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } + // Notify one blocked `lock()` operation. + self.0.wakers.notify_one(); } } diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index ed1d2185b..a0d0f07a8 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -10,7 +10,8 @@ use crate::future::Future; use crate::task::{Context, Poll, Waker}; /// Set if a write lock is held. -const WRITE_LOCK: usize = 1; +#[allow(clippy::identity_op)] +const WRITE_LOCK: usize = 1 << 0; /// Set if there are read operations blocked on the lock. const BLOCKED_READS: usize = 1 << 1; diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs new file mode 100644 index 000000000..8fd1b6210 --- /dev/null +++ b/src/sync/waker_set.rs @@ -0,0 +1,200 @@ +//! A common utility for building synchronization primitives. +//! +//! When an async operation is blocked, it needs to register itself somewhere so that it can be +//! notified later on. The `WakerSet` type helps with keeping track of such async operations and +//! notifying them when they may make progress. + +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use crossbeam_utils::Backoff; +use slab::Slab; + +use crate::task::{Context, Waker}; + +/// Set when the entry list is locked. +#[allow(clippy::identity_op)] +const LOCKED: usize = 1 << 0; + +/// Set when there are tasks for `notify_one()` to wake. +const NOTIFY_ONE: usize = 1 << 1; + +/// Set when there are tasks for `notify_all()` to wake. +const NOTIFY_ALL: usize = 1 << 2; + +/// Inner representation of `WakerSet`. +struct Inner { + /// A list of entries in the set. + /// + /// Each entry has an optional waker associated with the task that is executing the operation. + /// If the waker is set to `None`, that means the task has been woken up but hasn't removed + /// itself from the `WakerSet` yet. + /// + /// The key of each entry is its index in the `Slab`. + entries: Slab>, + + /// The number of entries that have the waker set to `None`. + none_count: usize, +} + +/// A set holding wakers. +pub struct WakerSet { + /// Holds three bits: `LOCKED`, `NOTIFY_ONE`, and `NOTIFY_ALL`. + flag: AtomicUsize, + + /// A set holding wakers. + inner: UnsafeCell, +} + +impl WakerSet { + /// Creates a new `WakerSet`. + #[inline] + pub fn new() -> WakerSet { + WakerSet { + flag: AtomicUsize::new(0), + inner: UnsafeCell::new(Inner { + entries: Slab::new(), + none_count: 0, + }), + } + } + + /// Inserts a waker for a blocked operation and returns a key associated with it. + pub fn insert(&self, cx: &Context<'_>) -> usize { + let w = cx.waker().clone(); + self.lock().entries.insert(Some(w)) + } + + /// Updates the waker of a previously inserted entry. + pub fn update(&self, key: usize, cx: &Context<'_>) { + let mut inner = self.lock(); + + match &mut inner.entries[key] { + None => { + // Fill in the waker. + let w = cx.waker().clone(); + inner.entries[key] = Some(w); + inner.none_count -= 1; + } + Some(w) => { + // Replace the waker if the existing one is different. + if !w.will_wake(cx.waker()) { + *w = cx.waker().clone(); + } + } + } + } + + /// Removes the waker of a completed operation. + pub fn complete(&self, key: usize) { + let mut inner = self.lock(); + if inner.entries.remove(key).is_none() { + inner.none_count -= 1; + } + } + + /// Removes the waker of a cancelled operation. + pub fn cancel(&self, key: usize) { + let mut inner = self.lock(); + if inner.entries.remove(key).is_none() { + inner.none_count -= 1; + + // The operation was cancelled and notified so notify another operation instead. + if let Some((_, opt_waker)) = inner.entries.iter_mut().next() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + inner.none_count += 1; + } + } + } + } + + /// Notifies one blocked operation. + #[inline] + pub fn notify_one(&self) { + // Use `SeqCst` ordering to synchronize with `Lock::drop()`. + if self.flag.load(Ordering::SeqCst) & NOTIFY_ONE != 0 { + self.notify(false); + } + } + + /// Notifies all blocked operations. + // TODO: Delete this attribute when `crate::sync::channel()` is stabilized. + #[cfg(feature = "unstable")] + #[inline] + pub fn notify_all(&self) { + // Use `SeqCst` ordering to synchronize with `Lock::drop()`. + if self.flag.load(Ordering::SeqCst) & NOTIFY_ALL != 0 { + self.notify(true); + } + } + + /// Notifies blocked operations, either one or all of them. + fn notify(&self, all: bool) { + let mut inner = &mut *self.lock(); + + for (_, opt_waker) in inner.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + inner.none_count += 1; + } + if !all { + break; + } + } + } + + /// Locks the list of entries. + #[cold] + fn lock(&self) -> Lock<'_> { + let backoff = Backoff::new(); + while self.flag.fetch_or(LOCKED, Ordering::Acquire) & LOCKED != 0 { + backoff.snooze(); + } + Lock { waker_set: self } + } +} + +/// A guard holding a `WakerSet` locked. +struct Lock<'a> { + waker_set: &'a WakerSet, +} + +impl Drop for Lock<'_> { + #[inline] + fn drop(&mut self) { + let mut flag = 0; + + // If there is at least one entry and all are `Some`, then `notify_one()` has work to do. + if !self.entries.is_empty() && self.none_count == 0 { + flag |= NOTIFY_ONE; + } + + // If there is at least one `Some` entry, then `notify_all()` has work to do. + if self.entries.len() - self.none_count > 0 { + flag |= NOTIFY_ALL; + } + + // Use `SeqCst` ordering to synchronize with `WakerSet::lock_to_notify()`. + self.waker_set.flag.store(flag, Ordering::SeqCst); + } +} + +impl Deref for Lock<'_> { + type Target = Inner; + + #[inline] + fn deref(&self) -> &Inner { + unsafe { &*self.waker_set.inner.get() } + } +} + +impl DerefMut for Lock<'_> { + #[inline] + fn deref_mut(&mut self) -> &mut Inner { + unsafe { &mut *self.waker_set.inner.get() } + } +} From 277fd521bc78c2d5502c7565cd5d0d8147318fdc Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 1 Nov 2019 11:30:51 +0900 Subject: [PATCH 112/707] Remove deprecated action --- .github/workflows/ci.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd8ec8997..ac49bd6e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,15 +52,10 @@ jobs: steps: - uses: actions/checkout@master - - id: component - uses: actions-rs/components-nightly@v1 - with: - component: rustfmt - - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: ${{ steps.component.outputs.toolchain }} + toolchain: nightly override: true components: rustfmt From dcd7c55cef51637c0a241b06e786eef5ecdc9935 Mon Sep 17 00:00:00 2001 From: Paulo Date: Fri, 1 Nov 2019 00:41:38 -0300 Subject: [PATCH 113/707] Put everything behind a 'stable' feature --- Cargo.toml | 2 ++ src/lib.rs | 63 ++++++++++++++++++++++++++++++-------------------- src/prelude.rs | 2 -- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63897053b..af72b6f60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,10 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] +default = ["stable"] docs = ["unstable"] unstable = ["broadcaster"] +stable = [] [dependencies] async-macros = "1.0.0" diff --git a/src/lib.rs b/src/lib.rs index b659c39e9..26856887e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,33 +49,46 @@ #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "2048"] -#[macro_use] -mod utils; +/// Declares stable items. +#[doc(hidden)] +macro_rules! cfg_stable { + ($($item:item)*) => { + $( + #[cfg(feature = "stable")] + $item + )* + } +} -pub mod fs; -pub mod future; -pub mod io; -pub mod net; -pub mod os; -pub mod path; -pub mod prelude; -pub mod stream; -pub mod sync; -pub mod task; +cfg_stable! { + #[macro_use] + mod utils; -cfg_unstable! { - pub mod pin; - pub mod process; + pub mod fs; + pub mod future; + pub mod io; + pub mod net; + pub mod os; + pub mod path; + pub mod prelude; + pub mod stream; + pub mod sync; + pub mod task; - mod unit; - mod vec; - mod result; - mod option; - mod string; - mod collections; + cfg_unstable! { + pub mod pin; + pub mod process; - #[doc(inline)] - pub use std::{write, writeln}; -} + mod unit; + mod vec; + mod result; + mod option; + mod string; + mod collections; -mod macros; + #[doc(inline)] + pub use std::{write, writeln}; + } + + mod macros; +} diff --git a/src/prelude.rs b/src/prelude.rs index 91432e0bb..3e235cfae 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -23,8 +23,6 @@ pub use crate::io::Seek as _; pub use crate::io::Write as _; #[doc(no_inline)] pub use crate::stream::Stream; -#[doc(no_inline)] -pub use crate::task_local; #[doc(hidden)] pub use crate::future::future::FutureExt as _; From a3b742188d6ba9e5a3d20865527164a02898db2e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 1 Nov 2019 12:54:43 +0100 Subject: [PATCH 114/707] fix doc tests (#431) * fix doc tests Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts --- src/stream/stream/min.rs | 2 +- src/stream/stream/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs index 1ab56065d..b4a8c7c16 100644 --- a/src/stream/stream/min.rs +++ b/src/stream/stream/min.rs @@ -1,5 +1,5 @@ +use std::cmp::{Ord, Ordering}; use std::marker::PhantomData; -use std::cmp::{Ordering, Ord}; use std::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 71139f4cc..f7905ed21 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -774,11 +774,10 @@ extension_trait! { # Examples - ``` + ```ignore # fn main() { async_std::task::block_on(async { # use std::collections::VecDeque; - use async_std::prelude::*; let s: VecDeque = vec![1, 2, 3].into_iter().collect(); From 063798ce494b3b20a1e7ef1b08f6a0cd0b0f2d9b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 1 Nov 2019 21:18:43 +0900 Subject: [PATCH 115/707] Add doc --- src/stream/from_iter.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index 8d3dba78b..43bc96112 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -6,6 +6,12 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { + /// A stream that created from iterator + /// + /// This stream is created by the [`from_iter`] function. + /// See it documentation for more. + /// + /// [`from_iter`]: fn.from_iter.html #[derive(Debug)] pub struct FromIter { iter: I, From 2e66c38453f73e48ee7c1ae1020e2038ed3b0021 Mon Sep 17 00:00:00 2001 From: Paulo Date: Fri, 1 Nov 2019 10:58:51 -0300 Subject: [PATCH 116/707] Simplify default feature --- Cargo.toml | 3 +-- src/lib.rs | 64 ++++++++++++++++++++------------------------------ src/prelude.rs | 2 ++ 3 files changed, 29 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af72b6f60..b117b113d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,10 +21,9 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -default = ["stable"] +default = [] docs = ["unstable"] unstable = ["broadcaster"] -stable = [] [dependencies] async-macros = "1.0.0" diff --git a/src/lib.rs b/src/lib.rs index 26856887e..ea05edf73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ //! features = ["unstable"] //! ``` +#![cfg(feature = "default")] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![allow(clippy::mutex_atomic, clippy::module_inception)] @@ -49,46 +50,33 @@ #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "2048"] -/// Declares stable items. -#[doc(hidden)] -macro_rules! cfg_stable { - ($($item:item)*) => { - $( - #[cfg(feature = "stable")] - $item - )* - } -} - -cfg_stable! { - #[macro_use] - mod utils; +#[macro_use] +mod utils; - pub mod fs; - pub mod future; - pub mod io; - pub mod net; - pub mod os; - pub mod path; - pub mod prelude; - pub mod stream; - pub mod sync; - pub mod task; +pub mod fs; +pub mod future; +pub mod io; +pub mod net; +pub mod os; +pub mod path; +pub mod prelude; +pub mod stream; +pub mod sync; +pub mod task; - cfg_unstable! { - pub mod pin; - pub mod process; +cfg_unstable! { + pub mod pin; + pub mod process; - mod unit; - mod vec; - mod result; - mod option; - mod string; - mod collections; + mod unit; + mod vec; + mod result; + mod option; + mod string; + mod collections; - #[doc(inline)] - pub use std::{write, writeln}; - } - - mod macros; + #[doc(inline)] + pub use std::{write, writeln}; } + +mod macros; diff --git a/src/prelude.rs b/src/prelude.rs index 3e235cfae..73a5952e2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -36,6 +36,8 @@ pub use crate::io::seek::SeekExt as _; pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; +#[doc(hidden)] +pub use crate::task_local; cfg_unstable! { #[doc(no_inline)] From 8e991bcd3a6273f634cb9dbd579b54a87173af3a Mon Sep 17 00:00:00 2001 From: Paulo Date: Fri, 1 Nov 2019 10:59:38 -0300 Subject: [PATCH 117/707] Fix typo --- src/prelude.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index 73a5952e2..91432e0bb 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -23,6 +23,8 @@ pub use crate::io::Seek as _; pub use crate::io::Write as _; #[doc(no_inline)] pub use crate::stream::Stream; +#[doc(no_inline)] +pub use crate::task_local; #[doc(hidden)] pub use crate::future::future::FutureExt as _; @@ -36,8 +38,6 @@ pub use crate::io::seek::SeekExt as _; pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; -#[doc(hidden)] -pub use crate::task_local; cfg_unstable! { #[doc(no_inline)] From d5fd035956096d6eb4404fc27a84ccade347d74c Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Fri, 1 Nov 2019 16:10:37 +0100 Subject: [PATCH 118/707] Small example for a TCP server that both handles IP v4 and v6 (#418) * Add a small example for listening to both ipv4 and ipv6 Presenting stream merge on Incoming. * Change stable checks workflow to not cover examples, but tests --- .github/workflows/ci.yml | 2 +- examples/tcp-ipv4-and-6-echo.rs | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 examples/tcp-ipv4-and-6-echo.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac49bd6e0..06712838e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --bins --examples + args: --all --bins --tests - name: check unstable uses: actions-rs/cargo@v1 diff --git a/examples/tcp-ipv4-and-6-echo.rs b/examples/tcp-ipv4-and-6-echo.rs new file mode 100644 index 000000000..aef5e15e5 --- /dev/null +++ b/examples/tcp-ipv4-and-6-echo.rs @@ -0,0 +1,44 @@ +//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets. +//! +//! To send messages, do: +//! +//! ```sh +//! $ nc 127.0.0.1 8080 +//! $ nc ::1 8080 +//! ``` + +use async_std::io; +use async_std::net::{TcpListener, TcpStream}; +use async_std::prelude::*; +use async_std::task; + +async fn process(stream: TcpStream) -> io::Result<()> { + println!("Accepted from: {}", stream.peer_addr()?); + + let (reader, writer) = &mut (&stream, &stream); + io::copy(reader, writer).await?; + + Ok(()) +} + +fn main() -> io::Result<()> { + task::block_on(async { + let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?; + println!("Listening on {}", ipv4_listener.local_addr()?); + let ipv6_listener = TcpListener::bind("[::1]:8080").await?; + println!("Listening on {}", ipv6_listener.local_addr()?); + + let ipv4_incoming = ipv4_listener.incoming(); + let ipv6_incoming = ipv6_listener.incoming(); + + let mut incoming = ipv4_incoming.merge(ipv6_incoming); + + while let Some(stream) = incoming.next().await { + let stream = stream?; + task::spawn(async { + process(stream).await.unwrap(); + }); + } + Ok(()) + }) +} From 81873ae5f3a0ea31bc20373c62e9c2b1ebe359ad Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 2 Nov 2019 01:27:27 +0900 Subject: [PATCH 119/707] fix --- src/io/stderr.rs | 2 +- src/io/stdin.rs | 2 +- src/io/stdout.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index b3cc71138..8986d48d3 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -122,7 +122,7 @@ impl Stderr { pub async fn lock(&self) -> StderrLock<'static> { static STDERR: Lazy = Lazy::new(std::io::stderr); - blocking::spawn(move || StderrLock(STDERR.lock())).await + spawn_blocking(move || StderrLock(STDERR.lock())).await } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index a72d05595..d7b26909f 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -178,7 +178,7 @@ impl Stdin { pub async fn lock(&self) -> StdinLock<'static> { static STDIN: Lazy = Lazy::new(std::io::stdin); - blocking::spawn(move || StdinLock(STDIN.lock())).await + spawn_blocking(move || StdinLock(STDIN.lock())).await } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 99c636d67..c6281d776 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -122,7 +122,7 @@ impl Stdout { pub async fn lock(&self) -> StdoutLock<'static> { static STDOUT: Lazy = Lazy::new(std::io::stdout); - blocking::spawn(move || StdoutLock(STDOUT.lock())).await + spawn_blocking(move || StdoutLock(STDOUT.lock())).await } } From 1a51ca424a04bb2bc9bb26c4ba03717ee1baf693 Mon Sep 17 00:00:00 2001 From: Sheyne Anderson Date: Fri, 1 Nov 2019 12:17:46 -0700 Subject: [PATCH 120/707] Fix typo in tutorial in book (#412) --- docs/src/tutorial/specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index bda457660..322c49fac 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy login1, login2, ... loginN: message ``` -Each of the specified clients than receives a `from login: message` message. +Each of the specified clients then receives a `from login: message` message. A possible session might look like this From 5adc608791346a0b8b97ad2bdae56da7f72ac7e0 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Fri, 1 Nov 2019 21:53:13 +0100 Subject: [PATCH 121/707] =?UTF-8?q?Spawn=20several=20threads=20when=20we?= =?UTF-8?q?=20fail=20to=20enqueue=20work=20in=20the=20blocki=E2=80=A6=20(#?= =?UTF-8?q?181)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rebase onto master * Switch to unbounded channels --- src/task/spawn_blocking.rs | 46 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index b6b5ea34b..3f4f18a17 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; -use crossbeam_channel::{bounded, Receiver, Sender}; +use crossbeam_channel::{unbounded, Receiver, Sender}; use once_cell::sync::Lazy; use crate::task::{JoinHandle, Task}; @@ -79,7 +79,7 @@ static POOL: Lazy = Lazy::new(|| { // before being acted on by a core. This helps keep // latency snappy in the overall async system by // reducing bufferbloat. - let (sender, receiver) = bounded(0); + let (sender, receiver) = unbounded(); Pool { sender, receiver } }); @@ -95,27 +95,31 @@ fn maybe_create_another_blocking_thread() { return; } - // We want to avoid having all threads terminate at - // exactly the same time, causing thundering herd - // effects. We want to stagger their destruction over - // 10 seconds or so to make the costs fade into - // background noise. - // - // Generate a simple random number of milliseconds - let rand_sleep_ms = u64::from(random(10_000)); + let n_to_spawn = std::cmp::min(2 + (workers / 10), 10); - thread::Builder::new() - .name("async-std/blocking".to_string()) - .spawn(move || { - let wait_limit = Duration::from_millis(1000 + rand_sleep_ms); + for _ in 0..n_to_spawn { + // We want to avoid having all threads terminate at + // exactly the same time, causing thundering herd + // effects. We want to stagger their destruction over + // 10 seconds or so to make the costs fade into + // background noise. + // + // Generate a simple random number of milliseconds + let rand_sleep_ms = u64::from(random(10_000)); - DYNAMIC_THREAD_COUNT.fetch_add(1, Ordering::Relaxed); - while let Ok(task) = POOL.receiver.recv_timeout(wait_limit) { - abort_on_panic(|| task.run()); - } - DYNAMIC_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed); - }) - .expect("cannot start a dynamic thread driving blocking tasks"); + thread::Builder::new() + .name("async-std/blocking".to_string()) + .spawn(move || { + let wait_limit = Duration::from_millis(1000 + rand_sleep_ms); + + DYNAMIC_THREAD_COUNT.fetch_add(1, Ordering::Relaxed); + while let Ok(task) = POOL.receiver.recv_timeout(wait_limit) { + abort_on_panic(|| task.run()); + } + DYNAMIC_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed); + }) + .expect("cannot start a dynamic thread driving blocking tasks"); + } } // Enqueues work, attempting to send to the threadpool in a From ec1a6ea3e88d04397f7a0c89c01c0796200f9616 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 2 Nov 2019 00:08:19 +0300 Subject: [PATCH 122/707] Fix typo (#439) --- src/stream/stream/mod.rs | 6 +++--- src/stream/stream/try_for_each.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f7905ed21..e46ca9e5c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -85,7 +85,7 @@ use nth::NthFuture; use partial_cmp::PartialCmpFuture; use position::PositionFuture; use try_fold::TryFoldFuture; -use try_for_each::TryForEeachFuture; +use try_for_each::TryForEachFuture; pub use chain::Chain; pub use filter::Filter; @@ -1396,12 +1396,12 @@ extension_trait! { fn try_for_each( self, f: F, - ) -> impl Future [TryForEeachFuture] + ) -> impl Future [TryForEachFuture] where Self: Sized, F: FnMut(Self::Item) -> Result<(), E>, { - TryForEeachFuture::new(self, f) + TryForEachFuture::new(self, f) } #[doc = r#" diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 578b854c1..6b66d2ea1 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -10,7 +10,7 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - pub struct TryForEeachFuture { + pub struct TryForEachFuture { #[pin] stream: S, f: F, @@ -19,9 +19,9 @@ pin_project! { } } -impl TryForEeachFuture { +impl TryForEachFuture { pub(crate) fn new(stream: S, f: F) -> Self { - TryForEeachFuture { + TryForEachFuture { stream, f, __from: PhantomData, @@ -30,7 +30,7 @@ impl TryForEeachFuture { } } -impl Future for TryForEeachFuture +impl Future for TryForEachFuture where S: Stream, S::Item: std::fmt::Debug, From 3dcad984b4c492196118cc6249e2d79b0415a279 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 2 Nov 2019 12:29:54 +0900 Subject: [PATCH 123/707] fix: To unstable feature --- src/io/stderr.rs | 7 ++++++- src/io/stdin.rs | 7 ++++++- src/io/stdout.rs | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 8986d48d3..a0d9b7135 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,4 +1,3 @@ -use once_cell::sync::Lazy; use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -7,6 +6,10 @@ use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + 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`]. @@ -119,6 +122,8 @@ 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); diff --git a/src/io/stdin.rs b/src/io/stdin.rs index d7b26909f..e5bb508d5 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,4 +1,3 @@ -use once_cell::sync::Lazy; use std::pin::Pin; use std::sync::Mutex; @@ -6,6 +5,10 @@ use crate::future::{self, Future}; use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + 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`]. @@ -175,6 +178,8 @@ 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); diff --git a/src/io/stdout.rs b/src/io/stdout.rs index c6281d776..bf22128fc 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,4 +1,3 @@ -use once_cell::sync::Lazy; use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -7,6 +6,10 @@ use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +cfg_unstable! { + 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`]. @@ -119,6 +122,8 @@ 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); From 5fb9d3e980fe7b25973d8930abc21c9b65762d70 Mon Sep 17 00:00:00 2001 From: Zhang Guyu Date: Sat, 2 Nov 2019 22:58:30 +0800 Subject: [PATCH 124/707] add Stream::copied (#442) --- src/stream/stream/copied.rs | 33 ++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/stream/stream/copied.rs diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs new file mode 100644 index 000000000..477d59d2f --- /dev/null +++ b/src/stream/stream/copied.rs @@ -0,0 +1,33 @@ +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use pin_project_lite::pin_project; +use std::pin::Pin; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Copied { + #[pin] + stream: S, + } +} + +impl Copied { + pub(super) fn new(stream: S) -> Self { + Copied { stream } + } +} + +impl<'a, S, T: 'a> Stream for Copied +where + S: Stream, + T: Copy, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.copied()) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e46ca9e5c..51ef29d1b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -25,6 +25,7 @@ mod all; mod any; mod chain; mod cmp; +mod copied; mod enumerate; mod eq; mod filter; @@ -88,6 +89,7 @@ use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; pub use chain::Chain; +pub use copied::Copied; pub use filter::Filter; pub use fuse::Fuse; pub use inspect::Inspect; @@ -369,6 +371,42 @@ extension_trait! { 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 std::collections::VecDeque; + + let v: VecDeque<_> = vec![&1, &2, &3].into_iter().collect(); + + let mut v_copied = v.copied(); + + assert_eq!(v_copied.next().await, Some(1)); + assert_eq!(v_copied.next().await, Some(2)); + assert_eq!(v_copied.next().await, Some(3)); + assert_eq!(v_copied.next().await, None); + + + # + # }) } + ``` + "#] + fn copied<'a,T>(self) -> Copied + where + Self: Sized + Stream, + T : 'a + Copy, + { + Copied::new(self) + } + #[doc = r#" Creates a stream that gives the current element's count as well as the next value. From 3a2e6d5b9255b3872f4cd1d3020977d3c6c94ecb Mon Sep 17 00:00:00 2001 From: yjh <465402634@qq.com> Date: Sat, 2 Nov 2019 22:59:15 +0800 Subject: [PATCH 125/707] add max_by_key (#408) * add max_by_key * fix conflict * fmt code --- src/stream/stream/max_by_key.rs | 60 +++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 39 +++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/stream/stream/max_by_key.rs diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs new file mode 100644 index 000000000..b3fb65bf4 --- /dev/null +++ b/src/stream/stream/max_by_key.rs @@ -0,0 +1,60 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxByKeyFuture { + #[pin] + stream: S, + max: Option, + key_by: K, + } +} + +impl MaxByKeyFuture { + pub(super) fn new(stream: S, key_by: K) -> Self { + Self { + stream, + max: None, + key_by, + } + } +} + +impl Future for MaxByKeyFuture +where + S: Stream, + K: FnMut(&S::Item) -> S::Item, + S::Item: Ord, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + let new = (this.key_by)(&new); + cx.waker().wake_by_ref(); + match this.max.take() { + None => *this.max = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take()), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 51ef29d1b..4a3875c44 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -43,6 +43,7 @@ mod le; mod lt; mod map; mod max_by; +mod max_by_key; mod min; mod min_by; mod min_by_key; @@ -77,6 +78,7 @@ use last::LastFuture; use le::LeFuture; use lt::LtFuture; use max_by::MaxByFuture; +use max_by_key::MaxByKeyFuture; use min::MinFuture; use min_by::MinByFuture; use min_by_key::MinByKeyFuture; @@ -767,6 +769,43 @@ extension_trait! { MinByKeyFuture::new(self, key_by) } + #[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. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![-1, -2, -3].into_iter().collect(); + + let max = s.clone().max_by_key(|x| x.abs()).await; + assert_eq!(max, Some(3)); + + let max = VecDeque::::new().max_by_key(|x| x.abs()).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by_key( + self, + key_by: K, + ) -> impl Future> [MaxByKeyFuture] + where + Self: Sized, + Self::Item: Ord, + K: FnMut(&Self::Item) -> Self::Item, + { + MaxByKeyFuture::new(self, key_by) + } + #[doc = r#" Returns the element that gives the minimum value with respect to the specified comparison function. If several elements are equally minimum, From 735fa6954ee62602c2aa34c3b125830be403645c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 2 Nov 2019 23:00:03 +0100 Subject: [PATCH 126/707] Replace select!/try_select! with Future::{race,try_race} (#405) * init Future::select Signed-off-by: Yoshua Wuyts * implement Future::select Signed-off-by: Yoshua Wuyts * try_select Signed-off-by: Yoshua Wuyts * fixes Signed-off-by: Yoshua Wuyts * works Signed-off-by: Yoshua Wuyts * pass clippy Signed-off-by: Yoshua Wuyts * please clippy Signed-off-by: Yoshua Wuyts * implement feedback from stjepan Signed-off-by: Yoshua Wuyts * rename select to race Signed-off-by: Yoshua Wuyts * fmt Signed-off-by: Yoshua Wuyts --- src/future/{future.rs => future/mod.rs} | 92 +++++++++++++++++++++++++ src/future/future/race.rs | 57 +++++++++++++++ src/future/future/try_race.rs | 66 ++++++++++++++++++ src/future/mod.rs | 34 ++++----- src/utils.rs | 2 +- 5 files changed, 234 insertions(+), 17 deletions(-) rename src/future/{future.rs => future/mod.rs} (67%) create mode 100644 src/future/future/race.rs create mode 100644 src/future/future/try_race.rs diff --git a/src/future/future.rs b/src/future/future/mod.rs similarity index 67% rename from src/future/future.rs rename to src/future/future/mod.rs index fe685176a..dad94daa8 100644 --- a/src/future/future.rs +++ b/src/future/future/mod.rs @@ -1,9 +1,13 @@ cfg_unstable! { mod delay; + mod race; + mod try_race; use std::time::Duration; use delay::DelayFuture; + use race::Race; + use try_race::TryRace; } extension_trait! { @@ -129,6 +133,94 @@ extension_trait! { { DelayFuture::new(self, dur) } + + #[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 macro consumes all futures passed, and once a future is + completed, all other futures are dropped. + + This macro is only usable inside of async functions, closures, and blocks. + + # 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(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn race( + self, + other: F + ) -> impl Future::Output> [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. + + # 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(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_race( + self, + other: F + ) -> impl Future::Output> [TryRace] + where + Self: std::future::Future> + Sized, + F: std::future::Future::Output>, + { + TryRace::new(self, other) + } } impl Future for Box { diff --git a/src/future/future/race.rs b/src/future/future/race.rs new file mode 100644 index 000000000..2fd604a73 --- /dev/null +++ b/src/future/future/race.rs @@ -0,0 +1,57 @@ +use std::pin::Pin; + +use async_macros::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct Race + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl Race +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for Race +where + L: Future, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + if Future::poll(Pin::new(&mut left), cx).is_ready() { + return Poll::Ready(left.take().unwrap()); + } + + let mut right = this.right; + if Future::poll(Pin::new(&mut right), cx).is_ready() { + return Poll::Ready(right.take().unwrap()); + } + + Poll::Pending + } +} diff --git a/src/future/future/try_race.rs b/src/future/future/try_race.rs new file mode 100644 index 000000000..d0ca4a90f --- /dev/null +++ b/src/future/future/try_race.rs @@ -0,0 +1,66 @@ +use std::pin::Pin; + +use async_macros::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct TryRace + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl TryRace +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for TryRace +where + L: Future>, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let mut left_errored = false; + + // Check if the left future is ready & successful. Continue if not. + let mut left = this.left; + if Future::poll(Pin::new(&mut left), cx).is_ready() { + if left.as_ref().output().unwrap().is_ok() { + return Poll::Ready(left.take().unwrap()); + } else { + left_errored = true; + } + } + + // Check if the right future is ready & successful. Return err if left + // future also resolved to err. Continue if not. + let mut right = this.right; + let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready(); + if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) { + return Poll::Ready(right.take().unwrap()); + } + + Poll::Pending + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index a45bf96cd..dd28f2848 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -4,16 +4,16 @@ //! //! Often it's desireable 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 `select` family of +//! single future that returns all of their outputs. The `race` family of //! operations converts multiple future into a single future that returns the //! first output. //! //! For operating on futures the following macros can be used: //! -//! | Name | Return signature | When does it return? | -//! | --- | --- | --- | -//! | `future::join` | `(T1, T2)` | Wait for all to complete -//! | `future::select` | `T` | Return on first value +//! | Name | Return signature | When does it return? | +//! | --- | --- | --- | +//! | [`future::join!`] | `(T1, T2)` | Wait for all to complete +//! | [`Future::race`] | `T` | Return on first value //! //! ## Fallible Futures Concurrency //! @@ -25,21 +25,26 @@ //! futures are dropped and an error is returned. This is referred to as //! "short-circuiting". //! -//! In the case of `try_select`, instead of returning the first future that +//! In the case of `try_race`, instead of returning the first future that //! completes it returns the first future that _successfully_ completes. This -//! means `try_select` will keep going until any one of the futures returns +//! means `try_race` will keep going until any one of the futures returns //! `Ok`, or _all_ futures have returned `Err`. //! //! However sometimes it can be useful to use the base variants of the macros //! even on futures that return `Result`. Here is an overview of operations that //! work on `Result`, and their respective semantics: //! -//! | Name | Return signature | When does it return? | -//! | --- | --- | --- | -//! | `future::join` | `(Result, Result)` | Wait for all to complete -//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete -//! | `future::select` | `Result` | Return on first value -//! | `future::try_select` | `Result` | Return on first `Ok`, reject on last Err +//! | Name | Return signature | When does it return? | +//! | --- | --- | --- | +//! | [`future::join!`] | `(Result, Result)` | Wait for all to complete +//! | [`future::try_join!`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete +//! | [`Future::race`] | `Result` | Return on first value +//! | [`Future::try_race`] | `Result` | Return on first `Ok`, reject on last Err +//! +//! [`future::join!`]: macro.join.html +//! [`future::try_join!`]: macro.try_join.html +//! [`Future::race`]: trait.Future.html#method.race +//! [`Future::try_race`]: trait.Future.html#method.try_race #[doc(inline)] pub use async_macros::{join, try_join}; @@ -57,9 +62,6 @@ mod ready; mod timeout; cfg_unstable! { - #[doc(inline)] - pub use async_macros::{select, try_select}; - pub use into_future::IntoFuture; mod into_future; } diff --git a/src/utils.rs b/src/utils.rs index 4f3ffe4f7..a1d851078 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -190,7 +190,7 @@ macro_rules! extension_trait { }; // Parse the return type in an extension method. - (@doc ($($head:tt)*) -> impl Future [$f:ty] $($tail:tt)*) => { + (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); }; (@ext ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { From 6f9436e575dbafe61f79639fb840ae32a54bcb76 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 2 Nov 2019 14:25:45 +0100 Subject: [PATCH 127/707] mark stdio-lock structs as unstable Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 12 +++++++++--- src/io/stderr.rs | 14 +++++++++++--- src/io/stdin.rs | 10 ++++++++-- src/io/stdout.rs | 12 ++++++++++-- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index c81d82f95..93753d104 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -282,9 +282,9 @@ pub use read::Read; pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; -pub use stderr::{stderr, Stderr, StderrLock}; -pub use stdin::{stdin, Stdin, StdinLock}; -pub use stdout::{stdout, Stdout, StdoutLock}; +pub use stderr::{stderr, Stderr}; +pub use stdin::{stdin, Stdin}; +pub use stdout::{stdout, Stdout}; pub use timeout::timeout; pub use write::Write; @@ -311,3 +311,9 @@ mod stdin; mod stdio; mod stdout; mod timeout; + +cfg_unstable! { + 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 a0d9b7135..8bd2180a4 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,4 +1,3 @@ -use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -8,6 +7,7 @@ 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. @@ -59,13 +59,19 @@ pub fn stderr() -> Stderr { 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. +/// +/// 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. @@ -234,7 +240,9 @@ cfg_windows! { } } -impl Write for StderrLock<'_> { +#[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<'_>, diff --git a/src/io/stdin.rs b/src/io/stdin.rs index e5bb508d5..26b7ee009 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -7,6 +7,7 @@ use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; cfg_unstable! { use once_cell::sync::Lazy; + use std::io::Read as _; } /// Constructs a new handle to the standard input of the current process. @@ -59,13 +60,18 @@ pub fn stdin() -> Stdin { 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. @@ -257,14 +263,14 @@ 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> { - use std::io::Read as StdRead; - Poll::Ready(self.0.read(buf)) } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index bf22128fc..c0565aa55 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,4 +1,3 @@ -use std::io::Write as StdWrite; use std::pin::Pin; use std::sync::Mutex; @@ -8,6 +7,7 @@ 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. @@ -59,13 +59,19 @@ pub fn stdout() -> Stdout { 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. +/// +/// 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. @@ -234,6 +240,8 @@ 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 fa91d7f856a9f0afc9689371ced6f1c941c346bc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 02:11:59 +0300 Subject: [PATCH 128/707] Stream::merge does not end prematurely if one stream is delayed (#437) * Stream::merge does not end prematurely if one stream is delayed * `cargo test` without features works * Stream::merge works correctly for unfused streams --- Cargo.toml | 8 +++ src/stream/stream/merge.rs | 26 ++++++---- src/stream/stream/mod.rs | 2 +- tests/stream.rs | 100 +++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 tests/stream.rs diff --git a/Cargo.toml b/Cargo.toml index b117b113d..4e9dc09cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,3 +56,11 @@ futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } # These are used by the book for examples futures-channel-preview = "=0.3.0-alpha.19" futures-util-preview = "=0.3.0-alpha.19" + +[[test]] +name = "stream" +required-features = ["unstable"] + +[[example]] +name = "tcp-ipv4-and-6-echo" +required-features = ["unstable"] diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index d926ec4fe..f3505acae 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -4,6 +4,9 @@ use std::task::{Context, Poll}; use futures_core::Stream; use pin_project_lite::pin_project; +use crate::prelude::*; +use crate::stream::Fuse; + pin_project! { /// A stream that merges two other streams into a single stream. /// @@ -17,15 +20,15 @@ pin_project! { #[derive(Debug)] pub struct Merge { #[pin] - left: L, + left: Fuse, #[pin] - right: R, + right: Fuse, } } -impl Merge { +impl Merge { pub(crate) fn new(left: L, right: R) -> Self { - Self { left, right } + Self { left: left.fuse(), right: right.fuse() } } } @@ -38,13 +41,14 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - if let Poll::Ready(Some(item)) = this.left.poll_next(cx) { - // The first stream made progress. The Merge needs to be polled - // again to check the progress of the second stream. - cx.waker().wake_by_ref(); - Poll::Ready(Some(item)) - } else { - this.right.poll_next(cx) + match this.left.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => this.right.poll_next(cx), + Poll::Pending => match this.right.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + } } } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4a3875c44..e182c0338 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1667,7 +1667,7 @@ extension_trait! { } #[doc = r#" - Searches for an element in a Stream that satisfies a predicate, returning + Searches for an element in a Stream that satisfies a predicate, returning its index. # Examples diff --git a/tests/stream.rs b/tests/stream.rs new file mode 100644 index 000000000..42a6191fd --- /dev/null +++ b/tests/stream.rs @@ -0,0 +1,100 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use async_std::prelude::*; +use async_std::stream; +use async_std::sync::channel; +use async_std::task; + +#[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 mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x); + } + xs + }); + + task::block_on(async move { + task::sleep(std::time::Duration::from_millis(500)).await; + sender.send(92).await; + drop(sender); + let xs = t.await; + assert_eq!(xs, vec![92]) + }); + + let (sender, receiver) = channel::(10); + + let mut s = stream::empty().merge(receiver); + let t = task::spawn(async move { + let mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x); + } + xs + }); + + task::block_on(async move { + task::sleep(std::time::Duration::from_millis(500)).await; + sender.send(92).await; + drop(sender); + let xs = t.await; + assert_eq!(xs, vec![92]) + }); +} + +pin_project! { + /// The opposite of `Fuse`: makes the stream panic if polled after termination. + struct Explode { + #[pin] + done: bool, + #[pin] + inner: S, + } +} + +impl Stream for Explode { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if *this.done { + panic!("KABOOM!") + } + let res = this.inner.poll_next(cx); + if let Poll::Ready(None) = &res { + *this.done = true; + } + res + } +} + +fn explode(s: S) -> Explode { + Explode { + done: false, + inner: s, + } +} + +#[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 { + let mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x) + } + xs + }); + assert_eq!(xs, vec![92, 92]); +} From dea1b67670d79ac8f6ef692c514a62c1c4be7229 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 24 Oct 2019 10:16:41 -0500 Subject: [PATCH 129/707] Skeleton cycle --- src/stream/stream/cycle.rs | 20 ++++++++++++++++++++ src/stream/stream/mod.rs | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/stream/stream/cycle.rs diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs new file mode 100644 index 000000000..881377f29 --- /dev/null +++ b/src/stream/stream/cycle.rs @@ -0,0 +1,20 @@ +use std::pin::Pin; + +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 +pub struct Cycle { + source: Vec, + index: usize, +} + +impl Stream for Cycle { + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e182c0338..4a845f114 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,9 +24,10 @@ mod all; mod any; mod chain; -mod cmp; -mod copied; mod enumerate; +mod cycle; +mod copied; +mod cmp; mod eq; mod filter; mod filter_map; From a096d5ec2dc7845fb05b09486342fe35dd46bfae Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 24 Oct 2019 10:21:42 -0500 Subject: [PATCH 130/707] stub out an example --- src/stream/stream/cycle.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 881377f29..7ed0774e0 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -18,3 +18,21 @@ impl Stream for Cycle { Poll::Pending } } + +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// +/// let values = vec![1,2,3]; +/// +/// # Ok(()) }) } +///``` +fn cycle(values: Vec) -> impl Stream { + Cycle { + source: values, + index: 0, + } +} From 486f9a964ca8d151b01ffa35a44316d8a265ddcd Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 24 Oct 2019 10:38:04 -0500 Subject: [PATCH 131/707] Cycle over a known set of values. --- src/stream/cycle.rs | 62 ++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 ++ src/stream/stream/cycle.rs | 38 ----------------------- src/stream/stream/mod.rs | 3 +- 4 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 src/stream/cycle.rs delete mode 100644 src/stream/stream/cycle.rs diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs new file mode 100644 index 000000000..943846934 --- /dev/null +++ b/src/stream/cycle.rs @@ -0,0 +1,62 @@ +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + /// A stream that will repeatedly yield the same list of elements + pub struct Cycle { + source: Vec, + index: usize, + len: usize, + } +} + +impl Stream for Cycle { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let value = self.source[self.index]; + + let next = self.index + 1; + + if next >= self.len { + self.as_mut().index = 0; + } else { + self.as_mut().index = next; + } + + Poll::Ready(Some(value)) + } +} + +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut s = stream::cycle(vec![1,2,3]); +/// +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(3)); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// # +/// # }) +/// ``` +pub fn cycle(source: Vec) -> impl Stream { + let len = source.len(); + Cycle { + source, + index: 0, + len, + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 07eecf28c..449c484e1 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -300,6 +300,7 @@ //! [`take`]: trait.Stream.html#method.take //! [`min`]: trait.Stream.html#method.min +pub use cycle::{cycle, Cycle}; pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; @@ -312,6 +313,7 @@ pub use stream::{ pub(crate) mod stream; +mod cycle; mod empty; mod from_fn; mod from_iter; diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs deleted file mode 100644 index 7ed0774e0..000000000 --- a/src/stream/stream/cycle.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::pin::Pin; - -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 -pub struct Cycle { - source: Vec, - index: usize, -} - -impl Stream for Cycle { - type Item = T; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Poll::Pending - } -} - -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// -/// let values = vec![1,2,3]; -/// -/// # Ok(()) }) } -///``` -fn cycle(values: Vec) -> impl Stream { - Cycle { - source: values, - index: 0, - } -} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4a845f114..d46ae8791 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,10 +24,11 @@ mod all; mod any; mod chain; -mod enumerate; +mod cmp; mod cycle; mod copied; mod cmp; +mod enumerate; mod eq; mod filter; mod filter_map; From 8126bb18821dd9be167ee892b9db8afa1e2d19b9 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 24 Oct 2019 16:40:27 -0500 Subject: [PATCH 132/707] use the module operator to calculate next index --- src/stream/cycle.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index 943846934..135a43070 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -22,11 +22,7 @@ impl Stream for Cycle { let next = self.index + 1; - if next >= self.len { - self.as_mut().index = 0; - } else { - self.as_mut().index = next; - } + self.as_mut().index = next % self.len; Poll::Ready(Some(value)) } From e1ba87e7c167dcb88a2c8cae4e6e2c73f6ea48c3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 24 Oct 2019 16:42:03 -0500 Subject: [PATCH 133/707] Add slightly better docs --- src/stream/cycle.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index 135a43070..b9dd87f9c 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -28,6 +28,8 @@ impl Stream for Cycle { } } +/// Creats a stream that yields the provided values infinitely and in order. +/// /// # Examples /// /// Basic usage: From 83ff11ff4c4b17ffada9317a61272b53420c9b81 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sat, 26 Oct 2019 03:12:06 -0500 Subject: [PATCH 134/707] Switch cycle to stream --- src/stream/cycle.rs | 67 ++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index b9dd87f9c..1d16f855b 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -7,24 +7,53 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that will repeatedly yield the same list of elements - pub struct Cycle { - source: Vec, + pub struct Cycle { + #[pin] + source: S, index: usize, - len: usize, + buffer: Vec, + state: CycleState, } } -impl Stream for Cycle { - type Item = T; +#[derive(Eq, PartialEq)] +enum CycleState { + FromStream, + FromBuffer, +} + +impl Stream for Cycle + where + S: Stream, + T: Copy, +{ + + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); - fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - let value = self.source[self.index]; + let mut next; + if CycleState::FromStream == *this.state { + next = futures_core::ready!(this.source.poll_next(cx)); - let next = self.index + 1; + if let Some(val) = next { + this.buffer.push(val); + } else { + *this.state = CycleState::FromBuffer; + next = Some(this.buffer[*this.index]); + } + } else { + let mut index = *this.index; + if index == this.buffer.len() { + index = 0 + } + next = Some(this.buffer[index]); - self.as_mut().index = next % self.len; + *this.index = index + 1; + } - Poll::Ready(Some(value)) + Poll::Ready(next) } } @@ -40,21 +69,21 @@ impl Stream for Cycle { /// use async_std::prelude::*; /// use async_std::stream; /// -/// let mut s = stream::cycle(vec![1,2,3]); +/// let mut s = stream::cycle(stream::once(7)); /// -/// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(2)); -/// assert_eq!(s.next().await, Some(3)); -/// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(2)); +/// 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)); /// # /// # }) /// ``` -pub fn cycle(source: Vec) -> impl Stream { - let len = source.len(); +pub fn cycle, T: Copy>(source: S) -> impl Stream { Cycle { source, index: 0, - len, + buffer: Vec::new(), + state: CycleState::FromStream, } } From 171cc82aed62e5d3f2918f7a1637203fb239dc04 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 08:37:16 -0500 Subject: [PATCH 135/707] Replace copy with clone bound --- src/stream/cycle.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index 1d16f855b..92f31ec12 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -25,7 +25,8 @@ enum CycleState { impl Stream for Cycle where S: Stream, - T: Copy, + T: Clone, + { type Item = S::Item; @@ -34,21 +35,22 @@ impl Stream for Cycle let this = self.project(); let mut next; - if CycleState::FromStream == *this.state { + if *this.state == CycleState::FromStream { next = futures_core::ready!(this.source.poll_next(cx)); if let Some(val) = next { - this.buffer.push(val); + this.buffer.push(val.clone()); + next = Some(val.clone()) } else { *this.state = CycleState::FromBuffer; - next = Some(this.buffer[*this.index]); + next = this.buffer.get(*this.index).map(|x| x.clone()); } } else { let mut index = *this.index; if index == this.buffer.len() { index = 0 } - next = Some(this.buffer[index]); + next = Some(this.buffer[index].clone()); *this.index = index + 1; } @@ -79,7 +81,7 @@ impl Stream for Cycle /// # /// # }) /// ``` -pub fn cycle, T: Copy>(source: S) -> impl Stream { +pub fn cycle, T: Clone>(source: S) -> impl Stream { Cycle { source, index: 0, From fd09e2f248ce8847a322362d6287e2d409e36158 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 08:54:15 -0500 Subject: [PATCH 136/707] Run fmt --- src/stream/cycle.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index 92f31ec12..5210a69be 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -22,13 +22,11 @@ enum CycleState { FromBuffer, } -impl Stream for Cycle - where - S: Stream, - T: Clone, - +impl Stream for Cycle +where + S: Stream, + T: Clone, { - type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -47,7 +45,7 @@ impl Stream for Cycle } } else { let mut index = *this.index; - if index == this.buffer.len() { + if index == this.buffer.len() { index = 0 } next = Some(this.buffer[index].clone()); From b979773505793ea9033ec54ae688854f85846998 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 09:10:18 -0500 Subject: [PATCH 137/707] Follow clippys advice --- src/stream/cycle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/cycle.rs b/src/stream/cycle.rs index 5210a69be..df8b95633 100644 --- a/src/stream/cycle.rs +++ b/src/stream/cycle.rs @@ -41,7 +41,7 @@ where next = Some(val.clone()) } else { *this.state = CycleState::FromBuffer; - next = this.buffer.get(*this.index).map(|x| x.clone()); + next = this.buffer.get(*this.index).cloned(); } } else { let mut index = *this.index; From 5aadc5e4e96bb3c340450a4e40ef5fb638cfe741 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 17:06:56 -0500 Subject: [PATCH 138/707] Make cycle a function on the stream type --- src/stream/mod.rs | 2 -- src/stream/{ => stream}/cycle.rs | 41 +++++++++----------------------- src/stream/stream/mod.rs | 34 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 32 deletions(-) rename src/stream/{ => stream}/cycle.rs (65%) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 449c484e1..07eecf28c 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -300,7 +300,6 @@ //! [`take`]: trait.Stream.html#method.take //! [`min`]: trait.Stream.html#method.min -pub use cycle::{cycle, Cycle}; pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; @@ -313,7 +312,6 @@ pub use stream::{ pub(crate) mod stream; -mod cycle; mod empty; mod from_fn; mod from_iter; diff --git a/src/stream/cycle.rs b/src/stream/stream/cycle.rs similarity index 65% rename from src/stream/cycle.rs rename to src/stream/stream/cycle.rs index df8b95633..518a804cb 100644 --- a/src/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -22,6 +22,17 @@ enum CycleState { FromBuffer, } +impl,> Cycle { + pub fn new(source: S) -> Cycle { + Cycle { + source, + index: 0, + buffer: Vec::new(), + state: CycleState::FromStream, + } + } +} + impl Stream for Cycle where S: Stream, @@ -57,33 +68,3 @@ where } } -/// Creats a stream that yields the provided values infinitely and in order. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::prelude::*; -/// use async_std::stream; -/// -/// let mut s = stream::cycle(stream::once(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)); -/// assert_eq!(s.next().await, Some(7)); -/// # -/// # }) -/// ``` -pub fn cycle, T: Clone>(source: S) -> impl Stream { - Cycle { - source, - index: 0, - buffer: Vec::new(), - state: CycleState::FromStream, - } -} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d46ae8791..274992b48 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod chain; +mod cycle; mod cmp; mod cycle; mod copied; @@ -91,6 +92,7 @@ use partial_cmp::PartialCmpFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; +use cycle::Cycle; pub use chain::Chain; pub use copied::Copied; @@ -411,6 +413,38 @@ extension_trait! { Copied::new(self) } + #[doc = r#" + Creats a stream that yields the provided values infinitely and in order. + + # Examples + + Basic usage: + + ``` + # async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + 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: Sized, + Self::Item: Clone, + { + Cycle::new(self) + } + #[doc = r#" Creates a stream that gives the current element's count as well as the next value. From ed5b095c7342c0690544089a05a5ba3766f5f6a1 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 17:09:01 -0500 Subject: [PATCH 139/707] Run fmt --- src/stream/stream/cycle.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 518a804cb..9c145904f 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -22,7 +22,7 @@ enum CycleState { FromBuffer, } -impl,> Cycle { +impl> Cycle { pub fn new(source: S) -> Cycle { Cycle { source, @@ -67,4 +67,3 @@ where Poll::Ready(next) } } - From 19381fa590c5af08cfdd4be6ca6d621e9c586dcc Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 29 Oct 2019 17:15:49 -0500 Subject: [PATCH 140/707] One clippy warning --- src/stream/stream/cycle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 9c145904f..91f33f97f 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -49,7 +49,7 @@ where if let Some(val) = next { this.buffer.push(val.clone()); - next = Some(val.clone()) + next = Some(val) } else { *this.state = CycleState::FromBuffer; next = this.buffer.get(*this.index).cloned(); From 197253aa73abc16cd8a37a92767f87490894a9fa Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 1 Nov 2019 17:45:54 -0500 Subject: [PATCH 141/707] Run fmt --- src/stream/stream/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 274992b48..83b77308d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,11 +24,9 @@ mod all; mod any; mod chain; -mod cycle; mod cmp; -mod cycle; mod copied; -mod cmp; +mod cycle; mod enumerate; mod eq; mod filter; @@ -68,6 +66,7 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; +use cycle::Cycle; use enumerate::Enumerate; use eq::EqFuture; use filter_map::FilterMap; @@ -92,7 +91,6 @@ use partial_cmp::PartialCmpFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; -use cycle::Cycle; pub use chain::Chain; pub use copied::Copied; From 0186124aef9716820d16ceefc9bf89425b3452e3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 1 Nov 2019 20:37:20 -0500 Subject: [PATCH 142/707] Simpler impl --- src/stream/stream/cycle.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 91f33f97f..4d2dbf584 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -22,8 +22,12 @@ enum CycleState { FromBuffer, } -impl> Cycle { - pub fn new(source: S) -> Cycle { +impl Cycle +where + S: Stream, + S::Item: Clone, +{ + pub fn new(source: S) -> Cycle { Cycle { source, index: 0, From eaa56580e3a513910c18c30633d672e64a913ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Ser=C3=A9?= Date: Fri, 1 Nov 2019 21:24:46 -0500 Subject: [PATCH 143/707] Update src/stream/stream/mod.rs Co-Authored-By: Yoshua Wuyts --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 83b77308d..747c54e10 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -412,7 +412,7 @@ extension_trait! { } #[doc = r#" - Creats a stream that yields the provided values infinitely and in order. + Creates a stream that yields the provided values infinitely and in order. # Examples From 9ee804f9edc0e8bfea3e9122e114ef854161dcb2 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 1 Nov 2019 22:57:32 -0500 Subject: [PATCH 144/707] Only one generic type needed --- src/stream/stream/cycle.rs | 18 +++++++++++------- src/stream/stream/mod.rs | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 4d2dbf584..c7781498e 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -7,11 +7,15 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that will repeatedly yield the same list of elements - pub struct Cycle { + pub struct Cycle +where + S: Stream, + S::Item: Clone, + { #[pin] source: S, index: usize, - buffer: Vec, + buffer: Vec, state: CycleState, } } @@ -22,12 +26,12 @@ enum CycleState { FromBuffer, } -impl Cycle +impl Cycle where S: Stream, S::Item: Clone, { - pub fn new(source: S) -> Cycle { + pub fn new(source: S) -> Cycle { Cycle { source, index: 0, @@ -37,10 +41,10 @@ where } } -impl Stream for Cycle +impl Stream for Cycle where - S: Stream, - T: Clone, + S: Stream, + S::Item: Clone, { type Item = S::Item; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 747c54e10..7b21bf072 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -435,7 +435,7 @@ extension_trait! { # }) ``` "#] - fn cycle(self) -> Cycle + fn cycle(self) -> Cycle where Self: Sized, Self::Item: Clone, From fbd5bd867de9ef6ece29463a03fa930ff2141bd6 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 1 Nov 2019 23:25:54 -0500 Subject: [PATCH 145/707] Revert "Only one generic type needed" This reverts commit e9b9284863a614b852c22d58205cb983fc26682a. --- src/stream/stream/cycle.rs | 18 +++++++----------- src/stream/stream/mod.rs | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index c7781498e..4d2dbf584 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -7,15 +7,11 @@ use crate::task::{Context, Poll}; pin_project! { /// A stream that will repeatedly yield the same list of elements - pub struct Cycle -where - S: Stream, - S::Item: Clone, - { + pub struct Cycle { #[pin] source: S, index: usize, - buffer: Vec, + buffer: Vec, state: CycleState, } } @@ -26,12 +22,12 @@ enum CycleState { FromBuffer, } -impl Cycle +impl Cycle where S: Stream, S::Item: Clone, { - pub fn new(source: S) -> Cycle { + pub fn new(source: S) -> Cycle { Cycle { source, index: 0, @@ -41,10 +37,10 @@ where } } -impl Stream for Cycle +impl Stream for Cycle where - S: Stream, - S::Item: Clone, + S: Stream, + T: Clone, { type Item = S::Item; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7b21bf072..747c54e10 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -435,7 +435,7 @@ extension_trait! { # }) ``` "#] - fn cycle(self) -> Cycle + fn cycle(self) -> Cycle where Self: Sized, Self::Item: Clone, From 57a6516e639317c8f89696dd8e8133a1c84a983b Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 1 Nov 2019 23:28:07 -0500 Subject: [PATCH 146/707] Make bounds on Stream impl simpler --- src/stream/stream/cycle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 4d2dbf584..8a31cc177 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -37,10 +37,10 @@ where } } -impl Stream for Cycle +impl Stream for Cycle where - S: Stream, - T: Clone, + S: Stream, + S::Item: Clone, { type Item = S::Item; From e0910be8fb85d6578d46be83e5fc4ff26fc874f1 Mon Sep 17 00:00:00 2001 From: "Abhishek C. Sharma" Date: Sun, 3 Nov 2019 11:34:49 +0530 Subject: [PATCH 147/707] Added Future::flatten --- src/future/future.rs | 27 +++++++++++++++++++++ src/future/future/flatten.rs | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/future/future/flatten.rs diff --git a/src/future/future.rs b/src/future/future.rs index fe685176a..1b88430f0 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -1,9 +1,12 @@ cfg_unstable! { mod delay; + mod flatten; use std::time::Duration; use delay::DelayFuture; + use flatten::FlattenFuture; + use crate::future::IntoFuture; } extension_trait! { @@ -129,6 +132,30 @@ extension_trait! { { 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_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(any(feature = "unstable", feature = "docs"))] + fn flatten(self) -> FlattenFuture::Future> + where + Self: Future + Sized, + Self::Output: IntoFuture + { + FlattenFuture::new(self) + } } impl Future for Box { diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs new file mode 100644 index 000000000..27ee22815 --- /dev/null +++ b/src/future/future/flatten.rs @@ -0,0 +1,46 @@ +use futures_core::ready; +use std::pin::Pin; + +use crate::future::Future; +use crate::future::IntoFuture; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[derive(Debug)] +pub enum FlattenFuture { + First(Fut1), + Second(Fut2), + Empty, +} + +impl FlattenFuture { + pub fn new(future: Fut1) -> FlattenFuture { + FlattenFuture::First(future) + } +} + +impl Future for FlattenFuture::Future> +where + Fut1: Future, + Fut1::Output: IntoFuture, +{ + type Output = ::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + loop { + match this { + FlattenFuture::First(fut1) => { + let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future(); + *this = FlattenFuture::Second(fut2); + } + FlattenFuture::Second(fut2) => { + let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx)); + *this = FlattenFuture::Empty; + return Poll::Ready(v); + } + FlattenFuture::Empty => unreachable!(), + } + } + } +} From 4942dc7f9fd7862f2dc3363f254c8f5065ae18d9 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sun, 3 Nov 2019 19:19:52 +0800 Subject: [PATCH 148/707] Add Stream cloned --- src/stream/stream/cloned.rs | 33 +++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/stream/stream/cloned.rs diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs new file mode 100644 index 000000000..779e49d15 --- /dev/null +++ b/src/stream/stream/cloned.rs @@ -0,0 +1,33 @@ +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use pin_project_lite::pin_project; +use std::pin::Pin; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Cloned { + #[pin] + stream: S, + } +} + +impl Cloned { + pub(super) fn new(stream: S) -> Self { + Self { stream } + } +} + +impl<'a, S, T: 'a> Stream for Cloned +where + S: Stream, + T: Clone, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.cloned()) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e182c0338..9595f80f8 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod chain; +mod cloned; mod cmp; mod copied; mod enumerate; @@ -91,6 +92,7 @@ use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; pub use chain::Chain; +pub use cloned::Cloned; pub use copied::Copied; pub use filter::Filter; pub use fuse::Fuse; @@ -373,6 +375,40 @@ extension_trait! { 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 std::collections::VecDeque; + + let v: VecDeque<_> = vec![&1, &2, &3].into_iter().collect(); + + let mut v_cloned = v.cloned(); + + 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 : 'a + Clone, + { + Cloned::new(self) + } + #[doc = r#" Creates an stream which copies all of its elements. @@ -395,7 +431,6 @@ extension_trait! { assert_eq!(v_copied.next().await, Some(2)); assert_eq!(v_copied.next().await, Some(3)); assert_eq!(v_copied.next().await, None); - # # }) } From ddbbbfc32af5c8572303086145a3932518fc5f74 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 3 Nov 2019 21:40:51 +0900 Subject: [PATCH 149/707] Replace `VecDeque` with `stream::from_iter` in examples (#447) --- src/option/sum.rs | 6 +- src/result/product.rs | 4 +- src/result/sum.rs | 4 +- src/stream/from_iter.rs | 2 +- src/stream/stream/mod.rs | 280 +++++++++++++++++++-------------------- 5 files changed, 143 insertions(+), 153 deletions(-) diff --git a/src/option/sum.rs b/src/option/sum.rs index 25dc92093..5c154f422 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -20,12 +20,10 @@ where ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let words: VecDeque<_> = vec!["have", "a", "great", "day"] - .into_iter() - .collect(); + let words = stream::from_iter(vec!["have", "a", "great", "day"]); let total: Option = words.map(|w| w.find('a')).sum().await; assert_eq!(total, Some(5)); # diff --git a/src/result/product.rs b/src/result/product.rs index 17afa94b2..fd242168f 100644 --- a/src/result/product.rs +++ b/src/result/product.rs @@ -20,10 +20,10 @@ where ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let v: VecDeque<_> = vec![1, 2, 4].into_iter().collect(); + let v = stream::from_iter(vec![1, 2, 4]); let res: Result = v.map(|x| if x < 0 { Err("Negative element found") diff --git a/src/result/sum.rs b/src/result/sum.rs index caca4f65b..dd687723c 100644 --- a/src/result/sum.rs +++ b/src/result/sum.rs @@ -20,10 +20,10 @@ where ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let v: VecDeque<_> = vec![1, 2].into_iter().collect(); + let v = stream::from_iter(vec![1, 2]); let res: Result = v.map(|x| if x < 0 { Err("Negative element found") diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index 43bc96112..5fd216dbf 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -12,7 +12,7 @@ pin_project! { /// See it documentation for more. /// /// [`from_iter`]: fn.from_iter.html - #[derive(Debug)] + #[derive(Clone, Debug)] pub struct FromIter { iter: I, } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e182c0338..923c52bf0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -280,11 +280,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); + 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)); @@ -317,9 +316,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); let mut stepped = s.step_by(2); assert_eq!(stepped.next().await, Some(0)); @@ -349,10 +348,10 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let first: VecDeque<_> = vec![0u8, 1].into_iter().collect(); - let second: VecDeque<_> = vec![2, 3].into_iter().collect(); + 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)); @@ -385,18 +384,17 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let v: VecDeque<_> = vec![&1, &2, &3].into_iter().collect(); - - let mut v_copied = v.copied(); + let s = stream::from_iter(vec![&1, &2, &3]); + let second = stream::from_iter(vec![2, 3]); - assert_eq!(v_copied.next().await, Some(1)); - assert_eq!(v_copied.next().await, Some(2)); - assert_eq!(v_copied.next().await, Some(3)); - assert_eq!(v_copied.next().await, None); - + 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); # # }) } ``` @@ -422,9 +420,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect(); + let s = stream::from_iter(vec!['a', 'b', 'c']); let mut s = s.enumerate(); assert_eq!(s.next().await, Some((0, 'a'))); @@ -452,9 +450,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1, 2, 3]); let mut s = s.map(|x| 2 * x); assert_eq!(s.next().await, Some(2)); @@ -486,10 +484,11 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; + + let s = stream::from_iter(vec![1, 2, 3, 4, 5]); - let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect(); - let sum = a + let sum = s .inspect(|x| println!("about to filter {}", x)) .filter(|x| x % 2 == 0) .inspect(|x| println!("made it through filter: {}", x)) @@ -518,11 +517,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1, 2, 3]); let last = s.last().await; assert_eq!(last, Some(3)); @@ -534,11 +532,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - - use async_std::prelude::*; + use async_std::stream; + use crate::async_std::prelude::*; - let s: VecDeque = vec![].into_iter().collect(); + let s = stream::empty::<()>(); let last = s.last().await; assert_eq!(last, None); @@ -599,11 +596,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); + 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)); @@ -631,14 +627,14 @@ extension_trait! { ``` # async_std::task::block_on(async { - use std::collections::VecDeque; use async_std::prelude::*; use async_std::stream::IntoStream; + use async_std::stream; - let inner1: VecDeque = vec![1,2,3].into_iter().collect(); - let inner2: VecDeque = vec![4,5,6].into_iter().collect(); + let inner1 = stream::from_iter(vec![1,2,3]); + let inner2 = stream::from_iter(vec![4,5,6]); - let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); + let s = stream::from_iter(vec![inner1, inner2]); let v :Vec<_> = s.flat_map(|s| s.into_stream()).collect().await; @@ -668,12 +664,12 @@ extension_trait! { ``` # async_std::task::block_on(async { - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let inner1: VecDeque = vec![1,2,3].into_iter().collect(); - let inner2: VecDeque = vec![4,5,6].into_iter().collect(); - let s: VecDeque<_> = vec![inner1, inner2].into_iter().collect(); + 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]); let v: Vec<_> = s.flatten().collect().await; @@ -701,11 +697,11 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect(); + let s = stream::from_iter(vec!["1", "lol", "3", "NaN", "5"]); let mut parsed = s.filter_map(|a| a.parse::().ok()); @@ -742,16 +738,15 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, -3].into_iter().collect(); + 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 = VecDeque::::new().min_by_key(|x| x.abs()).await; + let min = stream::empty::().min_by_key(|x| x.abs()).await; assert_eq!(min, None); # # }) } @@ -779,16 +774,15 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![-1, -2, -3].into_iter().collect(); + let s = stream::from_iter(vec![-1isize, -2, -3]); let max = s.clone().max_by_key(|x| x.abs()).await; assert_eq!(max, Some(3)); - let max = VecDeque::::new().max_by_key(|x| x.abs()).await; + let max = stream::empty::().min_by_key(|x| x.abs()).await; assert_eq!(max, None); # # }) } @@ -816,11 +810,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1u8, 2, 3]); let min = s.clone().min_by(|x, y| x.cmp(y)).await; assert_eq!(min, Some(1)); @@ -828,7 +821,7 @@ extension_trait! { let min = s.min_by(|x, y| y.cmp(x)).await; assert_eq!(min, Some(3)); - let min = VecDeque::::new().min_by(|x, y| x.cmp(y)).await; + let min = stream::empty::().min_by(|x, y| x.cmp(y)).await; assert_eq!(min, None); # # }) } @@ -854,15 +847,15 @@ extension_trait! { ```ignore # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1usize, 2, 3]); let min = s.clone().min().await; assert_eq!(min, Some(1)); - let min = VecDeque::::new().min().await; + let min = stream::empty::().min().await; assert_eq!(min, None); # # }) } @@ -888,11 +881,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1u8, 2, 3]); let max = s.clone().max_by(|x, y| x.cmp(y)).await; assert_eq!(max, Some(3)); @@ -900,7 +892,7 @@ extension_trait! { let max = s.max_by(|x, y| y.cmp(x)).await; assert_eq!(max, Some(1)); - let max = VecDeque::::new().max_by(|x, y| x.cmp(y)).await; + let max = stream::empty::().max_by(|x, y| x.cmp(y)).await; assert_eq!(max, None); # # }) } @@ -927,11 +919,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let mut s = stream::from_iter(vec![1u8, 2, 3]); let second = s.nth(1).await; assert_eq!(second, Some(2)); @@ -943,11 +934,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - + use async_std::stream; use async_std::prelude::*; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let mut s = stream::from_iter(vec![1u8, 2, 3]); let second = s.nth(0).await; assert_eq!(second, Some(1)); @@ -961,11 +951,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let mut s = stream::from_iter(vec![1u8, 2, 3]); let fourth = s.nth(4).await; assert_eq!(fourth, None); @@ -1056,9 +1045,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let mut s = stream::from_iter(vec![1u8, 2, 3]); let res = s.find(|x| *x == 2).await; assert_eq!(res, Some(2)); # @@ -1071,9 +1060,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let mut s= stream::from_iter(vec![1, 2, 3]); let res = s.find(|x| *x == 2).await; assert_eq!(res, Some(2)); @@ -1101,9 +1090,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect(); + 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)); @@ -1134,9 +1123,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1u8, 2, 3]); let sum = s.fold(0, |acc, x| acc + x).await; assert_eq!(sum, 6); @@ -1165,12 +1154,12 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; use std::sync::mpsc::channel; let (tx, rx) = channel(); - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1usize, 2, 3]); let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await; let v: Vec<_> = rx.iter().collect(); @@ -1271,11 +1260,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1isize, 2, 3]); let mut s = s.scan(1, |state, x| { *state = *state * x; Some(-*state) @@ -1312,11 +1300,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect(); + 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)); @@ -1342,11 +1329,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1u8, 2, 3]); let mut skipped = s.skip(2); assert_eq!(skipped.next().await, Some(3)); @@ -1408,9 +1394,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1usize, 2, 3]); let sum = s.try_fold(0, |acc, v| { if (acc+v) % 2 == 1 { Ok(v+3) @@ -1444,13 +1430,13 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use std::sync::mpsc::channel; use async_std::prelude::*; + use async_std::stream; let (tx, rx) = channel(); - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1u8, 2, 3]); let s = s.try_for_each(|v| { if v % 2 == 1 { tx.clone().send(v).unwrap(); @@ -1502,12 +1488,11 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; - use async_std::prelude::*; + use async_std::stream; - let l: VecDeque = vec![1, 2, 3].into_iter().collect(); - let r: VecDeque = vec![4, 5, 6, 7].into_iter().collect(); + 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))); @@ -1637,14 +1622,14 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; use std::cmp::Ordering; - let s1 = VecDeque::from(vec![1]); - let s2 = VecDeque::from(vec![1, 2]); - let s3 = VecDeque::from(vec![1, 2, 3]); - let s4 = VecDeque::from(vec![1, 2, 4]); + 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)); @@ -1676,9 +1661,9 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let s = stream::from_iter(vec![1usize, 2, 3]); let res = s.clone().position(|x| *x == 1).await; assert_eq!(res, Some(0)); @@ -1715,13 +1700,14 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; - + use async_std::stream; use std::cmp::Ordering; - let s1 = VecDeque::from(vec![1]); - let s2 = VecDeque::from(vec![1, 2]); - let s3 = VecDeque::from(vec![1, 2, 3]); - let s4 = VecDeque::from(vec![1, 2, 4]); + + 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); @@ -1751,11 +1737,13 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; - let single: VecDeque = vec![1].into_iter().collect(); - let single_ne: VecDeque = vec![10].into_iter().collect(); - let multi: VecDeque = vec![1,2].into_iter().collect(); - let multi_ne: VecDeque = vec![1,5].into_iter().collect(); + 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); @@ -1786,12 +1774,13 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + 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]); - let single: VecDeque = vec![1].into_iter().collect(); - let single_gt: VecDeque = vec![10].into_iter().collect(); - let multi: VecDeque = vec![1,2].into_iter().collect(); - let multi_gt: VecDeque = vec![1,5].into_iter().collect(); 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); @@ -1822,12 +1811,13 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + 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]); - let single: VecDeque = vec![1].into_iter().collect(); - let single_eq: VecDeque = vec![10].into_iter().collect(); - let multi: VecDeque = vec![1,2].into_iter().collect(); - let multi_eq: VecDeque = vec![1,5].into_iter().collect(); 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); @@ -1858,12 +1848,13 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + 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]); - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(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); @@ -1894,12 +1885,13 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + 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]); - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(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); @@ -1930,12 +1922,12 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(vec![1,5]); + 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); @@ -1977,10 +1969,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); let sum: u8 = s.sum().await; assert_eq!(sum, 10); From 78614c6c1d4ddc70ca7b83f9d902ff85da1eb677 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 3 Nov 2019 22:19:04 +0100 Subject: [PATCH 150/707] Clarify blocking in channel docs (#448) --- src/sync/channel.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 403bee742..d4b64230a 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -22,7 +22,7 @@ use crate::sync::WakerSet; /// /// 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 -/// blocking. +/// trying to await a message. /// /// # Panics /// @@ -44,7 +44,7 @@ use crate::sync::WakerSet; /// s.send(1).await; /// /// task::spawn(async move { -/// // This call blocks the current task because the channel is full. +/// // 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; /// }); @@ -102,8 +102,7 @@ pub struct Sender { impl Sender { /// Sends a message into the channel. /// - /// If the channel is full, this method will block the current task until the send operation - /// can proceed. + /// If the channel is full, this method will wait until there is space in the channel. /// /// # Examples /// @@ -346,9 +345,8 @@ pub struct Receiver { impl Receiver { /// Receives a message from the channel. /// - /// If the channel is empty and it still has senders, this method will block the current task - /// until the receive operation can proceed. If the channel is empty and there are no more - /// senders, this method returns `None`. + /// 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. /// /// # Examples /// From ed1cb49807d119f85e28631f3aef74b6c162bdb5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 4 Nov 2019 01:50:11 +0100 Subject: [PATCH 151/707] remove remaining instances of VecDeque stream Signed-off-by: Yoshua Wuyts --- src/option/product.rs | 4 ++-- src/stream/stream/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/option/product.rs b/src/option/product.rs index 8b66bc693..9b7274ff0 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -20,10 +20,10 @@ where ``` # fn main() { async_std::task::block_on(async { # - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let v: VecDeque<_> = vec![1, 2, 4].into_iter().collect(); + let v = stream::from_iter(vec![1, 2, 4]); let prod: Option = v.map(|x| if x < 0 { None diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 923c52bf0..4ab12a103 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -2012,10 +2012,10 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # async fn factorial(n: u32) -> u32 { - use std::collections::VecDeque; use async_std::prelude::*; + use async_std::stream; - let s: VecDeque<_> = (1..=n).collect(); + let s = stream::from_iter(1..=n); s.product().await } From 20cdf73bb053d1e6a9468775d96950327bd252e6 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 4 Nov 2019 02:40:55 +0100 Subject: [PATCH 152/707] Simplify RwLock using WakerSet (#440) --- src/sync/rwlock.rs | 290 +++++++++++++----------------------------- src/sync/waker_set.rs | 36 ++++-- 2 files changed, 115 insertions(+), 211 deletions(-) diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index a0d0f07a8..81b0735af 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -1,26 +1,21 @@ use std::cell::UnsafeCell; use std::fmt; +use std::isize; use std::ops::{Deref, DerefMut}; use std::pin::Pin; +use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; -use slab::Slab; - use crate::future::Future; -use crate::task::{Context, Poll, Waker}; +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; -/// Set if there are read operations blocked on the lock. -const BLOCKED_READS: usize = 1 << 1; - -/// Set if there are write operations blocked on the lock. -const BLOCKED_WRITES: usize = 1 << 2; - /// The value of a single blocked read contributing to the read count. -const ONE_READ: usize = 1 << 3; +const ONE_READ: usize = 1 << 1; /// The bits in which the read count is stored. const READ_COUNT_MASK: usize = !(ONE_READ - 1); @@ -56,8 +51,8 @@ const READ_COUNT_MASK: usize = !(ONE_READ - 1); /// ``` pub struct RwLock { state: AtomicUsize, - reads: std::sync::Mutex>>, - writes: std::sync::Mutex>>, + read_wakers: WakerSet, + write_wakers: WakerSet, value: UnsafeCell, } @@ -77,8 +72,8 @@ impl RwLock { pub fn new(t: T) -> RwLock { RwLock { state: AtomicUsize::new(0), - reads: std::sync::Mutex::new(Slab::new()), - writes: std::sync::Mutex::new(Slab::new()), + read_wakers: WakerSet::new(), + write_wakers: WakerSet::new(), value: UnsafeCell::new(t), } } @@ -104,100 +99,61 @@ impl RwLock { /// # }) /// ``` pub async fn read(&self) -> RwLockReadGuard<'_, T> { - pub struct LockFuture<'a, T> { + pub struct ReadFuture<'a, T> { lock: &'a RwLock, opt_key: Option, - acquired: bool, } - impl<'a, T> Future for LockFuture<'a, T> { + impl<'a, T> Future for ReadFuture<'a, T> { type Output = RwLockReadGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.lock.try_read() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + let poll = match self.lock.try_read() { + Some(guard) => Poll::Ready(guard), None => { - let mut reads = self.lock.reads.lock().unwrap(); - - // Register the current task. + // Insert this lock operation. match self.opt_key { - None => { - // Insert a new entry into the list of blocked reads. - let w = cx.waker().clone(); - let key = reads.insert(Some(w)); - self.opt_key = Some(key); - - if reads.len() == 1 { - self.lock.state.fetch_or(BLOCKED_READS, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked reads. Just - // reset the waker if it was removed. - if reads[key].is_none() { - let w = cx.waker().clone(); - reads[key] = Some(w); - } - } + None => self.opt_key = Some(self.lock.read_wakers.insert(cx)), + Some(key) => self.lock.read_wakers.update(key, cx), } // Try locking again because it's possible the lock got unlocked just - // before the current task was registered as a blocked task. + // before the current task was inserted into the waker set. match self.lock.try_read() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + Some(guard) => Poll::Ready(guard), None => Poll::Pending, } } + }; + + if poll.is_ready() { + // If the current task is in the set, remove it. + if let Some(key) = self.opt_key.take() { + self.lock.read_wakers.complete(key); + } } + + poll } } - impl Drop for LockFuture<'_, T> { + 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 { - let mut reads = self.lock.reads.lock().unwrap(); - let opt_waker = reads.remove(key); - - if reads.is_empty() { - self.lock.state.fetch_and(!BLOCKED_READS, Ordering::Relaxed); - } + self.lock.read_wakers.cancel(key); - if opt_waker.is_none() { - // We were awoken. Wake up another blocked read. - if let Some((_, opt_waker)) = reads.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - drop(reads); - - if !self.acquired { - // We didn't acquire the lock and didn't wake another blocked read. - // Wake a blocked write instead. - let mut writes = self.lock.writes.lock().unwrap(); - if let Some((_, opt_waker)) = writes.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - } + // If there are no active readers, wake one of the writers. + if self.lock.state.load(Ordering::SeqCst) & READ_COUNT_MASK == 0 { + self.lock.write_wakers.notify_one(); } } } } - LockFuture { + ReadFuture { lock: self, opt_key: None, - acquired: false, } .await } @@ -226,7 +182,7 @@ impl RwLock { /// # }) /// ``` pub fn try_read(&self) -> Option> { - let mut state = self.state.load(Ordering::Acquire); + let mut state = self.state.load(Ordering::SeqCst); loop { // If a write lock is currently held, then a read lock cannot be acquired. @@ -234,12 +190,17 @@ impl RwLock { 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::AcqRel, - Ordering::Acquire, + Ordering::SeqCst, + Ordering::SeqCst, ) { Ok(_) => return Some(RwLockReadGuard(self)), Err(s) => state = s, @@ -268,99 +229,59 @@ impl RwLock { /// # }) /// ``` pub async fn write(&self) -> RwLockWriteGuard<'_, T> { - pub struct LockFuture<'a, T> { + pub struct WriteFuture<'a, T> { lock: &'a RwLock, opt_key: Option, - acquired: bool, } - impl<'a, T> Future for LockFuture<'a, T> { + impl<'a, T> Future for WriteFuture<'a, T> { type Output = RwLockWriteGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.lock.try_write() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + let poll = match self.lock.try_write() { + Some(guard) => Poll::Ready(guard), None => { - let mut writes = self.lock.writes.lock().unwrap(); - - // Register the current task. + // Insert this lock operation. match self.opt_key { - None => { - // Insert a new entry into the list of blocked writes. - let w = cx.waker().clone(); - let key = writes.insert(Some(w)); - self.opt_key = Some(key); - - if writes.len() == 1 { - self.lock.state.fetch_or(BLOCKED_WRITES, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked writes. Just - // reset the waker if it was removed. - if writes[key].is_none() { - let w = cx.waker().clone(); - writes[key] = Some(w); - } - } + None => self.opt_key = Some(self.lock.write_wakers.insert(cx)), + Some(key) => self.lock.write_wakers.update(key, cx), } // Try locking again because it's possible the lock got unlocked just - // before the current task was registered as a blocked task. + // before the current task was inserted into the waker set. match self.lock.try_write() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } + Some(guard) => Poll::Ready(guard), None => Poll::Pending, } } + }; + + if poll.is_ready() { + // If the current task is in the set, remove it. + if let Some(key) = self.opt_key.take() { + self.lock.write_wakers.complete(key); + } } + + poll } } - impl Drop for LockFuture<'_, T> { + 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 { - let mut writes = self.lock.writes.lock().unwrap(); - let opt_waker = writes.remove(key); - - if writes.is_empty() { - self.lock - .state - .fetch_and(!BLOCKED_WRITES, Ordering::Relaxed); - } - - if opt_waker.is_none() && !self.acquired { - // We were awoken but didn't acquire the lock. Wake up another write. - if let Some((_, opt_waker)) = writes.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - drop(writes); - - // There are no blocked writes. Wake a blocked read instead. - let mut reads = self.lock.reads.lock().unwrap(); - if let Some((_, opt_waker)) = reads.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } + if !self.lock.write_wakers.cancel(key) { + // If no other blocked reader was notified, notify all readers. + self.lock.read_wakers.notify_all(); } } } } - LockFuture { + WriteFuture { lock: self, opt_key: None, - acquired: false, } .await } @@ -389,24 +310,10 @@ impl RwLock { /// # }) /// ``` pub fn try_write(&self) -> Option> { - let mut state = self.state.load(Ordering::Acquire); - - loop { - // If any kind of lock is currently held, then a write lock cannot be acquired. - if state & (WRITE_LOCK | READ_COUNT_MASK) != 0 { - return None; - } - - // Set the write lock. - match self.state.compare_exchange_weak( - state, - state | WRITE_LOCK, - Ordering::AcqRel, - Ordering::Acquire, - ) { - Ok(_) => return Some(RwLockWriteGuard(self)), - Err(s) => state = s, - } + if self.state.compare_and_swap(0, WRITE_LOCK, Ordering::SeqCst) == 0 { + Some(RwLockWriteGuard(self)) + } else { + None } } @@ -449,18 +356,15 @@ impl RwLock { impl fmt::Debug for RwLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_read() { - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - f.debug_struct("RwLock") - .field("data", &LockedPlaceholder) - .finish() + 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(), } } @@ -486,18 +390,11 @@ 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::AcqRel); - - // If this was the last read and there are blocked writes, wake one of them up. - if (state & READ_COUNT_MASK) == ONE_READ && state & BLOCKED_WRITES != 0 { - let mut writes = self.0.writes.lock().unwrap(); + let state = self.0.state.fetch_sub(ONE_READ, Ordering::SeqCst); - if let Some((_, opt_waker)) = writes.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } + // If this was the last read, wake one of the writers. + if state & READ_COUNT_MASK == ONE_READ { + self.0.write_wakers.notify_one(); } } } @@ -530,25 +427,12 @@ unsafe impl Sync for RwLockWriteGuard<'_, T> {} impl Drop for RwLockWriteGuard<'_, T> { fn drop(&mut self) { - let state = self.0.state.fetch_and(!WRITE_LOCK, Ordering::AcqRel); - - let mut guard = None; - - // Check if there are any blocked reads or writes. - if state & BLOCKED_READS != 0 { - guard = Some(self.0.reads.lock().unwrap()); - } else if state & BLOCKED_WRITES != 0 { - guard = Some(self.0.writes.lock().unwrap()); - } + self.0.state.store(0, Ordering::SeqCst); - // Wake up a single blocked task. - if let Some(mut guard) = guard { - if let Some((_, opt_waker)) = guard.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } + // Notify all blocked readers. + if !self.0.read_wakers.notify_all() { + // If there were no blocked readers, notify a blocked writer. + self.0.write_wakers.notify_one(); } } } diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 8fd1b6210..eb44a6730 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -95,8 +95,11 @@ impl WakerSet { } /// Removes the waker of a cancelled operation. - pub fn cancel(&self, key: usize) { + /// + /// Returns `true` if another blocked operation from the set was notified. + pub fn cancel(&self, key: usize) -> bool { let mut inner = self.lock(); + if inner.entries.remove(key).is_none() { inner.none_count -= 1; @@ -107,33 +110,45 @@ impl WakerSet { w.wake(); inner.none_count += 1; } + return true; } } + + false } /// Notifies one blocked operation. + /// + /// Returns `true` if an operation was notified. #[inline] - pub fn notify_one(&self) { + pub fn notify_one(&self) -> bool { // Use `SeqCst` ordering to synchronize with `Lock::drop()`. if self.flag.load(Ordering::SeqCst) & NOTIFY_ONE != 0 { - self.notify(false); + self.notify(false) + } else { + false } } /// Notifies all blocked operations. - // TODO: Delete this attribute when `crate::sync::channel()` is stabilized. - #[cfg(feature = "unstable")] + /// + /// Returns `true` if at least one operation was notified. #[inline] - pub fn notify_all(&self) { + pub fn notify_all(&self) -> bool { // Use `SeqCst` ordering to synchronize with `Lock::drop()`. if self.flag.load(Ordering::SeqCst) & NOTIFY_ALL != 0 { - self.notify(true); + self.notify(true) + } else { + false } } /// Notifies blocked operations, either one or all of them. - fn notify(&self, all: bool) { + /// + /// Returns `true` if at least one operation was notified. + fn notify(&self, all: bool) -> bool { let mut inner = &mut *self.lock(); + let mut notified = false; for (_, opt_waker) in inner.entries.iter_mut() { // If there is no waker in this entry, that means it was already woken. @@ -141,10 +156,15 @@ impl WakerSet { w.wake(); inner.none_count += 1; } + + notified = true; + if !all { break; } } + + notified } /// Locks the list of entries. From bf0cd5987aeb536a71ca09cdfbde7e01ffbbac6d Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 4 Nov 2019 11:49:43 +0800 Subject: [PATCH 153/707] Update src/stream/stream/cloned.rs Co-Authored-By: nasa --- src/stream/stream/cloned.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs index 779e49d15..1d7eef474 100644 --- a/src/stream/stream/cloned.rs +++ b/src/stream/stream/cloned.rs @@ -4,7 +4,6 @@ use pin_project_lite::pin_project; use std::pin::Pin; pin_project! { - #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct Cloned { #[pin] From 8bef812e78ab836491e904f2500f0c950e93ac2e Mon Sep 17 00:00:00 2001 From: yjh Date: Mon, 4 Nov 2019 11:49:50 +0800 Subject: [PATCH 154/707] Update src/stream/stream/cloned.rs Co-Authored-By: nasa --- src/stream/stream/cloned.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs index 1d7eef474..19dfbc87c 100644 --- a/src/stream/stream/cloned.rs +++ b/src/stream/stream/cloned.rs @@ -4,7 +4,7 @@ use pin_project_lite::pin_project; use std::pin::Pin; pin_project! { - #[allow(missing_debug_implementations)] + #[derive(Debug)] pub struct Cloned { #[pin] stream: S, From a3e68704bc0f4f55a188e97b6c3d41cac50992a9 Mon Sep 17 00:00:00 2001 From: "Abhishek C. Sharma" Date: Mon, 4 Nov 2019 13:58:14 +0530 Subject: [PATCH 155/707] Wrap state enum in public struct --- src/future/future/flatten.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs index 27ee22815..7b0561429 100644 --- a/src/future/future/flatten.rs +++ b/src/future/future/flatten.rs @@ -5,9 +5,13 @@ use crate::future::Future; use crate::future::IntoFuture; use crate::task::{Context, Poll}; -#[doc(hidden)] #[derive(Debug)] -pub enum FlattenFuture { +pub struct FlattenFuture { + state: State, +} + +#[derive(Debug)] +enum State { First(Fut1), Second(Fut2), Empty, @@ -15,7 +19,9 @@ pub enum FlattenFuture { impl FlattenFuture { pub fn new(future: Fut1) -> FlattenFuture { - FlattenFuture::First(future) + FlattenFuture { + state: State::First(future), + } } } @@ -27,19 +33,19 @@ where type Output = ::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; + let Self { state } = unsafe { self.get_unchecked_mut() }; loop { - match this { - FlattenFuture::First(fut1) => { + match state { + State::First(fut1) => { let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future(); - *this = FlattenFuture::Second(fut2); + *state = State::Second(fut2); } - FlattenFuture::Second(fut2) => { + State::Second(fut2) => { let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx)); - *this = FlattenFuture::Empty; + *state = State::Empty; return Poll::Ready(v); } - FlattenFuture::Empty => unreachable!(), + State::Empty => panic!("polled a completed future"), } } } From d7afcada76c8a49c411350c36beb5af41e1f36a0 Mon Sep 17 00:00:00 2001 From: "Abhishek C. Sharma" Date: Mon, 4 Nov 2019 15:19:47 +0530 Subject: [PATCH 156/707] Fixed ambiguous associated types --- src/future/future/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 0f3ab2804..8e6e90a66 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -153,10 +153,10 @@ extension_trait! { /// ``` #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(any(feature = "unstable", feature = "docs"))] - fn flatten(self) -> FlattenFuture::Future> + fn flatten(self) -> impl Future::Output as IntoFuture>::Output> [FlattenFuture::Output as IntoFuture>::Future>] where Self: Future + Sized, - Self::Output: IntoFuture + ::Output: IntoFuture { FlattenFuture::new(self) } From e9edadffc72a6fcb73803b1a0d32de6930eb1c9c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 4 Nov 2019 18:15:12 +0100 Subject: [PATCH 157/707] Fix a deadlock in channel --- src/sync/channel.rs | 95 ++++++++++++++----------------- src/sync/mutex.rs | 42 ++++++-------- src/sync/rwlock.rs | 90 +++++++++++++---------------- src/sync/waker_set.rs | 129 ++++++++++++++++++++++++------------------ 4 files changed, 174 insertions(+), 182 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index d4b64230a..c32628086 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -138,41 +138,34 @@ impl Sender { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let msg = self.msg.take().unwrap(); - - // Try sending the message. - let poll = match self.channel.try_send(msg) { - Ok(()) => Poll::Ready(()), - Err(TrySendError::Disconnected(msg)) => { - self.msg = Some(msg); - Poll::Pending + 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); } - Err(TrySendError::Full(msg)) => { - // Insert this send operation. - match self.opt_key { - None => self.opt_key = Some(self.channel.send_wakers.insert(cx)), - Some(key) => self.channel.send_wakers.update(key, cx), + + // 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)); - // Try sending the message again. - match self.channel.try_send(msg) { - Ok(()) => Poll::Ready(()), - Err(TrySendError::Disconnected(msg)) | Err(TrySendError::Full(msg)) => { - self.msg = Some(msg); - Poll::Pending + // If the channel is still full and not disconnected, return. + if self.channel.is_full() && !self.channel.is_disconnected() { + return Poll::Pending; } } } - }; - - if poll.is_ready() { - // If the current task is in the set, remove it. - if let Some(key) = self.opt_key.take() { - self.channel.send_wakers.complete(key); - } } - - poll } } @@ -543,34 +536,27 @@ fn poll_recv( opt_key: &mut Option, cx: &mut Context<'_>, ) -> Poll> { - // Try receiving a message. - let poll = match channel.try_recv() { - Ok(msg) => Poll::Ready(Some(msg)), - Err(TryRecvError::Disconnected) => Poll::Ready(None), - Err(TryRecvError::Empty) => { - // Insert this receive operation. - match *opt_key { - None => *opt_key = Some(wakers.insert(cx)), - Some(key) => wakers.update(key, cx), - } - - // Try receiving a message again. - match channel.try_recv() { - Ok(msg) => Poll::Ready(Some(msg)), - Err(TryRecvError::Disconnected) => Poll::Ready(None), - Err(TryRecvError::Empty) => Poll::Pending, - } - } - }; - - if poll.is_ready() { + loop { // If the current task is in the set, remove it. if let Some(key) = opt_key.take() { - wakers.complete(key); + wakers.remove(key); } - } - poll + // Try receiving a message. + match channel.try_recv() { + Ok(msg) => return Poll::Ready(Some(msg)), + Err(TryRecvError::Disconnected) => return Poll::Ready(None), + 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. @@ -862,6 +848,11 @@ impl Channel { } } + /// 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); diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index fcd030d8f..52c389852 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -104,32 +104,26 @@ impl Mutex { type Output = MutexGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let poll = match self.mutex.try_lock() { - Some(guard) => Poll::Ready(guard), - None => { - // Insert this lock operation. - match self.opt_key { - None => self.opt_key = Some(self.mutex.wakers.insert(cx)), - Some(key) => self.mutex.wakers.update(key, cx), - } - - // Try locking again because it's possible the mutex got unlocked just - // before the current task was inserted into the waker set. - match self.mutex.try_lock() { - Some(guard) => Poll::Ready(guard), - None => Poll::Pending, - } - } - }; - - if poll.is_ready() { + loop { // If the current task is in the set, remove it. if let Some(key) = self.opt_key.take() { - self.mutex.wakers.complete(key); + self.mutex.wakers.remove(key); } - } - poll + // 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; + } + } + } + } } } @@ -266,8 +260,8 @@ impl Drop for MutexGuard<'_, T> { // Use `SeqCst` ordering to synchronize with `WakerSet::insert()` and `WakerSet::update()`. self.0.locked.store(false, Ordering::SeqCst); - // Notify one blocked `lock()` operation. - self.0.wakers.notify_one(); + // Notify a blocked `lock()` operation if none were notified already. + self.0.wakers.notify_any(); } } diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 81b0735af..65b9dcad2 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -108,32 +108,26 @@ impl RwLock { type Output = RwLockReadGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let poll = match self.lock.try_read() { - Some(guard) => Poll::Ready(guard), - None => { - // Insert this lock operation. - match self.opt_key { - None => self.opt_key = Some(self.lock.read_wakers.insert(cx)), - Some(key) => self.lock.read_wakers.update(key, cx), - } - - // Try locking again because it's possible the lock got unlocked just - // before the current task was inserted into the waker set. - match self.lock.try_read() { - Some(guard) => Poll::Ready(guard), - None => Poll::Pending, - } - } - }; - - if poll.is_ready() { + loop { // If the current task is in the set, remove it. if let Some(key) = self.opt_key.take() { - self.lock.read_wakers.complete(key); + self.lock.read_wakers.remove(key); } - } - poll + // 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; + } + } + } + } } } @@ -143,9 +137,10 @@ impl RwLock { if let Some(key) = self.opt_key { self.lock.read_wakers.cancel(key); - // If there are no active readers, wake one of the writers. + // 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_one(); + self.lock.write_wakers.notify_any(); } } } @@ -238,32 +233,26 @@ impl RwLock { type Output = RwLockWriteGuard<'a, T>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let poll = match self.lock.try_write() { - Some(guard) => Poll::Ready(guard), - None => { - // Insert this lock operation. - match self.opt_key { - None => self.opt_key = Some(self.lock.write_wakers.insert(cx)), - Some(key) => self.lock.write_wakers.update(key, cx), - } - - // Try locking again because it's possible the lock got unlocked just - // before the current task was inserted into the waker set. - match self.lock.try_write() { - Some(guard) => Poll::Ready(guard), - None => Poll::Pending, - } - } - }; - - if poll.is_ready() { + loop { // If the current task is in the set, remove it. if let Some(key) = self.opt_key.take() { - self.lock.write_wakers.complete(key); + self.lock.write_wakers.remove(key); } - } - poll + // 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; + } + } + } + } } } @@ -392,9 +381,9 @@ 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 read, wake one of the writers. + // 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_one(); + self.0.write_wakers.notify_any(); } } } @@ -431,8 +420,9 @@ impl Drop for RwLockWriteGuard<'_, T> { // Notify all blocked readers. if !self.0.read_wakers.notify_all() { - // If there were no blocked readers, notify a blocked writer. - self.0.write_wakers.notify_one(); + // If there were no blocked readers, notify a blocked writer if none were notified + // already. + self.0.write_wakers.notify_any(); } } } diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index eb44a6730..57fbaaa2a 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -17,11 +17,11 @@ use crate::task::{Context, Waker}; #[allow(clippy::identity_op)] const LOCKED: usize = 1 << 0; -/// Set when there are tasks for `notify_one()` to wake. -const NOTIFY_ONE: usize = 1 << 1; +/// Set when there is at least one entry that has already been notified. +const NOTIFIED: usize = 1 << 1; -/// Set when there are tasks for `notify_all()` to wake. -const NOTIFY_ALL: usize = 1 << 2; +/// Set when there is at least one notifiable entry. +const NOTIFIABLE: usize = 1 << 2; /// Inner representation of `WakerSet`. struct Inner { @@ -34,8 +34,8 @@ struct Inner { /// The key of each entry is its index in the `Slab`. entries: Slab>, - /// The number of entries that have the waker set to `None`. - none_count: usize, + /// The number of notifiable entries. + notifiable: usize, } /// A set holding wakers. @@ -55,7 +55,7 @@ impl WakerSet { flag: AtomicUsize::new(0), inner: UnsafeCell::new(Inner { entries: Slab::new(), - none_count: 0, + notifiable: 0, }), } } @@ -63,34 +63,20 @@ impl WakerSet { /// Inserts a waker for a blocked operation and returns a key associated with it. pub fn insert(&self, cx: &Context<'_>) -> usize { let w = cx.waker().clone(); - self.lock().entries.insert(Some(w)) - } - - /// Updates the waker of a previously inserted entry. - pub fn update(&self, key: usize, cx: &Context<'_>) { let mut inner = self.lock(); - match &mut inner.entries[key] { - None => { - // Fill in the waker. - let w = cx.waker().clone(); - inner.entries[key] = Some(w); - inner.none_count -= 1; - } - Some(w) => { - // Replace the waker if the existing one is different. - if !w.will_wake(cx.waker()) { - *w = cx.waker().clone(); - } - } - } + let key = inner.entries.insert(Some(w)); + inner.notifiable += 1; + key } - /// Removes the waker of a completed operation. - pub fn complete(&self, key: usize) { + /// Removes the waker of an operation. + pub fn remove(&self, key: usize) { let mut inner = self.lock(); - if inner.entries.remove(key).is_none() { - inner.none_count -= 1; + + match inner.entries.remove(key) { + Some(_) => inner.notifiable -= 1, + None => {} } } @@ -100,31 +86,48 @@ impl WakerSet { pub fn cancel(&self, key: usize) -> bool { let mut inner = self.lock(); - if inner.entries.remove(key).is_none() { - inner.none_count -= 1; - - // The operation was cancelled and notified so notify another operation instead. - if let Some((_, opt_waker)) = inner.entries.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - inner.none_count += 1; + match inner.entries.remove(key) { + Some(_) => inner.notifiable -= 1, + None => { + // The operation was cancelled and notified so notify another operation instead. + for (_, opt_waker) in inner.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + inner.notifiable -= 1; + return true; + } } - return true; } } false } - /// Notifies one blocked operation. + /// 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. + #[inline] + #[cfg(feature = "unstable")] pub fn notify_one(&self) -> bool { // Use `SeqCst` ordering to synchronize with `Lock::drop()`. - if self.flag.load(Ordering::SeqCst) & NOTIFY_ONE != 0 { - self.notify(false) + if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 { + self.notify(Notify::One) } else { false } @@ -136,8 +139,8 @@ impl WakerSet { #[inline] pub fn notify_all(&self) -> bool { // Use `SeqCst` ordering to synchronize with `Lock::drop()`. - if self.flag.load(Ordering::SeqCst) & NOTIFY_ALL != 0 { - self.notify(true) + if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 { + self.notify(Notify::All) } else { false } @@ -146,7 +149,7 @@ impl WakerSet { /// Notifies blocked operations, either one or all of them. /// /// Returns `true` if at least one operation was notified. - fn notify(&self, all: bool) -> bool { + fn notify(&self, n: Notify) -> bool { let mut inner = &mut *self.lock(); let mut notified = false; @@ -154,12 +157,15 @@ impl WakerSet { // If there is no waker in this entry, that means it was already woken. if let Some(w) = opt_waker.take() { w.wake(); - inner.none_count += 1; - } + inner.notifiable -= 1; + notified = true; - notified = true; + if n == Notify::One { + break; + } + } - if !all { + if n == Notify::Any { break; } } @@ -188,14 +194,14 @@ impl Drop for Lock<'_> { fn drop(&mut self) { let mut flag = 0; - // If there is at least one entry and all are `Some`, then `notify_one()` has work to do. - if !self.entries.is_empty() && self.none_count == 0 { - flag |= NOTIFY_ONE; + // Set the `NOTIFIED` flag if there is at least one notified entry. + if self.entries.len() - self.notifiable > 0 { + flag |= NOTIFIED; } - // If there is at least one `Some` entry, then `notify_all()` has work to do. - if self.entries.len() - self.none_count > 0 { - flag |= NOTIFY_ALL; + // Set the `NOTIFIABLE` flag if there is at least one notifiable entry. + if self.notifiable > 0 { + flag |= NOTIFIABLE; } // Use `SeqCst` ordering to synchronize with `WakerSet::lock_to_notify()`. @@ -218,3 +224,14 @@ impl DerefMut for Lock<'_> { unsafe { &mut *self.waker_set.inner.get() } } } + +/// Notification strategy. +#[derive(Clone, Copy, Eq, PartialEq)] +enum Notify { + /// Make sure at least one entry is notified. + Any, + /// Notify one additional entry. + One, + /// Notify all entries. + All, +} From 5874392397ac5928f816f9da17eae5d84160a2e2 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 4 Nov 2019 18:48:49 +0100 Subject: [PATCH 158/707] Fix a clippy warning --- src/sync/waker_set.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 57fbaaa2a..a10f214f9 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -74,9 +74,8 @@ impl WakerSet { pub fn remove(&self, key: usize) { let mut inner = self.lock(); - match inner.entries.remove(key) { - Some(_) => inner.notifiable -= 1, - None => {} + if let Some(_) = inner.entries.remove(key) { + inner.notifiable -= 1; } } From 6d421de9926fa87ab78eae64c313caa318d67f2b Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 5 Nov 2019 10:16:00 +0000 Subject: [PATCH 159/707] Fix another clippy warning --- src/sync/waker_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index a10f214f9..7e3d8e157 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -74,7 +74,7 @@ impl WakerSet { pub fn remove(&self, key: usize) { let mut inner = self.lock(); - if let Some(_) = inner.entries.remove(key) { + if inner.entries.remove(key).is_some() { inner.notifiable -= 1; } } From a35602f375851fa82cdd0ffd73feea1ff06d4287 Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 5 Nov 2019 21:08:56 +0800 Subject: [PATCH 160/707] Update mod.rs --- src/stream/stream/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 01357b283..7112c3819 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -387,10 +387,10 @@ extension_trait! { use async_std::prelude::*; use std::collections::VecDeque; - let v: VecDeque<_> = vec![&1, &2, &3].into_iter().collect(); - + let v = stream::from_iter(vec![&1, &2, &3]); + let mut v_cloned = v.cloned(); - + assert_eq!(v_cloned.next().await, Some(1)); assert_eq!(v_cloned.next().await, Some(2)); assert_eq!(v_cloned.next().await, Some(3)); From 5179f30d2d8adc39259e4dfee810d14c0c02a698 Mon Sep 17 00:00:00 2001 From: yjh Date: Tue, 5 Nov 2019 21:15:33 +0800 Subject: [PATCH 161/707] use async_std::stream --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7112c3819..12b258d62 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -385,7 +385,7 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; let v = stream::from_iter(vec![&1, &2, &3]); From 43bb59cd0238f98fbf5fbe52a3ce56a83b32e8f4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 5 Nov 2019 17:49:05 +0100 Subject: [PATCH 162/707] Fix some links in docs --- src/future/future/mod.rs | 2 ++ src/io/read/mod.rs | 4 ++-- src/stream/stream/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index dad94daa8..2cd5c7e98 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -190,6 +190,8 @@ extension_trait! { The ordering of which value is yielded when two futures resolve simultaneously is intentionally left unspecified. + [`race`]: #method.race + # Examples ``` diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index ed61590be..f5c0a4c98 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -267,7 +267,7 @@ extension_trait! { 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. + calls to [`read`] may succeed. # Examples @@ -275,7 +275,7 @@ extension_trait! { [`File`]: ../fs/struct.File.html [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok - [`read()`]: tymethod.read + [`read`]: tymethod.read ```no_run # fn main() -> std::io::Result<()> { async_std::task::block_on(async { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4ab12a103..b77019da6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1520,7 +1520,7 @@ extension_trait! { standard library, used in a variety of contexts. The most basic pattern in which `collect()` is used is to turn one - collection into another. You take a collection, call [`stream`] on it, + collection into another. You take a collection, call [`into_stream`] on it, do a bunch of transformations, and then `collect()` at the end. Because `collect()` is so general, it can cause problems with type @@ -1561,7 +1561,7 @@ extension_trait! { # }) } ``` - [`stream`]: trait.Stream.html#tymethod.next + [`into_stream`]: trait.IntoStream.html#tymethod.into_stream "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] From 1707638ebbaaaa87ddf9ccb75f8e267e65800b64 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 5 Nov 2019 17:09:32 +0000 Subject: [PATCH 163/707] Update mod.rs --- 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 07eecf28c..29c3b7f15 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -178,7 +178,7 @@ //! produce a stream. What gives? //! //! There's a trait in the standard library for converting something into an -//! stream: [`IntoStream`]. This trait has one method, [`into_stream], +//! stream: [`IntoStream`]. This trait has one method, [`into_stream`], //! which converts the thing implementing [`IntoStream`] into a stream. //! //! Unlike `std::iter::IntoIterator`, `IntoStream` does not have compiler From ae8b051892c65067fa50a1950d9a824ff571c1e9 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 5 Nov 2019 21:00:12 +0100 Subject: [PATCH 164/707] rework lib.rs docs Signed-off-by: Yoshua Wuyts --- src/lib.rs | 133 +++++++++++++++++++++++++++++++++++++++++++--- src/stream/mod.rs | 2 +- 2 files changed, 126 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ea05edf73..c8d25d0a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,133 @@ -//! Async version of the Rust standard library. +//! # Async version of the Rust standard library //! -//! Modules in this crate are organized in the same way as in the standard library, except blocking +//! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested +//! shared abstractions for the [broader Rust ecosystem][crates.io]. It offers core types, like +//! [`Future`] and [`Stream`], library-defined [operations on language primitives](#primitives), +//! [standard macros](#macros), [I/O] and [multithreading], among [many other things][other]. +//! +//! `async-std` is available from [crates.io]. Once included, `async-std` can be accessed +//! in [`use`] statements through the path `async_std`, as in [`use async_std::future`]. +//! +//! [I/O]: io/index.html +//! [multithreading]: task/index.html +//! [other]: #what-is-in-the-standard-library-documentation +//! [`use`]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html +//! [`use async_std::future`]: future/index.html +//! [crates.io]: https://crates.io +//! [`Future`]: future/trait.Future.html +//! [`Stream`]: stream/trait.Stream.html +//! +//! # How to read this documentation +//! +//! If you already know the name of what you are looking for, the fastest way to +//! find it is to use the search +//! bar at the top of the page. +//! +//! Otherwise, you may want to jump to one of these useful sections: +//! +//! * [`async_std::*` modules](#modules) +//! * [Async macros](#macros) +//! * [The Async Prelude](prelude/index.html) +//! * [Cargo.toml feature flags](#features) +//! * [Examples](#examples) +//! +//! If this is your first time, the documentation for `async-std` is +//! written to be casually perused. Clicking on interesting things should +//! generally lead you to interesting places. Still, there are important bits +//! you don't want to miss, so read on for a tour of the `async-std` and +//! its documentation! +//! +//! Once you are familiar with the contents of `async-std` you may +//! begin to find the verbosity of the prose distracting. At this stage in your +//! development you may want to press the `[-]` button near the top of the +//! page to collapse it into a more skimmable view. +//! +//! While you are looking at that `[-]` button also notice the `[src]` +//! button. Rust's API documentation comes with the source code and you are +//! encouraged to read it. The `async-std` source is generally high +//! quality and a peek behind the curtains is often enlightening. +//! +//! Modules in this crate are organized in the same way as in `async-std`, except blocking //! functions have been replaced with async functions and threads have been replaced with //! lightweight tasks. //! -//! More information, reading materials, and other resources: +//! You can find more information, reading materials, and other resources here: +//! +//! * [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) +//! * [Discord chat](https://discord.gg/JvZeVNe) +//! +//! # What is in the `async-std` documentation? +//! +//! First, `async-std` is divided into a number of focused +//! modules, [all listed further down this page](#modules). These modules are +//! the bedrock upon which async Rust is forged, and they have mighty names +//! like [`async_std::os`] and [`async_std::task`]. Modules' documentation +//! typically includes an overview of the module along with examples, and are +//! a smart place to start familiarizing yourself with the library. +//! +//! Third, `async-std` defines [The Async Prelude], a small collection +//! of items - mostly traits - that should be imported into every module of +//! every async crate. The traits in the prelude are pervasive, making the +//! prelude documentation a good entry point to learning about the library. +//! +//! [The Async Prelude]: prelude/index.html +//! [`async_std::os`]: os/index.html +//! [`async_std::task`]: task/index.html +//! +//! And finally, `async-std` exports a number of async macros, and +//! [lists them on this page](#macros). +//! +//! # Contributing changes to the documentation +//! +//! Check out the rust contribution guidelines [here](https://async.rs/contribute). +//! The source for this documentation can be found on [Github](https://github.com/async-rs). +//! To contribute changes, make sure you read the guidelines first, then submit +//! pull-requests for your suggested changes. +//! +//! Contributions are appreciated! If you see a part of the docs that can be +//! improved, submit a PR, or chat with us first on +//! [Discord](https://discord.gg/JvZeVNe). +//! +//! # A Tour of `async-std` +//! +//! The rest of this crate documentation is dedicated to pointing out notable +//! features of `async-std`. +//! +//! ## Platform abstractions and I/O +//! +//! Besides basic data types, `async-std` is largely concerned with +//! abstracting over differences in common platforms, most notably Windows and +//! Unix derivatives. +//! +//! Common types of I/O, including [files], [TCP], [UDP], are defined in the +//! [`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. +//! +//! [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 +//! +//! ## Timeouts, intervals, and delays +//! +//! `async-std` provides several methods to manipulate time: +//! +//! * [`task::sleep`] to wait for a duration to pass without blocking. +//! * [`stream::interval`] for emitting an event at a set interval. +//! * [`future::timeout`] to time-out futures if they don't resolve within a +//! set interval. //! -//! * [🌐 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) -//! * [💬 Discord chat](https://discord.gg/JvZeVNe) +//! [`task::sleep`]: task/fn.sleep.html +//! [`stream::interval`]: stream/fn.interval.html +//! [`future::timeout`]: future/fn.timeout.html //! //! # Examples //! diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 07eecf28c..29c3b7f15 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -178,7 +178,7 @@ //! produce a stream. What gives? //! //! There's a trait in the standard library for converting something into an -//! stream: [`IntoStream`]. This trait has one method, [`into_stream], +//! stream: [`IntoStream`]. This trait has one method, [`into_stream`], //! which converts the thing implementing [`IntoStream`] into a stream. //! //! Unlike `std::iter::IntoIterator`, `IntoStream` does not have compiler From a757cc02dc721b1128a8bcaa49cc68822a08525f Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 6 Nov 2019 00:21:32 +0100 Subject: [PATCH 165/707] Expose extension traits in preludes --- src/future/future/mod.rs | 16 ++++++++++++++++ src/io/buf_read/mod.rs | 9 ++++++++- src/io/prelude.rs | 18 +++++++++--------- src/io/read/mod.rs | 9 ++++++++- src/io/seek/mod.rs | 9 ++++++++- src/io/stdin.rs | 2 +- src/io/write/mod.rs | 9 ++++++++- src/prelude.rs | 32 +++++++++++++++++--------------- src/stream/from_stream.rs | 6 ++---- src/stream/stream/mod.rs | 9 ++++++++- src/sync/mod.rs | 12 +++++------- src/utils.rs | 7 ++++--- 12 files changed, 94 insertions(+), 44 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 2cd5c7e98..548cccb87 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -23,6 +23,14 @@ extension_trait! { "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 @@ -36,6 +44,9 @@ extension_trait! { `.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#" @@ -110,6 +121,11 @@ extension_trait! { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll; } + #[doc = r#" + Extension methods for [`Future`]. + + [`Future`]: ../future/trait.Future.html + "#] pub trait FutureExt: std::future::Future { /// Returns a Future that delays execution for a specified time. /// diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index b82971fe7..45c5f28c9 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -25,7 +25,7 @@ extension_trait! { [`std::io::BufRead`]. The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + available when [`BufReadExt`] from the [prelude] is imported: ``` # #[allow(unused_imports)] @@ -36,6 +36,8 @@ extension_trait! { [`futures::io::AsyncBufRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html [provided methods]: #provided-methods + [`BufReadExt`]: ../io/prelude/trait.BufReadExt.html + [prelude]: ../prelude/index.html "#] pub trait BufRead { #[doc = r#" @@ -62,6 +64,11 @@ extension_trait! { fn consume(self: Pin<&mut Self>, amt: usize); } + #[doc = r#" + Extension methods for [`BufRead`]. + + [`BufRead`]: ../trait.BufRead.html + "#] pub trait BufReadExt: futures_io::AsyncBufRead { #[doc = r#" Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. diff --git a/src/io/prelude.rs b/src/io/prelude.rs index fb1b94562..c90b289af 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -1,4 +1,4 @@ -//! The async I/O Prelude +//! The async I/O prelude. //! //! The purpose of this module is to alleviate imports of many common I/O traits //! by adding a glob import to the top of I/O heavy modules: @@ -17,11 +17,11 @@ pub use crate::io::Seek; #[doc(no_inline)] pub use crate::io::Write; -#[doc(hidden)] -pub use crate::io::buf_read::BufReadExt as _; -#[doc(hidden)] -pub use crate::io::read::ReadExt as _; -#[doc(hidden)] -pub use crate::io::seek::SeekExt as _; -#[doc(hidden)] -pub use crate::io::write::WriteExt as _; +#[doc(inline)] +pub use crate::io::buf_read::BufReadExt; +#[doc(inline)] +pub use crate::io::read::ReadExt; +#[doc(inline)] +pub use crate::io::seek::SeekExt; +#[doc(inline)] +pub use crate::io::write::WriteExt; diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index f5c0a4c98..56f632356 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -31,7 +31,7 @@ extension_trait! { [`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 the prelude is imported: + trait itself, but they become available when [`ReadExt`] from the [prelude] is imported: ``` # #[allow(unused_imports)] @@ -43,6 +43,8 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/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#" @@ -66,6 +68,11 @@ extension_trait! { } } + #[doc = r#" + Extension methods for [`Read`]. + + [`Read`]: ../trait.Read.html + "#] pub trait ReadExt: futures_io::AsyncRead { #[doc = r#" Reads some bytes from the byte stream. diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index ec2dd8f91..7dc30aeed 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -18,7 +18,7 @@ extension_trait! { [`std::io::Seek`]. The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + available when [`SeekExt`] the [prelude] is imported: ``` # #[allow(unused_imports)] @@ -29,6 +29,8 @@ extension_trait! { [`futures::io::AsyncSeek`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html [provided methods]: #provided-methods + [`SeekExt`]: ../io/prelude/trait.SeekExt.html + [prelude]: ../prelude/index.html "#] pub trait Seek { #[doc = r#" @@ -41,6 +43,11 @@ extension_trait! { ) -> Poll>; } + #[doc = r#" + Extension methods for [`Seek`]. + + [`Seek`]: ../trait.Seek.html + "#] pub trait SeekExt: futures_io::AsyncSeek { #[doc = r#" Seeks to a new position in a byte stream. diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 26b7ee009..bd6580c2f 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -173,7 +173,7 @@ impl Stdin { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; - /// use crate::async_std::prelude::*; + /// use async_std::prelude::*; /// /// let mut buffer = String::new(); /// diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index a07c89681..eb114344a 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -26,7 +26,7 @@ extension_trait! { 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 - the prelude is imported: + [`WriteExt`] from the [prelude] is imported: ``` # #[allow(unused_imports)] @@ -40,6 +40,8 @@ extension_trait! { [`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#" @@ -74,6 +76,11 @@ extension_trait! { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } + #[doc = r#" + Extension methods for [`Write`]. + + [`Write`]: ../trait.Write.html + "#] pub trait WriteExt: futures_io::AsyncWrite { #[doc = r#" Writes some bytes into the byte stream. diff --git a/src/prelude.rs b/src/prelude.rs index 91432e0bb..93218f116 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,6 +13,16 @@ #[doc(no_inline)] pub use crate::future::Future; +#[doc(no_inline)] +pub use crate::stream::Stream; +#[doc(no_inline)] +pub use crate::task_local; + +#[doc(inline)] +pub use crate::future::future::FutureExt; +#[doc(inline)] +pub use crate::stream::stream::StreamExt; + #[doc(no_inline)] pub use crate::io::BufRead as _; #[doc(no_inline)] @@ -21,23 +31,15 @@ pub use crate::io::Read as _; pub use crate::io::Seek as _; #[doc(no_inline)] pub use crate::io::Write as _; -#[doc(no_inline)] -pub use crate::stream::Stream; -#[doc(no_inline)] -pub use crate::task_local; -#[doc(hidden)] -pub use crate::future::future::FutureExt as _; -#[doc(hidden)] +#[doc(no_inline)] pub use crate::io::buf_read::BufReadExt as _; -#[doc(hidden)] -pub use crate::io::read::ReadExt as _; -#[doc(hidden)] -pub use crate::io::seek::SeekExt as _; -#[doc(hidden)] -pub use crate::io::write::WriteExt as _; -#[doc(hidden)] -pub use crate::stream::stream::StreamExt as _; +#[doc(no_inline)] +pub use crate::io::prelude::ReadExt as _; +#[doc(no_inline)] +pub use crate::io::prelude::SeekExt as _; +#[doc(no_inline)] +pub use crate::io::prelude::WriteExt as _; cfg_unstable! { #[doc(no_inline)] diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 54a222910..e8707cef6 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -15,9 +15,8 @@ use std::pin::Pin; /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use crate::async_std::stream::FromStream; /// use async_std::prelude::*; -/// use async_std::stream; +/// use async_std::stream::{self, FromStream}; /// /// let five_fives = stream::repeat(5).take(5); /// @@ -117,9 +116,8 @@ pub trait FromStream { /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// use crate::async_std::stream::FromStream; /// use async_std::prelude::*; - /// use async_std::stream; + /// use async_std::stream::{self, FromStream}; /// /// let five_fives = stream::repeat(5).take(5); /// diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b77019da6..990ef7b2a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -138,7 +138,7 @@ extension_trait! { [`std::iter::Iterator`]. The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + available when [`StreamExt`] from the [prelude] is imported: ``` # #[allow(unused_imports)] @@ -149,6 +149,8 @@ extension_trait! { [`futures::stream::Stream`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html [provided methods]: #provided-methods + [`StreamExt`]: ../prelude/trait.StreamExt.html + [prelude]: ../prelude/index.html "#] pub trait Stream { #[doc = r#" @@ -210,6 +212,11 @@ extension_trait! { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } + #[doc = r#" + Extension methods for [`Stream`]. + + [`Stream`]: ../stream/trait.Stream.html + "#] pub trait StreamExt: futures_core::stream::Stream { #[doc = r#" Advances the stream and returns the next value. diff --git a/src/sync/mod.rs b/src/sync/mod.rs index ea991fe6a..ab551eca4 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -134,7 +134,7 @@ //! inter-task synchronisation mechanism, at the cost of some //! extra memory. //! -//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at +//! - [`Mutex`]: Mutual exclusion mechanism, which ensures that at //! most one task at a time is able to access some data. //! //! - [`RwLock`]: Provides a mutual exclusion mechanism which allows @@ -142,13 +142,11 @@ //! writer at a time. In some cases, this can be more efficient than //! a mutex. //! -//! [`Arc`]: crate::sync::Arc -//! [`Barrier`]: crate::sync::Barrier -//! [`Condvar`]: crate::sync::Condvar +//! [`Arc`]: struct.Arc.html +//! [`Barrier`]: struct.Barrier.html //! [`channel`]: fn.channel.html -//! [`Mutex`]: crate::sync::Mutex -//! [`Once`]: crate::sync::Once -//! [`RwLock`]: crate::sync::RwLock +//! [`Mutex`]: struct.Mutex.html +//! [`RwLock`]: struct.RwLock.html //! //! # Examples //! diff --git a/src/utils.rs b/src/utils.rs index a1d851078..7758cc742 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -144,6 +144,7 @@ macro_rules! extension_trait { $($body_base:tt)* } + #[doc = $doc_ext:tt] pub trait $ext:ident: $base:path { $($body_ext:tt)* } @@ -177,13 +178,13 @@ macro_rules! extension_trait { pub use $base as $name; // The extension trait that adds methods to any type implementing the base trait. - /// Extension trait. - pub trait $ext: $base { + #[doc = $doc_ext] + 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 {} + impl $ext for T {} // Shim trait impls that only appear in docs. $(#[cfg(feature = "docs")] $imp)* From 1c87e97e9c5b35a7edf448856b21bc5d8389c302 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 6 Nov 2019 01:02:39 +0100 Subject: [PATCH 166/707] Apply suggestions from code review Co-Authored-By: Stjepan Glavina --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c8d25d0a3..92cff5c5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,7 @@ //! typically includes an overview of the module along with examples, and are //! a smart place to start familiarizing yourself with the library. //! -//! Third, `async-std` defines [The Async Prelude], a small collection +//! Second, `async-std` defines [The Async Prelude], a small collection //! of items - mostly traits - that should be imported into every module of //! every async crate. The traits in the prelude are pervasive, making the //! prelude documentation a good entry point to learning about the library. @@ -82,16 +82,16 @@ //! //! # Contributing changes to the documentation //! -//! Check out the rust contribution guidelines [here](https://async.rs/contribute). -//! The source for this documentation can be found on [Github](https://github.com/async-rs). +//! Check out the Rust contribution guidelines [here](https://async.rs/contribute). +//! The source for this documentation can be found on [GitHub](https://github.com/async-rs). //! To contribute changes, make sure you read the guidelines first, then submit -//! pull-requests for your suggested changes. +//! pull requests for your suggested changes. //! //! Contributions are appreciated! If you see a part of the docs that can be //! improved, submit a PR, or chat with us first on //! [Discord](https://discord.gg/JvZeVNe). //! -//! # A Tour of `async-std` +//! # A tour of `async-std` //! //! The rest of this crate documentation is dedicated to pointing out notable //! features of `async-std`. From f4fb8a35344357de3a7d888e26d9d71353608485 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 6 Nov 2019 01:04:46 +0100 Subject: [PATCH 167/707] change one line Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 92cff5c5c..4320089ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ //! //! # Contributing changes to the documentation //! -//! Check out the Rust contribution guidelines [here](https://async.rs/contribute). +//! Check out `async-std`'s contribution guidelines [here](https://async.rs/contribute). //! The source for this documentation can be found on [GitHub](https://github.com/async-rs). //! To contribute changes, make sure you read the guidelines first, then submit //! pull requests for your suggested changes. From c3254d78d9d89df0386d86f6a3ddf8626d6944c3 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 6 Nov 2019 01:17:35 +0100 Subject: [PATCH 168/707] Fix a re-rexport --- src/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prelude.rs b/src/prelude.rs index 93218f116..f8583fd25 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -33,7 +33,7 @@ pub use crate::io::Seek as _; pub use crate::io::Write as _; #[doc(no_inline)] -pub use crate::io::buf_read::BufReadExt as _; +pub use crate::io::prelude::BufReadExt as _; #[doc(no_inline)] pub use crate::io::prelude::ReadExt as _; #[doc(no_inline)] From d502453057223ab8ebcdcf4d0c514fb70687c2f9 Mon Sep 17 00:00:00 2001 From: Gabriel Majeri Date: Wed, 6 Nov 2019 10:35:31 +0200 Subject: [PATCH 169/707] Remove doc `Stream` impl for `VecDeque` (#461) --- src/stream/stream/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b77019da6..c7e794a76 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -2067,14 +2067,6 @@ extension_trait! { } } - impl Stream for std::collections::VecDeque { - type Item = T; - - 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; From 93b01e36ed890556030380cba06a88e5661cfaf0 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 6 Nov 2019 19:29:17 +0000 Subject: [PATCH 170/707] Clippy fixes (#462) --- src/io/buf_read/read_line.rs | 8 ++++++-- src/io/read/read_to_string.rs | 6 +++++- src/sync/mod.rs | 4 +++- src/task/block_on.rs | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs index 29866be0f..04c61c1d7 100644 --- a/src/io/buf_read/read_line.rs +++ b/src/io/buf_read/read_line.rs @@ -37,8 +37,12 @@ impl Future for ReadLineFuture<'_, T> { )) })) } else { - debug_assert!(buf.is_empty()); - debug_assert_eq!(*read, 0); + #[allow(clippy::debug_assert_with_mut_call)] + { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + } + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. mem::swap(unsafe { buf.as_mut_vec() }, bytes); Poll::Ready(ret) diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs index 60773e07a..5f1a4d256 100644 --- a/src/io/read/read_to_string.rs +++ b/src/io/read/read_to_string.rs @@ -37,7 +37,11 @@ impl Future for ReadToStringFuture<'_, T> { )) })) } else { - debug_assert!(buf.is_empty()); + #[allow(clippy::debug_assert_with_mut_call)] + { + debug_assert!(buf.is_empty()); + } + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. mem::swap(unsafe { buf.as_mut_vec() }, bytes); Poll::Ready(ret) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index ea991fe6a..56c5f8116 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -15,7 +15,7 @@ //! //! Consider the following code, operating on some global static variables: //! -//! ```rust +//! ``` //! static mut A: u32 = 0; //! static mut B: u32 = 0; //! static mut C: u32 = 0; @@ -175,6 +175,8 @@ //! # }) //! ``` +#![allow(clippy::needless_doctest_main)] + #[doc(inline)] pub use std::sync::{Arc, Weak}; diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 54a415f53..d320cb2fd 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -89,6 +89,7 @@ where 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) } From c34e0f8a35855e86dab3dbf1381933b77c238a68 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 6 Nov 2019 20:20:27 +0000 Subject: [PATCH 171/707] Update futures to 0.3 (#463) * Update futures to 0.3 * Fix a search-and-replace error * Fix imports in tests * Fix an import --- Cargo.toml | 10 +++------ docs/src/tutorial/all_together.md | 7 +++---- docs/src/tutorial/clean_shutdown.md | 14 ++++++------- .../connecting_readers_and_writers.md | 7 +++---- docs/src/tutorial/handling_disconnection.md | 21 ++++++++----------- docs/src/tutorial/implementing_a_client.md | 4 ++-- docs/src/tutorial/sending_messages.md | 7 +++---- src/path/path.rs | 4 ++-- src/sync/barrier.rs | 6 +++--- 9 files changed, 34 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e9dc09cf..9583cdee7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,8 +32,8 @@ broadcaster = { version = "0.2.6", optional = true, default-features = false, fe crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" crossbeam-utils = "0.6.6" -futures-core-preview = "=0.3.0-alpha.19" -futures-io-preview = "=0.3.0-alpha.19" +futures-core = "0.3.0" +futures-io = "0.3.0" futures-timer = "1.0.2" kv-log-macro = "1.0.4" log = { version = "0.4.8", features = ["kv_unstable"] } @@ -51,11 +51,7 @@ femme = "1.2.0" rand = "0.7.2" # surf = "1.0.2" tempdir = "0.3.7" -futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } - -# These are used by the book for examples -futures-channel-preview = "=0.3.0-alpha.19" -futures-util-preview = "=0.3.0-alpha.19" +futures = "0.3.0" [[test]] name = "stream" diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index dcc06616a..789283e66 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; use async_std::{ io::{self, BufReader}, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_channel::mpsc; -use futures_util::SinkExt; +use futures::channel::mpsc; +use futures::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 234067a3a..5dcc7f263 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -22,16 +22,15 @@ Let's add waiting to the server: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -156,16 +155,15 @@ And to the broker: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index c1ebe9b8a..fcc42b63d 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -12,15 +12,14 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # net::TcpStream, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::sink::SinkExt; +# use futures::channel::mpsc; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index a1f51d134..acb744b09 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::net::TcpStream; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -70,11 +69,10 @@ We use the `select` macro for this purpose: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{net::TcpStream, prelude::*}; -use futures_channel::mpsc; -use futures_util::{select, FutureExt}; +use futures::channel::mpsc; +use futures::{select, FutureExt}; # use std::sync::Arc; # type Receiver = mpsc::UnboundedReceiver; @@ -122,16 +120,15 @@ The final code looks like this: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; use async_std::{ io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_channel::mpsc; -use futures_util::{select, FutureExt, SinkExt}; +use futures::channel::mpsc; +use futures::{select, FutureExt, SinkExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 3aac67f35..fd728555d 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -16,14 +16,14 @@ With async, we can just use the `select!` macro. ```rust,edition2018 # extern crate async_std; -# extern crate futures_util; +# extern crate futures; use async_std::{ io::{stdin, BufReader}, net::{TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_util::{select, FutureExt}; +use futures::{select, FutureExt}; type Result = std::result::Result>; diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index b381aacbc..3f426d020 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # net::TcpStream, # prelude::*, # }; -use futures_channel::mpsc; // 1 -use futures_util::sink::SinkExt; +use futures::channel::mpsc; // 1 +use futures::sink::SinkExt; use std::sync::Arc; # type Result = std::result::Result>; diff --git a/src/path/path.rs b/src/path/path.rs index 22dab769d..43adbbbce 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -615,9 +615,9 @@ impl Path { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::path::Path; /// use async_std::fs; - /// use futures_util::stream::StreamExt; + /// use async_std::path::Path; + /// use async_std::prelude::*; /// /// let path = Path::new("/laputa"); /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 0163e3fc5..2822d5469 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -204,9 +204,9 @@ impl BarrierWaitResult { #[cfg(test)] mod test { - use futures_channel::mpsc::unbounded; - use futures_util::sink::SinkExt; - use futures_util::stream::StreamExt; + use futures::channel::mpsc::unbounded; + use futures::sink::SinkExt; + use futures::stream::StreamExt; use crate::sync::{Arc, Barrier}; use crate::task; From 929027796e92dc0ec227ec881b40decb82efedbf Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 7 Nov 2019 03:06:38 +0100 Subject: [PATCH 172/707] hide future::Flatten Signed-off-by: Yoshua Wuyts --- src/future/future/flatten.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs index 7b0561429..0e831442c 100644 --- a/src/future/future/flatten.rs +++ b/src/future/future/flatten.rs @@ -18,7 +18,7 @@ enum State { } impl FlattenFuture { - pub fn new(future: Fut1) -> FlattenFuture { + pub(crate) fn new(future: Fut1) -> FlattenFuture { FlattenFuture { state: State::First(future), } From eb1ef3f4e4459fc4ac9ca95a661eb98a46e7cb12 Mon Sep 17 00:00:00 2001 From: Abhishek C Sharma Date: Thu, 7 Nov 2019 19:19:05 +0000 Subject: [PATCH 173/707] Minor documentation fix for race and try_race (#473) --- src/future/future/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 3a666e8a2..d7bd75be7 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -187,11 +187,9 @@ extension_trait! { 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 macro consumes all futures passed, and once a future is + Note that this function consumes all futures passed, and once a future is completed, all other futures are dropped. - This macro is only usable inside of async functions, closures, and blocks. - # Examples ``` From f8e82564d9851f6d43980e87d302d98c246e9f0c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 7 Nov 2019 21:46:58 +0000 Subject: [PATCH 174/707] Rename stream_extend to extend (#464) * Rename stream_extend to extend * Remove Extend from prelude * Add stream::extend() --- src/collections/binary_heap/extend.rs | 6 +-- src/collections/binary_heap/from_stream.rs | 4 +- src/collections/btree_map/extend.rs | 6 +-- src/collections/btree_map/from_stream.rs | 4 +- src/collections/btree_set/extend.rs | 6 +-- src/collections/btree_set/from_stream.rs | 4 +- src/collections/hash_map/extend.rs | 6 +-- src/collections/hash_map/from_stream.rs | 4 +- src/collections/hash_set/extend.rs | 6 +-- src/collections/hash_set/from_stream.rs | 4 +- src/collections/linked_list/extend.rs | 6 +-- src/collections/linked_list/from_stream.rs | 4 +- src/collections/vec_deque/extend.rs | 6 +-- src/collections/vec_deque/from_stream.rs | 4 +- src/path/pathbuf.rs | 8 ++-- src/stream/extend.rs | 52 +++++++++++++++------- src/stream/from_stream.rs | 5 +-- src/stream/mod.rs | 2 +- src/string/extend.rs | 22 ++++----- src/string/from_stream.rs | 12 ++--- src/unit/extend.rs | 17 +++++++ src/unit/mod.rs | 1 + src/vec/extend.rs | 6 +-- src/vec/from_stream.rs | 4 +- 24 files changed, 119 insertions(+), 80 deletions(-) create mode 100644 src/unit/extend.rs diff --git a/src/collections/binary_heap/extend.rs b/src/collections/binary_heap/extend.rs index 4503fe393..439bf139e 100644 --- a/src/collections/binary_heap/extend.rs +++ b/src/collections/binary_heap/extend.rs @@ -2,10 +2,10 @@ use std::collections::BinaryHeap; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for BinaryHeap { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for BinaryHeap { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index c8e44e93a..99bca2095 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -1,7 +1,7 @@ use std::collections::BinaryHeap; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for BinaryHeap { #[inline] @@ -17,7 +17,7 @@ impl FromStream for BinaryHeap { pin_utils::pin_mut!(stream); let mut out = BinaryHeap::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/btree_map/extend.rs b/src/collections/btree_map/extend.rs index ae02c4248..19d306ffe 100644 --- a/src/collections/btree_map/extend.rs +++ b/src/collections/btree_map/extend.rs @@ -2,10 +2,10 @@ use std::collections::BTreeMap; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend<(K, V)> for BTreeMap { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend<(K, V)> for BTreeMap { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index bd80c0697..cc944029b 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for BTreeMap { #[inline] @@ -17,7 +17,7 @@ impl FromStream<(K, V)> for BTreeMap { pin_utils::pin_mut!(stream); let mut out = BTreeMap::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/btree_set/extend.rs b/src/collections/btree_set/extend.rs index ccf033783..422640b15 100644 --- a/src/collections/btree_set/extend.rs +++ b/src/collections/btree_set/extend.rs @@ -2,10 +2,10 @@ use std::collections::BTreeSet; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for BTreeSet { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for BTreeSet { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index bd2a744ba..6c88a8d40 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for BTreeSet { #[inline] @@ -17,7 +17,7 @@ impl FromStream for BTreeSet { pin_utils::pin_mut!(stream); let mut out = BTreeSet::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/hash_map/extend.rs b/src/collections/hash_map/extend.rs index c34bb9ed3..0f4ce0c6e 100644 --- a/src/collections/hash_map/extend.rs +++ b/src/collections/hash_map/extend.rs @@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash}; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend<(K, V)> for HashMap +impl stream::Extend<(K, V)> for HashMap where K: Eq + Hash, H: BuildHasher + Default, { - fn stream_extend<'a, S: IntoStream + 'a>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index 2b7bbc9b7..c3445e154 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for HashMap where @@ -22,7 +22,7 @@ where pin_utils::pin_mut!(stream); let mut out = HashMap::with_hasher(Default::default()); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/hash_set/extend.rs b/src/collections/hash_set/extend.rs index 123e844e2..ba872b438 100644 --- a/src/collections/hash_set/extend.rs +++ b/src/collections/hash_set/extend.rs @@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash}; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for HashSet +impl stream::Extend for HashSet where T: Eq + Hash, H: BuildHasher + Default, { - fn stream_extend<'a, S: IntoStream + 'a>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index 42447fef0..8afc0db5a 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for HashSet where @@ -22,7 +22,7 @@ where pin_utils::pin_mut!(stream); let mut out = HashSet::with_hasher(Default::default()); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/linked_list/extend.rs b/src/collections/linked_list/extend.rs index 63a1a97c3..b0dff009d 100644 --- a/src/collections/linked_list/extend.rs +++ b/src/collections/linked_list/extend.rs @@ -2,10 +2,10 @@ use std::collections::LinkedList; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for LinkedList { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for LinkedList { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index 3ffe149bb..3d4c8265e 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -1,7 +1,7 @@ use std::collections::LinkedList; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for LinkedList { #[inline] @@ -17,7 +17,7 @@ impl FromStream for LinkedList { pin_utils::pin_mut!(stream); let mut out = LinkedList::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/vec_deque/extend.rs b/src/collections/vec_deque/extend.rs index 17e396ab8..dd2ddce3c 100644 --- a/src/collections/vec_deque/extend.rs +++ b/src/collections/vec_deque/extend.rs @@ -2,10 +2,10 @@ use std::collections::VecDeque; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for VecDeque { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for VecDeque { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 8903de0ea..3a5e5851c 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -1,7 +1,7 @@ use std::collections::VecDeque; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for VecDeque { #[inline] @@ -17,7 +17,7 @@ impl FromStream for VecDeque { pin_utils::pin_mut!(stream); let mut out = VecDeque::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index a90ebabb2..12f5ac393 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -6,7 +6,7 @@ use crate::path::Path; #[cfg(feature = "unstable")] use crate::prelude::*; #[cfg(feature = "unstable")] -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; /// This struct is an async version of [`std::path::PathBuf`]. /// @@ -241,8 +241,8 @@ impl AsRef for PathBuf { } #[cfg(feature = "unstable")] -impl> Extend

for PathBuf { - fn stream_extend<'a, S: IntoStream>( +impl> stream::Extend

for PathBuf { + fn extend<'a, S: IntoStream>( &'a mut self, stream: S, ) -> Pin + 'a>> @@ -275,7 +275,7 @@ impl<'b, P: AsRef + 'b> FromStream

for PathBuf { pin_utils::pin_mut!(stream); let mut out = Self::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 350418d87..5e39f1981 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::IntoStream; -/// Extend a collection with the contents of a stream. +/// Extends a collection with the contents of a stream. /// /// Streams produce a series of values asynchronously, and collections can also be thought of as a /// series of values. The `Extend` trait bridges this gap, allowing you to extend a collection @@ -17,11 +17,11 @@ use crate::stream::IntoStream; /// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; -/// use async_std::stream::{self, Extend}; +/// use async_std::stream; /// /// let mut v: Vec = vec![1, 2]; /// let s = stream::repeat(3usize).take(3); -/// v.stream_extend(s).await; +/// stream::Extend::extend(&mut v, s).await; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # @@ -31,7 +31,7 @@ use crate::stream::IntoStream; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. - fn stream_extend<'a, T: IntoStream + 'a>( + fn extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, ) -> Pin + 'a>> @@ -39,15 +39,37 @@ pub trait Extend { A: 'a; } -impl Extend<()> for () { - fn stream_extend<'a, T: IntoStream + 'a>( - &'a mut self, - stream: T, - ) -> Pin + 'a>> { - let stream = stream.into_stream(); - Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(_) = stream.next().await {} - }) - } +/// Extends a collection with the contents of a stream. +/// +/// Streams produce a series of values asynchronously, and collections can also be thought of as a +/// series of values. The [`Extend`] trait bridges this gap, allowing you to extend a collection +/// asynchronously by including the contents of that stream. When extending a collection with an +/// already existing key, that entry is updated or, in the case of collections that permit multiple +/// entries with equal keys, that entry is inserted. +/// +/// [`Extend`]: trait.Extend.html +/// +/// ## Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut v: Vec = vec![1, 2]; +/// let s = stream::repeat(3usize).take(3); +/// stream::extend(&mut v, s).await; +/// +/// assert_eq!(v, vec![1, 2, 3, 3, 3]); +/// # +/// # }) +/// ``` +pub async fn extend<'a, C, A, T>(collection: &mut C, stream: T) +where + C: Extend, + A: 'a, + T: IntoStream + 'a, +{ + Extend::extend(collection, stream).await } diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index e8707cef6..6e5200aec 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -45,8 +45,7 @@ use std::pin::Pin; /// /// ``` /// use async_std::prelude::*; -/// use async_std::stream::{Extend, FromStream, IntoStream}; -/// use async_std::stream; +/// use async_std::stream::{self, FromStream, IntoStream}; /// use std::pin::Pin; /// /// // A sample collection, that's just a wrapper over Vec @@ -76,7 +75,7 @@ use std::pin::Pin; /// let mut c = MyCollection::new(); /// /// let mut v = vec![]; -/// v.stream_extend(stream).await; +/// stream::extend(&mut v, stream).await; /// /// for i in v { /// c.add(i); diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 29c3b7f15..692c5de4f 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -332,7 +332,7 @@ cfg_unstable! { pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; - pub use extend::Extend; + pub use extend::{extend, Extend}; pub use from_stream::FromStream; pub use fused_stream::FusedStream; pub use interval::{interval, Interval}; diff --git a/src/string/extend.rs b/src/string/extend.rs index 8572cc3ce..769f1ec83 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -2,10 +2,10 @@ use std::borrow::Cow; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { @@ -17,8 +17,8 @@ impl Extend for String { } } -impl<'b> Extend<&'b char> for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl<'b> stream::Extend<&'b char> for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, //TODO: Remove the underscore when uncommenting the body of this impl _stream: S, @@ -32,8 +32,8 @@ impl<'b> Extend<&'b char> for String { } } -impl<'b> Extend<&'b str> for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl<'b> stream::Extend<&'b str> for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> @@ -44,8 +44,8 @@ impl<'b> Extend<&'b str> for String { } } -impl Extend for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { @@ -53,8 +53,8 @@ impl Extend for String { } } -impl<'b> Extend> for String { - fn stream_extend<'a, S: IntoStream> + 'a>( +impl<'b> stream::Extend> for String { + fn extend<'a, S: IntoStream> + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs index 276d13a73..e0b2da955 100644 --- a/src/string/from_stream.rs +++ b/src/string/from_stream.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for String { #[inline] @@ -17,7 +17,7 @@ impl FromStream for String { pin_utils::pin_mut!(stream); let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -37,7 +37,7 @@ impl<'b> FromStream<&'b char> for String { pin_utils::pin_mut!(stream); let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -57,7 +57,7 @@ impl<'b> FromStream<&'b str> for String { pin_utils::pin_mut!(stream); let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -77,7 +77,7 @@ impl FromStream for String { pin_utils::pin_mut!(stream); let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -97,7 +97,7 @@ impl<'b> FromStream> for String { pin_utils::pin_mut!(stream); let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/unit/extend.rs b/src/unit/extend.rs new file mode 100644 index 000000000..27f5d4e96 --- /dev/null +++ b/src/unit/extend.rs @@ -0,0 +1,17 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{self, IntoStream}; + +impl stream::Extend<()> for () { + fn extend<'a, T: IntoStream + 'a>( + &'a mut self, + stream: T, + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(_) = stream.next().await {} + }) + } +} diff --git a/src/unit/mod.rs b/src/unit/mod.rs index cb8063d0b..bbf28987d 100644 --- a/src/unit/mod.rs +++ b/src/unit/mod.rs @@ -4,3 +4,4 @@ //! asynchronously with values of type `()`. mod from_stream; +mod extend; diff --git a/src/vec/extend.rs b/src/vec/extend.rs index ecf68c83f..302fc7a87 100644 --- a/src/vec/extend.rs +++ b/src/vec/extend.rs @@ -1,10 +1,10 @@ use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for Vec { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for Vec { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index b326b04c8..7b6cda1a7 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for Vec { #[inline] @@ -19,7 +19,7 @@ impl FromStream for Vec { pin_utils::pin_mut!(stream); let mut out = vec![]; - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } From 266e6326eba64d2f5752601b34e8bc05ddd221ba Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 7 Nov 2019 22:48:23 +0100 Subject: [PATCH 175/707] document path submodule (#467) Signed-off-by: Yoshua Wuyts --- src/path/mod.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/path/mod.rs b/src/path/mod.rs index 36b9f2f08..e9843d75e 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -2,7 +2,68 @@ //! //! This module is an async version of [`std::path`]. //! +//! This module provides two types, [`PathBuf`] and [`Path`][`Path`] (akin to [`String`] +//! and [`str`]), for working with paths abstractly. These types are thin wrappers +//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly +//! on strings according to the local platform's path syntax. +//! +//! Paths can be parsed into [`Component`]s by iterating over the structure +//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly +//! correspond to the substrings between path separators (`/` or `\`). You can +//! reconstruct an equivalent path from components with the [`push`] method on +//! [`PathBuf`]; note that the paths may differ syntactically by the +//! normalization described in the documentation for the [`components`] method. +//! //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html +//! +//! ## Simple usage +//! +//! Path manipulation includes both parsing components from slices and building +//! new owned paths. +//! +//! To parse a path, you can create a [`Path`] slice from a [`str`] +//! slice and start asking questions: +//! +//! ``` +//! use async_std::path::Path; +//! use std::ffi::OsStr; +//! +//! let path = Path::new("/tmp/foo/bar.txt"); +//! +//! let parent = path.parent(); +//! assert_eq!(parent, Some(Path::new("/tmp/foo"))); +//! +//! let file_stem = path.file_stem(); +//! assert_eq!(file_stem, Some(OsStr::new("bar"))); +//! +//! let extension = path.extension(); +//! assert_eq!(extension, Some(OsStr::new("txt"))); +//! ``` +//! +//! To build or modify paths, use [`PathBuf`]: +//! +//! ``` +//! use async_std::path::PathBuf; +//! +//! // This way works... +//! let mut path = PathBuf::from("c:\\"); +//! +//! path.push("windows"); +//! path.push("system32"); +//! +//! path.set_extension("dll"); +//! ``` +//! +//! [`Component`]: enum.Component.html +//! [`components`]: struct.Path.html#method.components +//! [`PathBuf`]: struct.PathBuf.html +//! [`Path`]: struct.Path.html +//! [`push`]: struct.PathBuf.html#method.push +//! [`String`]: https://doc.rust-lang.org/std/string/struct.String.html +//! +//! [`str`]: https://doc.rust-lang.org/std/primitive.str.html +//! [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html +//! [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html mod ancestors; mod path; From bc245033827dfe35049c9a368b53d04117ebe626 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 7 Nov 2019 22:01:36 +0000 Subject: [PATCH 176/707] Fix deadlock when all receivers are dropped (#474) * Fix deadlock when all receivers are dropped * Add a comment to explain the behavior of try_send * Disable clippy --- .github/workflows/ci.yml | 22 +++++++++++----------- src/sync/channel.rs | 14 +++++++++++--- tests/channel.rs | 8 +++++++- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06712838e..5d5e7c7e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,14 +74,14 @@ 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 + # 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 diff --git a/src/sync/channel.rs b/src/sync/channel.rs index c32628086..dc7bee13c 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -677,6 +677,14 @@ impl Channel { 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); @@ -699,8 +707,8 @@ impl Channel { // Try moving the tail. match self.tail.compare_exchange_weak( - tail, - new_tail, + tail | mark_bit, + new_tail | mark_bit, Ordering::SeqCst, Ordering::Relaxed, ) { @@ -732,7 +740,7 @@ impl Channel { // ...then the channel is full. // Check if the channel is disconnected. - if tail & self.mark_bit != 0 { + if mark_bit != 0 { return Err(TrySendError::Disconnected(msg)); } else { return Err(TrySendError::Full(msg)); diff --git a/tests/channel.rs b/tests/channel.rs index f7938904d..34bd888fc 100644 --- a/tests/channel.rs +++ b/tests/channel.rs @@ -25,7 +25,13 @@ fn smoke() { drop(s); assert_eq!(r.recv().await, None); - }) + }); + + task::block_on(async { + let (s, r) = channel(10); + drop(r); + s.send(1).await; + }); } #[test] From 84880c4d8b361ce17fb2a5e32ab20ed49e518328 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 7 Nov 2019 23:10:55 +0100 Subject: [PATCH 177/707] re-export async-attributes (#238) * re-export async-attributes Signed-off-by: Yoshua Wuyts * doc order Signed-off-by: Yoshua Wuyts * rebase + rename feature to "attributes" Signed-off-by: Yoshua Wuyts * only expose test and main Signed-off-by: Yoshua Wuyts * async-attributes 1.1.0 Signed-off-by: Yoshua Wuyts --- Cargo.toml | 4 +++- src/lib.rs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9583cdee7..54ab0f1bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,12 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] default = [] -docs = ["unstable"] +docs = ["unstable", "attributes"] unstable = ["broadcaster"] +attributes = ["async-attributes"] [dependencies] +async-attributes = { version = "1.1.0", optional = true } async-macros = "1.0.0" async-task = "1.0.0" broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } diff --git a/src/lib.rs b/src/lib.rs index 4320089ef..4863cbbcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,6 +157,19 @@ //! version = "0.99" //! features = ["unstable"] //! ``` +//! +//! Items marked with +//! attributes +//! are available only when the `attributes` Cargo feature is enabled: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "0.99" +//! features = ["attributes"] +//! ``` #![cfg(feature = "default")] #![cfg_attr(feature = "docs", feature(doc_cfg))] @@ -170,6 +183,11 @@ #[macro_use] mod utils; +#[cfg(feature = "attributes")] +#[cfg_attr(feature = "docs", doc(cfg(attributes)))] +#[doc(inline)] +pub use async_attributes::{main, test}; + pub mod fs; pub mod future; pub mod io; From f588ba6bdd8d4f1dbd9bf01a091cae32325af4a4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 7 Nov 2019 23:39:54 +0000 Subject: [PATCH 178/707] Spawn more than one blocking thread (#475) * Spawn more than 1 blocking thread * Fix a bug * Fix check when the thread is last sleeping --- src/task/spawn_blocking.rs | 110 ++++++++++++++----------------------- src/utils.rs | 8 ++- 2 files changed, 49 insertions(+), 69 deletions(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 3f4f18a17..6076d1bcd 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -1,4 +1,4 @@ -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; @@ -8,8 +8,6 @@ use once_cell::sync::Lazy; use crate::task::{JoinHandle, Task}; use crate::utils::{abort_on_panic, random}; -type Runnable = async_task::Task; - /// Spawns a blocking task. /// /// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This @@ -44,14 +42,16 @@ 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) } -const MAX_THREADS: u64 = 10_000; +type Runnable = async_task::Task; -static DYNAMIC_THREAD_COUNT: AtomicU64 = AtomicU64::new(0); +/// The number of sleeping worker threads. +static SLEEPING: AtomicUsize = AtomicUsize::new(0); struct Pool { sender: Sender, @@ -59,78 +59,52 @@ struct Pool { } static POOL: Lazy = Lazy::new(|| { - for _ in 0..2 { - thread::Builder::new() - .name("async-std/blocking".to_string()) - .spawn(|| { - abort_on_panic(|| { - for task in &POOL.receiver { - task.run(); - } - }) - }) - .expect("cannot start a thread driving blocking tasks"); - } + // Start a single worker thread waiting for the first task. + start_thread(); - // We want to use an unbuffered channel here to help - // us drive our dynamic control. In effect, the - // kernel's scheduler becomes the queue, reducing - // the number of buffers that work must flow through - // before being acted on by a core. This helps keep - // latency snappy in the overall async system by - // reducing bufferbloat. let (sender, receiver) = unbounded(); Pool { sender, receiver } }); -// Create up to MAX_THREADS dynamic blocking task worker threads. -// Dynamic threads will terminate themselves if they don't -// receive any work after between one and ten seconds. -fn maybe_create_another_blocking_thread() { - // We use a `Relaxed` atomic operation because - // it's just a heuristic, and would not lose correctness - // even if it's random. - let workers = DYNAMIC_THREAD_COUNT.load(Ordering::Relaxed); - if workers >= MAX_THREADS { - return; - } +fn start_thread() { + SLEEPING.fetch_add(1, Ordering::SeqCst); - let n_to_spawn = std::cmp::min(2 + (workers / 10), 10); + // Generate a random duration of time between 1 second and 10 seconds. If the thread doesn't + // receive the next task in this duration of time, it will stop running. + let timeout = Duration::from_millis(1000 + u64::from(random(9_000))); - for _ in 0..n_to_spawn { - // We want to avoid having all threads terminate at - // exactly the same time, causing thundering herd - // effects. We want to stagger their destruction over - // 10 seconds or so to make the costs fade into - // background noise. - // - // Generate a simple random number of milliseconds - let rand_sleep_ms = u64::from(random(10_000)); + thread::Builder::new() + .name("async-std/blocking".to_string()) + .spawn(move || { + loop { + let 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; + } + } - thread::Builder::new() - .name("async-std/blocking".to_string()) - .spawn(move || { - let wait_limit = Duration::from_millis(1000 + rand_sleep_ms); + // Stop the thread. + return; + } + }; - DYNAMIC_THREAD_COUNT.fetch_add(1, Ordering::Relaxed); - while let Ok(task) = POOL.receiver.recv_timeout(wait_limit) { - abort_on_panic(|| task.run()); + // 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(); } - DYNAMIC_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed); - }) - .expect("cannot start a dynamic thread driving blocking tasks"); - } -} -// Enqueues work, attempting to send to the threadpool in a -// nonblocking way and spinning up another worker thread if -// there is not a thread ready to accept the work. -pub(crate) fn schedule(task: Runnable) { - if let Err(err) = POOL.sender.try_send(task) { - // We were not able to send to the channel without - // blocking. Try to spin up another thread and then - // retry sending while blocking. - maybe_create_another_blocking_thread(); - POOL.sender.send(err.into_inner()).unwrap(); - } + // Run the task. + abort_on_panic(|| task.run()); + + SLEEPING.fetch_add(1, Ordering::SeqCst); + } + }) + .expect("cannot start a blocking thread"); } diff --git a/src/utils.rs b/src/utils.rs index 7758cc742..cfdf5fa95 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -25,7 +25,13 @@ pub fn random(n: u32) -> u32 { use std::num::Wrapping; thread_local! { - static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); + 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| { From 335bd34470d24870e0cbdaddadc7408105b299b9 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 8 Nov 2019 00:56:58 +0100 Subject: [PATCH 179/707] Add "std" feature flag (#476) * core feature Signed-off-by: Yoshua Wuyts * introduce std + default features Signed-off-by: Yoshua Wuyts * test std features on ci Signed-off-by: Yoshua Wuyts * finish up all features Signed-off-by: Yoshua Wuyts * Fix task_local macro * Remove crossbeam-channel and futures-timer from std * Move future::timeout() behind cfg_default --- .github/workflows/ci.yml | 6 +++ Cargo.toml | 66 ++++++++++++++++++++++----------- src/future/future/mod.rs | 8 ++-- src/future/mod.rs | 7 +++- src/io/mod.rs | 80 +++++++++++++++++++++------------------- src/lib.rs | 43 ++++++++++++++------- src/macros.rs | 52 ++++++++++++++++++++++++++ src/os/unix/mod.rs | 11 ++++-- src/os/windows/mod.rs | 4 +- src/prelude.rs | 56 +++++++++++++++------------- src/sync/waker_set.rs | 3 +- src/task/mod.rs | 64 +++++++++++++++++--------------- src/task/task_local.rs | 51 ------------------------- src/utils.rs | 31 ++++++++++++++-- 14 files changed, 287 insertions(+), 195 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d5e7c7e9..031ffc96f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,12 @@ jobs: command: check args: --features unstable --all --benches --bins --examples --tests + - name: check std only + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features --features std + - name: tests uses: actions-rs/cargo@v1 with: diff --git a/Cargo.toml b/Cargo.toml index 54ab0f1bd..7c860c03a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,32 +21,54 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -default = [] -docs = ["unstable", "attributes"] -unstable = ["broadcaster"] -attributes = ["async-attributes"] +default = [ + "std", + "async-task", + "crossbeam-channel", + "crossbeam-deque", + "futures-timer", + "kv-log-macro", + "log", + "mio", + "mio-uds", + "num_cpus", + "pin-project-lite", +] +docs = ["unstable"] +unstable = ["default", "broadcaster"] +std = [ + "async-macros", + "crossbeam-utils", + "futures-core", + "futures-io", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] [dependencies] async-attributes = { version = "1.1.0", optional = true } -async-macros = "1.0.0" -async-task = "1.0.0" +async-macros = { version = "1.0.0", optional = true } +async-task = { version = "1.0.0", optional = true } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } -crossbeam-channel = "0.3.9" -crossbeam-deque = "0.7.1" -crossbeam-utils = "0.6.6" -futures-core = "0.3.0" -futures-io = "0.3.0" -futures-timer = "1.0.2" -kv-log-macro = "1.0.4" -log = { version = "0.4.8", features = ["kv_unstable"] } -memchr = "2.2.1" -mio = "0.6.19" -mio-uds = "0.6.7" -num_cpus = "1.10.1" -once_cell = "1.2.0" -pin-project-lite = "0.1" -pin-utils = "0.1.0-alpha.4" -slab = "0.4.2" +crossbeam-channel = { version = "0.3.9", optional = true } +crossbeam-deque = { version = "0.7.1", optional = true } +crossbeam-utils = { version = "0.6.6", optional = true } +futures-core = { version = "0.3.0", optional = true } +futures-io = { version = "0.3.0", optional = true } +futures-timer = { version = "1.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.2.1", optional = true } +mio = { version = "0.6.19", optional = true } +mio-uds = { version = "0.6.7", optional = true } +num_cpus = { version = "1.10.1", optional = true } +once_cell = { version = "1.2.0", optional = true } +pin-project-lite = { version = "0.1", optional = true } +pin-utils = { version = "0.1.0-alpha.4", optional = true } +slab = { version = "0.4.2", optional = true } [dev-dependencies] femme = "1.2.0" diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index d7bd75be7..d712dc806 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -144,8 +144,8 @@ extension_trait! { /// dbg!(a.await); /// # }) /// ``` + #[cfg(all(feature = "default", feature = "unstable"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] fn delay(self, dur: Duration) -> impl Future [DelayFuture] where Self: Future + Sized @@ -167,8 +167,8 @@ extension_trait! { /// assert_eq!(future.await, 1); /// # }) /// ``` + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(any(feature = "unstable", feature = "docs"))] fn flatten(self) -> impl Future::Output as IntoFuture>::Output> [FlattenFuture::Output as IntoFuture>::Future>] where Self: Future + Sized, @@ -206,7 +206,7 @@ extension_trait! { # }); ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn race( self, @@ -252,7 +252,7 @@ extension_trait! { # Ok(()) }) } ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn try_race( self, diff --git a/src/future/mod.rs b/src/future/mod.rs index dd28f2848..4e7d79a87 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -53,13 +53,16 @@ pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; pub use ready::ready; -pub use timeout::{timeout, TimeoutError}; pub(crate) mod future; mod pending; mod poll_fn; mod ready; -mod timeout; + +cfg_default! { + pub use timeout::{timeout, TimeoutError}; + mod timeout; +} cfg_unstable! { pub use into_future::IntoFuture; diff --git a/src/io/mod.rs b/src/io/mod.rs index 93753d104..c47115931 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -269,48 +269,54 @@ //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap -#[doc(inline)] -pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; +cfg_std! { + #[doc(inline)] + pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -pub use buf_read::{BufRead, Lines}; -pub use buf_reader::BufReader; -pub use buf_writer::BufWriter; -pub use copy::copy; -pub use cursor::Cursor; -pub use empty::{empty, Empty}; -pub use read::Read; -pub use repeat::{repeat, Repeat}; -pub use seek::Seek; -pub use sink::{sink, Sink}; -pub use stderr::{stderr, Stderr}; -pub use stdin::{stdin, Stdin}; -pub use stdout::{stdout, Stdout}; -pub use timeout::timeout; -pub use write::Write; + pub use buf_read::{BufRead, Lines}; + pub use buf_reader::BufReader; + pub use buf_writer::BufWriter; + pub use copy::copy; + pub use cursor::Cursor; + pub use empty::{empty, Empty}; + pub use read::Read; + pub use repeat::{repeat, Repeat}; + pub use seek::Seek; + pub use sink::{sink, Sink}; + pub use write::Write; -// For use in the print macros. -#[doc(hidden)] -pub use stdio::{_eprint, _print}; + pub mod prelude; -pub mod prelude; + pub(crate) mod buf_read; + pub(crate) mod read; + pub(crate) mod seek; + pub(crate) mod write; -pub(crate) mod buf_read; -pub(crate) mod read; -pub(crate) mod seek; -pub(crate) mod write; + mod buf_reader; + mod buf_writer; + mod copy; + mod cursor; + mod empty; + mod repeat; + mod sink; +} + +cfg_default! { + // For use in the print macros. + #[doc(hidden)] + pub use stdio::{_eprint, _print}; -mod buf_reader; -mod buf_writer; -mod copy; -mod cursor; -mod empty; -mod repeat; -mod sink; -mod stderr; -mod stdin; -mod stdio; -mod stdout; -mod timeout; + 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! { pub use stderr::StderrLock; diff --git a/src/lib.rs b/src/lib.rs index 4863cbbcb..04ed8fb63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! # Async version of the Rust standard library //! //! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested -//! shared abstractions for the [broader Rust ecosystem][crates.io]. It offers core types, like +//! shared abstractions for the [broader Rust ecosystem][crates.io]. It offers std types, like //! [`Future`] and [`Stream`], library-defined [operations on language primitives](#primitives), //! [standard macros](#macros), [I/O] and [multithreading], among [many other things][other]. //! @@ -170,8 +170,17 @@ //! version = "0.99" //! features = ["attributes"] //! ``` +//! +//! Additionally it's possible to only use the core traits and combinators by +//! only enabling the `std` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "0.99" +//! default-features = false +//! features = ["std"] +//! ``` -#![cfg(feature = "default")] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![allow(clippy::mutex_atomic, clippy::module_inception)] @@ -188,16 +197,24 @@ mod utils; #[doc(inline)] pub use async_attributes::{main, test}; -pub mod fs; -pub mod future; -pub mod io; -pub mod net; -pub mod os; -pub mod path; -pub mod prelude; -pub mod stream; -pub mod sync; -pub mod task; +#[cfg(feature = "std")] +mod macros; + +cfg_std! { + pub mod future; + pub mod io; + pub mod os; + pub mod prelude; + pub mod stream; + pub mod sync; + pub mod task; +} + +cfg_default! { + pub mod fs; + pub mod path; + pub mod net; +} cfg_unstable! { pub mod pin; @@ -213,5 +230,3 @@ cfg_unstable! { #[doc(inline)] pub use std::{write, writeln}; } - -mod macros; diff --git a/src/macros.rs b/src/macros.rs index f932e47e8..b7811d2ea 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -165,3 +165,55 @@ macro_rules! eprintln { } ); } + +/// Declares task-local values. +/// +/// The macro wraps any number of static declarations and makes them task-local. Attributes and +/// visibility modifiers are allowed. +/// +/// Each declared value is of the accessor type [`LocalKey`]. +/// +/// [`LocalKey`]: task/struct.LocalKey.html +/// +/// # Examples +/// +/// ``` +/// # +/// use std::cell::Cell; +/// +/// use async_std::task; +/// use async_std::prelude::*; +/// +/// task_local! { +/// static VAL: Cell = Cell::new(5); +/// } +/// +/// task::block_on(async { +/// let v = VAL.with(|c| c.get()); +/// assert_eq!(v, 5); +/// }); +/// ``` +#[cfg(feature = "default")] +#[macro_export] +macro_rules! task_local { + () => (); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { + #[inline] + fn __init() -> $t { + $init + } + + $crate::task::LocalKey { + __init, + __key: ::std::sync::atomic::AtomicU32::new(0), + } + }; + ); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + $crate::task_local!($(#[$attr])* $vis static $name: $t = $init); + $crate::task_local!($($rest)*); + ); +} diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 722cfe6b8..c389d95a5 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -1,5 +1,10 @@ //! Platform-specific extensions for Unix platforms. -pub mod fs; -pub mod io; -pub mod net; +cfg_std! { + pub mod io; +} + +cfg_default! { + pub mod fs; + pub mod net; +} diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 30218f0ef..f3350007b 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -1,3 +1,5 @@ //! Platform-specific extensions for Windows. -pub mod io; +cfg_std! { + pub mod io; +} diff --git a/src/prelude.rs b/src/prelude.rs index f8583fd25..2a1fa4154 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,35 +11,39 @@ //! use async_std::prelude::*; //! ``` -#[doc(no_inline)] -pub use crate::future::Future; -#[doc(no_inline)] -pub use crate::stream::Stream; -#[doc(no_inline)] -pub use crate::task_local; +cfg_std! { + #[doc(no_inline)] + pub use crate::future::Future; + #[doc(no_inline)] + pub use crate::stream::Stream; -#[doc(inline)] -pub use crate::future::future::FutureExt; -#[doc(inline)] -pub use crate::stream::stream::StreamExt; + #[doc(inline)] + pub use crate::future::future::FutureExt; + #[doc(inline)] + pub use crate::stream::stream::StreamExt; + #[doc(no_inline)] + pub use crate::io::BufRead as _; + #[doc(no_inline)] + pub use crate::io::Read as _; + #[doc(no_inline)] + pub use crate::io::Seek as _; + #[doc(no_inline)] + pub use crate::io::Write as _; -#[doc(no_inline)] -pub use crate::io::BufRead as _; -#[doc(no_inline)] -pub use crate::io::Read as _; -#[doc(no_inline)] -pub use crate::io::Seek as _; -#[doc(no_inline)] -pub use crate::io::Write as _; + #[doc(no_inline)] + pub use crate::io::prelude::BufReadExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::ReadExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::SeekExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::WriteExt as _; +} -#[doc(no_inline)] -pub use crate::io::prelude::BufReadExt as _; -#[doc(no_inline)] -pub use crate::io::prelude::ReadExt as _; -#[doc(no_inline)] -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)] diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 7e3d8e157..5ba4cfbd9 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -7,12 +7,11 @@ use std::cell::UnsafeCell; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Waker}; use crossbeam_utils::Backoff; use slab::Slab; -use crate::task::{Context, Waker}; - /// Set when the entry list is locked. #[allow(clippy::identity_op)] const LOCKED: usize = 1 << 0; diff --git a/src/task/mod.rs b/src/task/mod.rs index 72d559a76..bcdea72c2 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -118,41 +118,45 @@ //! [`task_local!`]: ../macro.task_local.html //! [`with`]: struct.LocalKey.html#method.with -#[doc(inline)] -pub use std::task::{Context, Poll, Waker}; +cfg_std! { + #[doc(inline)] + pub use std::task::{Context, Poll, Waker}; -#[doc(inline)] -pub use async_macros::ready; + #[doc(inline)] + pub use async_macros::ready; +} -pub use block_on::block_on; -pub use builder::Builder; -pub use current::current; -pub use join_handle::JoinHandle; -pub use sleep::sleep; -pub use spawn::spawn; -pub use task::Task; -pub use task_id::TaskId; -pub use task_local::{AccessError, LocalKey}; +cfg_default! { + pub use block_on::block_on; + pub use builder::Builder; + pub use current::current; + pub use task::Task; + pub use task_id::TaskId; + pub use join_handle::JoinHandle; + pub use sleep::sleep; + pub use spawn::spawn; + pub use task_local::{AccessError, LocalKey}; -#[cfg(any(feature = "unstable", test))] -pub use spawn_blocking::spawn_blocking; -#[cfg(not(any(feature = "unstable", test)))] -pub(crate) use spawn_blocking::spawn_blocking; + use builder::Runnable; + use task_local::LocalsMap; -use builder::Runnable; -use task_local::LocalsMap; + mod block_on; + mod builder; + mod current; + mod executor; + mod join_handle; + mod sleep; + mod spawn; + mod spawn_blocking; + mod task; + mod task_id; + mod task_local; -mod block_on; -mod builder; -mod current; -mod executor; -mod join_handle; -mod sleep; -mod spawn; -mod spawn_blocking; -mod task; -mod task_id; -mod task_local; + #[cfg(any(feature = "unstable", test))] + pub use spawn_blocking::spawn_blocking; + #[cfg(not(any(feature = "unstable", test)))] + pub(crate) use spawn_blocking::spawn_blocking; +} cfg_unstable! { pub use yield_now::yield_now; diff --git a/src/task/task_local.rs b/src/task/task_local.rs index 0869cab46..72e53d72a 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -5,57 +5,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; use crate::task::Task; -/// Declares task-local values. -/// -/// The macro wraps any number of static declarations and makes them task-local. Attributes and -/// visibility modifiers are allowed. -/// -/// Each declared value is of the accessor type [`LocalKey`]. -/// -/// [`LocalKey`]: task/struct.LocalKey.html -/// -/// # Examples -/// -/// ``` -/// # -/// use std::cell::Cell; -/// -/// use async_std::task; -/// use async_std::prelude::*; -/// -/// task_local! { -/// static VAL: Cell = Cell::new(5); -/// } -/// -/// task::block_on(async { -/// let v = VAL.with(|c| c.get()); -/// assert_eq!(v, 5); -/// }); -/// ``` -#[macro_export] -macro_rules! task_local { - () => (); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( - $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { - #[inline] - fn __init() -> $t { - $init - } - - $crate::task::LocalKey { - __init, - __key: ::std::sync::atomic::AtomicU32::new(0), - } - }; - ); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - $crate::task_local!($(#[$attr])* $vis static $name: $t = $init); - $crate::task_local!($($rest)*); - ); -} - /// The key for accessing a task-local value. /// /// Every task-local value is lazily initialized on first access and destroyed when the task diff --git a/src/utils.rs b/src/utils.rs index cfdf5fa95..13dbe37d5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,7 @@ -use std::mem; - /// Calls a function and aborts if it panics. /// /// This is useful in unsafe code where we can't recover from panics. +#[cfg(feature = "default")] #[inline] pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { struct Bomb; @@ -15,11 +14,12 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { let bomb = Bomb; let t = f(); - mem::forget(bomb); + std::mem::forget(bomb); t } /// Generates a random number in `0..n`. +#[cfg(feature = "default")] pub fn random(n: u32) -> u32 { use std::cell::Cell; use std::num::Wrapping; @@ -53,6 +53,7 @@ pub fn random(n: u32) -> u32 { } /// Defers evaluation of a block of code until the end of the scope. +#[cfg(feature = "default")] #[doc(hidden)] macro_rules! defer { ($($body:tt)*) => { @@ -130,6 +131,30 @@ macro_rules! cfg_not_docs { } } +/// Declares std items. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_std { + ($($item:item)*) => { + $( + #[cfg(feature = "std")] + $item + )* + } +} + +/// Declares default items. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_default { + ($($item:item)*) => { + $( + #[cfg(feature = "default")] + $item + )* + } +} + /// Defines an extension trait for a base trait. /// /// In generated docs, the base trait will contain methods from the extension trait. In actual From fd088fea38ac84d67587e70632241e0f983e6a9e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 8 Nov 2019 01:11:35 +0100 Subject: [PATCH 180/707] 0.99.12 (#469) * 0.99.12 Signed-off-by: Yoshua Wuyts * Update changelog with latest changes --- CHANGELOG.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eda60387a..3568abf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [0.99.12] - 2019-11-07 + +[API Documentation](https://docs.rs/async-std/0.99.12/async-std) + +This patch upgrades us to `futures` 0.3, support for `async/await` on Rust +Stable, performance improvements, and brand new module-level documentation. + +## Added + +- Added `Future::flatten` as "unstable". +- Added `Future::race` as "unstable" (replaces `future::select!`). +- Added `Future::try_race` as "unstable" (replaces `future::try_select!`). +- Added `Stderr::lock` as "unstable". +- Added `Stdin::lock` as "unstable". +- Added `Stdout::lock` as "unstable". +- Added `Stream::copied` as "unstable". +- Added `Stream::eq` as "unstable". +- Added `Stream::max_by_key` as "unstable". +- Added `Stream::min` as "unstable". +- Added `Stream::ne` as "unstable". +- Added `Stream::position` as "unstable". +- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`. +- Added `TcpListener` and `TcpStream` integration tests. +- Added `stream::from_iter`. +- Added `sync::WakerSet` for internal use. +- Added an example to handle both `IP v4` and `IP v6` connections. +- Added the `default` Cargo feature. +- Added the `attributes` Cargo feature. +- Added the `std` Cargo feature. + +## Changed + +- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread. +- Fixed a bug with `Stream::merge` where sometimes it ended too soon. +- Fixed a bug with our GitHub actions setup. +- Fixed an issue where our channels could spuriously deadlock. +- Refactored the `task` module. +- Removed a deprecated GitHub action. +- Replaced `futures-preview` with `futures`. +- Replaced `lazy_static` with `once_cell`. +- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`. +- Simplified `sync::RwLock` using the internal `sync::WakerSet` type. +- Updated the `path` submodule documentation to match std. +- Updated the mod-level documentation to match std. + +## Removed + +- Removed `future::select!` (replaced by `Future::race`). +- Removed `future::try_select!` (replaced by `Future::try_race`). + # [0.99.11] - 2019-10-29 This patch introduces `async_std::sync::channel`, a novel asynchronous port of diff --git a/Cargo.toml b/Cargo.toml index 7c860c03a..48b7d0b61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.11" +version = "0.99.12" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From ab2f64cd842be1929573342ffb7a26cf7048ef8c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Fri, 8 Nov 2019 02:38:49 +0100 Subject: [PATCH 181/707] Mark extend() as unstable --- src/stream/extend.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 5e39f1981..0d26afab4 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -65,6 +65,8 @@ pub trait Extend { /// # /// # }) /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub async fn extend<'a, C, A, T>(collection: &mut C, stream: T) where C: Extend, From b14282457c9128fa86c2b98392d2cee3c48b1cdc Mon Sep 17 00:00:00 2001 From: "Abhishek C. Sharma" Date: Fri, 8 Nov 2019 11:19:52 +0530 Subject: [PATCH 182/707] Add Future::join and Future::try_join --- src/future/future/join.rs | 62 ++++++++++++++++++++++++ src/future/future/mod.rs | 88 +++++++++++++++++++++++++++++++++++ src/future/future/try_join.rs | 72 ++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 src/future/future/join.rs create mode 100644 src/future/future/try_join.rs diff --git a/src/future/future/join.rs b/src/future/future/join.rs new file mode 100644 index 000000000..90ea3237a --- /dev/null +++ b/src/future/future/join.rs @@ -0,0 +1,62 @@ +use std::pin::Pin; + +use async_macros::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct Join + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl Join +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for Join +where + L: Future, + R: Future, +{ + type Output = (L::Output, R::Output); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + let mut right = this.right; + + if Future::poll(Pin::new(&mut left), cx).is_ready() { + if right.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); + } + } + + if Future::poll(Pin::new(&mut right), cx).is_ready() { + if left.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); + } + } + + Poll::Pending + } +} diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index d712dc806..729ace7c9 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -3,6 +3,8 @@ cfg_unstable! { mod flatten; mod race; mod try_race; + mod join; + mod try_join; use std::time::Duration; @@ -11,6 +13,8 @@ cfg_unstable! { use crate::future::IntoFuture; use race::Race; use try_race::TryRace; + use join::Join; + use try_join::TryJoin; } extension_trait! { @@ -264,6 +268,90 @@ extension_trait! { { 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(2u8); + + let f = a.join(b); + assert_eq!(f.await, (1u8, 2u8)); + # }); + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn join( + self, + other: F + ) -> impl Future::Output, ::Output)> [Join] + where + Self: std::future::Future + Sized, + F: std::future::Future::Output>, + { + 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::(2u8)); + + let f = a.try_join(b); + assert_eq!(f.await, Ok((1u8, 2u8))); + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_join( + self, + other: F + ) -> impl Future> [TryJoin] + where + Self: std::future::Future> + Sized, + F: std::future::Future::Output>, + { + TryJoin::new(self, other) + } } impl Future for Box { diff --git a/src/future/future/try_join.rs b/src/future/future/try_join.rs new file mode 100644 index 000000000..58ae6d620 --- /dev/null +++ b/src/future/future/try_join.rs @@ -0,0 +1,72 @@ +use std::pin::Pin; + +use async_macros::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct TryJoin + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl TryJoin +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for TryJoin +where + L: Future>, + R: Future, +{ + type Output = Result<(T, T), E>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + let mut right = this.right; + + if Future::poll(Pin::new(&mut left), cx).is_ready() { + if left.as_ref().output().unwrap().is_err() { + return Poll::Ready(Err(left.take().unwrap().err().unwrap())); + } else if right.as_ref().output().is_some() { + return Poll::Ready(Ok(( + left.take().unwrap().ok().unwrap(), + right.take().unwrap().ok().unwrap(), + ))); + } + } + + if Future::poll(Pin::new(&mut right), cx).is_ready() { + if right.as_ref().output().unwrap().is_err() { + return Poll::Ready(Err(right.take().unwrap().err().unwrap())); + } else if left.as_ref().output().is_some() { + return Poll::Ready(Ok(( + left.take().unwrap().ok().unwrap(), + right.take().unwrap().ok().unwrap(), + ))); + } + } + + Poll::Pending + } +} From fb19ebde1729e42c7fd90f63149d1732d5bb2610 Mon Sep 17 00:00:00 2001 From: laizy Date: Fri, 8 Nov 2019 16:56:55 +0800 Subject: [PATCH 183/707] add `Sync` constraint for RwLock to prevent memory unsafety (#479) --- src/sync/rwlock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 65b9dcad2..e042bbd2a 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -57,7 +57,7 @@ pub struct RwLock { } unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} +unsafe impl Sync for RwLock {} impl RwLock { /// Creates a new reader-writer lock. From e74e246bbb673957068f30d901ac307708515a5c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 8 Nov 2019 11:15:47 +0100 Subject: [PATCH 184/707] fix attributes feature Signed-off-by: Yoshua Wuyts --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 48b7d0b61..bdbeafa6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,9 @@ default = [ "num_cpus", "pin-project-lite", ] -docs = ["unstable"] +docs = ["attributes", "unstable"] unstable = ["default", "broadcaster"] +attributes = ["async-attributes"] std = [ "async-macros", "crossbeam-utils", From 8f3366072f615d14030ba997b3fa30612e5d3044 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 8 Nov 2019 22:08:53 +1100 Subject: [PATCH 185/707] Add FromIterator and Extend trait implementations for PathBuf --- src/path/mod.rs | 4 ++++ src/path/pathbuf.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/path/mod.rs b/src/path/mod.rs index e9843d75e..059e6050b 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -52,6 +52,10 @@ //! path.push("system32"); //! //! path.set_extension("dll"); +//! +//! // ... but push is best used if you don't know everything up +//! // front. If you do, this way is better: +//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect(); //! ``` //! //! [`Component`]: enum.Component.html diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 12f5ac393..a19011155 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -280,3 +280,17 @@ impl<'b, P: AsRef + 'b> FromStream

for PathBuf { }) } } + +impl> std::iter::FromIterator

for PathBuf { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +impl> std::iter::Extend

for PathBuf { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } +} From d2d63348c70401b5cb80843d19ff3e21074b1641 Mon Sep 17 00:00:00 2001 From: nasa Date: Fri, 8 Nov 2019 22:05:53 +0900 Subject: [PATCH 186/707] Stable and beta add to CI (#482) * Add stable and beta * Add benches --- .github/workflows/ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 031ffc96f..ca83f9b40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [nightly] + rust: [nightly, beta, stable] steps: - uses: actions/checkout@master @@ -38,7 +38,13 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --features unstable --all --benches --bins --examples --tests + args: --features unstable --all --bins --examples --tests + - name: check bench + uses: actions-rs/cargo@v1 + if: matrix.rust == 'nightly' + with: + command: check + args: --benches - name: check std only uses: actions-rs/cargo@v1 From 4a78f731b7510af1a5f7bfedfb78852cb40a0248 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Sat, 9 Nov 2019 00:00:03 +0100 Subject: [PATCH 187/707] fix: stream::take_while (#485) When the predicate is false, the stream should be ended. --- src/stream/stream/mod.rs | 5 +++-- src/stream/stream/take_while.rs | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f9a9e3a41..d0d693508 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -303,6 +303,7 @@ extension_trait! { # # }) } + ``` "#] fn take_while

(self, predicate: P) -> TakeWhile where @@ -397,9 +398,9 @@ extension_trait! { use async_std::stream; let v = stream::from_iter(vec![&1, &2, &3]); - + let mut v_cloned = v.cloned(); - + assert_eq!(v_cloned.next().await, Some(1)); assert_eq!(v_cloned.next().await, Some(2)); assert_eq!(v_cloned.next().await, Some(3)); diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 35978b47c..9b2945fdb 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -45,10 +45,12 @@ where let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), - Some(_) => { - cx.waker().wake_by_ref(); - Poll::Pending + Some(v) => { + if (this.predicate)(&v) { + Poll::Ready(Some(v)) + } else { + Poll::Ready(None) + } } None => Poll::Ready(None), } From f04b6f6fe98dd62d6301da30d7f80ec4250cdd99 Mon Sep 17 00:00:00 2001 From: "Abhishek C. Sharma" Date: Sat, 9 Nov 2019 13:09:47 +0530 Subject: [PATCH 188/707] Change module level docs for future to refer to join and try_join functions instead of macros --- src/future/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index 4e7d79a87..fa5a7abde 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -8,17 +8,17 @@ //! operations converts multiple future into a single future that returns the //! first output. //! -//! For operating on futures the following macros can be used: +//! For operating on futures the following functions can be used: //! //! | Name | Return signature | When does it return? | //! | --- | --- | --- | -//! | [`future::join!`] | `(T1, T2)` | Wait for all to complete +//! | [`Future::join`] | `(T1, T2)` | Wait for all to complete //! | [`Future::race`] | `T` | Return on first value //! //! ## Fallible Futures Concurrency //! //! For operating on futures that return `Result` additional `try_` variants of -//! the macros mentioned before can be used. These macros are aware of `Result`, +//! the functions mentioned before can be used. These functions are aware of `Result`, //! and will behave slightly differently from their base variants. //! //! In the case of `try_join`, if any of the futures returns `Err` all @@ -30,19 +30,19 @@ //! means `try_race` will keep going until any one of the futures returns //! `Ok`, or _all_ futures have returned `Err`. //! -//! However sometimes it can be useful to use the base variants of the macros +//! However sometimes it can be useful to use the base variants of the functions //! even on futures that return `Result`. Here is an overview of operations that //! work on `Result`, and their respective semantics: //! //! | Name | Return signature | When does it return? | //! | --- | --- | --- | -//! | [`future::join!`] | `(Result, Result)` | Wait for all to complete -//! | [`future::try_join!`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete +//! | [`Future::join`] | `(Result, Result)` | Wait for all to complete +//! | [`Future::try_join`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete //! | [`Future::race`] | `Result` | Return on first value //! | [`Future::try_race`] | `Result` | Return on first `Ok`, reject on last Err //! -//! [`future::join!`]: macro.join.html -//! [`future::try_join!`]: macro.try_join.html +//! [`Future::join`]: trait.Future.html#method.join +//! [`Future::try_join`]: trait.Future.html#method.try_join //! [`Future::race`]: trait.Future.html#method.race //! [`Future::try_race`]: trait.Future.html#method.try_race From 548733e5d5f748664e73f61334c239e51af0b8b8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 9 Nov 2019 11:22:09 +0100 Subject: [PATCH 189/707] Cleanup stream traits (#487) * Cleanup stream traits * Fix docs --- src/collections/binary_heap/from_stream.rs | 8 +-- src/collections/btree_map/from_stream.rs | 8 +-- src/collections/btree_set/from_stream.rs | 8 +-- src/collections/hash_map/from_stream.rs | 8 +-- src/collections/hash_set/from_stream.rs | 8 +-- src/collections/linked_list/from_stream.rs | 8 +-- src/collections/vec_deque/from_stream.rs | 8 +-- src/fs/file_type.rs | 6 +- src/fs/metadata.rs | 16 ++--- src/fs/permissions.rs | 4 +- src/fs/read_dir.rs | 2 +- src/future/future/delay.rs | 2 +- src/future/future/flatten.rs | 7 +-- src/future/into_future.rs | 2 +- src/future/pending.rs | 2 +- src/future/poll_fn.rs | 2 +- src/future/timeout.rs | 2 +- src/io/buf_read/read_line.rs | 2 +- src/io/buf_read/read_until.rs | 2 +- src/io/buf_writer.rs | 3 +- src/io/copy.rs | 2 +- src/io/read/read.rs | 2 +- src/io/read/read_exact.rs | 2 +- src/io/read/read_to_end.rs | 2 +- src/io/read/read_to_string.rs | 2 +- src/io/read/read_vectored.rs | 2 +- src/io/seek/seek.rs | 2 +- src/io/stderr.rs | 2 +- src/io/stdin.rs | 3 +- src/io/stdout.rs | 2 +- src/io/timeout.rs | 2 +- src/io/write/flush.rs | 2 +- src/io/write/write.rs | 2 +- src/io/write/write_all.rs | 2 +- src/io/write/write_fmt.rs | 2 +- src/io/write/write_vectored.rs | 2 +- src/net/addr.rs | 2 +- src/net/tcp/listener.rs | 3 +- src/option/from_stream.rs | 7 +-- src/os/unix/net/listener.rs | 3 +- src/path/pathbuf.rs | 28 ++++----- src/prelude.rs | 2 +- src/result/from_stream.rs | 7 +-- src/stream/extend.rs | 5 +- src/stream/from_fn.rs | 2 +- src/stream/from_iter.rs | 9 ++- src/stream/from_stream.rs | 40 ++++++++----- src/stream/interval.rs | 4 +- src/stream/product.rs | 2 +- src/stream/repeat_with.rs | 2 +- src/stream/stream/all.rs | 2 +- src/stream/stream/any.rs | 2 +- src/stream/stream/cmp.rs | 2 +- src/stream/stream/eq.rs | 2 +- src/stream/stream/find.rs | 2 +- src/stream/stream/find_map.rs | 2 +- src/stream/stream/fold.rs | 2 +- src/stream/stream/for_each.rs | 2 +- src/stream/stream/ge.rs | 2 +- src/stream/stream/gt.rs | 2 +- src/stream/stream/last.rs | 2 +- src/stream/stream/le.rs | 2 +- src/stream/stream/lt.rs | 2 +- src/stream/stream/max_by.rs | 2 +- src/stream/stream/max_by_key.rs | 2 +- src/stream/stream/merge.rs | 1 - src/stream/stream/min.rs | 2 +- src/stream/stream/min_by.rs | 2 +- src/stream/stream/min_by_key.rs | 2 +- src/stream/stream/mod.rs | 2 +- src/stream/stream/ne.rs | 2 +- src/stream/stream/next.rs | 2 +- src/stream/stream/nth.rs | 2 +- src/stream/stream/partial_cmp.rs | 2 +- src/stream/stream/position.rs | 2 +- src/stream/stream/timeout.rs | 2 +- src/stream/stream/try_fold.rs | 2 +- src/stream/stream/try_for_each.rs | 2 +- src/stream/sum.rs | 10 ++-- src/string/extend.rs | 69 +++++++++++++++------- src/string/from_stream.rs | 36 ++++------- src/sync/mutex.rs | 2 +- src/sync/rwlock.rs | 2 +- src/task/block_on.rs | 2 +- src/task/builder.rs | 2 +- src/task/spawn.rs | 3 +- src/task/yield_now.rs | 2 +- src/unit/from_stream.rs | 7 +-- src/vec/from_stream.rs | 31 ++++------ 89 files changed, 229 insertions(+), 249 deletions(-) diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index 99bca2095..148a57f40 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -1,16 +1,14 @@ use std::collections::BinaryHeap; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for BinaryHeap { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index cc944029b..e0653ab5b 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -1,16 +1,14 @@ use std::collections::BTreeMap; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for BTreeMap { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index 6c88a8d40..c4197df44 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -1,16 +1,14 @@ use std::collections::BTreeSet; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for BTreeSet { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index c3445e154..bf47d8e79 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for HashMap @@ -10,12 +11,9 @@ where H: BuildHasher + Default, { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index 8afc0db5a..69b38538e 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for HashSet @@ -10,12 +11,9 @@ where H: BuildHasher + Default, { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index 3d4c8265e..122624711 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -1,16 +1,14 @@ use std::collections::LinkedList; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for LinkedList { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 3a5e5851c..767ec068e 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -1,16 +1,14 @@ use std::collections::VecDeque; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for VecDeque { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs index 11f47d1c7..d7ce25704 100644 --- a/src/fs/file_type.rs +++ b/src/fs/file_type.rs @@ -40,7 +40,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_dir(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this file type represents a regular file. @@ -60,7 +60,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_file(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this file type represents a symbolic link. @@ -78,7 +78,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_symlink(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 1383ec21f..2948016e2 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -78,7 +78,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn file_type(&self) -> FileType { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this metadata is for a regular directory. @@ -98,7 +98,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_dir(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this metadata is for a regular file. @@ -118,7 +118,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_file(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the file size in bytes. @@ -136,7 +136,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn len(&self) -> u64 { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the permissions from this metadata. @@ -154,7 +154,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn permissions(&self) -> Permissions { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the last modification time. @@ -177,7 +177,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn modified(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the last access time. @@ -200,7 +200,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn accessed(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the creation time. @@ -223,7 +223,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn created(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs index 1339a7c7d..50aa45cd1 100644 --- a/src/fs/permissions.rs +++ b/src/fs/permissions.rs @@ -29,7 +29,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn readonly(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Configures the read-only flag. @@ -50,7 +50,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn set_readonly(&mut self, readonly: bool) { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index fe12fa6d1..5e51065b6 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,7 +1,7 @@ use std::pin::Pin; +use std::future::Future; use crate::fs::DirEntry; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::stream::Stream; diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index d672541ee..45658f45c 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -1,10 +1,10 @@ use std::pin::Pin; use std::time::Duration; +use std::future::Future; use futures_timer::Delay; use pin_project_lite::pin_project; -use crate::future::Future; use crate::task::{Context, Poll}; pin_project! { diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs index 0e831442c..1d3164405 100644 --- a/src/future/future/flatten.rs +++ b/src/future/future/flatten.rs @@ -1,9 +1,8 @@ -use futures_core::ready; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; -use crate::future::IntoFuture; -use crate::task::{Context, Poll}; +use crate::future::{IntoFuture}; +use crate::task::{ready, Context, Poll}; #[derive(Debug)] pub struct FlattenFuture { diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 42839a203..a9a818757 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -1,4 +1,4 @@ -use crate::future::Future; +use std::future::Future; /// Convert a type into a `Future`. /// diff --git a/src/future/pending.rs b/src/future/pending.rs index 2138a3012..39f7cab14 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::task::{Context, Poll}; /// Never resolves to a value. diff --git a/src/future/poll_fn.rs b/src/future/poll_fn.rs index a808f97f3..194526400 100644 --- a/src/future/poll_fn.rs +++ b/src/future/poll_fn.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::task::{Context, Poll}; /// Creates a new future wrapping around a function returning [`Poll`]. diff --git a/src/future/timeout.rs b/src/future/timeout.rs index c745d7322..ff87ae4f7 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -2,11 +2,11 @@ use std::error::Error; use std::fmt; use std::pin::Pin; use std::time::Duration; +use std::future::Future; use futures_timer::Delay; use pin_project_lite::pin_project; -use crate::future::Future; use crate::task::{Context, Poll}; /// Awaits a future or times out after a duration of time. diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs index 04c61c1d7..b66079bc8 100644 --- a/src/io/buf_read/read_line.rs +++ b/src/io/buf_read/read_line.rs @@ -1,9 +1,9 @@ use std::mem; use std::pin::Pin; use std::str; +use std::future::Future; use super::read_until_internal; -use crate::future::Future; use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; diff --git a/src/io/buf_read/read_until.rs b/src/io/buf_read/read_until.rs index 72385abbe..bda1eee90 100644 --- a/src/io/buf_read/read_until.rs +++ b/src/io/buf_read/read_until.rs @@ -1,7 +1,7 @@ use std::pin::Pin; +use std::future::Future; use super::read_until_internal; -use crate::future::Future; use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 6327ca71e..8fa9eba4e 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -1,12 +1,11 @@ use std::fmt; use std::pin::Pin; -use futures_core::ready; use pin_project_lite::pin_project; use crate::io::write::WriteExt; use crate::io::{self, Seek, SeekFrom, Write}; -use crate::task::{Context, Poll}; +use crate::task::{Context, Poll, ready}; const DEFAULT_CAPACITY: usize = 8 * 1024; diff --git a/src/io/copy.rs b/src/io/copy.rs index 098df8d70..753f5e349 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,8 +1,8 @@ use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read.rs b/src/io/read/read.rs index c46aff66a..0ba04e571 100644 --- a/src/io/read/read.rs +++ b/src/io/read/read.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs index c970f431f..71cf004da 100644 --- a/src/io/read/read_exact.rs +++ b/src/io/read/read_exact.rs @@ -1,7 +1,7 @@ use std::mem; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_to_end.rs b/src/io/read/read_to_end.rs index d76ee8c42..c7c47b8f6 100644 --- a/src/io/read/read_to_end.rs +++ b/src/io/read/read_to_end.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs index 5f1a4d256..5b74389e6 100644 --- a/src/io/read/read_to_string.rs +++ b/src/io/read/read_to_string.rs @@ -1,9 +1,9 @@ use std::mem; use std::pin::Pin; use std::str; +use std::future::Future; use super::read_to_end_internal; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs index 8e52ba2dc..b4c61b8f6 100644 --- a/src/io/read/read_vectored.rs +++ b/src/io/read/read_vectored.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, IoSliceMut, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/seek/seek.rs b/src/io/seek/seek.rs index 65743be2d..74aa93e5b 100644 --- a/src/io/seek/seek.rs +++ b/src/io/seek/seek.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Seek, SeekFrom}; use crate::task::{Context, Poll}; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 8bd2180a4..5ff8a029d 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::sync::Mutex; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; diff --git a/src/io/stdin.rs b/src/io/stdin.rs index bd6580c2f..167ea2dd3 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,7 +1,8 @@ use std::pin::Pin; use std::sync::Mutex; +use std::future::Future; -use crate::future::{self, Future}; +use crate::future; use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; diff --git a/src/io/stdout.rs b/src/io/stdout.rs index c0565aa55..1711c090e 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::sync::Mutex; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; diff --git a/src/io/timeout.rs b/src/io/timeout.rs index ec3668ea5..6e22dbf26 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -1,11 +1,11 @@ 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 crate::future::Future; use crate::io; /// Awaits an I/O future or times out after a duration of time. diff --git a/src/io/write/flush.rs b/src/io/write/flush.rs index 08f2b5b4e..590c12e89 100644 --- a/src/io/write/flush.rs +++ b/src/io/write/flush.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write.rs b/src/io/write/write.rs index da6e5c50d..8f13091dc 100644 --- a/src/io/write/write.rs +++ b/src/io/write/write.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs index 5353f7ab8..f04c55d65 100644 --- a/src/io/write/write_all.rs +++ b/src/io/write/write_all.rs @@ -1,7 +1,7 @@ use std::mem; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index ad3e94ade..ec7847f22 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs index 5f8492b77..cdb49d429 100644 --- a/src/io/write/write_vectored.rs +++ b/src/io/write/write_vectored.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, IoSlice, Write}; use crate::task::{Context, Poll}; diff --git a/src/net/addr.rs b/src/net/addr.rs index c17ff4985..2769dd5e2 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -2,8 +2,8 @@ use std::mem; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 6fd27f0f1..f98bbdc75 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,7 +1,8 @@ use std::net::SocketAddr; +use std::future::Future; use std::pin::Pin; -use crate::future::{self, Future}; +use crate::future; use crate::io; use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index e4da809eb..d2d53b600 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -11,12 +11,9 @@ where /// elements are taken, and `None` is returned. Should no `None` /// occur, a container with the values of each `Option` is returned. #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 9bd86d381..675ef481f 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -2,12 +2,13 @@ use std::fmt; use std::pin::Pin; +use std::future::Future; use mio_uds; use super::SocketAddr; use super::UnixStream; -use crate::future::{self, Future}; +use crate::future; use crate::io; use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 12f5ac393..c95103f22 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -242,36 +242,30 @@ impl AsRef for PathBuf { #[cfg(feature = "unstable")] impl> stream::Extend

for PathBuf { - fn extend<'a, S: IntoStream>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> - where - P: 'a, - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); - //TODO: This can be added back in once this issue is resolved: - // https://github.com/rust-lang/rust/issues/58234 - //self.reserve(stream.size_hint().0); + Box::pin(async move { + pin_utils::pin_mut!(stream); - Box::pin(stream.for_each(move |item| self.push(item.as_ref()))) + while let Some(item) = stream.next().await { + self.push(item.as_ref()); + } + }) } } #[cfg(feature = "unstable")] impl<'b, P: AsRef + 'b> FromStream

for PathBuf { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { - let stream = stream.into_stream(); - + ) -> Pin + 'a>> { Box::pin(async move { + let stream = stream.into_stream(); pin_utils::pin_mut!(stream); let mut out = Self::new(); diff --git a/src/prelude.rs b/src/prelude.rs index 2a1fa4154..a2a14a182 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,7 +13,7 @@ cfg_std! { #[doc(no_inline)] - pub use crate::future::Future; + pub use std::future::Future; #[doc(no_inline)] pub use crate::stream::Stream; diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 6033eb973..9296797d1 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -11,12 +11,9 @@ where /// elements are taken, and the `Err` is returned. Should no `Err` /// occur, a container with the values of each `Result` is returned. #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 0d26afab4..7bdfd343d 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -34,9 +34,7 @@ pub trait Extend { fn extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, - ) -> Pin + 'a>> - where - A: 'a; + ) -> Pin + 'a>>; } /// Extends a collection with the contents of a stream. @@ -70,7 +68,6 @@ pub trait Extend { pub async fn extend<'a, C, A, T>(collection: &mut C, stream: T) where C: Extend, - A: 'a, T: IntoStream + 'a, { Extend::extend(collection, stream).await diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 5260d8788..a28a90147 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index 5fd216dbf..a83afcebd 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -18,8 +18,11 @@ pin_project! { } } +/// Converts an iterator into a stream. +/// /// # Examples -///``` +/// +/// ``` /// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; @@ -34,8 +37,8 @@ pin_project! { /// assert_eq!(s.next().await, None); /// # /// # }) -///```` -pub fn from_iter(iter: I) -> FromIter<::IntoIter> { +/// ``` +pub fn from_iter(iter: I) -> FromIter { FromIter { iter: iter.into_iter(), } diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 6e5200aec..67b9b3df0 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -1,7 +1,8 @@ -use super::IntoStream; - +use std::future::Future; use std::pin::Pin; +use crate::stream::IntoStream; + /// Conversion from a `Stream`. /// /// By implementing `FromStream` for a type, you define how it will be created from a stream. @@ -15,21 +16,24 @@ use std::pin::Pin; /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::prelude::*; -/// use async_std::stream::{self, FromStream}; +/// # +/// use async_std::prelude::*; +/// use async_std::stream::{self, FromStream}; /// -/// let five_fives = stream::repeat(5).take(5); +/// let five_fives = stream::repeat(5).take(5); /// -/// let v = Vec::from_stream(five_fives).await; +/// let v = Vec::from_stream(five_fives).await; /// -/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// # /// # Ok(()) }) } /// ``` /// /// Using `collect` to implicitly use `FromStream` /// -///``` +/// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # /// use async_std::prelude::*; /// use async_std::stream; /// let five_fives = stream::repeat(5).take(5); @@ -39,7 +43,7 @@ use std::pin::Pin; /// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// # /// # Ok(()) }) } -///``` +/// ``` /// /// Implementing `FromStream` for your type: /// @@ -68,7 +72,7 @@ use std::pin::Pin; /// impl FromStream for MyCollection { /// fn from_stream<'a, S: IntoStream + 'a>( /// stream: S, -/// ) -> Pin + 'a>> { +/// ) -> Pin + 'a>> { /// let stream = stream.into_stream(); /// /// Box::pin(async move { @@ -86,6 +90,7 @@ use std::pin::Pin; /// } /// /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # /// // Now we can make a new stream... /// let stream = stream::repeat(5).take(5); /// @@ -100,6 +105,7 @@ use std::pin::Pin; /// let c: MyCollection = stream.collect().await; /// /// assert_eq!(c.0, vec![5, 5, 5, 5, 5]); +/// # /// # Ok(()) }) } ///``` /// @@ -115,17 +121,19 @@ pub trait FromStream { /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// use async_std::prelude::*; - /// use async_std::stream::{self, FromStream}; + /// # + /// use async_std::prelude::*; + /// use async_std::stream::{self, FromStream}; /// - /// let five_fives = stream::repeat(5).take(5); + /// let five_fives = stream::repeat(5).take(5); /// - /// let v = Vec::from_stream(five_fives).await; + /// let v = Vec::from_stream(five_fives).await; /// - /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// # /// # Ok(()) }) } /// ``` fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>>; + ) -> Pin + 'a>>; } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index efec43626..b0df71419 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -2,10 +2,10 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; -use futures_core::future::Future; -use futures_core::stream::Stream; use futures_timer::Delay; +use crate::prelude::*; + /// Creates a new stream that yields at a set interval. /// /// The stream first yields after `dur`, and continues to yield every diff --git a/src/stream/product.rs b/src/stream/product.rs index 71b14c704..2f5bf4c39 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; /// Trait to represent types that can be created by multiplying the elements of a stream. diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index de53bc9da..6e7cfa3bf 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 3b65fc764..7b84abe36 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index a23adf4bf..c7fc76652 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index df08e9db9..19437e709 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index 5343c1a0c..addcfa2eb 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -1,9 +1,9 @@ use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index 93624c03e..b37a6a460 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index dfcf92d66..16993fc5f 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; #[doc(hidden)] diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 5b0eb124b..66a767294 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 4696529be..6383ed785 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index 3dc6031c5..f9012697b 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 513ca764a..81e95a1ab 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index eba01e5c2..188da3c8f 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,8 +1,8 @@ use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index af7270056..35b04bfb0 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index 524f26893..86c31295c 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index a626b284a..cfba9b93d 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -1,9 +1,9 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index b3fb65bf4..b5bc7e0c2 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -1,9 +1,9 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index f3505acae..fe3579e9d 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -1,7 +1,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; -use futures_core::Stream; use pin_project_lite::pin_project; use crate::prelude::*; diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs index b4a8c7c16..4ce52be9b 100644 --- a/src/stream/stream/min.rs +++ b/src/stream/stream/min.rs @@ -1,10 +1,10 @@ use std::cmp::{Ord, Ordering}; use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index ab12aa05b..fc332c265 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,9 +1,9 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 6557f2296..8179fb312 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -1,9 +1,9 @@ use std::cmp::Ordering; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d0d693508..76130bbb2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -112,10 +112,10 @@ use std::cmp::Ordering; use std::marker::PhantomData; cfg_unstable! { + use std::future::Future; use std::pin::Pin; use std::time::Duration; - use crate::future::Future; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs index ffeaca815..ec11d1fdc 100644 --- a/src/stream/stream/ne.rs +++ b/src/stream/stream/ne.rs @@ -1,9 +1,9 @@ use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs index de75f5e93..23abb0b49 100644 --- a/src/stream/stream/next.rs +++ b/src/stream/stream/next.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index e7e042a95..711287a38 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; +use std::future::Future; -use crate::future::Future; use crate::stream::Stream; #[doc(hidden)] diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index e30c6ea8d..6bc28f78c 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; +use std::future::Future; use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs index 3cd5b84cd..3d8f40d50 100644 --- a/src/stream/stream/position.rs +++ b/src/stream/stream/position.rs @@ -1,8 +1,8 @@ use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 3c14811fb..636e406e9 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -2,11 +2,11 @@ use std::error::Error; use std::fmt; use std::pin::Pin; use std::time::Duration; +use std::future::Future; use futures_timer::Delay; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index 80392e108..bf92ff51a 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::future::Future; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 6b66d2ea1..36198f9e5 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,9 +1,9 @@ +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/sum.rs b/src/stream/sum.rs index dadbc3471..9607bafa7 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,6 +1,6 @@ +use std::future::Future; use std::pin::Pin; -use crate::future::Future; use crate::stream::Stream; /// Trait to represent types that can be created by summing up a stream. @@ -23,9 +23,9 @@ pub trait Sum: Sized { S: Stream + 'a; } -use core::ops::Add; -use core::num::Wrapping; use crate::stream::stream::StreamExt; +use core::num::Wrapping; +use core::ops::Add; macro_rules! integer_sum { (@impls $zero: expr, $($a:ty)*) => ($( @@ -75,5 +75,5 @@ macro_rules! float_sum { ); } -integer_sum!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } -float_sum!{ f32 f64 } +integer_sum! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_sum! { f32 f64 } diff --git a/src/string/extend.rs b/src/string/extend.rs index 769f1ec83..55bec0c55 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -10,25 +10,32 @@ impl stream::Extend for String { stream: S, ) -> Pin + 'a>> { let stream = stream.into_stream(); - self.reserve(stream.size_hint().0); - Box::pin(stream.for_each(move |c| self.push(c))) + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push(item); + } + }) } } impl<'b> stream::Extend<&'b char> for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, - //TODO: Remove the underscore when uncommenting the body of this impl - _stream: S, - ) -> Pin + 'a>> - where - 'b: 'a, - { - //TODO: This can be uncommented when `copied` is added to Stream/StreamExt - //Box::pin(stream.into_stream().copied()) - unimplemented!() + stream: S, + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push(*item); + } + }) } } @@ -36,11 +43,16 @@ impl<'b> stream::Extend<&'b str> for String { fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> - where - 'b: 'a, - { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(s))) + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(item); + } + }) } } @@ -49,7 +61,15 @@ impl stream::Extend for String { &'a mut self, stream: S, ) -> Pin + 'a>> { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s))) + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(&item); + } + }) } } @@ -57,10 +77,15 @@ impl<'b> stream::Extend> for String { fn extend<'a, S: IntoStream> + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> - where - 'b: 'a, - { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s))) + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(&item); + } + }) } } diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs index e0b2da955..eb6818c15 100644 --- a/src/string/from_stream.rs +++ b/src/string/from_stream.rs @@ -1,16 +1,14 @@ use std::borrow::Cow; use std::pin::Pin; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -25,12 +23,9 @@ impl FromStream for String { impl<'b> FromStream<&'b char> for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -45,12 +40,9 @@ impl<'b> FromStream<&'b char> for String { impl<'b> FromStream<&'b str> for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -65,12 +57,9 @@ impl<'b> FromStream<&'b str> for String { impl FromStream for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -85,12 +74,9 @@ impl FromStream for String { impl<'b> FromStream> for String { #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 52c389852..5bec6a23e 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -3,8 +3,8 @@ use std::fmt; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; +use std::future::Future; -use crate::future::Future; use crate::sync::WakerSet; use crate::task::{Context, Poll}; diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index e042bbd2a..bc3f64052 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -4,9 +4,9 @@ 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::future::Future; use crate::sync::WakerSet; use crate::task::{Context, Poll}; diff --git a/src/task/block_on.rs b/src/task/block_on.rs index d320cb2fd..f61a22b6a 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::future::Future; use std::mem::{self, ManuallyDrop}; use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; @@ -8,7 +9,6 @@ use crossbeam_utils::sync::Parker; use kv_log_macro::trace; use log::log_enabled; -use crate::future::Future; use crate::task::{Context, Poll, Task, Waker}; /// Spawns a task and blocks the current thread on its result. diff --git a/src/task/builder.rs b/src/task/builder.rs index a61d7859c..afd4c2c1c 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,7 +1,7 @@ use kv_log_macro::trace; use log::log_enabled; +use std::future::Future; -use crate::future::Future; use crate::io; use crate::task::executor; use crate::task::{JoinHandle, Task}; diff --git a/src/task/spawn.rs b/src/task/spawn.rs index da2957b07..f81a483d2 100644 --- a/src/task/spawn.rs +++ b/src/task/spawn.rs @@ -1,4 +1,5 @@ -use crate::future::Future; +use std::future::Future; + use crate::task::{Builder, JoinHandle}; /// Spawns a task. diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 5cd0ce5ba..03f83e2e9 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::task::{Context, Poll}; /// Cooperatively gives up a timeslice to the task scheduler. diff --git a/src/unit/from_stream.rs b/src/unit/from_stream.rs index a238982da..da216e22c 100644 --- a/src/unit/from_stream.rs +++ b/src/unit/from_stream.rs @@ -5,12 +5,9 @@ use crate::stream::{FromStream, IntoStream}; impl FromStream<()> for () { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { Box::pin(stream.into_stream().for_each(|_| ())) } } diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index 7b6cda1a7..cdd4767dc 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -3,13 +3,14 @@ use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; +use crate::prelude::*; use crate::stream::{self, FromStream, IntoStream}; impl FromStream for Vec { #[inline] fn from_stream<'a, S: IntoStream>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a>> where ::IntoStream: 'a, { @@ -27,12 +28,9 @@ impl FromStream for Vec { impl<'b, T: Clone> FromStream for Cow<'b, [T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -45,12 +43,9 @@ impl<'b, T: Clone> FromStream for Cow<'b, [T]> { impl FromStream for Box<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -63,12 +58,9 @@ impl FromStream for Box<[T]> { impl FromStream for Rc<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { @@ -81,12 +73,9 @@ impl FromStream for Rc<[T]> { impl FromStream for Arc<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> - where - ::IntoStream: 'a, - { + ) -> Pin + 'a>> { let stream = stream.into_stream(); Box::pin(async move { From d8e52c100241245489c08bfca2011d050ea4eb4e Mon Sep 17 00:00:00 2001 From: Jayson Reis Date: Sat, 9 Nov 2019 12:11:08 +0100 Subject: [PATCH 190/707] Implement FromStr for PathBuf This makes PathBuf compatible with std version as you can simply call let path: PathBuf = FromStr::from_str(s).unwrap() --- src/path/pathbuf.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index c95103f22..aae5de647 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,6 +1,7 @@ use std::ffi::{OsStr, OsString}; #[cfg(feature = "unstable")] use std::pin::Pin; +use std::str::FromStr; use crate::path::Path; #[cfg(feature = "unstable")] @@ -228,6 +229,14 @@ impl From<&str> for PathBuf { } } +impl FromStr for PathBuf { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(std::path::PathBuf::from(s).into()) + } +} + impl AsRef for PathBuf { fn as_ref(&self) -> &Path { Path::new(&self.inner) From 74882c119de5985e170f66e9e48513f024d1fb60 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 9 Nov 2019 12:44:14 +0100 Subject: [PATCH 191/707] check attributes Signed-off-by: Yoshua Wuyts --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 031ffc96f..cf8dee6a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,12 @@ jobs: command: check args: --no-default-features --features std + - name: check attributes + uses: actions-rs/cargo@v1 + with: + command: check + args: --features attributes + - name: tests uses: actions-rs/cargo@v1 with: From 9e185f1c3e710d1bc26ea80d86dd03e134e3f98b Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 9 Nov 2019 16:59:35 +0100 Subject: [PATCH 192/707] Unstable feature: copy takes arguments by value (#471) * Unstable feature: copy takes arguments by value * Fix feature flags --- src/io/copy.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/io/copy.rs b/src/io/copy.rs index 753f5e349..8ec3c1af1 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -43,6 +43,7 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` +#[cfg(any(feature = "docs", not(feature = "unstable")))] pub async fn copy(reader: &mut R, writer: &mut W) -> io::Result where R: Read + Unpin + ?Sized, @@ -91,3 +92,90 @@ where }; future.await } + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `reader` and then +/// write it into `writer` in a streaming fashion until `reader` +/// returns EOF. +/// +/// On success, the total number of bytes that were copied from +/// `reader` to `writer` is returned. +/// +/// If you’re wanting to copy the contents of one file to another and you’re +/// working with filesystem paths, see the [`fs::copy`] function. +/// +/// This function is an async version of [`std::io::copy`]. +/// +/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html +/// [`fs::copy`]: ../fs/fn.copy.html +/// +/// # Errors +/// +/// This function will return an error immediately if any call to `read` or +/// `write` returns an error. All instances of `ErrorKind::Interrupted` are +/// handled by this function and the underlying operation is retried. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// +/// let mut reader: &[u8] = b"hello"; +/// let mut writer = io::stdout(); +/// +/// io::copy(&mut reader, &mut writer).await?; +/// # +/// # Ok(()) }) } +/// ``` +#[cfg(all(feature = "unstable", not(feature = "docs")))] +pub async fn copy(reader: R, writer: W) -> io::Result +where + R: Read + Unpin, + W: Write + Unpin, +{ + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, + } + } + + impl Future for CopyFuture + where + R: BufRead, + W: Write + Unpin, + { + type Output = io::Result; + + 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() { + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); + } + + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *this.amt += i as u64; + this.reader.as_mut().consume(i); + } + } + } + + let future = CopyFuture { + reader: BufReader::new(reader), + writer, + amt: 0, + }; + future.await +} From ac1042a9cac6cb6ae2ca7d66981d442cfcc6286a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 9 Nov 2019 17:02:17 +0100 Subject: [PATCH 193/707] note on Stream::merge ordering (#491) * note on Stream::merge ordering Signed-off-by: Yoshua Wuyts * Update src/stream/stream/mod.rs --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 76130bbb2..8ea1459f2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1655,8 +1655,8 @@ extension_trait! { #[doc = r#" Combines multiple streams into a single stream of all their outputs. - Items are yielded as soon as they're received, and the stream continues yield until both - streams have been exhausted. + 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. # Examples From 96d35607427eb5b1e3f4d9ae93dbe4aa561ef681 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 9 Nov 2019 17:02:48 +0100 Subject: [PATCH 194/707] remove future::*join macros (#492) Signed-off-by: Yoshua Wuyts --- src/future/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index fa5a7abde..8b51a6a5f 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -46,9 +46,6 @@ //! [`Future::race`]: trait.Future.html#method.race //! [`Future::try_race`]: trait.Future.html#method.try_race -#[doc(inline)] -pub use async_macros::{join, try_join}; - pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; From d4f38e783fd18ece0fe691dcee78a8a41aa55d1a Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 9 Nov 2019 17:26:19 +0100 Subject: [PATCH 195/707] Cleanup future module --- src/future/future/delay.rs | 4 ++-- src/future/future/flatten.rs | 7 ++++--- src/future/future/mod.rs | 15 +++++++++------ src/future/future/race.rs | 2 +- src/future/into_future.rs | 1 - src/future/pending.rs | 2 +- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs index 45658f45c..641084ff3 100644 --- a/src/future/future/delay.rs +++ b/src/future/future/delay.rs @@ -1,6 +1,6 @@ +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; @@ -9,7 +9,7 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] - #[derive(Debug)] + #[allow(missing_debug_implementations)] pub struct DelayFuture { #[pin] future: F, diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs index 1d3164405..a07b140cc 100644 --- a/src/future/future/flatten.rs +++ b/src/future/future/flatten.rs @@ -1,10 +1,11 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; -use crate::future::{IntoFuture}; +use crate::future::IntoFuture; use crate::task::{ready, Context, Poll}; -#[derive(Debug)] +#[doc(hidden)] +#[allow(missing_debug_implementations)] pub struct FlattenFuture { state: State, } diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 729ace7c9..5fdaf4b1a 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -152,7 +152,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn delay(self, dur: Duration) -> impl Future [DelayFuture] where - Self: Future + Sized + Self: Sized, { DelayFuture::new(self, dur) } @@ -173,10 +173,13 @@ extension_trait! { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flatten(self) -> impl Future::Output as IntoFuture>::Output> [FlattenFuture::Output as IntoFuture>::Future>] + fn flatten( + self, + ) -> impl Future::Output> + [FlattenFuture::Future>] where - Self: Future + Sized, - ::Output: IntoFuture + Self: Sized, + ::Output: IntoFuture, { FlattenFuture::new(self) } @@ -214,7 +217,7 @@ extension_trait! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn race( self, - other: F + other: F, ) -> impl Future::Output> [Race] where Self: std::future::Future + Sized, @@ -258,7 +261,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn try_race( + fn try_race( self, other: F ) -> impl Future::Output> [TryRace] diff --git a/src/future/future/race.rs b/src/future/future/race.rs index 2fd604a73..ed034f05e 100644 --- a/src/future/future/race.rs +++ b/src/future/future/race.rs @@ -1,10 +1,10 @@ +use std::future::Future; use std::pin::Pin; use async_macros::MaybeDone; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; -use std::future::Future; pin_project! { #[allow(missing_docs)] diff --git a/src/future/into_future.rs b/src/future/into_future.rs index a9a818757..8e5e5e046 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -45,7 +45,6 @@ pub trait IntoFuture { impl IntoFuture for T { type Output = T::Output; - type Future = T; fn into_future(self) -> Self::Future { diff --git a/src/future/pending.rs b/src/future/pending.rs index 39f7cab14..968972b51 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -1,6 +1,6 @@ +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; -use std::future::Future; use crate::task::{Context, Poll}; From 122e87364bef463c5afbf7681ec3ce35a3a7f577 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 9 Nov 2019 23:07:26 +0100 Subject: [PATCH 196/707] Remove cache padding in channels --- src/sync/channel.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index dc7bee13c..392c8511f 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; -use crossbeam_utils::{Backoff, CachePadded}; +use crossbeam_utils::Backoff; use crate::stream::Stream; use crate::sync::WakerSet; @@ -577,7 +577,7 @@ struct Channel { /// represent the lap. The mark bit in the head is always zero. /// /// Messages are popped from the head of the channel. - head: CachePadded, + head: AtomicUsize, /// The tail of the channel. /// @@ -586,7 +586,7 @@ struct Channel { /// represent the lap. The mark bit indicates that the channel is disconnected. /// /// Messages are pushed into the tail of the channel. - tail: CachePadded, + tail: AtomicUsize, /// The buffer holding slots. buffer: *mut Slot, @@ -660,8 +660,8 @@ impl Channel { cap, one_lap, mark_bit, - head: CachePadded::new(AtomicUsize::new(head)), - tail: CachePadded::new(AtomicUsize::new(tail)), + head: AtomicUsize::new(head), + tail: AtomicUsize::new(tail), send_wakers: WakerSet::new(), recv_wakers: WakerSet::new(), stream_wakers: WakerSet::new(), From 417b548692cd250806b9bd8acb3e5917581a7eab Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 00:31:33 +0100 Subject: [PATCH 197/707] Cleanup path module (#497) * Cleanup path module * Derive clone for PathBuf and remove unused import * impl AsRef for std::path::PathBuf * Fix a doc comment --- src/path/components.rs | 82 +++++++++ src/path/iter.rs | 82 +++++++++ src/path/mod.rs | 23 +-- src/path/path.rs | 367 +++++++++++++++++++++++++++++++++-------- src/path/pathbuf.rs | 145 ++++++++++++---- 5 files changed, 575 insertions(+), 124 deletions(-) create mode 100644 src/path/components.rs create mode 100644 src/path/iter.rs diff --git a/src/path/components.rs b/src/path/components.rs new file mode 100644 index 000000000..51649c55c --- /dev/null +++ b/src/path/components.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::iter::FusedIterator; + +use crate::path::{Component, Path}; + +/// An iterator over the [`Component`]s of a [`Path`]. +/// +/// This `struct` is created by the [`components`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// +/// for component in path.components() { +/// println!("{:?}", component); +/// } +/// ``` +/// +/// [`Component`]: enum.Component.html +/// [`components`]: struct.Path.html#method.components +/// [`Path`]: struct.Path.html +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Components<'a> { + pub(crate) inner: std::path::Components<'a>, +} + +impl<'a> Components<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut components = Path::new("/tmp/foo/bar.txt").components(); + /// components.next(); + /// components.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path().into() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + self.inner.next() + } +} + +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + self.inner.next_back() + } +} + +impl FusedIterator for Components<'_> {} + +impl AsRef for Component<'_> { + fn as_ref(&self) -> &Path { + self.as_os_str().as_ref() + } +} diff --git a/src/path/iter.rs b/src/path/iter.rs new file mode 100644 index 000000000..b4061003b --- /dev/null +++ b/src/path/iter.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::fmt; +use std::iter::FusedIterator; + +use crate::path::{Component, Components, Path}; + +/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. +/// +/// This `struct` is created by the [`iter`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`Component`]: enum.Component.html +/// [`iter`]: struct.Path.html#method.iter +/// [`OsStr`]: ../../std/ffi/struct.OsStr.html +/// [`Path`]: struct.Path.html +#[derive(Clone)] +pub struct Iter<'a> { + pub(crate) inner: Components<'a>, +} + +impl<'a> Iter<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); + /// iter.next(); + /// iter.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +impl fmt::Debug for Iter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_tuple("Iter") + .field(&DebugHelper(self.as_path())) + .finish() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +impl FusedIterator for Iter<'_> {} diff --git a/src/path/mod.rs b/src/path/mod.rs index 059e6050b..7ce9b62d6 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -70,25 +70,18 @@ //! [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html mod ancestors; +mod components; +mod iter; mod path; mod pathbuf; -// Structs re-export #[doc(inline)] -pub use std::path::{Components, Display, Iter, PrefixComponent, StripPrefixError}; +pub use std::path::{ + is_separator, Component, Display, Prefix, PrefixComponent, StripPrefixError, MAIN_SEPARATOR, +}; -// Enums re-export -#[doc(inline)] -pub use std::path::{Component, Prefix}; - -// Constants re-export -#[doc(inline)] -pub use std::path::MAIN_SEPARATOR; - -// Functions re-export -#[doc(inline)] -pub use std::path::is_separator; - -use ancestors::Ancestors; +pub use ancestors::Ancestors; +pub use components::Components; +pub use iter::Iter; pub use path::Path; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index 43adbbbce..dfe9426a4 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,12 +1,51 @@ -use std::ffi::OsStr; +use std::borrow::{Cow, ToOwned}; +use std::cmp::Ordering; +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}; -use crate::{fs, io}; +/// A slice of a path. +/// /// This struct is an async version of [`std::path::Path`]. /// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` on Unix and by either +/// `/` or `\` on Windows), extracting the file name, determining whether the path +/// is absolute, and so on. +/// +/// This is an *unsized* type, meaning that it must always be used behind a +/// pointer like `&` or `Box`. For an owned version of this type, +/// see [`PathBuf`]. +/// +/// [`PathBuf`]: struct.PathBuf.html /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html -#[derive(Debug, PartialEq)] +/// +/// More details about the overall approach can be found in +/// the [module documentation](index.html). +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use std::ffi::OsStr; +/// +/// // Note: this example does work on Windows +/// let path = Path::new("./foo/bar.txt"); +/// +/// let parent = path.parent(); +/// assert_eq!(parent, Some(Path::new("./foo"))); +/// +/// let file_stem = path.file_stem(); +/// assert_eq!(file_stem, Some(OsStr::new("bar"))); +/// +/// let extension = path.extension(); +/// assert_eq!(extension, Some(OsStr::new("txt"))); +/// ``` +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Path { inner: std::path::Path, } @@ -38,14 +77,25 @@ impl Path { unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } - /// Yields the underlying [`OsStr`] slice. + /// Returns the underlying [`OsStr`] slice. /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use async_std::path::Path; + /// + /// let os_str = Path::new("foo.txt").as_os_str(); + /// assert_eq!(os_str, OsStr::new("foo.txt")); + /// ``` pub fn as_os_str(&self) -> &OsStr { self.inner.as_os_str() } - /// Yields a [`&str`] slice if the `Path` is valid unicode. + /// Returns a [`&str`] slice if the `Path` is valid unicode. /// /// This conversion may entail doing a check for UTF-8 validity. /// Note that validation is performed because non-UTF-8 strings are @@ -86,7 +136,7 @@ impl Path { /// /// Had `path` contained invalid unicode, the `to_string_lossy` call might /// have returned `"fo�.txt"`. - pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -106,14 +156,16 @@ impl Path { PathBuf::from(self.inner.to_path_buf()) } - /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// Returns `true` if the `Path` is absolute, i.e. if it is independent of /// the current directory. /// /// * On Unix, a path is absolute if it starts with the root, so - /// `is_absolute` and [`has_root`] are equivalent. + /// `is_absolute` and [`has_root`] are equivalent. /// /// * On Windows, a path is absolute if it has a prefix and starts with the - /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// + /// [`has_root`]: #method.has_root /// /// # Examples /// @@ -122,16 +174,16 @@ impl Path { /// /// assert!(!Path::new("foo.txt").is_absolute()); /// ``` - /// - /// [`has_root`]: #method.has_root pub fn is_absolute(&self) -> bool { self.inner.is_absolute() } - /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// Returns `true` if the `Path` is relative, i.e. not absolute. /// /// See [`is_absolute`]'s documentation for more details. /// + /// [`is_absolute`]: #method.is_absolute + /// /// # Examples /// /// ``` @@ -139,8 +191,6 @@ impl Path { /// /// assert!(Path::new("foo.txt").is_relative()); /// ``` - /// - /// [`is_absolute`]: #method.is_absolute pub fn is_relative(&self) -> bool { self.inner.is_relative() } @@ -150,9 +200,9 @@ impl Path { /// * On Unix, a path has a root if it begins with `/`. /// /// * On Windows, a path has a root if it: - /// * has no prefix and begins with a separator, e.g., `\windows` - /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` - /// * has any non-disk prefix, e.g., `\\server\share` + /// * has no prefix and begins with a separator, e.g. `\windows` + /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g. `\\server\share` /// /// # Examples /// @@ -196,6 +246,9 @@ impl Path { /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, /// namely `&self`. /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html + /// [`parent`]: struct.Path.html#method.parent + /// /// # Examples /// /// ``` @@ -207,9 +260,6 @@ impl Path { /// assert_eq!(ancestors.next(), Some(Path::new("/").into())); /// assert_eq!(ancestors.next(), None); /// ``` - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html - /// [`parent`]: struct.Path.html#method.parent pub fn ancestors(&self) -> Ancestors<'_> { Ancestors { next: Some(&self) } } @@ -226,9 +276,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::Path; /// use std::ffi::OsStr; /// + /// use async_std::path::Path; + /// /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); @@ -240,7 +291,7 @@ impl Path { self.inner.file_name() } - /// Returns a path that, when joined onto `base`, yields `self`. + /// Returns a path that becomes `self` when joined onto `base`. /// /// # Errors /// @@ -314,15 +365,15 @@ impl Path { self.inner.ends_with(child.as_ref()) } - /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// Extracts the stem (non-extension) portion of [`file_name`]. /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// /// The stem is: /// - /// * [`None`], if there is no file name; - /// * The entire file name if there is no embedded `.`; - /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * The entire file name if there is no embedded `.` + /// * The entire file name if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name before the final `.` /// /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None @@ -340,16 +391,16 @@ impl Path { self.inner.file_stem() } - /// Extracts the extension of [`self.file_name`], if possible. + /// Extracts the extension of [`file_name`], if possible. /// /// The extension is: /// - /// * [`None`], if there is no file name; - /// * [`None`], if there is no embedded `.`; - /// * [`None`], if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * [`None`], if there is no embedded `.` + /// * [`None`], if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name after the final `.` /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples @@ -442,24 +493,27 @@ impl Path { /// and `a/b/../c` are distinct, to account for the possibility that `b` /// is a symbolic link (so its parent isn't `a`). /// + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + /// /// # Examples /// /// ``` - /// use async_std::path::{Path, Component}; /// use std::ffi::OsStr; /// + /// use async_std::path::{Path, Component}; + /// /// let mut components = Path::new("/tmp/foo.txt").components(); /// /// assert_eq!(components.next(), Some(Component::RootDir)); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); - /// assert_eq!(components.next(), None) + /// assert_eq!(components.next(), None); /// ``` - /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir pub fn components(&self) -> Components<'_> { - self.inner.components() + Components { + inner: self.inner.components(), + } } /// Produces an iterator over the path's components viewed as [`OsStr`] @@ -474,9 +528,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::{self, Path}; /// use std::ffi::OsStr; /// + /// use async_std::path::{self, Path}; + /// /// let mut it = Path::new("/tmp/foo.txt").iter(); /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); @@ -484,7 +539,9 @@ impl Path { /// assert_eq!(it.next(), None) /// ``` pub fn iter(&self) -> Iter<'_> { - self.inner.iter() + Iter { + inner: self.components(), + } } /// Returns an object that implements [`Display`] for safely printing paths @@ -505,7 +562,7 @@ impl Path { self.inner.display() } - /// Queries the file system to get information about a file, directory, etc. + /// Reads the metadata of a file or directory. /// /// This function will traverse symbolic links to query information about the /// destination file. @@ -522,7 +579,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.metadata().await.expect("metadata call failed"); + /// let metadata = path.metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } @@ -531,7 +588,7 @@ impl Path { fs::metadata(self).await } - /// Queries the metadata about a file without following symlinks. + /// Reads the metadata of a file or directory without following symbolic links. /// /// This is an alias to [`fs::symlink_metadata`]. /// @@ -545,7 +602,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// let metadata = path.symlink_metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } @@ -554,8 +611,10 @@ impl Path { fs::symlink_metadata(self).await } - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. + /// Returns the canonical form of a path. + /// + /// The returned path is in absolute form with all intermediate components normalized and + /// symbolic links resolved. /// /// This is an alias to [`fs::canonicalize`]. /// @@ -569,7 +628,7 @@ impl Path { /// use async_std::path::{Path, PathBuf}; /// /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().await.unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// assert_eq!(path.canonicalize().await?, PathBuf::from("/foo/test/bar.rs")); /// # /// # Ok(()) }) } /// ``` @@ -591,7 +650,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/laputa/sky_castle.rs"); - /// let path_link = path.read_link().await.expect("read_link call failed"); + /// let path_link = path.read_link().await?; /// # /// # Ok(()) }) } /// ``` @@ -599,9 +658,9 @@ impl Path { fs::read_link(self).await } - /// Returns an iterator over the entries within a directory. + /// Returns a stream over the entries within a directory. /// - /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New + /// The stream will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New /// errors may be encountered after an iterator is initially constructed. /// /// This is an alias to [`fs::read_dir`]. @@ -620,7 +679,8 @@ impl Path { /// use async_std::prelude::*; /// /// let path = Path::new("/laputa"); - /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); + /// let mut dir = fs::read_dir(&path).await?; + /// /// while let Some(res) = dir.next().await { /// let entry = res?; /// println!("{}", entry.file_name().to_string_lossy()); @@ -710,6 +770,7 @@ impl Path { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::path::Path; + /// /// assert_eq!(Path::new("./is_a_directory/").is_dir().await, true); /// assert_eq!(Path::new("a_file.txt").is_dir().await, false); /// # @@ -736,6 +797,15 @@ impl Path { /// /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html /// [`PathBuf`]: struct.PathBuf.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path: Box = Path::new("foo.txt").into(); + /// let path_buf = path.into_path_buf(); + /// ``` pub fn into_path_buf(self: Box) -> PathBuf { let rw = Box::into_raw(self) as *mut std::path::Path; let inner = unsafe { Box::from_raw(rw) }; @@ -743,27 +813,42 @@ impl Path { } } -impl<'a> From<&'a std::path::Path> for &'a Path { - fn from(path: &'a std::path::Path) -> &'a Path { - &Path::new(path.as_os_str()) +impl From<&Path> for Box { + fn from(path: &Path) -> Box { + let boxed: Box = path.inner.into(); + let rw = Box::into_raw(boxed) as *mut Path; + unsafe { Box::from_raw(rw) } } } -impl<'a> Into<&'a std::path::Path> for &'a Path { - fn into(self) -> &'a std::path::Path { - std::path::Path::new(&self.inner) +impl From<&Path> for Arc { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } } } -impl AsRef for Path { - fn as_ref(&self) -> &std::path::Path { - self.into() +impl From<&Path> for Rc { + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } } } -impl AsRef for std::path::Path { - fn as_ref(&self) -> &Path { - self.into() +impl ToOwned for Path { + type Owned = PathBuf; + + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &OsStr { + self.inner.as_ref() } } @@ -773,13 +858,26 @@ impl AsRef for Path { } } -impl AsRef for Path { - fn as_ref(&self) -> &OsStr { - self.inner.as_ref() +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) } } -impl AsRef for OsStr { +impl<'a> From<&'a Path> for Cow<'a, Path> { + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +impl AsRef for Cow<'_, OsStr> { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +impl AsRef for OsString { fn as_ref(&self) -> &Path { Path::new(self) } @@ -797,16 +895,139 @@ impl AsRef for String { } } -impl AsRef for std::path::PathBuf { +impl AsRef for PathBuf { fn as_ref(&self) -> &Path { - Path::new(self) + self } } -impl std::borrow::ToOwned for Path { - type Owned = PathBuf; +impl<'a> IntoIterator for &'a PathBuf { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; - fn to_owned(&self) -> PathBuf { - self.to_path_buf() + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +impl<'a> IntoIterator for &'a Path { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(PathBuf, Path); +impl_cmp!(PathBuf, &'a Path); +impl_cmp!(Cow<'a, Path>, Path); +impl_cmp!(Cow<'a, Path>, &'b Path); +impl_cmp!(Cow<'a, Path>, PathBuf); + +macro_rules! impl_cmp_os_str { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self.as_ref(), other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self.as_ref(), other) + } + } + }; +} + +impl_cmp_os_str!(PathBuf, OsStr); +impl_cmp_os_str!(PathBuf, &'a OsStr); +impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>); +impl_cmp_os_str!(PathBuf, OsString); +impl_cmp_os_str!(Path, OsStr); +impl_cmp_os_str!(Path, &'a OsStr); +impl_cmp_os_str!(Path, Cow<'a, OsStr>); +impl_cmp_os_str!(Path, OsString); +impl_cmp_os_str!(&'a Path, OsStr); +impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>); +impl_cmp_os_str!(&'a Path, OsString); + +impl<'a> From<&'a std::path::Path> for &'a Path { + fn from(path: &'a std::path::Path) -> &'a Path { + &Path::new(path.as_os_str()) + } +} + +impl<'a> Into<&'a std::path::Path> for &'a Path { + fn into(self) -> &'a std::path::Path { + std::path::Path::new(&self.inner) + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &std::path::Path { + self.into() + } +} + +impl AsRef for std::path::Path { + fn as_ref(&self) -> &Path { + self.into() + } +} + +impl AsRef for std::path::PathBuf { + fn as_ref(&self) -> &Path { + let p: &std::path::Path = self.as_ref(); + p.into() } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index cf2e6bfe1..56a63a47e 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,7 +1,12 @@ +use std::borrow::{Borrow, Cow}; use std::ffi::{OsStr, OsString}; +use std::iter::{self, FromIterator}; +use std::ops::Deref; #[cfg(feature = "unstable")] use std::pin::Pin; +use std::rc::Rc; use std::str::FromStr; +use std::sync::Arc; use crate::path::Path; #[cfg(feature = "unstable")] @@ -12,7 +17,7 @@ use crate::stream::{self, FromStream, IntoStream}; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html -#[derive(Debug, PartialEq, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -98,9 +103,9 @@ impl PathBuf { /// let mut p = PathBuf::from("/test/test.rs"); /// /// p.pop(); - /// assert_eq!(Path::new("/test"), p.as_ref()); + /// assert_eq!(Path::new("/test"), p); /// p.pop(); - /// assert_eq!(Path::new("/"), p.as_ref()); + /// assert_eq!(Path::new("/"), p); /// ``` pub fn pop(&mut self) -> bool { self.inner.pop() @@ -165,7 +170,7 @@ impl PathBuf { self.inner.set_extension(extension) } - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// Consumes the `PathBuf`, returning its internal [`OsString`] storage. /// /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html /// @@ -191,41 +196,46 @@ impl PathBuf { } } -impl std::ops::Deref for PathBuf { - type Target = Path; - - fn deref(&self) -> &Path { - self.as_ref() +impl From> for PathBuf { + fn from(boxed: Box) -> PathBuf { + boxed.into_path_buf() } } -impl std::borrow::Borrow for PathBuf { - fn borrow(&self) -> &Path { - &**self +impl From for Box { + fn from(p: PathBuf) -> Box { + p.into_boxed_path() } } -impl From for PathBuf { - fn from(path: std::path::PathBuf) -> PathBuf { - PathBuf { inner: path } +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_path_buf().into_boxed_path() } } -impl Into for PathBuf { - fn into(self) -> std::path::PathBuf { - self.inner +impl> From<&T> for PathBuf { + fn from(s: &T) -> PathBuf { + PathBuf::from(s.as_ref().to_os_string()) } } impl From for PathBuf { - fn from(path: OsString) -> PathBuf { - std::path::PathBuf::from(path).into() + fn from(s: OsString) -> PathBuf { + PathBuf { inner: s.into() } } } -impl From<&str> for PathBuf { - fn from(path: &str) -> PathBuf { - std::path::PathBuf::from(path).into() +impl From for OsString { + fn from(path_buf: PathBuf) -> OsString { + path_buf.inner.into() + } +} + +impl From for PathBuf { + fn from(s: String) -> PathBuf { + PathBuf::from(OsString::from(s)) } } @@ -233,18 +243,77 @@ impl FromStr for PathBuf { type Err = core::convert::Infallible; fn from_str(s: &str) -> Result { - Ok(std::path::PathBuf::from(s).into()) + Ok(PathBuf::from(s)) } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &Path { +impl> FromIterator

for PathBuf { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +impl> iter::Extend

for PathBuf { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } +} + +impl Deref for PathBuf { + type Target = Path; + + fn deref(&self) -> &Path { Path::new(&self.inner) } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &std::path::Path { +impl Borrow for PathBuf { + fn borrow(&self) -> &Path { + self.deref() + } +} + +impl<'a> From for Cow<'a, Path> { + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) + } +} + +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) + } +} + +impl<'a> From> for PathBuf { + #[inline] + fn from(p: Cow<'a, Path>) -> Self { + p.into_owned() + } +} + +impl From for Arc { + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +impl From for Rc { + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &OsStr { self.inner.as_ref() } } @@ -284,16 +353,20 @@ impl<'b, P: AsRef + 'b> FromStream

for PathBuf { } } -impl> std::iter::FromIterator

for PathBuf { - fn from_iter>(iter: I) -> PathBuf { - let mut buf = PathBuf::new(); - buf.extend(iter); - buf +impl From for PathBuf { + fn from(path: std::path::PathBuf) -> PathBuf { + PathBuf { inner: path } } } -impl> std::iter::Extend

for PathBuf { - fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |p| self.push(p.as_ref())); +impl Into for PathBuf { + fn into(self) -> std::path::PathBuf { + self.inner + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &std::path::Path { + self.inner.as_ref() } } From 79bbf4938deeef223f405e1756945fea45965986 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 10:44:12 +0100 Subject: [PATCH 198/707] Randomize Stream::merge to improve the throughput. Implements #490. --- src/stream/stream/merge.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index fe3579e9d..ababcf425 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -27,7 +27,10 @@ pin_project! { impl Merge { pub(crate) fn new(left: L, right: R) -> Self { - Self { left: left.fuse(), right: right.fuse() } + Self { + left: left.fuse(), + right: right.fuse(), + } } } @@ -40,14 +43,19 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - match this.left.poll_next(cx) { + let (first, second) = if (utils::random(1) == 1) { + (this.left, this.right) + } else { + (this.right, this.left) + }; + match first.poll_next(cx) { Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.right.poll_next(cx), - Poll::Pending => match this.right.poll_next(cx) { + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { Poll::Ready(Some(item)) => Poll::Ready(Some(item)), Poll::Ready(None) => Poll::Pending, Poll::Pending => Poll::Pending, - } + }, } } } From 352f18bc2a404b755dd7cd021ca50b3a79a7cc14 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 11:10:36 +0100 Subject: [PATCH 199/707] Use async_std::sync::Arc in examples (#501) --- src/stream/from_fn.rs | 3 +-- src/sync/mod.rs | 4 +--- src/sync/mutex.rs | 12 +++--------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index a28a90147..f7a421fc8 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -34,8 +34,7 @@ pin_project! { /// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; -/// use async_std::sync::Mutex; -/// use std::sync::Arc; +/// use async_std::sync::{Arc, Mutex}; /// use async_std::stream; /// /// let count = Arc::new(Mutex::new(0u8)); diff --git a/src/sync/mod.rs b/src/sync/mod.rs index fdeb48c35..088c520b0 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -155,9 +155,7 @@ //! ``` //! # async_std::task::block_on(async { //! # -//! use std::sync::Arc; -//! -//! use async_std::sync::Mutex; +//! use async_std::sync::{Arc, Mutex}; //! use async_std::task; //! //! let m1 = Arc::new(Mutex::new(0)); diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 5bec6a23e..2c0ac0cc3 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -19,9 +19,7 @@ use crate::task::{Context, Poll}; /// ``` /// # async_std::task::block_on(async { /// # -/// use std::sync::Arc; -/// -/// use async_std::sync::Mutex; +/// use async_std::sync::{Arc, Mutex}; /// use async_std::task; /// /// let m = Arc::new(Mutex::new(0)); @@ -77,9 +75,7 @@ impl Mutex { /// ``` /// # async_std::task::block_on(async { /// # - /// use std::sync::Arc; - /// - /// use async_std::sync::Mutex; + /// use async_std::sync::{Arc, Mutex}; /// use async_std::task; /// /// let m1 = Arc::new(Mutex::new(10)); @@ -155,9 +151,7 @@ impl Mutex { /// ``` /// # async_std::task::block_on(async { /// # - /// use std::sync::Arc; - /// - /// use async_std::sync::Mutex; + /// use async_std::sync::{Arc, Mutex}; /// use async_std::task; /// /// let m1 = Arc::new(Mutex::new(10)); From 0c37d4af106487f575d5fbd6f9a764ed25418c5f Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:25:50 +0100 Subject: [PATCH 200/707] Anonymous function to avoid type issues --- src/stream/stream/merge.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index ababcf425..6c8c20b7b 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -5,6 +5,7 @@ use pin_project_lite::pin_project; use crate::prelude::*; use crate::stream::Fuse; +use crate::utils; pin_project! { /// A stream that merges two other streams into a single stream. @@ -43,19 +44,27 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - let (first, second) = if (utils::random(1) == 1) { - (this.left, this.right) + if utils::random(1) == 1 { + poll_next_in_order(cx, this.left, this.right) } else { - (this.right, this.left) - }; - match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => second.poll_next(cx), - Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, + poll_next_in_order(cx, this.right, this.left) } } } + +/// Pools the next item, trying in order, first the first item, then the second one. +fn poll_next_in_order(cx: &mut Context<'_>, first: F, second: S) -> Poll> +where + F: Stream, + S: Stream, +{ + match first.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } +} From e48e4637361c37cc18d447c5ec3fc2eb135c6fd5 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:26:32 +0100 Subject: [PATCH 201/707] Duplicating code due to strange Rust error. --- src/stream/stream/merge.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 6c8c20b7b..b08b586e4 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -45,26 +45,25 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if utils::random(1) == 1 { - poll_next_in_order(cx, this.left, this.right) + match this.left.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => this.right.poll_next(cx), + Poll::Pending => match this.right.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } } else { - poll_next_in_order(cx, this.right, this.left) + match this.right.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => this.left.poll_next(cx), + Poll::Pending => match this.left.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } } } } - -/// Pools the next item, trying in order, first the first item, then the second one. -fn poll_next_in_order(cx: &mut Context<'_>, first: F, second: S) -> Poll> -where - F: Stream, - S: Stream, -{ - match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => second.poll_next(cx), - Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } -} From 5d558ca213327f465feb60ac96ad9ae8421002e2 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:39:30 +0100 Subject: [PATCH 202/707] Fixed test, order is no longer guaranteed --- src/stream/stream/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ea1459f2..7c4bceb0e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1670,11 +1670,14 @@ extension_trait! { let c = stream::once(3u8); let mut s = a.merge(b).merge(c); + let mut lst = Vec::new(); - assert_eq!(s.next().await, Some(1u8)); - assert_eq!(s.next().await, Some(2u8)); - assert_eq!(s.next().await, Some(3u8)); - assert_eq!(s.next().await, None); + while let Some(n) = s.next().await { + lst.push(n) + } + + lst.sort_unstable(); + assert_eq!(&lst, &[1u8, 2u8, 3u8]); # }); ``` "#] From b591fc68bdee365cfc1f6228c2d2f264b1b2f6e8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 12:17:00 +0100 Subject: [PATCH 203/707] Changed semantics of throttle to non-dropping variant with backpressure --- examples/throttle.rs | 27 ++++++++++++++++++++++++++ src/stream/stream/mod.rs | 29 ++++++++++++++++++++++++++-- src/stream/stream/throttle.rs | 36 +++++++++++++---------------------- 3 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 examples/throttle.rs diff --git a/examples/throttle.rs b/examples/throttle.rs new file mode 100644 index 000000000..1b9a6f2ec --- /dev/null +++ b/examples/throttle.rs @@ -0,0 +1,27 @@ +//! Spawns a timed task which gets throttled. + +fn main() { + #[cfg(feature = "unstable")] + { + use async_std::prelude::*; + use async_std::task; + + task::block_on(async { + use async_std::stream; + use std::time::Duration; + + // emit value every 1 second + let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + + // throttle for 2 seconds + let s = s.throttle(Duration::from_secs(2)); + + s.for_each(|(n, _)| { + dbg!(n); + }) + .await; + // => 0 .. 1 .. 2 .. 3 + // with a pause of 2 seconds between each print + }) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8c39eb967..8b30c5e96 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -117,7 +117,6 @@ use std::time::Duration; cfg_unstable! { use std::future::Future; use std::pin::Pin; - use std::time::Duration; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; @@ -316,7 +315,33 @@ extension_trait! { TakeWhile::new(self, predicate) } - fn throttle(self, d: Duration) -> Throttle + #[doc = r#" + Limit the amount of items yielded per timeslice in a stream. + + # Examples + ```ignore + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use std::time::Duration; + + // emit value every 1 second + let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + + // throttle for 2 seconds + let s = s.throttle(Duration::from_secs(2)); + + s.for_each(|(n, _)| { + dbg!(n); + }) + .await; + // => 0 .. 1 .. 2 .. 3 + // with a pause of 2 seconds between each print + # + # }) } + ``` + "#] + fn throttle(self, d: Duration) -> Throttle where Self: Sized, { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 2a0cc5630..010839cc6 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -7,60 +7,50 @@ use futures_timer::Delay; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that only yields one element once every `duration`, and drops all others. +/// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. /// #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct Throttle { +pub struct Throttle { stream: S, duration: Duration, delay: Option, - last: Option, } -impl Unpin for Throttle {} +impl Unpin for Throttle {} -impl Throttle { +impl Throttle { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(duration: Duration); pin_utils::unsafe_pinned!(delay: Option); - pin_utils::unsafe_unpinned!(last: Option); pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, duration, delay: None, - last: None, } } } -impl Stream for Throttle { +impl Stream for Throttle { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if let Some(d) = self.as_mut().delay().as_pin_mut() { if d.poll(cx).is_ready() { - if let Some(v) = self.as_mut().last().take() { - // Sets last to None. - *self.as_mut().delay() = Some(Delay::new(self.duration)); - return Poll::Ready(Some(v)); - } else { - *self.as_mut().delay() = None; - } + *self.as_mut().delay() = None; + } else { + return Poll::Pending; } } match self.as_mut().stream().poll_next(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => return Poll::Ready(None), + Poll::Pending => { + cx.waker().wake_by_ref(); // Continue driving even though emitting Pending + Poll::Pending + } + Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - if self.as_mut().delay().is_some() { - *self.as_mut().last() = Some(v); - cx.waker().wake_by_ref(); // Continue driving even though emitting Pending - return Poll::Pending; - } - *self.as_mut().delay() = Some(Delay::new(self.duration)); Poll::Ready(Some(v)) } From 139a34b6852b5bf74bb97ead4a26241f4a88edde Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 12:26:32 +0100 Subject: [PATCH 204/707] Make throttle an unstable feature --- src/stream/stream/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8b30c5e96..756e8e960 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -60,7 +60,6 @@ mod skip_while; mod step_by; mod take; mod take_while; -mod throttle; mod try_fold; mod try_for_each; mod zip; @@ -107,16 +106,15 @@ 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 zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use std::time::Duration; cfg_unstable! { use std::future::Future; use std::pin::Pin; + use std::time::Duration; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; @@ -125,11 +123,13 @@ cfg_unstable! { pub use flatten::Flatten; pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; + pub use throttle::Throttle; mod merge; mod flatten; mod flat_map; mod timeout; + mod throttle; } extension_trait! { @@ -315,6 +315,7 @@ extension_trait! { TakeWhile::new(self, predicate) } + #[cfg(all(feature = "default", feature = "unstable"))] #[doc = r#" Limit the amount of items yielded per timeslice in a stream. From c2f750d2882b4215ae6962d419056ba00b000f6d Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 10:21:42 +0100 Subject: [PATCH 205/707] Cleanup stream module --- src/io/mod.rs | 18 ++-- src/stream/extend.rs | 6 +- src/stream/from_fn.rs | 81 ++++++------------ src/stream/from_iter.rs | 2 +- src/stream/mod.rs | 4 +- src/stream/once.rs | 2 +- src/stream/repeat.rs | 2 +- src/stream/repeat_with.rs | 79 +++++++---------- src/stream/stream/chain.rs | 2 +- src/stream/stream/cloned.rs | 1 + src/stream/stream/copied.rs | 4 +- src/stream/stream/cycle.rs | 81 +++++++----------- src/stream/stream/enumerate.rs | 3 +- src/stream/stream/filter.rs | 9 +- src/stream/stream/filter_map.rs | 19 ++--- src/stream/stream/find.rs | 18 ++-- src/stream/stream/find_map.rs | 20 ++--- src/stream/stream/flat_map.rs | 17 ++-- src/stream/stream/flatten.rs | 35 ++++++-- src/stream/stream/fold.rs | 14 ++-- src/stream/stream/for_each.rs | 9 +- src/stream/stream/inspect.rs | 9 +- src/stream/stream/map.rs | 15 ++-- src/stream/stream/mod.rs | 135 +++++++++++++++--------------- src/stream/stream/position.rs | 49 ++++++----- src/stream/stream/skip_while.rs | 9 +- src/stream/stream/take_while.rs | 9 +- src/stream/stream/timeout.rs | 2 +- src/stream/stream/try_fold.rs | 45 +++++----- src/stream/stream/try_for_each.rs | 43 ++++------ src/stream/stream/zip.rs | 2 +- 31 files changed, 315 insertions(+), 429 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index c47115931..4e8323052 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -19,8 +19,8 @@ //! [`File`]s: //! //! ```no_run -//! use async_std::prelude::*; //! use async_std::fs::File; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -47,9 +47,9 @@ //! coming from: //! //! ```no_run -//! use async_std::io::prelude::*; -//! use async_std::io::SeekFrom; //! use async_std::fs::File; +//! use async_std::io::SeekFrom; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -82,9 +82,9 @@ //! methods to any reader: //! //! ```no_run -//! use async_std::io::prelude::*; -//! use async_std::io::BufReader; //! use async_std::fs::File; +//! use async_std::io::BufReader; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -104,9 +104,9 @@ //! to [`write`][`Write::write`]: //! //! ```no_run -//! use async_std::io::prelude::*; -//! use async_std::io::BufWriter; //! use async_std::fs::File; +//! use async_std::io::BufWriter; +//! use async_std::io::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -179,9 +179,9 @@ //! lines: //! //! ```no_run -//! use async_std::prelude::*; -//! use async_std::io::BufReader; //! use async_std::fs::File; +//! use async_std::io::BufReader; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 7bdfd343d..c48fe1ed8 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -65,10 +65,10 @@ pub trait Extend { /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub async fn extend<'a, C, A, T>(collection: &mut C, stream: T) +pub async fn extend<'a, C, T, S>(collection: &mut C, stream: S) where - C: Extend, - T: IntoStream + 'a, + C: Extend, + S: IntoStream + 'a, { Extend::extend(collection, stream).await } diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index a28a90147..24432c7eb 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,28 +1,21 @@ -use std::marker::PhantomData; use std::pin::Pin; -use std::future::Future; - -use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - /// A stream that yields elements by calling a closure. - /// - /// This stream is created by the [`from_fn`] function. See its - /// documentation for more. - /// - /// [`from_fn`]: fn.from_fn.html - #[derive(Debug)] - pub struct FromFn { - f: F, - #[pin] - future: Option, - __t: PhantomData, - } +/// A stream that yields elements by calling a closure. +/// +/// This stream is created by the [`from_fn`] function. See its +/// documentation for more. +/// +/// [`from_fn`]: fn.from_fn.html +#[derive(Clone, Debug)] +pub struct FromFn { + f: F, } +impl Unpin for FromFn {} + /// Creates a new stream where to produce each new element a provided closure is called. /// /// This allows creating a custom stream with any behaviour without using the more verbose @@ -34,22 +27,15 @@ pin_project! { /// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; -/// use async_std::sync::Mutex; -/// use std::sync::Arc; /// use async_std::stream; /// -/// let count = Arc::new(Mutex::new(0u8)); +/// let mut count = 0u8; /// let s = stream::from_fn(|| { -/// let count = Arc::clone(&count); -/// -/// async move { -/// *count.lock().await += 1; -/// -/// if *count.lock().await > 3 { -/// None -/// } else { -/// Some(*count.lock().await) -/// } +/// count += 1; +/// if count > 3 { +/// None +/// } else { +/// Some(count) /// } /// }); /// @@ -61,38 +47,21 @@ pin_project! { /// # /// # }) /// ``` -pub fn from_fn(f: F) -> FromFn +pub fn from_fn(f: F) -> FromFn where - F: FnMut() -> Fut, - Fut: Future>, + F: FnMut() -> Option, { - FromFn { - f, - future: None, - __t: PhantomData, - } + FromFn { f } } -impl Stream for FromFn +impl Stream for FromFn where - F: FnMut() -> Fut, - Fut: Future>, + F: FnMut() -> Option, { type Item = T; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut this = self.project(); - loop { - if this.future.is_some() { - let next = - futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - this.future.set(None); - - return Poll::Ready(next); - } else { - let fut = (this.f)(); - this.future.set(Some(fut)); - } - } + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + let item = (&mut self.f)(); + Poll::Ready(item) } } diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index a83afcebd..d7a31d6c4 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -6,7 +6,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - /// A stream that created from iterator + /// A stream that was created from iterator. /// /// This stream is created by the [`from_iter`] function. /// See it documentation for more. diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 692c5de4f..f7828822a 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -306,9 +306,7 @@ pub use from_iter::{from_iter, FromIter}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; -pub use stream::{ - Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, TakeWhile, Zip, -}; +pub use stream::*; pub(crate) mod stream; diff --git a/src/stream/once.rs b/src/stream/once.rs index d993c1603..a33bd6ac3 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -33,7 +33,7 @@ pin_project! { /// documentation for more. /// /// [`once`]: fn.once.html - #[derive(Debug)] + #[derive(Clone, Debug)] pub struct Once { value: Option, } diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index aaaff0c67..f3dfdbd85 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -33,7 +33,7 @@ where /// documentation for more. /// /// [`repeat`]: fn.repeat.html -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Repeat { item: T, } diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index 6e7cfa3bf..e183a77ca 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,28 +1,21 @@ -use std::marker::PhantomData; use std::pin::Pin; -use std::future::Future; - -use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - /// A stream that repeats elements of type `T` endlessly by applying a provided closure. - /// - /// This stream is created by the [`repeat_with`] function. See its - /// documentation for more. - /// - /// [`repeat_with`]: fn.repeat_with.html - #[derive(Debug)] - pub struct RepeatWith { - f: F, - #[pin] - future: Option, - __a: PhantomData, - } +/// A stream that repeats elements of type `T` endlessly by applying a provided closure. +/// +/// This stream is created by the [`repeat_with`] function. See its +/// documentation for more. +/// +/// [`repeat_with`]: fn.repeat_with.html +#[derive(Clone, Debug)] +pub struct RepeatWith { + f: F, } +impl Unpin for RepeatWith {} + /// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. /// /// # Examples @@ -35,7 +28,7 @@ pin_project! { /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::repeat_with(|| async { 1 }); +/// let s = stream::repeat_with(|| 1); /// /// pin_utils::pin_mut!(s); /// @@ -54,48 +47,38 @@ pin_project! { /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::repeat_with(|| async { 1u8 }).take(2); +/// let mut n = 1; +/// let s = stream::repeat_with(|| { +/// let item = n; +/// n *= 2; +/// item +/// }) +/// .take(4); /// /// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(4)); +/// assert_eq!(s.next().await, Some(8)); /// assert_eq!(s.next().await, None); /// # }) /// ``` -pub fn repeat_with(repeater: F) -> RepeatWith +pub fn repeat_with(repeater: F) -> RepeatWith where - F: FnMut() -> Fut, - Fut: Future, + F: FnMut() -> T, { - RepeatWith { - f: repeater, - future: None, - __a: PhantomData, - } + RepeatWith { f: repeater } } -impl Stream for RepeatWith +impl Stream for RepeatWith where - F: FnMut() -> Fut, - Fut: Future, + F: FnMut() -> T, { - type Item = A; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut this = self.project(); - loop { - if this.future.is_some() { - let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - - this.future.set(None); - - return Poll::Ready(Some(res)); - } else { - let fut = (this.f)(); + type Item = T; - this.future.set(Some(fut)); - } - } + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + let item = (&mut self.f)(); + Poll::Ready(Some(item)) } } diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 5e0eeb48c..f6d9cf641 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -7,7 +7,7 @@ use crate::prelude::*; use crate::task::{Context, Poll}; pin_project! { - /// Chains two streams one after another. + /// A stream that chains two streams one after another. /// /// This `struct` is created by the [`chain`] method on [`Stream`]. See its /// documentation for more. diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs index 19dfbc87c..4c77c5c9e 100644 --- a/src/stream/stream/cloned.rs +++ b/src/stream/stream/cloned.rs @@ -4,6 +4,7 @@ use pin_project_lite::pin_project; use std::pin::Pin; pin_project! { + /// A stream that clones the elements of an underlying stream. #[derive(Debug)] pub struct Cloned { #[pin] diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs index 477d59d2f..e3c8367b6 100644 --- a/src/stream/stream/copied.rs +++ b/src/stream/stream/copied.rs @@ -4,8 +4,8 @@ use pin_project_lite::pin_project; use std::pin::Pin; pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] + /// A stream that copies the elements of an underlying stream. + #[derive(Debug)] pub struct Copied { #[pin] stream: S, diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 8a31cc177..7f01a61db 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -1,73 +1,54 @@ +use std::mem::ManuallyDrop; use std::pin::Pin; -use pin_project_lite::pin_project; - use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - /// A stream that will repeatedly yield the same list of elements - pub struct Cycle { - #[pin] - source: S, - index: usize, - buffer: Vec, - state: CycleState, - } -} - -#[derive(Eq, PartialEq)] -enum CycleState { - FromStream, - FromBuffer, +/// A stream that will repeatedly yield the same list of elements. +#[derive(Debug)] +pub struct Cycle { + orig: S, + source: ManuallyDrop, } -impl Cycle +impl Cycle where - S: Stream, - S::Item: Clone, + S: Stream + Clone, { - pub fn new(source: S) -> Cycle { + pub fn new(source: S) -> Cycle { Cycle { - source, - index: 0, - buffer: Vec::new(), - state: CycleState::FromStream, + orig: source.clone(), + source: ManuallyDrop::new(source), } } } -impl Stream for Cycle +impl Drop for Cycle { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self.source); + } + } +} + +impl Stream for Cycle where - S: Stream, - S::Item: Clone, + S: Stream + Clone, { type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.project(); - - let mut next; - if *this.state == CycleState::FromStream { - next = futures_core::ready!(this.source.poll_next(cx)); - - if let Some(val) = next { - this.buffer.push(val.clone()); - next = Some(val) - } else { - *this.state = CycleState::FromBuffer; - next = this.buffer.get(*this.index).cloned(); + unsafe { + let this = self.get_unchecked_mut(); + + 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) + } } - } else { - let mut index = *this.index; - if index == this.buffer.len() { - index = 0 - } - next = Some(this.buffer[index].clone()); - - *this.index = index + 1; } - - Poll::Ready(next) } } diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index 2a3afa877..a758010ea 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -6,8 +6,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] + #[derive(Debug)] pub struct Enumerate { #[pin] stream: S, diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index a2562e771..594b09497 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; @@ -15,25 +14,23 @@ pin_project! { /// [`filter`]: trait.Stream.html#method.filter /// [`Stream`]: trait.Stream.html #[derive(Debug)] - pub struct Filter { + pub struct Filter { #[pin] stream: S, predicate: P, - __t: PhantomData, } } -impl Filter { +impl Filter { pub(super) fn new(stream: S, predicate: P) -> Self { Filter { stream, predicate, - __t: PhantomData, } } } -impl Stream for Filter +impl Stream for Filter where S: Stream, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 6a4593f97..e110f514f 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; @@ -7,29 +6,21 @@ use pin_project_lite::pin_project; use crate::stream::Stream; pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct FilterMap { + #[derive(Debug)] + pub struct FilterMap { #[pin] stream: S, f: F, - __from: PhantomData, - __to: PhantomData, } } -impl FilterMap { +impl FilterMap { pub(crate) fn new(stream: S, f: F) -> Self { - FilterMap { - stream, - f, - __from: PhantomData, - __to: PhantomData, - } + FilterMap { stream, f } } } -impl Stream for FilterMap +impl Stream for FilterMap where S: Stream, F: FnMut(S::Item) -> Option, diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index b37a6a460..0c5ad62ff 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -1,31 +1,25 @@ -use std::marker::PhantomData; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct FindFuture<'a, S, P, T> { +pub struct FindFuture<'a, S, P> { stream: &'a mut S, p: P, - __t: PhantomData, } -impl<'a, S, P, T> FindFuture<'a, S, P, T> { +impl<'a, S, P> FindFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, p: P) -> Self { - FindFuture { - stream, - p, - __t: PhantomData, - } + FindFuture { stream, p } } } -impl Unpin for FindFuture<'_, S, P, T> {} +impl Unpin for FindFuture<'_, S, P> {} -impl<'a, S, P> Future for FindFuture<'a, S, P, S::Item> +impl<'a, S, P> Future for FindFuture<'a, S, P> where S: Stream + Unpin + Sized, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index 16993fc5f..b10bd9cad 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -1,33 +1,25 @@ -use std::marker::PhantomData; +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use std::future::Future; use crate::stream::Stream; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct FindMapFuture<'a, S, F, T, B> { +pub struct FindMapFuture<'a, S, F> { stream: &'a mut S, f: F, - __b: PhantomData, - __t: PhantomData, } -impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> { +impl<'a, S, F> FindMapFuture<'a, S, F> { pub(super) fn new(stream: &'a mut S, f: F) -> Self { - FindMapFuture { - stream, - f, - __b: PhantomData, - __t: PhantomData, - } + FindMapFuture { stream, f } } } -impl Unpin for FindMapFuture<'_, S, F, T, B> {} +impl Unpin for FindMapFuture<'_, S, F> {} -impl<'a, S, B, F> Future for FindMapFuture<'a, S, F, S::Item, B> +impl<'a, S, B, F> Future for FindMapFuture<'a, S, F> where S: Stream + Unpin + Sized, F: FnMut(S::Item) -> Option, diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index ed3268ea9..ab45c9c72 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -1,33 +1,36 @@ -use pin_project_lite::pin_project; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::prelude::*; use crate::stream::stream::map::Map; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; pin_project! { + /// A stream that maps each element to a stream, and yields the elements of the produced + /// streams. + /// /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its /// documentation for more. /// /// [`flat_map`]: trait.Stream.html#method.flat_map /// [`Stream`]: trait.Stream.html - #[allow(missing_debug_implementations)] - pub struct FlatMap { + pub struct FlatMap { #[pin] - stream: Map, + stream: Map, #[pin] inner_stream: Option, } } -impl FlatMap +impl FlatMap where S: Stream, U: IntoStream, F: FnMut(S::Item) -> U, { - pub(super) fn new(stream: S, f: F) -> FlatMap { + pub(super) fn new(stream: S, f: F) -> FlatMap { FlatMap { stream: stream.map(f), inner_stream: None, @@ -35,7 +38,7 @@ where } } -impl Stream for FlatMap +impl Stream for FlatMap where S: Stream, S::Item: IntoStream, diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 5e791cda3..edaffd044 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -1,30 +1,38 @@ -use pin_project_lite::pin_project; +use std::fmt; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; pin_project! { + /// A stream that flattens one level of nesting in an stream of things that can be turned into + /// streams. + /// /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its /// documentation for more. /// /// [`flatten`]: trait.Stream.html#method.flatten /// [`Stream`]: trait.Stream.html - #[allow(missing_debug_implementations)] - pub struct Flatten { + pub struct Flatten + where + S: Stream, + S::Item: IntoStream, + { #[pin] stream: S, #[pin] - inner_stream: Option, + inner_stream: Option<::IntoStream>, } } -impl Flatten +impl Flatten where S: Stream, S::Item: IntoStream, { - pub(super) fn new(stream: S) -> Flatten { + pub(super) fn new(stream: S) -> Flatten { Flatten { stream, inner_stream: None, @@ -32,7 +40,7 @@ where } } -impl Stream for Flatten::IntoStream> +impl Stream for Flatten where S: Stream, S::Item: IntoStream, @@ -56,3 +64,16 @@ where } } } + +impl fmt::Debug for Flatten +where + S: fmt::Debug + Stream, + S::Item: IntoStream, + U: fmt::Debug + Stream, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Flatten") + .field("inner", &self.stream) + .finish() + } +} diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 66a767294..c4da59150 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,6 +1,5 @@ -use std::marker::PhantomData; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; @@ -8,29 +7,26 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct FoldFuture { + #[derive(Debug)] + pub struct FoldFuture { #[pin] stream: S, f: F, acc: Option, - __t: PhantomData, } } -impl FoldFuture { +impl FoldFuture { pub(super) fn new(stream: S, init: B, f: F) -> Self { FoldFuture { stream, f, acc: Some(init), - __t: PhantomData, } } } -impl Future for FoldFuture +impl Future for FoldFuture where S: Stream + Sized, F: FnMut(B, S::Item) -> B, diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 6383ed785..01833fd9e 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use std::future::Future; @@ -10,25 +9,23 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - pub struct ForEachFuture { + pub struct ForEachFuture { #[pin] stream: S, f: F, - __t: PhantomData, } } -impl ForEachFuture { +impl ForEachFuture { pub(super) fn new(stream: S, f: F) -> Self { ForEachFuture { stream, f, - __t: PhantomData, } } } -impl Future for ForEachFuture +impl Future for ForEachFuture where S: Stream + Sized, F: FnMut(S::Item), diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index ba60b0cec..acf22465c 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; @@ -15,25 +14,23 @@ pin_project! { /// [`inspect`]: trait.Stream.html#method.inspect /// [`Stream`]: trait.Stream.html #[derive(Debug)] - pub struct Inspect { + pub struct Inspect { #[pin] stream: S, f: F, - __t: PhantomData, } } -impl Inspect { +impl Inspect { pub(super) fn new(stream: S, f: F) -> Self { Inspect { stream, f, - __t: PhantomData, } } } -impl Stream for Inspect +impl Stream for Inspect where S: Stream, F: FnMut(&S::Item), diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index a1fafc30f..7accb6fce 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; @@ -7,29 +6,25 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct Map { + /// A stream that maps value of another stream with a function. + #[derive(Debug)] + pub struct Map { #[pin] stream: S, f: F, - __from: PhantomData, - __to: PhantomData, } } -impl Map { +impl Map { pub(crate) fn new(stream: S, f: F) -> Self { Map { stream, f, - __from: PhantomData, - __to: PhantomData, } } } -impl Stream for Map +impl Stream for Map where S: Stream, F: FnMut(S::Item) -> B, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ea1459f2..5756a21e6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -288,6 +288,7 @@ extension_trait! { Creates a stream that yields elements based on a predicate. # Examples + ``` # fn main() { async_std::task::block_on(async { # @@ -300,12 +301,11 @@ extension_trait! { 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 + fn take_while

(self, predicate: P) -> TakeWhile where Self: Sized, P: FnMut(&Self::Item) -> bool, @@ -410,10 +410,10 @@ extension_trait! { # }) } ``` "#] - fn cloned<'a,T>(self) -> Cloned + fn cloned<'a, T>(self) -> Cloned where Self: Sized + Stream, - T : 'a + Clone, + T: Clone + 'a, { Cloned::new(self) } @@ -443,10 +443,10 @@ extension_trait! { # }) } ``` "#] - fn copied<'a,T>(self) -> Copied + fn copied<'a, T>(self) -> Copied where Self: Sized + Stream, - T : 'a + Copy, + T: Copy + 'a, { Copied::new(self) } @@ -475,10 +475,9 @@ extension_trait! { # }) ``` "#] - fn cycle(self) -> Cycle - where - Self: Sized, - Self::Item: Clone, + fn cycle(self) -> Cycle + where + Self: Clone + Sized, { Cycle::new(self) } @@ -505,7 +504,6 @@ extension_trait! { assert_eq!(s.next().await, Some((1, 'b'))); assert_eq!(s.next().await, Some((2, 'c'))); assert_eq!(s.next().await, None); - # # }) } ``` @@ -540,7 +538,7 @@ extension_trait! { # }) } ``` "#] - fn map(self, f: F) -> Map + fn map(self, f: F) -> Map where Self: Sized, F: FnMut(Self::Item) -> B, @@ -565,17 +563,18 @@ extension_trait! { 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; + .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 + fn inspect(self, f: F) -> Inspect where Self: Sized, F: FnMut(&Self::Item), @@ -618,7 +617,6 @@ extension_trait! { # # }) } ``` - "#] fn last( self, @@ -685,7 +683,7 @@ extension_trait! { # }) } ``` "#] - fn filter

(self, predicate: P) -> Filter + fn filter

(self, predicate: P) -> Filter where Self: Sized, P: FnMut(&Self::Item) -> bool, @@ -721,7 +719,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flat_map(self, f: F) -> FlatMap + fn flat_map(self, f: F) -> FlatMap where Self: Sized, U: IntoStream, @@ -755,7 +753,7 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self: Sized, Self::Item: IntoStream, @@ -796,7 +794,7 @@ extension_trait! { # }) } ``` "#] - fn filter_map(self, f: F) -> FilterMap + fn filter_map(self, f: F) -> FilterMap where Self: Sized, F: FnMut(Self::Item) -> Option, @@ -804,7 +802,7 @@ extension_trait! { FilterMap::new(self, f) } - #[doc = r#" + #[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. @@ -828,19 +826,19 @@ extension_trait! { # }) } ``` "#] - fn min_by_key( + fn min_by_key( self, - key_by: K, - ) -> impl Future> [MinByKeyFuture] + key_by: F, + ) -> impl Future> [MinByKeyFuture] where Self: Sized, - Self::Item: Ord, - K: FnMut(&Self::Item) -> Self::Item, + B: Ord, + F: FnMut(&Self::Item) -> B, { MinByKeyFuture::new(self, key_by) } - #[doc = r#" + #[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. @@ -864,14 +862,14 @@ extension_trait! { # }) } ``` "#] - fn max_by_key( + fn max_by_key( self, - key_by: K, - ) -> impl Future> [MaxByKeyFuture] + key_by: F, + ) -> impl Future> [MaxByKeyFuture] where Self: Sized, - Self::Item: Ord, - K: FnMut(&Self::Item) -> Self::Item, + B: Ord, + F: FnMut(&Self::Item) -> B, { MaxByKeyFuture::new(self, key_by) } @@ -1043,7 +1041,7 @@ extension_trait! { n: usize, ) -> impl Future> + '_ [NthFuture<'_, Self>] where - Self: Sized, + Self: Unpin + Sized, { NthFuture::new(self, n) } @@ -1151,9 +1149,9 @@ extension_trait! { fn find

( &mut self, p: P, - ) -> impl Future> + '_ [FindFuture<'_, Self, P, Self::Item>] + ) -> impl Future> + '_ [FindFuture<'_, Self, P>] where - Self: Sized, + Self: Unpin + Sized, P: FnMut(&Self::Item) -> bool, { FindFuture::new(self, p) @@ -1179,9 +1177,9 @@ extension_trait! { fn find_map( &mut self, f: F, - ) -> impl Future> + '_ [FindMapFuture<'_, Self, F, Self::Item, B>] + ) -> impl Future> + '_ [FindMapFuture<'_, Self, F>] where - Self: Sized, + Self: Unpin + Sized, F: FnMut(Self::Item) -> Option, { FindMapFuture::new(self, f) @@ -1213,7 +1211,7 @@ extension_trait! { self, init: B, f: F, - ) -> impl Future [FoldFuture] + ) -> impl Future [FoldFuture] where Self: Sized, F: FnMut(B, Self::Item) -> B, @@ -1248,7 +1246,7 @@ extension_trait! { fn for_each( self, f: F, - ) -> impl Future [ForEachFuture] + ) -> impl Future [ForEachFuture] where Self: Sized, F: FnMut(Self::Item), @@ -1389,7 +1387,7 @@ extension_trait! { # }) } ``` "#] - fn skip_while

(self, predicate: P) -> SkipWhile + fn skip_while

(self, predicate: P) -> SkipWhile where Self: Sized, P: FnMut(&Self::Item) -> bool, @@ -1472,7 +1470,7 @@ extension_trait! { use async_std::prelude::*; use async_std::stream; - let s = stream::from_iter(vec![1usize, 2, 3]); + 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) @@ -1487,12 +1485,12 @@ extension_trait! { ``` "#] fn try_fold( - self, + &mut self, init: T, f: F, - ) -> impl Future> [TryFoldFuture] + ) -> impl Future> + '_ [TryFoldFuture<'_, Self, F, T>] where - Self: Sized, + Self: Unpin + Sized, F: FnMut(B, Self::Item) -> Result, { TryFoldFuture::new(self, init, f) @@ -1512,7 +1510,7 @@ extension_trait! { let (tx, rx) = channel(); - let s = stream::from_iter(vec![1u8, 2, 3]); + 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(); @@ -1533,11 +1531,11 @@ extension_trait! { ``` "#] fn try_for_each( - self, + &mut self, f: F, - ) -> impl Future [TryForEachFuture] + ) -> impl Future + 'a [TryForEachFuture<'_, Self, F>] where - Self: Sized, + Self: Unpin + Sized, F: FnMut(Self::Item) -> Result<(), E>, { TryForEachFuture::new(self, f) @@ -1582,7 +1580,7 @@ extension_trait! { #[inline] fn zip(self, other: U) -> Zip where - Self: Sized + Stream, + Self: Sized, U: Stream, { Zip::new(self, other) @@ -1641,7 +1639,6 @@ extension_trait! { "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] fn collect<'a, B>( self, ) -> impl Future + 'a [Pin + 'a>>] @@ -1740,28 +1737,28 @@ extension_trait! { use async_std::stream; let s = stream::from_iter(vec![1usize, 2, 3]); - let res = s.clone().position(|x| *x == 1).await; + let res = s.clone().position(|x| x == 1).await; assert_eq!(res, Some(0)); - let res = s.clone().position(|x| *x == 2).await; + let res = s.clone().position(|x| x == 2).await; assert_eq!(res, Some(1)); - let res = s.clone().position(|x| *x == 3).await; + let res = s.clone().position(|x| x == 3).await; assert_eq!(res, Some(2)); - let res = s.clone().position(|x| *x == 4).await; + let res = s.clone().position(|x| x == 4).await; assert_eq!(res, None); # # }) } ``` "#] fn position

( - self, - predicate: P - ) -> impl Future> [PositionFuture] + &mut self, + predicate: P, + ) -> impl Future> + '_ [PositionFuture<'_, Self, P>] where - Self: Sized, - P: FnMut(&Self::Item) -> bool, + Self: Unpin + Sized, + P: FnMut(Self::Item) -> bool, { PositionFuture::new(self, predicate) } @@ -1805,10 +1802,12 @@ extension_trait! { CmpFuture::new(self, other) } - #[doc = r#" + #[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 { # @@ -1833,7 +1832,7 @@ extension_trait! { other: S ) -> impl Future [NeFuture] where - Self: Sized + Stream, + Self: Sized, S: Sized + Stream, ::Item: PartialEq, { @@ -2026,11 +2025,11 @@ extension_trait! { } #[doc = r#" - Sums the elements of an iterator. + Sums the elements of a stream. Takes each element, adds them together, and returns the result. - An empty iterator returns the zero value of the type. + An empty streams returns the zero value of the type. # Panics @@ -2063,15 +2062,15 @@ extension_trait! { ) -> impl Future + 'a [Pin + 'a>>] where Self: Sized + Stream + 'a, - S: Sum, + S: Sum, { Sum::sum(self) } #[doc = r#" - Iterates over the entire iterator, multiplying all the elements + Multiplies all elements of the stream. - An empty iterator returns the one value of the type. + An empty stream returns the one value of the type. # Panics diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs index 3d8f40d50..5a51d7a73 100644 --- a/src/stream/stream/position.rs +++ b/src/stream/stream/position.rs @@ -1,24 +1,21 @@ -use std::pin::Pin; use std::future::Future; - -use pin_project_lite::pin_project; +use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct PositionFuture { - #[pin] - stream: S, - predicate: P, - index:usize, - } +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct PositionFuture<'a, S, P> { + stream: &'a mut S, + predicate: P, + index: usize, } -impl PositionFuture { - pub(super) fn new(stream: S, predicate: P) -> Self { +impl<'a, S, P> Unpin for PositionFuture<'a, S, P> {} + +impl<'a, S, P> PositionFuture<'a, S, P> { + pub(super) fn new(stream: &'a mut S, predicate: P) -> Self { PositionFuture { stream, predicate, @@ -27,23 +24,25 @@ impl PositionFuture { } } -impl Future for PositionFuture +impl<'a, S, P> Future for PositionFuture<'a, S, P> where - S: Stream, - P: FnMut(&S::Item) -> bool, + S: Stream + Unpin, + P: FnMut(S::Item) -> bool, { type Output = Option; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - let next = futures_core::ready!(this.stream.poll_next(cx)); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); match next { - Some(v) if (this.predicate)(&v) => Poll::Ready(Some(*this.index)), - Some(_) => { - cx.waker().wake_by_ref(); - *this.index += 1; - Poll::Pending + Some(v) => { + if (&mut self.predicate)(v) { + Poll::Ready(Some(self.index)) + } else { + cx.waker().wake_by_ref(); + self.index += 1; + Poll::Pending + } } None => Poll::Ready(None), } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 6435d81c6..5cb273eeb 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; @@ -15,25 +14,23 @@ pin_project! { /// [`skip_while`]: trait.Stream.html#method.skip_while /// [`Stream`]: trait.Stream.html #[derive(Debug)] - pub struct SkipWhile { + pub struct SkipWhile { #[pin] stream: S, predicate: Option

, - __t: PhantomData, } } -impl SkipWhile { +impl SkipWhile { pub(crate) fn new(stream: S, predicate: P) -> Self { SkipWhile { stream, predicate: Some(predicate), - __t: PhantomData, } } } -impl Stream for SkipWhile +impl Stream for SkipWhile where S: Stream, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 9b2945fdb..08b5a86c9 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use pin_project_lite::pin_project; @@ -15,25 +14,23 @@ pin_project! { /// [`take_while`]: trait.Stream.html#method.take_while /// [`Stream`]: trait.Stream.html #[derive(Debug)] - pub struct TakeWhile { + pub struct TakeWhile { #[pin] stream: S, predicate: P, - __t: PhantomData, } } -impl TakeWhile { +impl TakeWhile { pub(super) fn new(stream: S, predicate: P) -> Self { TakeWhile { stream, predicate, - __t: PhantomData, } } } -impl Stream for TakeWhile +impl Stream for TakeWhile where S: Stream, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 636e406e9..560a0e410 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -22,7 +22,7 @@ pin_project! { } impl Timeout { - pub fn new(stream: S, dur: Duration) -> Timeout { + pub(crate) fn new(stream: S, dur: Duration) -> Timeout { let delay = Delay::new(dur); Timeout { stream, delay } diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index bf92ff51a..efb9e339f 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,58 +1,51 @@ -use std::marker::PhantomData; use std::pin::Pin; -use std::future::Future; - -use pin_project_lite::pin_project; +use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct TryFoldFuture { - #[pin] - stream: S, - f: F, - acc: Option, - __t: PhantomData, - } +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct TryFoldFuture<'a, S, F, T> { + stream: &'a mut S, + f: F, + acc: Option, } -impl TryFoldFuture { - pub(super) fn new(stream: S, init: T, f: F) -> Self { +impl<'a, S, F, T> Unpin for TryFoldFuture<'a, S, F, T> {} + +impl<'a, S, F, T> TryFoldFuture<'a, S, F, T> { + pub(super) fn new(stream: &'a mut S, init: T, f: F) -> Self { TryFoldFuture { stream, f, acc: Some(init), - __t: PhantomData, } } } -impl Future for TryFoldFuture +impl<'a, S, F, T, E> Future for TryFoldFuture<'a, S, F, T> where - S: Stream + Sized, + S: Stream + Unpin, F: FnMut(T, S::Item) -> Result, { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); match next { Some(v) => { - let old = this.acc.take().unwrap(); - let new = (this.f)(old, v); + let old = self.acc.take().unwrap(); + let new = (&mut self.f)(old, v); match new { - Ok(o) => *this.acc = Some(o), + Ok(o) => self.acc = Some(o), Err(e) => return Poll::Ready(Err(e)), } } - None => return Poll::Ready(Ok(this.acc.take().unwrap())), + None => return Poll::Ready(Ok(self.acc.take().unwrap())), } } } diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 36198f9e5..30e318502 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,52 +1,39 @@ use std::future::Future; -use std::marker::PhantomData; use std::pin::Pin; -use pin_project_lite::pin_project; - use crate::stream::Stream; use crate::task::{Context, Poll}; -pin_project! { - #[doc(hidden)] - #[allow(missing_debug_implementations)] - pub struct TryForEachFuture { - #[pin] - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, - } +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct TryForEachFuture<'a, S, F> { + stream: &'a mut S, + f: F, } -impl TryForEachFuture { - pub(crate) fn new(stream: S, f: F) -> Self { - TryForEachFuture { - stream, - f, - __from: PhantomData, - __to: PhantomData, - } +impl<'a, S, F> Unpin for TryForEachFuture<'a, S, F> {} + +impl<'a, S, F> TryForEachFuture<'a, S, F> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + TryForEachFuture { stream, f } } } -impl Future for TryForEachFuture +impl<'a, S, F, E> Future for TryForEachFuture<'a, S, F> where - S: Stream, - S::Item: std::fmt::Debug, + S: Stream + Unpin, F: FnMut(S::Item) -> Result<(), E>, { type Output = Result<(), E>; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - let item = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + let item = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); match item { None => return Poll::Ready(Ok(())), Some(v) => { - let res = (this.f)(v); + let res = (&mut self.f)(v); if let Err(e) = res { return Poll::Ready(Err(e)); } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 27681f375..f57d73590 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -7,7 +7,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - /// An iterator that iterates two other iterators simultaneously. + /// A stream that takes items from two other streams simultaneously. /// /// This `struct` is created by the [`zip`] method on [`Stream`]. See its /// documentation for more. From ef958f0408c713de6b93cb995d00244212472de8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 13:09:35 +0100 Subject: [PATCH 206/707] Use pin_project_lite instead for throttle --- src/stream/stream/mod.rs | 3 ++- src/stream/stream/throttle.rs | 36 +++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 756e8e960..a91517d8c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -315,7 +315,6 @@ extension_trait! { TakeWhile::new(self, predicate) } - #[cfg(all(feature = "default", feature = "unstable"))] #[doc = r#" Limit the amount of items yielded per timeslice in a stream. @@ -342,6 +341,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn throttle(self, d: Duration) -> Throttle where Self: Sized, diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 010839cc6..881360786 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -3,26 +3,25 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. -/// #[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Throttle { - stream: S, - duration: Duration, - delay: Option, +pin_project! { + /// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Throttle { + #[pin] + stream: S, + duration: Duration, + #[pin] + delay: Option, + } } -impl Unpin for Throttle {} - impl Throttle { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(duration: Duration); - pin_utils::unsafe_pinned!(delay: Option); - pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, @@ -35,23 +34,24 @@ impl Throttle { impl Stream for Throttle { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(d) = self.as_mut().delay().as_pin_mut() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if let Some(d) = this.delay.as_mut().as_pin_mut() { if d.poll(cx).is_ready() { - *self.as_mut().delay() = None; + this.delay.set(None); } else { return Poll::Pending; } } - match self.as_mut().stream().poll_next(cx) { + match this.stream.poll_next(cx) { Poll::Pending => { cx.waker().wake_by_ref(); // Continue driving even though emitting Pending Poll::Pending } Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - *self.as_mut().delay() = Some(Delay::new(self.duration)); + this.delay.set(Some(Delay::new(*this.duration))); Poll::Ready(Some(v)) } } From 5438258ceec7932fa407c5c7e0697c7ccec7d6f2 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 13:19:59 +0100 Subject: [PATCH 207/707] Remove unused import --- src/stream/from_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index e4078662c..24432c7eb 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -28,7 +28,6 @@ impl Unpin for FromFn {} /// # /// use async_std::prelude::*; /// use async_std::stream; -/// use async_std::sync::{Arc, Mutex}; /// /// let mut count = 0u8; /// let s = stream::from_fn(|| { From eea7af24db69585c7b51e3ff363cbc473a8ed84d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 10 Nov 2019 17:56:12 +0100 Subject: [PATCH 208/707] fix bugs in changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3568abf5b..374714330 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -388,8 +388,9 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.11...HEAD -[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.12...HEAD +[0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 +[0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 [0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10 [0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8 From 4aa9928ece3d968ea229842cbdaf53dd81fc11c2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 10 Nov 2019 18:14:46 +0100 Subject: [PATCH 209/707] v1.0.0 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 44 +++++++++++++++++++++++++++++- Cargo.toml | 2 +- docs/src/tutorial/specification.md | 4 +-- src/lib.rs | 6 ++-- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 374714330..c890f7a49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.0.0] - 2019-11-11 + +[API Documentation](https://docs.rs/async-std/1.0.0/async-std) + +This release marks the `1.0.0` release of async-std; a major milestone for our +development. This release itself mostly includes quality of life improvements +for all of modules, including more consistent API bounds for a lot of our +submodules. + +The biggest change is that we're now using the full semver range, +`major.minor.patch`, and any breaking changes to our "stable" APIs will require +an update of the `major` number. + +We're excited we've hit this milestone together with you all. Thank you! + +## Added + +- Added `Future::join` as "unstable", replacing `future::join!`. +- Added `Future::try_join` as "unstable", replacing `future::try_join!`. +- Enabled `stable` and `beta` channel testing on CI. +- Implemented `FromIterator` and `Extend` for `PathBuf`. +- Implemented `FromStream` for `PathBuf`. +- Loosened the trait bounds of `io::copy` on "unstable". + +## Changed + +- Added a `Sync` bound to `RwLock`, resolving a memory safety issue. +- Fixed a bug in `Stream::take_while` where it could continue after it should've + ended. +- Fixed a bug where our `attributes` Cargo feature wasn't working as intended. +- Improved documentation of `Stream::merge`, documenting ordering guarantees. +- Update doc imports in examples to prefer async-std's types. +- Various quality of life improvements to the `future` submodule. +- Various quality of life improvements to the `path` submodule. +- Various quality of life improvements to the `stream` submodule. + +## Removed + +- Removed `future::join!` in favor of `Future::join`. +- Removed `future::try_join!` in favor of `Future::try_join`. + # [0.99.12] - 2019-11-07 [API Documentation](https://docs.rs/async-std/0.99.12/async-std) @@ -388,7 +429,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.12...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 [0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 [0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10 diff --git a/Cargo.toml b/Cargo.toml index bdbeafa6f..c1c686555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.12" +version = "1.0.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index 322c49fac..c384ec243 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -50,6 +50,6 @@ Add the following lines to `Cargo.toml`: ```toml [dependencies] -futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await" ] } -async-std = "0.99" +futures = "0.3.0" +async-std = "1.00" ``` diff --git a/src/lib.rs b/src/lib.rs index 04ed8fb63..ddc6462ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,7 +154,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "0.99" +//! version = "1.0.0" //! features = ["unstable"] //! ``` //! @@ -167,7 +167,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "0.99" +//! version = "1.0.0" //! features = ["attributes"] //! ``` //! @@ -176,7 +176,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "0.99" +//! version = "1.0.0" //! default-features = false //! features = ["std"] //! ``` From 9ad0cf9f800e33776e99b7d5a4ded4c36d62a1df Mon Sep 17 00:00:00 2001 From: CosciaDiPollo <57640603+CosciaDiPollo@users.noreply.github.com> Date: Mon, 11 Nov 2019 21:14:55 +0100 Subject: [PATCH 210/707] Correct a typo on the async-std version (#508) Correct a typo on the async-std version in the Cargo.toml file of the documentation. --- docs/src/tutorial/specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index c384ec243..307c79e93 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -51,5 +51,5 @@ Add the following lines to `Cargo.toml`: ```toml [dependencies] futures = "0.3.0" -async-std = "1.00" +async-std = "1.0.0" ``` From 7d2282dbd284c80a5ae32f1a4d1c24234755f147 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Mon, 11 Nov 2019 22:11:06 +0100 Subject: [PATCH 211/707] fix merge conflict --- src/stream/stream/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 653261fb6..2f21c3842 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -68,11 +68,8 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; -<<<<<<< HEAD use count::CountFuture; -======= use cycle::Cycle; ->>>>>>> master use enumerate::Enumerate; use eq::EqFuture; use filter_map::FilterMap; From 37922408e56560eec2049ba8b57ca4217a203211 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Mon, 11 Nov 2019 22:17:29 +0100 Subject: [PATCH 212/707] use pin_project --- src/stream/stream/count.rs | 29 ++++++++++++++++------------- src/stream/stream/mod.rs | 6 +++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index b6d53ca84..221b0f0c4 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -1,20 +1,22 @@ +use std::future::Future; use std::pin::Pin; -use crate::future::Future; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct CountFuture { - stream: S, - count: usize, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct CountFuture { + #[pin] + stream: S, + count: usize, + } } impl CountFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(count: usize); - pub(crate) fn new(stream: S) -> Self { CountFuture { stream, count: 0 } } @@ -26,16 +28,17 @@ where { type Output = usize; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(_) => { cx.waker().wake_by_ref(); - *self.as_mut().count() += 1; + *this.count += 1; Poll::Pending } - None => Poll::Ready(self.count), + None => Poll::Ready(*this.count), } } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2f21c3842..d65922718 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1813,10 +1813,10 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s1 = VecDeque::from(vec![0]); - let s2 = VecDeque::from(vec![1, 2, 3]); + 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); From 6677d52c2df87eab7f06400f9fc1099d725c96f8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:35:29 +0100 Subject: [PATCH 213/707] Improve thread creating algorithm in spawn_blocking --- src/task/spawn_blocking.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 6076d1bcd..a93a68bf4 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -68,16 +68,13 @@ static POOL: Lazy = Lazy::new(|| { fn start_thread() { SLEEPING.fetch_add(1, Ordering::SeqCst); - - // Generate a random duration of time between 1 second and 10 seconds. If the thread doesn't - // receive the next task in this duration of time, it will stop running. - let timeout = Duration::from_millis(1000 + u64::from(random(9_000))); + let timeout = Duration::from_secs(10); thread::Builder::new() .name("async-std/blocking".to_string()) .spawn(move || { loop { - let task = match POOL.receiver.recv_timeout(timeout) { + let mut task = match POOL.receiver.recv_timeout(timeout) { Ok(task) => task, Err(_) => { // Check whether this is the last sleeping thread. @@ -100,8 +97,22 @@ fn start_thread() { start_thread(); } - // Run the task. - abort_on_panic(|| task.run()); + 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); } From 21c5c48cb6cbf2cd6ab687f4e7938bcb5e35cea3 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:37:54 +0100 Subject: [PATCH 214/707] Lower the timeout to 1 second --- src/task/spawn_blocking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index a93a68bf4..6cce082df 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -68,7 +68,7 @@ static POOL: Lazy = Lazy::new(|| { fn start_thread() { SLEEPING.fetch_add(1, Ordering::SeqCst); - let timeout = Duration::from_secs(10); + let timeout = Duration::from_secs(1); thread::Builder::new() .name("async-std/blocking".to_string()) From 1a50ffd144684199325cbd8a38fc0953373dc5ee Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:38:22 +0100 Subject: [PATCH 215/707] Delete unused import --- src/task/spawn_blocking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 6cce082df..578afa4e3 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -6,7 +6,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use once_cell::sync::Lazy; use crate::task::{JoinHandle, Task}; -use crate::utils::{abort_on_panic, random}; +use crate::utils::abort_on_panic; /// Spawns a blocking task. /// From b5b2b5a0a34c6a4ebaf078aa8b8f8eb02a6593ac Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 00:48:26 +0100 Subject: [PATCH 216/707] 1.0.1 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 14 +++++++++++++- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c890f7a49..37c14c73d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.0.1] - 2019-11-12 + +We were seeing a regression in our fs performance, caused by too many +long-running tasks. This patch fixes that regression by being more proactive +about closing down idle threads. + +## Changes + +- Improved thread startup/shutdown algorithm in spawn_blocking. +- Fixed a typo in the tutorial. + # [1.0.0] - 2019-11-11 [API Documentation](https://docs.rs/async-std/1.0.0/async-std) @@ -429,7 +440,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD +[1.0.0]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 [0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 diff --git a/Cargo.toml b/Cargo.toml index c1c686555..4d19a934a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.0.0" +version = "1.0.1" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 9d7b2d6696ad369a5710700a7b4c3321fcd05cd8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 01:34:07 +0100 Subject: [PATCH 217/707] fix changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c14c73d..9d19fddb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [1.0.1] - 2019-11-12 +[API Documentation](https://docs.rs/async-std/1.0.1/async-std) + We were seeing a regression in our fs performance, caused by too many long-running tasks. This patch fixes that regression by being more proactive about closing down idle threads. ## Changes -- Improved thread startup/shutdown algorithm in spawn_blocking. +- Improved thread startup/shutdown algorithm in `task::spawn_blocking`. - Fixed a typo in the tutorial. # [1.0.0] - 2019-11-11 @@ -441,7 +443,7 @@ task::blocking(async { - Initial beta release [Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD -[1.0.0]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 +[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 [0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 From 0d5c7a217fac0ff611c4e0ad59d8480a4314c454 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 02:09:38 +0100 Subject: [PATCH 218/707] stabilize task::yield_now Signed-off-by: Yoshua Wuyts --- src/task/mod.rs | 8 +++----- src/task/yield_now.rs | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index bcdea72c2..198e57870 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -124,6 +124,9 @@ cfg_std! { #[doc(inline)] pub use async_macros::ready; + + pub use yield_now::yield_now; + mod yield_now; } cfg_default! { @@ -157,8 +160,3 @@ cfg_default! { #[cfg(not(any(feature = "unstable", test)))] pub(crate) use spawn_blocking::spawn_blocking; } - -cfg_unstable! { - pub use yield_now::yield_now; - mod yield_now; -} diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 03f83e2e9..403069663 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -26,8 +26,6 @@ use crate::task::{Context, Poll}; /// # /// # }) /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub async fn yield_now() { YieldNow(false).await From 1b7d5bea6b4fdda52deeeb1c4e0f76533bb6ecb3 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 12 Nov 2019 12:19:11 +0000 Subject: [PATCH 219/707] Enable surf example 1.0.3 has been released with the required change --- Cargo.toml | 2 +- examples/surf-web.rs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d19a934a..a005404b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ slab = { version = "0.4.2", optional = true } [dev-dependencies] femme = "1.2.0" rand = "0.7.2" -# surf = "1.0.2" +surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.0" diff --git a/examples/surf-web.rs b/examples/surf-web.rs index b3101d15c..df139e5b5 100644 --- a/examples/surf-web.rs +++ b/examples/surf-web.rs @@ -1,6 +1,3 @@ -/* TODO: Once the next version of surf released, re-enable this example. -//! Sends an HTTP request to the Rust website. - use async_std::task; fn main() -> Result<(), surf::Exception> { @@ -18,6 +15,3 @@ fn main() -> Result<(), surf::Exception> { Ok(()) }) } -*/ - -fn main() {} From 7c7386735ef013f29a72cea801689a71e3db74ac Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:34:31 +0100 Subject: [PATCH 220/707] Wrap around throttle comment Co-Authored-By: Yoshua Wuyts --- src/stream/stream/throttle.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 881360786..44ce7e165 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -9,7 +9,13 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - /// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. + /// A stream that only yields one element once every `duration`. + /// + /// This `struct` is created by the [`throttle`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`throttle`]: trait.Stream.html#method.throttle + /// [`Stream`]: trait.Stream.html #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct Throttle { From 6f6d5e9d205765a6676a08c74e00c3f6001da93e Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:35:03 +0100 Subject: [PATCH 221/707] Updated throttle fn comments. Co-Authored-By: Yoshua Wuyts --- src/stream/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index a91517d8c..6a6d43d0e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -318,6 +318,7 @@ extension_trait! { #[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 ```ignore # fn main() { async_std::task::block_on(async { From 88cbf2c119371830714e6e26417a00439e968659 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:30:28 +0100 Subject: [PATCH 222/707] Change throttle test to run in milliseconds --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index a91517d8c..6a7f89fd2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -319,23 +319,23 @@ extension_trait! { Limit the amount of items yielded per timeslice in a stream. # Examples - ```ignore + ``` # fn main() { async_std::task::block_on(async { # + use async_std::prelude::*; use async_std::stream; use std::time::Duration; // emit value every 1 second - let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + let s = stream::interval(Duration::from_millis(5)).enumerate().take(3); // throttle for 2 seconds - let s = s.throttle(Duration::from_secs(2)); + let mut s = s.throttle(Duration::from_millis(10)); - s.for_each(|(n, _)| { - dbg!(n); - }) - .await; - // => 0 .. 1 .. 2 .. 3 + assert_eq!(s.next().await, Some((0, ()))); + assert_eq!(s.next().await, Some((1, ()))); + assert_eq!(s.next().await, Some((2, ()))); + assert_eq!(s.next().await, None); // with a pause of 2 seconds between each print # # }) } From 6990c1403f85f598f5bd3536a9a01ef7c5464a31 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 15:07:20 +0100 Subject: [PATCH 223/707] Reimplemented throttle to never drop Delay, added boolean flag --- src/stream/stream/mod.rs | 2 +- src/stream/stream/throttle.rs | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cc1646cd6..48a1a2011 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -318,7 +318,7 @@ extension_trait! { #[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. + 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 { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 44ce7e165..8896899ed 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; +use std::time::{Duration, Instant}; use futures_timer::Delay; use pin_project_lite::pin_project; @@ -23,7 +23,9 @@ pin_project! { stream: S, duration: Duration, #[pin] - delay: Option, + blocked: bool, + #[pin] + delay: Delay, } } @@ -32,7 +34,8 @@ impl Throttle { Throttle { stream, duration, - delay: None, + blocked: false, + delay: Delay::new(Duration::default()), } } } @@ -42,9 +45,10 @@ impl Stream for Throttle { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); - if let Some(d) = this.delay.as_mut().as_pin_mut() { + if *this.blocked { + let d = this.delay.as_mut(); if d.poll(cx).is_ready() { - this.delay.set(None); + *this.blocked = false; } else { return Poll::Pending; } @@ -57,7 +61,8 @@ impl Stream for Throttle { } Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - this.delay.set(Some(Delay::new(*this.duration))); + *this.blocked = true; + this.delay.reset(Instant::now() + *this.duration); Poll::Ready(Some(v)) } } From 4ab7b213de8d8f8307f0a1877cb4a4d4c802e792 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 15:38:02 +0100 Subject: [PATCH 224/707] Updated example to be consistent; added timing measurements to throttle --- examples/throttle.rs | 2 +- src/stream/stream/mod.rs | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/throttle.rs b/examples/throttle.rs index 1b9a6f2ec..74c1fd30f 100644 --- a/examples/throttle.rs +++ b/examples/throttle.rs @@ -11,7 +11,7 @@ fn main() { use std::time::Duration; // emit value every 1 second - let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + let s = stream::interval(Duration::from_secs(1)).enumerate(); // throttle for 2 seconds let s = s.throttle(Duration::from_secs(2)); diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 48a1a2011..cec874fcd 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -325,19 +325,32 @@ extension_trait! { # use async_std::prelude::*; use async_std::stream; - use std::time::Duration; + use std::time::{Duration, Instant}; - // emit value every 1 second - let s = stream::interval(Duration::from_millis(5)).enumerate().take(3); + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)) + .enumerate() + .take(3); - // throttle for 2 seconds + // throttle for 10 milliseconds let mut s = s.throttle(Duration::from_millis(10)); + let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 5 && duration_ms < 15); + assert_eq!(s.next().await, Some((1, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 15 && duration_ms < 25); + assert_eq!(s.next().await, Some((2, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 25 && duration_ms < 35); + assert_eq!(s.next().await, None); - // with a pause of 2 seconds between each print + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 35 && duration_ms < 45); # # }) } ``` From c5b3a98e5b4f12f80f5e9b5ddb135c22da60502f Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 16:22:25 +0100 Subject: [PATCH 225/707] Increased throttle test to 10x time --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cec874fcd..86645fce1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,30 +327,30 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; - // emit value every 5 milliseconds - let s = stream::interval(Duration::from_millis(5)) + // emit value every 50 milliseconds + let s = stream::interval(Duration::from_millis(50)) .enumerate() .take(3); - // throttle for 10 milliseconds - let mut s = s.throttle(Duration::from_millis(10)); + // throttle for 100 milliseconds + let mut s = s.throttle(Duration::from_millis(100)); let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 5 && duration_ms < 15); + assert!(duration_ms >= 50 && duration_ms < 150); assert_eq!(s.next().await, Some((1, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 15 && duration_ms < 25); + assert!(duration_ms >= 150 && duration_ms < 250); assert_eq!(s.next().await, Some((2, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 25 && duration_ms < 35); + assert!(duration_ms >= 250 && duration_ms < 350); assert_eq!(s.next().await, None); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 35 && duration_ms < 45); + assert!(duration_ms >= 350 && duration_ms < 450); # # }) } ``` From 74a7d93611119ac9affea692d77745bcc3abaad8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 17:34:13 +0100 Subject: [PATCH 226/707] upgrade async-macros to 2.0.0 (#519) Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a005404b7..e9207395e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ std = [ [dependencies] async-attributes = { version = "1.1.0", optional = true } -async-macros = { version = "1.0.0", optional = true } +async-macros = { version = "2.0.0", optional = true } 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.3.9", optional = true } From f611ceccc87aaf6b6cfb5bdee8bfffaffee5498f Mon Sep 17 00:00:00 2001 From: Devashish Dixit Date: Wed, 13 Nov 2019 00:47:03 +0800 Subject: [PATCH 227/707] Run cargo fmt for doc comments (#515) --- rustfmt.toml | 1 + src/macros.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 1082fd888..c6d404e21 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,2 @@ version = "Two" +format_code_in_doc_comments = true diff --git a/src/macros.rs b/src/macros.rs index b7811d2ea..638a2348b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,8 +23,8 @@ /// ``` /// # async_std::task::block_on(async { /// # -/// use async_std::prelude::*; /// use async_std::io; +/// use async_std::prelude::*; /// use async_std::print; /// /// print!("this ").await; @@ -181,8 +181,8 @@ macro_rules! eprintln { /// # /// use std::cell::Cell; /// -/// use async_std::task; /// use async_std::prelude::*; +/// use async_std::task; /// /// task_local! { /// static VAL: Cell = Cell::new(5); From f0875d2dca8428f140c709bfc45f651748692e7b Mon Sep 17 00:00:00 2001 From: Grzegorz Gierlach Date: Tue, 12 Nov 2019 19:34:08 +0100 Subject: [PATCH 228/707] Cleaning up stream pinning. --- src/collections/binary_heap/from_stream.rs | 2 -- src/collections/btree_map/from_stream.rs | 2 -- src/collections/btree_set/from_stream.rs | 2 -- src/collections/hash_map/from_stream.rs | 2 -- src/collections/hash_set/from_stream.rs | 2 -- src/collections/linked_list/from_stream.rs | 2 -- src/collections/vec_deque/from_stream.rs | 2 -- src/option/from_stream.rs | 2 -- src/option/product.rs | 2 -- src/option/sum.rs | 2 -- src/path/pathbuf.rs | 7 ++----- src/result/from_stream.rs | 2 -- src/result/product.rs | 2 -- src/result/sum.rs | 2 -- src/stream/from_fn.rs | 3 +-- src/stream/repeat_with.rs | 4 +--- src/string/extend.rs | 10 ---------- src/string/from_stream.rs | 10 ---------- src/unit/extend.rs | 2 +- src/vec/from_stream.rs | 10 ---------- 20 files changed, 5 insertions(+), 67 deletions(-) diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index 148a57f40..6851948e6 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for BinaryHeap { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BinaryHeap::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index e0653ab5b..853122361 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream<(K, V)> for BTreeMap { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeMap::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index c4197df44..318af9e65 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for BTreeSet { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeSet::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index bf47d8e79..d74a7ccfa 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashMap::with_hasher(Default::default()); stream::extend(&mut out, stream).await; out diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index 69b38538e..dc5e61e39 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashSet::with_hasher(Default::default()); stream::extend(&mut out, stream).await; out diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index 122624711..d93bbb7be 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for LinkedList { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = LinkedList::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 767ec068e..241bd74e9 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for VecDeque { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = VecDeque::new(); stream::extend(&mut out, stream).await; out diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index d2d53b600..867911433 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = false; diff --git a/src/option/product.rs b/src/option/product.rs index 9b7274ff0..abaab73ed 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -39,8 +39,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; diff --git a/src/option/sum.rs b/src/option/sum.rs index 5c154f422..d2e44830f 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -34,8 +34,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 56a63a47e..808acb2eb 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -327,8 +327,6 @@ impl> stream::Extend

for PathBuf { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push(item.as_ref()); } @@ -342,10 +340,9 @@ impl<'b, P: AsRef + 'b> FromStream

for PathBuf { fn from_stream<'a, S: IntoStream + 'a>( stream: S, ) -> Pin + 'a>> { - Box::pin(async move { - let stream = stream.into_stream(); - pin_utils::pin_mut!(stream); + let stream = stream.into_stream(); + Box::pin(async move { let mut out = Self::new(); stream::extend(&mut out, stream).await; out diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 9296797d1..a8490d691 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; diff --git a/src/result/product.rs b/src/result/product.rs index fd242168f..ec9d94a80 100644 --- a/src/result/product.rs +++ b/src/result/product.rs @@ -39,8 +39,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; diff --git a/src/result/sum.rs b/src/result/sum.rs index dd687723c..ccc4240e0 100644 --- a/src/result/sum.rs +++ b/src/result/sum.rs @@ -39,8 +39,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 24432c7eb..3ace65835 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ impl Unpin for FromFn {} /// use async_std::stream; /// /// let mut count = 0u8; -/// let s = stream::from_fn(|| { +/// let mut s = stream::from_fn(|| { /// count += 1; /// if count > 3 { /// None @@ -39,7 +39,6 @@ impl Unpin for FromFn {} /// } /// }); /// -/// pin_utils::pin_mut!(s); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(2)); /// assert_eq!(s.next().await, Some(3)); diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index e183a77ca..954693d81 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -28,9 +28,7 @@ impl Unpin for RepeatWith {} /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::repeat_with(|| 1); -/// -/// pin_utils::pin_mut!(s); +/// let mut s = stream::repeat_with(|| 1); /// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); diff --git a/src/string/extend.rs b/src/string/extend.rs index 55bec0c55..43bd46d61 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -13,8 +13,6 @@ impl stream::Extend for String { self.reserve(stream.size_hint().0); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push(item); } @@ -30,8 +28,6 @@ impl<'b> stream::Extend<&'b char> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push(*item); } @@ -47,8 +43,6 @@ impl<'b> stream::Extend<&'b str> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push_str(item); } @@ -64,8 +58,6 @@ impl stream::Extend for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push_str(&item); } @@ -81,8 +73,6 @@ impl<'b> stream::Extend> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(item) = stream.next().await { self.push_str(&item); } diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs index eb6818c15..375ac3715 100644 --- a/src/string/from_stream.rs +++ b/src/string/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); stream::extend(&mut out, stream).await; out @@ -29,8 +27,6 @@ impl<'b> FromStream<&'b char> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); stream::extend(&mut out, stream).await; out @@ -46,8 +42,6 @@ impl<'b> FromStream<&'b str> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); stream::extend(&mut out, stream).await; out @@ -63,8 +57,6 @@ impl FromStream for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); stream::extend(&mut out, stream).await; out @@ -80,8 +72,6 @@ impl<'b> FromStream> for String { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); stream::extend(&mut out, stream).await; out diff --git a/src/unit/extend.rs b/src/unit/extend.rs index 27f5d4e96..5b0bc1d10 100644 --- a/src/unit/extend.rs +++ b/src/unit/extend.rs @@ -9,8 +9,8 @@ impl stream::Extend<()> for () { stream: T, ) -> Pin + 'a>> { let stream = stream.into_stream(); + Box::pin(async move { - pin_utils::pin_mut!(stream); while let Some(_) = stream.next().await {} }) } diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index cdd4767dc..e88e8202e 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -17,8 +17,6 @@ impl FromStream for Vec { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = vec![]; stream::extend(&mut out, stream).await; out @@ -34,8 +32,6 @@ impl<'b, T: Clone> FromStream for Cow<'b, [T]> { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Cow::Owned(FromStream::from_stream(stream).await) }) } @@ -49,8 +45,6 @@ impl FromStream for Box<[T]> { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into_boxed_slice() }) } @@ -64,8 +58,6 @@ impl FromStream for Rc<[T]> { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into() }) } @@ -79,8 +71,6 @@ impl FromStream for Arc<[T]> { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into() }) } From e442eba625fb881dedc7572cf591e7f8b51ef0d0 Mon Sep 17 00:00:00 2001 From: Grzegorz Gierlach Date: Tue, 12 Nov 2019 19:51:58 +0100 Subject: [PATCH 229/707] Cleaning up stream pinning. --- src/path/pathbuf.rs | 2 ++ src/stream/from_fn.rs | 4 +++- src/stream/repeat_with.rs | 4 +++- src/string/extend.rs | 10 ++++++++++ src/unit/extend.rs | 2 ++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 808acb2eb..e684df89f 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -327,6 +327,8 @@ impl> stream::Extend

for PathBuf { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(item.as_ref()); } diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 3ace65835..8067176e7 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ impl Unpin for FromFn {} /// use async_std::stream; /// /// let mut count = 0u8; -/// let mut s = stream::from_fn(|| { +/// let s = stream::from_fn(|| { /// count += 1; /// if count > 3 { /// None @@ -39,6 +39,8 @@ impl Unpin for FromFn {} /// } /// }); /// +/// pin_utils::pin_mut!(s); +/// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(2)); /// assert_eq!(s.next().await, Some(3)); diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index 954693d81..e183a77ca 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -28,7 +28,9 @@ impl Unpin for RepeatWith {} /// use async_std::prelude::*; /// use async_std::stream; /// -/// let mut s = stream::repeat_with(|| 1); +/// let s = stream::repeat_with(|| 1); +/// +/// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); diff --git a/src/string/extend.rs b/src/string/extend.rs index 43bd46d61..55bec0c55 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -13,6 +13,8 @@ impl stream::Extend for String { self.reserve(stream.size_hint().0); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(item); } @@ -28,6 +30,8 @@ impl<'b> stream::Extend<&'b char> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(*item); } @@ -43,6 +47,8 @@ impl<'b> stream::Extend<&'b str> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(item); } @@ -58,6 +64,8 @@ impl stream::Extend for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(&item); } @@ -73,6 +81,8 @@ impl<'b> stream::Extend> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(&item); } diff --git a/src/unit/extend.rs b/src/unit/extend.rs index 5b0bc1d10..55c8e0d08 100644 --- a/src/unit/extend.rs +++ b/src/unit/extend.rs @@ -11,6 +11,8 @@ impl stream::Extend<()> for () { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(_) = stream.next().await {} }) } From 2dfdc1c4821c097066f93f64d49abe035616c9dc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:07:39 +0100 Subject: [PATCH 230/707] polish lib.rs examples Signed-off-by: Yoshua Wuyts --- src/lib.rs | 50 ++++++++++++++++++++++++++++++++++++++------ src/task/block_on.rs | 8 ++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ddc6462ca..5442909f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,17 +131,55 @@ //! //! # Examples //! -//! Spawn a task and block the current thread on its result: +//! All examples require the [`"attributes"` feature](#features) to be enabled. +//! This feature is not enabled by default because it significantly impacts +//! compile times. See [`task::block_on`] for an alternative way to start +//! executing tasks. //! +//! Call an async function from the main function: +//! +//! ``` +//! async fn say_hello() { +//! println!("Hello, world!"); +//! } +//! +//! #[async_std::main] +//! async fn main() { +//! say_hello().await; +//! } +//! ``` +//! +//! Await two futures concurrently, and return a tuple of their output: +//! +//! ``` +//! #[async_std::main] +//! async fn main() { +//! let a = || async move { 1u8 }; +//! let b = || async move { 2u8 }; +//! assert_eq!(a.join(b).await, (1u8, 2u8)) +//! } //! ``` -//! use async_std::task; //! -//! fn main() { -//! task::block_on(async { -//! println!("Hello, world!"); -//! }) +//! Create a UDP server that echoes back each received message to the sender: +//! +//! ```no_run +//! use async_std::net::UdpSocket; +//! +//! #[async_std::main] +//! async fn main() -> std::io::Result<()> { +//! let mut socket = UdpSocket::bind("127.0.0.1:8080")?; +//! println!("Listening on {}", socket.local_addr()?); +//! +//! let mut buf = vec![0u8; 1024]; +//! +//! loop { +//! let (recv, peer) = socket.recv_from(&mut buf).await?; +//! let sent = socket.send_to(&buf[..recv], &peer).await?; +//! println!("Sent {} out of {} bytes to {}", sent, recv, peer); +//! } //! } //! ``` +//! [`task::block_on`]: task/fn.block_on.html //! //! # Features //! diff --git a/src/task/block_on.rs b/src/task/block_on.rs index f61a22b6a..80259c579 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -28,9 +28,11 @@ use crate::task::{Context, Poll, Task, Waker}; /// ```no_run /// use async_std::task; /// -/// task::block_on(async { -/// println!("Hello, world!"); -/// }) +/// fn main() { +/// task::block_on(async { +/// println!("Hello, world!"); +/// }) +/// } /// ``` pub fn block_on(future: F) -> T where From 1431ee04220bd3292f38a913523e4b432997a41b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:25:52 +0100 Subject: [PATCH 231/707] polish README.md examples Signed-off-by: Yoshua Wuyts --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9af20a39f..3c074fee5 100644 --- a/README.md +++ b/README.md @@ -75,19 +75,21 @@ syntax. ## Examples ```rust -use async_std::task; +async fn say_hello() { + println!("Hello, world!"); +} -fn main() { - task::block_on(async { - println!("Hello, world!"); - }) +#[async_std::main] +async fn main() { + say_hello().await; } ``` More examples, including networking and file access, can be found in our -[`examples`] directory. +[`examples`] directory and in our [documentation]. [`examples`]: https://github.com/async-rs/async-std/tree/master/examples +[documentation]: https://docs.rs/async-std#examples ## Philosophy From 79962e20a5a9fe346a02e2eefd258569259732d8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:37:43 +0100 Subject: [PATCH 232/707] enable attributes feature Signed-off-by: Yoshua Wuyts --- .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 99436b72b..1d14e21d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --features unstable + args: --all --features unstable attributes check_fmt_and_docs: name: Checking fmt and docs From 879af6dc857d37f51dc48c638b06ee1a922dade9 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 10:50:09 +0800 Subject: [PATCH 233/707] Add Stream max --- src/stream/stream/max.rs | 60 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 35 +++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/stream/stream/max.rs diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs new file mode 100644 index 000000000..d8ff119d2 --- /dev/null +++ b/src/stream/stream/max.rs @@ -0,0 +1,60 @@ +use std::cmp::{Ord, Ordering}; +use std::marker::PhantomData; +use std::pin::Pin; +use std::future::Future; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxFuture { + #[pin] + stream: S, + _compare: PhantomData, + max: Option, + } +} + +impl MaxFuture { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + _compare: PhantomData, + max: None, + } + } +} + +impl Future for MaxFuture +where + S: Stream, + S::Item: Ord, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.max.take() { + None => *this.max = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take()), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5756a21e6..a3cf05521 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -44,6 +44,7 @@ mod last; mod le; mod lt; mod map; +mod max; mod max_by; mod max_by_key; mod min; @@ -80,6 +81,7 @@ use gt::GtFuture; use last::LastFuture; use le::LeFuture; use lt::LtFuture; +use max::MaxFuture; use max_by::MaxByFuture; use max_by_key::MaxByKeyFuture; use min::MinFuture; @@ -913,6 +915,39 @@ extension_trait! { } #[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. + + # Examples + + ```ignore + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1usize, 2, 3]); + + let max = s.clone().max().await; + assert_eq!(max, Some(3)); + + let max = stream::empty::().max().await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max( + self, + ) -> impl Future> [MaxFuture] + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MaxFuture::new(self) + } + + #[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. From 9d634cb2a7cc045d3e3b8da0ad1e40ffc3cb525a Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 12:42:59 +0800 Subject: [PATCH 234/707] refactor io dir to be same with std --- src/io/buf_reader.rs | 6 ++---- src/io/buf_writer.rs | 6 ++---- src/io/mod.rs | 2 ++ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 1d00b526c..e6d8e669c 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -4,11 +4,9 @@ use std::{cmp, fmt}; use pin_project_lite::pin_project; -use crate::io::{self, BufRead, Read, Seek, SeekFrom}; +use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; use crate::task::{Context, Poll}; -const DEFAULT_CAPACITY: usize = 8 * 1024; - pin_project! { /// Adds buffering to any reader. /// @@ -72,7 +70,7 @@ impl BufReader { /// # Ok(()) }) } /// ``` pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_CAPACITY, inner) + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new buffered reader with the specified capacity. diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 8fa9eba4e..35b511f80 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -4,11 +4,9 @@ use std::pin::Pin; use pin_project_lite::pin_project; use crate::io::write::WriteExt; -use crate::io::{self, Seek, SeekFrom, Write}; +use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE}; use crate::task::{Context, Poll, ready}; -const DEFAULT_CAPACITY: usize = 8 * 1024; - pin_project! { /// Wraps a writer and buffers its output. /// @@ -107,7 +105,7 @@ impl BufWriter { /// # Ok(()) }) } /// ``` pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_CAPACITY, inner) + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new `BufWriter` with the specified buffer capacity. diff --git a/src/io/mod.rs b/src/io/mod.rs index 4e8323052..065aaab50 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -269,6 +269,8 @@ //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap +const DEFAULT_BUF_SIZE: usize = 8 * 1024; + cfg_std! { #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; From 5adb112a00b7854f16025a6f343796723c444fe1 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 13:52:16 +0800 Subject: [PATCH 235/707] export IntoInnerError for io --- src/io/buf_writer.rs | 26 +++++++++++++++++++++++++- src/io/mod.rs | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 35b511f80..ce6a97b37 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -85,8 +85,32 @@ pin_project! { } } +/// An error returned by `into_inner` which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// use async_std::io::BufWriter; +/// use async_std::net::TcpStream; +/// +/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?); +/// +/// // unwrap the TcpStream and flush the buffer +/// let stream = match buf_writer.into_inner().await { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// # +/// # Ok(()) }) } +///``` #[derive(Debug)] -pub struct IntoInnerError(W, std::io::Error); +pub struct IntoInnerError(W, 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/mod.rs b/src/io/mod.rs index 065aaab50..0c8144b5a 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -277,7 +277,7 @@ cfg_std! { pub use buf_read::{BufRead, Lines}; pub use buf_reader::BufReader; - pub use buf_writer::BufWriter; + pub use buf_writer::{BufWriter, IntoInnerError}; pub use copy::copy; pub use cursor::Cursor; pub use empty::{empty, Empty}; From 6f4bea07a11a07b2b1d8a0c9a35a65903bc56e36 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 13 Nov 2019 15:27:29 +0100 Subject: [PATCH 236/707] Update version requirements in the tutorial --- docs/src/tutorial/specification.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index 307c79e93..7b1a01670 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -38,18 +38,10 @@ $ cargo new a-chat $ cd a-chat ``` -At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience: - -```bash -$ rustup override add nightly -$ rustc --version -rustc 1.38.0-nightly (c4715198b 2019-08-05) -``` - Add the following lines to `Cargo.toml`: ```toml [dependencies] futures = "0.3.0" -async-std = "1.0.0" +async-std = "1" ``` From 0c2282ffdc63fa1c9d1aab8d836675279805207c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 13 Nov 2019 20:32:37 +0100 Subject: [PATCH 237/707] Optimization: a slot for the next task to run (#529) * Optimization: a slot for the next task to run * Only notify workers when a task is pushed into a queue --- benches/mutex.rs | 4 +- src/sync/mutex.rs | 1 + src/sync/waker_set.rs | 5 +- src/task/executor/pool.rs | 131 +++++++++++++++++++++++++------------- 4 files changed, 91 insertions(+), 50 deletions(-) diff --git a/benches/mutex.rs b/benches/mutex.rs index b159ba127..4f1910a6f 100644 --- a/benches/mutex.rs +++ b/benches/mutex.rs @@ -2,9 +2,7 @@ extern crate test; -use std::sync::Arc; - -use async_std::sync::Mutex; +use async_std::sync::{Arc, Mutex}; use async_std::task; use test::Bencher; diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 2c0ac0cc3..4d2cf2512 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -170,6 +170,7 @@ impl Mutex { /// # /// # }) /// ``` + #[inline] pub fn try_lock(&self) -> Option> { if !self.locked.swap(true, Ordering::SeqCst) { Some(MutexGuard(self)) diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 5ba4cfbd9..7e897af15 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -60,6 +60,7 @@ impl WakerSet { } /// Inserts a waker for a blocked operation and returns a key associated with it. + #[cold] pub fn insert(&self, cx: &Context<'_>) -> usize { let w = cx.waker().clone(); let mut inner = self.lock(); @@ -70,6 +71,7 @@ impl WakerSet { } /// Removes the waker of an operation. + #[cold] pub fn remove(&self, key: usize) { let mut inner = self.lock(); @@ -81,6 +83,7 @@ impl WakerSet { /// Removes the waker of a cancelled operation. /// /// Returns `true` if another blocked operation from the set was notified. + #[cold] pub fn cancel(&self, key: usize) -> bool { let mut inner = self.lock(); @@ -147,6 +150,7 @@ impl WakerSet { /// Notifies blocked operations, either one or all of them. /// /// Returns `true` if at least one operation was notified. + #[cold] fn notify(&self, n: Notify) -> bool { let mut inner = &mut *self.lock(); let mut notified = false; @@ -172,7 +176,6 @@ impl WakerSet { } /// Locks the list of entries. - #[cold] fn lock(&self) -> Lock<'_> { let backoff = Backoff::new(); while self.flag.fetch_or(LOCKED, Ordering::Acquire) & LOCKED != 0 { diff --git a/src/task/executor/pool.rs b/src/task/executor/pool.rs index 1e743844a..08694dd4f 100644 --- a/src/task/executor/pool.rs +++ b/src/task/executor/pool.rs @@ -1,10 +1,11 @@ -use std::cell::UnsafeCell; +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; @@ -32,9 +33,18 @@ static POOL: Lazy = Lazy::new(|| { 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(|| abort_on_panic(|| main_loop(worker))) + .spawn(|| { + let _ = PROCESSOR.with(|p| p.set(proc)); + abort_on_panic(|| main_loop()); + }) .expect("cannot start a thread driving tasks"); } @@ -45,59 +55,75 @@ static POOL: Lazy = Lazy::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! { - /// Local task queue associated with the current worker thread. - static QUEUE: UnsafeCell>> = UnsafeCell::new(None); + /// Worker thread state. + static PROCESSOR: OnceCell = OnceCell::new(); } /// Schedules a new runnable task for execution. pub(crate) fn schedule(task: Runnable) { - QUEUE.with(|queue| { - let local = unsafe { (*queue.get()).as_ref() }; - - // If the current thread is a worker thread, push the task into its local task queue. - // Otherwise, push it into the global task queue. - match local { - None => POOL.injector.push(task), - Some(q) => q.push(task), + 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(); + } } - }); - - // Notify a sleeping worker that new work just came in. - POOL.sleepers.notify_one(); + }) } /// Main loop running a worker thread. -fn main_loop(local: Worker) { - // Initialize the local task queue. - QUEUE.with(|queue| unsafe { *queue.get() = Some(local) }); +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 step = 0; + let mut fails = 0; loop { // Try to find a runnable task. match find_runnable() { Some(task) => { - // Found. Now run the task. + fails = 0; + + // Run the found task. task.run(); - step = 0; } None => { + fails += 1; + // Yield the current thread or put it to sleep. - match step { - 0..=2 => { - thread::yield_now(); - step += 1; - } - 3 => { - thread::sleep(Duration::from_micros(10)); - step += 1; - } - _ => { - POOL.sleepers.wait(); - step = 0; - } + if fails <= YIELDS { + thread::yield_now(); + } else if fails <= YIELDS + SLEEPS { + thread::sleep(Duration::from_micros(10)); + } else { + POOL.sleepers.wait(); + fails = 0; } } } @@ -106,29 +132,42 @@ fn main_loop(local: Worker) { /// Find the next runnable task. fn find_runnable() -> Option { - let pool = &*POOL; - - QUEUE.with(|queue| { - let local = unsafe { (*queue.get()).as_ref().unwrap() }; + /// 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. - local.pop().or_else(|| { + 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(&local) + 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 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 rotated = r.iter().chain(l.iter()); - rotated.map(|s| s.steal_batch_and_pop(&local)).collect() + 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. From 8473b738d05bfe47d477dbc49bf5bf14550e68e4 Mon Sep 17 00:00:00 2001 From: sclaire-1 <54961957+sclaire-1@users.noreply.github.com> Date: Wed, 13 Nov 2019 16:33:44 -0800 Subject: [PATCH 238/707] Edit tutorial index.md Edited the structure of sentences to make it easier to read --- docs/src/tutorial/index.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md index 99ddf8eb3..aee0b3f40 100644 --- a/docs/src/tutorial/index.md +++ b/docs/src/tutorial/index.md @@ -1,11 +1,14 @@ # Tutorial: Writing a chat -Nothing is as simple as a chat server, right? Not quite, chat servers -already expose you to all the fun of asynchronous programming: how -do you handle clients connecting concurrently. How do you handle them disconnecting? +Nothing is simpler than creating a chat server, right? +Not quite, chat servers expose you to all the fun of asynchronous programming: -How do you distribute the messages? +How will the server handle clients connecting concurrently? -In this tutorial, we will show you how to write one in `async-std`. +How will it handle them disconnecting? + +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). From 90c67c223a383feaf7f898f6bbfd8b7ac4feee89 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 10:26:56 +0100 Subject: [PATCH 239/707] Decreased throttle test time to original values; only test lower bound --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 86645fce1..99a112036 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,30 +327,30 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; - // emit value every 50 milliseconds - let s = stream::interval(Duration::from_millis(50)) + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)) .enumerate() .take(3); - // throttle for 100 milliseconds - let mut s = s.throttle(Duration::from_millis(100)); + // throttle for 10 milliseconds + let mut s = s.throttle(Duration::from_millis(10)); let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 50 && duration_ms < 150); + assert!(duration_ms >= 5); assert_eq!(s.next().await, Some((1, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 150 && duration_ms < 250); + assert!(duration_ms >= 15); assert_eq!(s.next().await, Some((2, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 250 && duration_ms < 350); + assert!(duration_ms >= 25); assert_eq!(s.next().await, None); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 350 && duration_ms < 450); + assert!(duration_ms >= 35); # # }) } ``` From 9ebe41f2d62f41aef481b2b4d780e6309080ade0 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Thu, 14 Nov 2019 10:34:09 +0100 Subject: [PATCH 240/707] Update src/stream/stream/mod.rs Co-Authored-By: nasa --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d65922718..98c63ff9b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1824,7 +1824,7 @@ extension_trait! { # }) } ``` "#] - fn count(self) -> impl Future [CountFuture] + fn count(self) -> impl Future [CountFuture] where Self: Sized, { From dda65cbff0c9a68b0c6efda2a61754065fdee4dc Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 11:29:49 +0100 Subject: [PATCH 241/707] Start throttle measurement before initialisation --- src/stream/stream/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 99a112036..de4a8fb7d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,6 +327,8 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; + let start = Instant::now(); + // emit value every 5 milliseconds let s = stream::interval(Duration::from_millis(5)) .enumerate() @@ -335,7 +337,6 @@ extension_trait! { // throttle for 10 milliseconds let mut s = s.throttle(Duration::from_millis(10)); - let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); assert!(duration_ms >= 5); From 154644880021950268e523c17f46dc6de50ef6a1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 21:27:14 +0100 Subject: [PATCH 242/707] remove throttle example Signed-off-by: Yoshua Wuyts --- examples/throttle.rs | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 examples/throttle.rs diff --git a/examples/throttle.rs b/examples/throttle.rs deleted file mode 100644 index 74c1fd30f..000000000 --- a/examples/throttle.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Spawns a timed task which gets throttled. - -fn main() { - #[cfg(feature = "unstable")] - { - use async_std::prelude::*; - use async_std::task; - - task::block_on(async { - use async_std::stream; - use std::time::Duration; - - // emit value every 1 second - let s = stream::interval(Duration::from_secs(1)).enumerate(); - - // throttle for 2 seconds - let s = s.throttle(Duration::from_secs(2)); - - s.for_each(|(n, _)| { - dbg!(n); - }) - .await; - // => 0 .. 1 .. 2 .. 3 - // with a pause of 2 seconds between each print - }) - } -} From fe3c9ef626801455028325f3a5bfeefa97406470 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 17 Oct 2019 14:23:14 +0200 Subject: [PATCH 243/707] First attempt at successor --- src/stream/stream/mod.rs | 1 + src/stream/stream/successor.rs | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/stream/stream/successor.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d6292c32d..b5583e100 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -57,6 +57,7 @@ mod partial_cmp; mod position; mod scan; mod skip; +mod successor; mod skip_while; mod step_by; mod take; diff --git a/src/stream/stream/successor.rs b/src/stream/stream/successor.rs new file mode 100644 index 000000000..519729f91 --- /dev/null +++ b/src/stream/stream/successor.rs @@ -0,0 +1,59 @@ +use std::pin::Pin; +use std::marker::PhantomData; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[derive(Debug)] +pub struct Successor +where Fut: Future +{ + successor: F, + next: T, + _marker: PhantomData +} + +pub fn successor(func: F, start: T) -> Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + { + Successor { + successor: func, + next: start, + _marker: PhantomData, + } + } + +impl Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + +{ + pin_utils::unsafe_unpinned!(successor: F); + pin_utils::unsafe_unpinned!(next: T); +} + +impl Stream for Successor +where + Fut: Future, + F: FnMut(T) -> Fut, + T: Copy, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + match self.as_mut().successor()(self.next).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(val) => { + self.next = val; + Poll::Ready(Some(val)) + } + } + } +} From 02b261de10d09c699fb949d3ac5347282756e1d8 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 17 Oct 2019 23:27:41 +0200 Subject: [PATCH 244/707] It compiles! Store the future and poll it instead of creating multiple new ones --- src/stream/mod.rs | 2 + src/stream/stream/mod.rs | 1 - src/stream/stream/successor.rs | 59 ------------------- src/stream/successor.rs | 102 +++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 60 deletions(-) delete mode 100644 src/stream/stream/successor.rs create mode 100644 src/stream/successor.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f7828822a..f410e0808 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -303,6 +303,7 @@ pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; +pub use successor::{successor, Successor}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; @@ -316,6 +317,7 @@ mod from_iter; mod once; mod repeat; mod repeat_with; +mod successor; cfg_unstable! { mod double_ended_stream; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b5583e100..d6292c32d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -57,7 +57,6 @@ mod partial_cmp; mod position; mod scan; mod skip; -mod successor; mod skip_while; mod step_by; mod take; diff --git a/src/stream/stream/successor.rs b/src/stream/stream/successor.rs deleted file mode 100644 index 519729f91..000000000 --- a/src/stream/stream/successor.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::pin::Pin; -use std::marker::PhantomData; - -use crate::future::Future; -use crate::stream::Stream; -use crate::task::{Context, Poll}; - -#[derive(Debug)] -pub struct Successor -where Fut: Future -{ - successor: F, - next: T, - _marker: PhantomData -} - -pub fn successor(func: F, start: T) -> Successor -where - F: FnMut(T) -> Fut, - Fut: Future, - T: Copy, - { - Successor { - successor: func, - next: start, - _marker: PhantomData, - } - } - -impl Successor -where - F: FnMut(T) -> Fut, - Fut: Future, - T: Copy, - -{ - pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: T); -} - -impl Stream for Successor -where - Fut: Future, - F: FnMut(T) -> Fut, - T: Copy, -{ - type Item = T; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - - match self.as_mut().successor()(self.next).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(val) => { - self.next = val; - Poll::Ready(Some(val)) - } - } - } -} diff --git a/src/stream/successor.rs b/src/stream/successor.rs new file mode 100644 index 000000000..434ef9793 --- /dev/null +++ b/src/stream/successor.rs @@ -0,0 +1,102 @@ +use std::pin::Pin; +use std::marker::PhantomData; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that yields elements by calling an async closure with the previous value as an +/// argument +/// +/// This stream is constructed by [`successor`] function +/// +/// [`successor`]: fn.successor.html +#[derive(Debug)] +pub struct Successor +where Fut: Future +{ + successor: F, + future: Option, + next: T, + _marker: PhantomData +} + +/// Creates a new stream where to produce each new element a clousre is called with the previous +/// value. +/// +/// #Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::successor(22, |val| { +/// async move { +/// val + 1 +/// } +/// }); +/// +/// pin_utils::pin_mut!(s); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(3)); +/// # +/// # }) } +/// +/// ``` +pub fn successor(start: T, func: F) -> Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + { + Successor { + successor: func, + future: None, + next: start, + _marker: PhantomData, + } + } + +impl Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + +{ + pin_utils::unsafe_unpinned!(successor: F); + pin_utils::unsafe_unpinned!(next: T); + pin_utils::unsafe_pinned!(future: Option); + +} + +impl Stream for Successor +where + Fut: Future, + F: FnMut(T) -> Fut, + T: Copy, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match &self.future { + Some(_) => { + let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + self.as_mut().future().set(None); + + Poll::Ready(Some(next)) + }, + None => { + let x = self.next; + let fut = (self.as_mut().successor())(x); + self.as_mut().future().set(Some(fut)); + // Probably can poll the value here? + Poll::Pending + } + } + } +} + From 95a3e53fcdcab2d0610e54aa9520c152a11a1ceb Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 18 Oct 2019 09:00:38 +0200 Subject: [PATCH 245/707] Only use the Option of the future to decide to construct a new one --- src/stream/successor.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 434ef9793..3ddeef475 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -39,9 +39,9 @@ where Fut: Future /// }); /// /// pin_utils::pin_mut!(s); -/// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(2)); -/// assert_eq!(s.next().await, Some(3)); +/// assert_eq!(s.next().await, Some(23)); +/// assert_eq!(s.next().await, Some(24)); +/// assert_eq!(s.next().await, Some(25)); /// # /// # }) } /// @@ -83,20 +83,18 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &self.future { - Some(_) => { - let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - Poll::Ready(Some(next)) - }, None => { let x = self.next; let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); - // Probably can poll the value here? - Poll::Pending } + _ => {}, } + + let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + *self.as_mut().next() = next; + self.as_mut().future().set(None); + Poll::Ready(Some(next)) } } From 8b662b659df21c09bc5f00c9e57f353f99457fd4 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 18 Oct 2019 09:10:15 +0200 Subject: [PATCH 246/707] Run rustfmt --- src/stream/mod.rs | 1 + src/stream/successor.rs | 32 +++++++++++++++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f410e0808..bab9dc797 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -308,6 +308,7 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; +pub use successor::{successor, Successor}; pub(crate) mod stream; diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 3ddeef475..8e93956ff 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::marker::PhantomData; +use std::pin::Pin; use crate::future::Future; use crate::stream::Stream; @@ -12,13 +12,14 @@ use crate::task::{Context, Poll}; /// /// [`successor`]: fn.successor.html #[derive(Debug)] -pub struct Successor -where Fut: Future +pub struct Successor +where + Fut: Future, { successor: F, future: Option, next: T, - _marker: PhantomData + _marker: PhantomData, } /// Creates a new stream where to produce each new element a clousre is called with the previous @@ -51,29 +52,27 @@ where F: FnMut(T) -> Fut, Fut: Future, T: Copy, - { - Successor { - successor: func, - future: None, - next: start, - _marker: PhantomData, - } +{ + Successor { + successor: func, + future: None, + next: start, + _marker: PhantomData, } +} -impl Successor +impl Successor where F: FnMut(T) -> Fut, Fut: Future, T: Copy, - { pin_utils::unsafe_unpinned!(successor: F); pin_utils::unsafe_unpinned!(next: T); pin_utils::unsafe_pinned!(future: Option); - } -impl Stream for Successor +impl Stream for Successor where Fut: Future, F: FnMut(T) -> Fut, @@ -88,7 +87,7 @@ where let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); } - _ => {}, + _ => {} } let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); @@ -97,4 +96,3 @@ where Poll::Ready(Some(next)) } } - From 554d5cfbc1ec93c4240c9cdbfac90ea62bd596ea Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 13:37:26 +0200 Subject: [PATCH 247/707] Slight renamings --- src/stream/successor.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 8e93956ff..32353d0ad 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -12,7 +12,7 @@ use crate::task::{Context, Poll}; /// /// [`successor`]: fn.successor.html #[derive(Debug)] -pub struct Successor +pub struct Successors where Fut: Future, { @@ -22,7 +22,7 @@ where _marker: PhantomData, } -/// Creates a new stream where to produce each new element a clousre is called with the previous +/// Creates a new stream where to produce each new element a closure is called with the previous /// value. /// /// #Examples @@ -33,7 +33,7 @@ where /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successor(22, |val| { +/// let s = stream::successors(22, |val| { /// async move { /// val + 1 /// } @@ -47,13 +47,13 @@ where /// # }) } /// /// ``` -pub fn successor(start: T, func: F) -> Successor +pub fn successors(start: T, func: F) -> Successors where F: FnMut(T) -> Fut, Fut: Future, T: Copy, { - Successor { + Successors { successor: func, future: None, next: start, @@ -61,7 +61,7 @@ where } } -impl Successor +impl Successors where F: FnMut(T) -> Fut, Fut: Future, @@ -72,7 +72,7 @@ where pin_utils::unsafe_pinned!(future: Option); } -impl Stream for Successor +impl Stream for Successors where Fut: Future, F: FnMut(T) -> Fut, From 266754897ec4574959ef35202af4d15bc83860e3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 13:38:32 +0200 Subject: [PATCH 248/707] Rename the module to 'successors' --- src/stream/mod.rs | 2 +- src/stream/{successor.rs => successors.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/stream/{successor.rs => successors.rs} (100%) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index bab9dc797..d5cc5ac1d 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,7 +318,7 @@ mod from_iter; mod once; mod repeat; mod repeat_with; -mod successor; +mod successors; cfg_unstable! { mod double_ended_stream; diff --git a/src/stream/successor.rs b/src/stream/successors.rs similarity index 100% rename from src/stream/successor.rs rename to src/stream/successors.rs From 8d97e0f974175040840a15e9ab568a2560f1658d Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 17:56:35 +0200 Subject: [PATCH 249/707] Only produes empty value if next is ever a 'None' --- src/stream/successors.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 32353d0ad..e70841650 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -14,11 +14,11 @@ use crate::task::{Context, Poll}; #[derive(Debug)] pub struct Successors where - Fut: Future, + Fut: Future>, { successor: F, future: Option, - next: T, + next: Option, _marker: PhantomData, } @@ -33,9 +33,9 @@ where /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(22, |val| { +/// let s = stream::successors(Some(22), |val| { /// async move { -/// val + 1 +/// Some(val + 1) /// } /// }); /// @@ -43,14 +43,25 @@ where /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); +/// +/// +///let never = stream::successors(None, |val: usize| { +/// async move { +/// Some(val + 1) +/// } +/// }); +/// +/// pin_utils::pin_mut!(never); +/// assert_eq!(never.next().await, None); +/// assert_eq!(never.next().await, None); /// # /// # }) } /// /// ``` -pub fn successors(start: T, func: F) -> Successors +pub fn successors(start: Option, func: F) -> Successors where F: FnMut(T) -> Fut, - Fut: Future, + Fut: Future>, T: Copy, { Successors { @@ -64,26 +75,30 @@ where impl Successors where F: FnMut(T) -> Fut, - Fut: Future, + Fut: Future>, T: Copy, { pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: T); + pin_utils::unsafe_unpinned!(next: Option); pin_utils::unsafe_pinned!(future: Option); } impl Stream for Successors where - Fut: Future, + Fut: Future>, F: FnMut(T) -> Fut, T: Copy, { type Item = T; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.next.is_none() { + return Poll::Ready(None); + } + match &self.future { None => { - let x = self.next; + let x = self.next.unwrap(); let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); } @@ -93,6 +108,6 @@ where let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); *self.as_mut().next() = next; self.as_mut().future().set(None); - Poll::Ready(Some(next)) + Poll::Ready(next) } } From af928163e44731b27b23f1a01e8d4e7f3432a6cc Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 13:57:41 +0100 Subject: [PATCH 250/707] Got further! Thx Josh! --- src/stream/successors.rs | 80 +++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index e70841650..4186c667e 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,25 +1,31 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::mem; use crate::future::Future; use crate::stream::Stream; -use crate::task::{Context, Poll}; +use crate::task::{Context, Poll, ready}; -/// A stream that yields elements by calling an async closure with the previous value as an -/// argument -/// -/// This stream is constructed by [`successor`] function -/// -/// [`successor`]: fn.successor.html -#[derive(Debug)] -pub struct Successors -where - Fut: Future>, -{ - successor: F, - future: Option, - next: Option, - _marker: PhantomData, + + +pin_project_lite::pin_project! { + /// A stream that yields elements by calling an async closure with the previous value as an + /// argument + /// + /// This stream is constructed by [`successor`] function + /// + /// [`successor`]: fn.successor.html + #[derive(Debug)] + pub struct Successors + where + Fut: Future>, + { + successor: F, + #[pin] + future: Option, + slot: Option, + _marker: PhantomData, + } } /// Creates a new stream where to produce each new element a closure is called with the previous @@ -40,6 +46,7 @@ where /// }); /// /// pin_utils::pin_mut!(s); +/// assert_eq!(s.next().await, Some(22)); /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); @@ -58,31 +65,20 @@ where /// # }) } /// /// ``` -pub fn successors(start: Option, func: F) -> Successors +pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(T) -> Fut, Fut: Future>, T: Copy, { Successors { - successor: func, + successor: succ, future: None, - next: start, + slot: first, _marker: PhantomData, } } -impl Successors -where - F: FnMut(T) -> Fut, - Fut: Future>, - T: Copy, -{ - pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: Option); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for Successors where Fut: Future>, @@ -91,23 +87,23 @@ where { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.next.is_none() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if this.slot.is_none() { return Poll::Ready(None); } - match &self.future { - None => { - let x = self.next.unwrap(); - let fut = (self.as_mut().successor())(x); - self.as_mut().future().set(Some(fut)); - } - _ => {} + if this.future.is_none() { + let x = this.slot.unwrap(); + let fut = (this.successor)(x); + this.future.set(Some(fut)); } - let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - *self.as_mut().next() = next; - self.as_mut().future().set(None); + let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); + + this.future.set(None); + mem::swap(this.slot, &mut next); Poll::Ready(next) } } From a257b7018c83748ff98f07a9592aa7b29f6f62ef Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 14:29:43 +0100 Subject: [PATCH 251/707] Rename some variables to match iter --- src/stream/successors.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 4186c667e..7f598f514 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use std::mem; @@ -12,19 +11,18 @@ pin_project_lite::pin_project! { /// A stream that yields elements by calling an async closure with the previous value as an /// argument /// - /// This stream is constructed by [`successor`] function + /// This stream is constructed by [`successors`] function /// - /// [`successor`]: fn.successor.html + /// [`succcessors`]: fn.succssors.html #[derive(Debug)] pub struct Successors where Fut: Future>, { - successor: F, + succ: F, #[pin] future: Option, slot: Option, - _marker: PhantomData, } } @@ -72,10 +70,9 @@ where T: Copy, { Successors { - successor: succ, + succ: succ, future: None, slot: first, - _marker: PhantomData, } } @@ -95,14 +92,15 @@ where } if this.future.is_none() { - let x = this.slot.unwrap(); - let fut = (this.successor)(x); + let fut = (this.succ)(this.slot.unwrap()); this.future.set(Some(fut)); } let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); this.future.set(None); + + // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration mem::swap(this.slot, &mut next); Poll::Ready(next) } From 243cdd7ff1fe9ff2262ac0f7c7a729988dfa9482 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 17:56:31 +0100 Subject: [PATCH 252/707] Slight miss-merge --- src/stream/mod.rs | 1 - src/stream/successors.rs | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d5cc5ac1d..d980f6cc3 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -303,7 +303,6 @@ pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; -pub use successor::{successor, Successor}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 7f598f514..fb4e1c60e 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -5,9 +5,11 @@ use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll, ready}; +use pin_project_lite::pin_project; -pin_project_lite::pin_project! { + +pin_project! { /// A stream that yields elements by calling an async closure with the previous value as an /// argument /// From 4c09cdbeace41a6d44ac7017275330dbf8c0ba55 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 18:03:07 +0100 Subject: [PATCH 253/707] Mark successors as unstable --- src/stream/mod.rs | 3 ++- src/stream/successors.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d980f6cc3..47635eedb 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -317,7 +317,6 @@ mod from_iter; mod once; mod repeat; mod repeat_with; -mod successors; cfg_unstable! { mod double_ended_stream; @@ -328,6 +327,7 @@ cfg_unstable! { mod interval; mod into_stream; mod product; + mod successors; mod sum; pub use double_ended_stream::DoubleEndedStream; @@ -339,5 +339,6 @@ cfg_unstable! { pub use into_stream::IntoStream; pub use product::Product; pub use stream::Merge; + pub use successors::{successors, Successors}; pub use sum::Sum; } diff --git a/src/stream/successors.rs b/src/stream/successors.rs index fb4e1c60e..0295b33e3 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -16,6 +16,8 @@ pin_project! { /// This stream is constructed by [`successors`] function /// /// [`succcessors`]: fn.succssors.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Successors where @@ -65,6 +67,8 @@ pin_project! { /// # }) } /// /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(T) -> Fut, From bfb42b432ee636ab4005df3d24a70f5729363af9 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:07:48 +0100 Subject: [PATCH 254/707] Rearrange docs to match 'repeat' --- src/stream/successors.rs | 46 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 0295b33e3..f7d5bd8e9 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -7,33 +7,10 @@ use crate::task::{Context, Poll, ready}; use pin_project_lite::pin_project; - - -pin_project! { - /// A stream that yields elements by calling an async closure with the previous value as an - /// argument - /// - /// This stream is constructed by [`successors`] function - /// - /// [`succcessors`]: fn.succssors.html - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[derive(Debug)] - pub struct Successors - where - Fut: Future>, - { - succ: F, - #[pin] - future: Option, - slot: Option, - } -} - /// Creates a new stream where to produce each new element a closure is called with the previous /// value. /// -/// #Examples +/// # Examples /// /// ``` /// # fn main() { async_std::task::block_on(async { @@ -82,6 +59,27 @@ where } } +pin_project! { + /// A stream that yields elements by calling an async closure with the previous value as an + /// argument + /// + /// 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 + Fut: Future>, + { + succ: F, + #[pin] + future: Option, + slot: Option, + } +} + impl Stream for Successors where Fut: Future>, From 7677e9a3dfdada2bf8f97eadb286258c194c1e4b Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:13:57 +0100 Subject: [PATCH 255/707] Make the closure take a borrow to the value --- src/stream/successors.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index f7d5bd8e9..446ffe568 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -18,7 +18,7 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |val| { +/// let s = stream::successors(Some(22), |&val| { /// async move { /// Some(val + 1) /// } @@ -31,9 +31,9 @@ use pin_project_lite::pin_project; /// assert_eq!(s.next().await, Some(25)); /// /// -///let never = stream::successors(None, |val: usize| { +///let never = stream::successors(None, |_| { /// async move { -/// Some(val + 1) +/// Some(1) /// } /// }); /// @@ -48,7 +48,7 @@ use pin_project_lite::pin_project; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where - F: FnMut(T) -> Fut, + F: FnMut(&T) -> Fut, Fut: Future>, T: Copy, { @@ -83,7 +83,7 @@ pin_project! { impl Stream for Successors where Fut: Future>, - F: FnMut(T) -> Fut, + F: FnMut(&T) -> Fut, T: Copy, { type Item = T; @@ -96,7 +96,7 @@ where } if this.future.is_none() { - let fut = (this.succ)(this.slot.unwrap()); + let fut = (this.succ)(&this.slot.unwrap()); this.future.set(Some(fut)); } From f14b37ff17618a72dbb441cb1a33cb122c70339d Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:15:38 +0100 Subject: [PATCH 256/707] Remoe the T: Copy bound on the item --- src/stream/successors.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 446ffe568..e86512bf0 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -50,7 +50,6 @@ pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Fut, Fut: Future>, - T: Copy, { Successors { succ: succ, @@ -84,7 +83,6 @@ impl Stream for Successors where Fut: Future>, F: FnMut(&T) -> Fut, - T: Copy, { type Item = T; @@ -96,7 +94,7 @@ where } if this.future.is_none() { - let fut = (this.succ)(&this.slot.unwrap()); + let fut = (this.succ)(this.slot.as_ref().unwrap()); this.future.set(Some(fut)); } From 786a52a09d40bb9303f237c6bac756132e1651c9 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 14 Nov 2019 21:37:51 +0100 Subject: [PATCH 257/707] Slight miss-merge --- src/stream/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 47635eedb..d8b96ec22 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -307,7 +307,6 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; -pub use successor::{successor, Successor}; pub(crate) mod stream; From 64216b8e6bf24ccb95a296650e08cc56cc59ad74 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 14 Nov 2019 21:49:24 +0100 Subject: [PATCH 258/707] Take a normal closure, not an async one --- src/stream/successors.rs | 51 ++++++++++------------------------------ 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index e86512bf0..d5840eec5 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,9 +1,8 @@ use std::pin::Pin; use std::mem; -use crate::future::Future; use crate::stream::Stream; -use crate::task::{Context, Poll, ready}; +use crate::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -18,11 +17,7 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |&val| { -/// async move { -/// Some(val + 1) -/// } -/// }); +/// let s = stream::successors(Some(22), |&val| Some(val + 1) ); /// /// pin_utils::pin_mut!(s); /// assert_eq!(s.next().await, Some(22)); @@ -30,30 +25,18 @@ use pin_project_lite::pin_project; /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); /// -/// -///let never = stream::successors(None, |_| { -/// async move { -/// Some(1) -/// } -/// }); -/// -/// pin_utils::pin_mut!(never); -/// assert_eq!(never.next().await, None); -/// assert_eq!(never.next().await, None); /// # /// # }) } /// /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub fn successors(first: Option, succ: F) -> Successors +pub fn successors(first: Option, succ: F) -> Successors where - F: FnMut(&T) -> Fut, - Fut: Future>, + F: FnMut(&T) -> Option, { Successors { - succ: succ, - future: None, + succ, slot: first, } } @@ -68,39 +51,29 @@ pin_project! { #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] - pub struct Successors + pub struct Successors where - Fut: Future>, + F: FnMut(&T) -> Option { succ: F, - #[pin] - future: Option, slot: Option, } } -impl Stream for Successors +impl Stream for Successors where - Fut: Future>, - F: FnMut(&T) -> Fut, + F: FnMut(&T) -> Option, { type Item = T; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut this = self.project(); + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let this = self.project(); if this.slot.is_none() { return Poll::Ready(None); } - if this.future.is_none() { - let fut = (this.succ)(this.slot.as_ref().unwrap()); - this.future.set(Some(fut)); - } - - let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - - this.future.set(None); + let mut next = (this.succ)(&this.slot.as_ref().unwrap()); // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration mem::swap(this.slot, &mut next); From 31f129ebe7a8f3c43b596f848934c586c7a131e8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 22:37:04 +0100 Subject: [PATCH 259/707] backlink channel types Signed-off-by: Yoshua Wuyts --- src/sync/channel.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 392c8511f..2647f6502 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -71,6 +71,11 @@ pub fn channel(cap: usize) -> (Sender, Receiver) { /// The sending side of a channel. /// +/// This struct is created by the [`channel`] function. See its +/// documentation for more. +/// +/// [`channel`]: fn.channel.html +/// /// # Examples /// /// ``` @@ -298,8 +303,11 @@ impl fmt::Debug for Sender { /// The receiving side of a channel. /// -/// This type implements the [`Stream`] trait, which means it can act as an asynchronous iterator. +/// 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 From 30ff7b09b64a52e727f498f53f34f62b02de26ca Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 22:45:46 +0100 Subject: [PATCH 260/707] mark Stream::count as unstable Signed-off-by: Yoshua Wuyts --- src/stream/stream/count.rs | 2 ++ src/stream/stream/mod.rs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 221b0f0c4..09657cfff 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(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct CountFuture { #[pin] stream: S, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 51ac857a8..281e4d886 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -26,7 +26,6 @@ mod any; mod chain; mod cloned; mod cmp; -mod count; mod copied; mod cycle; mod enumerate; @@ -69,7 +68,6 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; -use count::CountFuture; use cycle::Cycle; use enumerate::Enumerate; use eq::EqFuture; @@ -123,12 +121,14 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; + use count::CountFuture; pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; + mod count; mod merge; mod flatten; mod flat_map; @@ -1911,6 +1911,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn count(self) -> impl Future [CountFuture] where Self: Sized, From 4ef55d4d7bf51cc5d0a97a33b1773cc398da5167 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 15 Nov 2019 09:01:41 +0900 Subject: [PATCH 261/707] Enable CI on master branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99436b72b..dac0ff44f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: pull_request: push: branches: + - master - staging - trying From de67bf0fd4449e3eec9058238c3e682101f8a18f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 15 Nov 2019 11:17:39 +0900 Subject: [PATCH 262/707] feat: Add stream by_ref --- src/stream/stream/mod.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d886..2bef88ff3 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1404,6 +1404,52 @@ extension_trait! { } } + #[doc = r#" + Borrows an stream, rather than consuming it. + + This is useful to allow applying stream adaptors while still retaining ownership of the original stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let a = vec![1isize, 2, 3]; + + let stream = stream::from_iter(a); + + let sum: isize = stream.take(5).sum().await; + + assert_eq!(sum, 6); + + // 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); + + // let's try that again + let a = vec![1isize, 2, 3]; + + let mut stream = stream::from_iter(a); + + // instead, we add in a .by_ref() + let sum: isize = stream.by_ref().take(2).sum().await; + + assert_eq!(sum, 3); + + // now this is just fine: + assert_eq!(stream.next().await, Some(3)); + assert_eq!(stream.next().await, None); + # + # }) } + ``` + "#] + fn by_ref(&mut self) -> &mut Self { + self + } + #[doc = r#" A stream adaptor similar to [`fold`] that holds internal state and produces a new stream. From 11268a80fbc1fe833bee5d022eb99379a6aa937c Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 12:28:03 +0800 Subject: [PATCH 263/707] add stream-partition --- src/stream/stream/mod.rs | 38 +++++++++++++++++++++++ src/stream/stream/partition.rs | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/stream/stream/partition.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d886..1d9ae6e15 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -54,6 +54,7 @@ mod ne; mod next; mod nth; mod partial_cmp; +mod partition; mod position; mod scan; mod skip; @@ -91,6 +92,7 @@ use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use partition::PartitionFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; @@ -1308,6 +1310,42 @@ extension_trait! { FoldFuture::new(self, init, f) } + #[doc = r#" + A combinator that applies a function to every element in a stream + creating two collections from it. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + 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]); + + # + # }) } + ``` + "#] + fn partition( + self, + f: F, + ) -> impl Future [PartitionFuture] + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + B: Default, + { + PartitionFuture::new(self, f) + } + #[doc = r#" Call a closure on each element of the stream. diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs new file mode 100644 index 000000000..46e957cb0 --- /dev/null +++ b/src/stream/stream/partition.rs @@ -0,0 +1,57 @@ +use std::future::Future; +use std::pin::Pin; +use std::default::Default; +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[derive(Debug)] + pub struct PartitionFuture { + #[pin] + stream: S, + f: F, + res: Option<(B, B)>, + } +} + +impl PartitionFuture { + pub(super) fn new(stream: S, f: F) -> Self { + Self { + stream, + f, + res: Some((B::default(), B::default())), + } + } +} + +impl Future for PartitionFuture +where + S: Stream + Sized, + F: FnMut(&S::Item) -> bool, + B: Default + Extend, +{ + type Output = (B, B); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some(v) => { + let mut res = this.res.take().unwrap(); + match (this.f)(&v) { + true => res.0.extend(Some(v)), + false => res.1.extend(Some(v)), + }; + + *this.res = Some(res); + } + None => return Poll::Ready(this.res.take().unwrap()), + } + } + } +} From d76b32e6d45e2bd6b5693e0705490332af616fa2 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 14:23:34 +0800 Subject: [PATCH 264/707] make it unstable and fix trait bound --- src/stream/stream/mod.rs | 10 +++++++--- src/stream/stream/partition.rs | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 1d9ae6e15..672a0855b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -54,7 +54,6 @@ mod ne; mod next; mod nth; mod partial_cmp; -mod partition; mod position; mod scan; mod skip; @@ -92,7 +91,6 @@ use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; -use partition::PartitionFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; @@ -122,8 +120,11 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; + use crate::stream::Extend; use count::CountFuture; + use partition::PartitionFuture; + pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; @@ -134,6 +135,7 @@ cfg_unstable! { mod merge; mod flatten; mod flat_map; + mod partition; mod timeout; mod throttle; } @@ -1334,6 +1336,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn partition( self, f: F, @@ -1341,7 +1345,7 @@ extension_trait! { where Self: Sized, F: FnMut(&Self::Item) -> bool, - B: Default, + B: Default + Extend, { PartitionFuture::new(self, f) } diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index 46e957cb0..ba4938cf4 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -8,6 +8,9 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Debug)] + #[allow(missing_debug_implementations)] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct PartitionFuture { #[pin] stream: S, From 76ec9c45638231127fd63e7279bd3c1be1430cfd Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 14:33:34 +0800 Subject: [PATCH 265/707] update doc url --- docs/src/overview/std-and-library-futures.md | 6 +++--- 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/net/tcp/stream.rs | 6 +++--- src/stream/stream/mod.rs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md index 5c5f96f5f..9b4801edb 100644 --- a/docs/src/overview/std-and-library-futures.md +++ b/docs/src/overview/std-and-library-futures.md @@ -4,11 +4,11 @@ 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-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`. +- 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-preview/0.3.0-alpha.17/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`. +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-preview/0.3.0-alpha.17/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 [`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. 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-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`. diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 45c5f28c9..d919a782c 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -34,7 +34,7 @@ extension_trait! { [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html [`futures::io::AsyncBufRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html + 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 diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 56f632356..0d7f4dcc5 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -40,7 +40,7 @@ extension_trait! { [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html [`futures::io::AsyncRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html + 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 diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 7dc30aeed..e97cabe78 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -27,7 +27,7 @@ extension_trait! { [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html [`futures::io::AsyncSeek`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html + https://docs.rs/futures/0.3/futures/stream/trait.Stream.html [provided methods]: #provided-methods [`SeekExt`]: ../io/prelude/trait.SeekExt.html [prelude]: ../prelude/index.html diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index eb114344a..0ed91dda6 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -35,7 +35,7 @@ extension_trait! { [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html [`futures::io::AsyncWrite`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html + 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 diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 13a1752f2..1da9c7c2b 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -22,9 +22,9 @@ use crate::task::{spawn_blocking, Context, Poll}; /// [`connect`]: struct.TcpStream.html#method.connect /// [accepting]: struct.TcpListener.html#method.accept /// [listener]: struct.TcpListener.html -/// [`AsyncRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html -/// [`AsyncWrite`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html -/// [`futures::io`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/index.html +/// [`AsyncRead`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html +/// [`futures::io`]: https://docs.rs/futures/0.3/futures/io/index.html /// [`shutdown`]: struct.TcpStream.html#method.shutdown /// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html /// diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d886..de5eb38e6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -157,7 +157,7 @@ extension_trait! { [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html [`futures::stream::Stream`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html + https://docs.rs/futures/0.3/futures/stream/trait.Stream.html [provided methods]: #provided-methods [`StreamExt`]: ../prelude/trait.StreamExt.html [prelude]: ../prelude/index.html From 74caed2d4bcf6097c798d96d424c8d217fd520ce Mon Sep 17 00:00:00 2001 From: yjh Date: Fri, 15 Nov 2019 18:22:06 +0800 Subject: [PATCH 266/707] Update src/io/seek/mod.rs Co-Authored-By: Taiki Endo --- src/io/seek/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index e97cabe78..f565ca46b 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -27,7 +27,7 @@ extension_trait! { [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html [`futures::io::AsyncSeek`]: - https://docs.rs/futures/0.3/futures/stream/trait.Stream.html + 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 From 31cf932d808bdfb3cbdf024968b333f23d510547 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 00:24:59 +0900 Subject: [PATCH 267/707] wip: Add stream unzip --- src/stream/stream/mod.rs | 13 ++++++++++ src/stream/stream/unzip.rs | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/stream/stream/unzip.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 893858375..900bde3a9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -130,6 +130,7 @@ cfg_unstable! { pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; + pub use unzip::UnzipFuture; mod count; mod merge; @@ -138,6 +139,7 @@ cfg_unstable! { mod partition; mod timeout; mod throttle; + mod unzip; } extension_trait! { @@ -1717,6 +1719,17 @@ extension_trait! { Zip::new(self, other) } + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn unzip(self) -> impl Future [UnzipFuture] + where + FromA: Default + Extend, + FromB: Default + Extend, + Self: Stream + Sized, + { + UnzipFuture::new(self) + } + #[doc = r#" Transforms a stream into a collection. diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs new file mode 100644 index 000000000..ef1ff3a06 --- /dev/null +++ b/src/stream/stream/unzip.rs @@ -0,0 +1,53 @@ +use std::future::Future; +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub struct UnzipFuture { + #[pin] + stream: S, + res: (FromA, FromB), + } +} + +impl UnzipFuture +where + FromA: Default, + FromB: Default, +{ + pub(super) fn new(stream: S) -> Self { + UnzipFuture { + stream, + res: (FromA::default(), FromB::default()), + } + } +} + +impl Future for UnzipFuture +where + S: Stream, + FromA: Default + Extend + Copy, + FromB: Default + Extend + Copy, +{ + type Output = (FromA, FromB); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some((a, b)) => { + this.res.0.extend(Some(a)); + this.res.1.extend(Some(b)); + Poll::Pending + } + None => Poll::Ready(*this.res), + } + } +} From df92c633375f43d319d01792476e073df21b2c21 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 00:29:54 +0900 Subject: [PATCH 268/707] fix: Add unstable features --- src/stream/stream/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2bef88ff3..bc2482d34 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1446,6 +1446,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn by_ref(&mut self) -> &mut Self { self } From 3564be9c0ce12ba3cebcd77bd9f31e51af6807d8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 16:58:33 +0100 Subject: [PATCH 269/707] update futures-timer dep Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e9207395e..b8d24dfae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ crossbeam-deque = { version = "0.7.1", optional = true } crossbeam-utils = { version = "0.6.6", optional = true } futures-core = { version = "0.3.0", optional = true } futures-io = { version = "0.3.0", optional = true } -futures-timer = { version = "1.0.2", 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.2.1", optional = true } From 8779c04dc7d0dd1ea8ede5b03fe531931219e2a1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 16:59:58 +0100 Subject: [PATCH 270/707] upgrade all deps Signed-off-by: Yoshua Wuyts --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8d24dfae..7ffaae3a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,33 +50,33 @@ std = [ ] [dependencies] -async-attributes = { version = "1.1.0", optional = true } +async-attributes = { version = "1.1.1", optional = true } async-macros = { version = "2.0.0", optional = true } 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.3.9", optional = true } -crossbeam-deque = { version = "0.7.1", optional = true } -crossbeam-utils = { version = "0.6.6", optional = true } -futures-core = { version = "0.3.0", optional = true } -futures-io = { version = "0.3.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 } +futures-io = { version = "0.3.1", 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.2.1", optional = true } mio = { version = "0.6.19", optional = true } mio-uds = { version = "0.6.7", optional = true } -num_cpus = { version = "1.10.1", optional = true } +num_cpus = { version = "1.11.1", optional = true } once_cell = { version = "1.2.0", optional = true } -pin-project-lite = { version = "0.1", optional = true } +pin-project-lite = { version = "0.1.1", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } [dev-dependencies] -femme = "1.2.0" +femme = "1.3.0" rand = "0.7.2" surf = "1.0.3" tempdir = "0.3.7" -futures = "0.3.0" +futures = "0.3.1" [[test]] name = "stream" From 603b3c508559129bf2f866291511e35db0a93c4d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:16:35 +0900 Subject: [PATCH 271/707] add: Add stream unzip --- src/stream/stream/mod.rs | 2 +- src/stream/stream/unzip.rs | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 900bde3a9..04aa4d68e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -124,13 +124,13 @@ cfg_unstable! { 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 unzip::UnzipFuture; mod count; mod merge; diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index ef1ff3a06..4f5dfa198 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -7,12 +7,13 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { + #[derive(Clone, Debug)] #[cfg(all(feature = "default", feature = "unstable"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub struct UnzipFuture { + pub struct UnzipFuture { #[pin] stream: S, - res: (FromA, FromB), + res: Option<(FromA, FromB)>, } } @@ -24,7 +25,7 @@ where pub(super) fn new(stream: S) -> Self { UnzipFuture { stream, - res: (FromA::default(), FromB::default()), + res: Some((FromA::default(), FromB::default())), } } } @@ -32,22 +33,27 @@ where impl Future for UnzipFuture where S: Stream, - FromA: Default + Extend + Copy, - FromB: Default + Extend + Copy, + FromA: Default + Extend, + FromB: Default + Extend, { type Output = (FromA, FromB); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); - let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - match next { - Some((a, b)) => { - this.res.0.extend(Some(a)); - this.res.1.extend(Some(b)); - Poll::Pending + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some((a, b)) => { + let mut res = this.res.take().unwrap(); + res.0.extend(Some(a)); + res.1.extend(Some(b)); + + *this.res = Some(res); + } + None => return Poll::Ready(this.res.take().unwrap()), } - None => Poll::Ready(*this.res), } } } From 91ee4c7b9fddcf0c245d9527b75fec2642846702 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:16:48 +0900 Subject: [PATCH 272/707] doc: Add stream unzip doc --- src/stream/stream/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 04aa4d68e..e0e9b7e44 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1719,6 +1719,33 @@ extension_trait! { Zip::new(self, other) } + #[doc = r#" + Converts an stream of pairs into a pair of containers. + + unzip() consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. + + This function is, in some sense, the opposite of [`zip`]. + + [`zip`]: trait.Stream.html#method.zip + + # Example + + ``` + # 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)]); + + let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; + + assert_eq!(left, [1, 3]); + assert_eq!(right, [2, 4]); + # + # }) } + ``` + "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn unzip(self) -> impl Future [UnzipFuture] From 6cbf48f12d1ffa58b97256686a95005f57737fec Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:29:16 +0900 Subject: [PATCH 273/707] fix clippy warn --- src/future/future/join.rs | 14 ++++++-------- src/stream/stream/last.rs | 2 +- src/stream/stream/partition.rs | 15 ++++++++------- src/task/executor/pool.rs | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/future/future/join.rs b/src/future/future/join.rs index 90ea3237a..5cfbd99af 100644 --- a/src/future/future/join.rs +++ b/src/future/future/join.rs @@ -45,16 +45,14 @@ where let mut left = this.left; let mut right = this.right; - if Future::poll(Pin::new(&mut left), cx).is_ready() { - if right.as_ref().output().is_some() { - return Poll::Ready((left.take().unwrap(), right.take().unwrap())); - } + let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready(); + if is_left_ready && right.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); } - if Future::poll(Pin::new(&mut right), cx).is_ready() { - if left.as_ref().output().is_some() { - return Poll::Ready((left.take().unwrap(), right.take().unwrap())); - } + let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready(); + if is_right_ready && left.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); } Poll::Pending diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index 188da3c8f..60f88068e 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index ba4938cf4..077d4ec28 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -1,14 +1,13 @@ +use pin_project_lite::pin_project; +use std::default::Default; use std::future::Future; use std::pin::Pin; -use std::default::Default; -use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { #[derive(Debug)] - #[allow(missing_debug_implementations)] #[cfg(all(feature = "default", feature = "unstable"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct PartitionFuture { @@ -46,10 +45,12 @@ where match next { Some(v) => { let mut res = this.res.take().unwrap(); - match (this.f)(&v) { - true => res.0.extend(Some(v)), - false => res.1.extend(Some(v)), - }; + + if (this.f)(&v) { + res.0.extend(Some(v)) + } else { + res.1.extend(Some(v)) + } *this.res = Some(res); } diff --git a/src/task/executor/pool.rs b/src/task/executor/pool.rs index 08694dd4f..5249b3d93 100644 --- a/src/task/executor/pool.rs +++ b/src/task/executor/pool.rs @@ -43,7 +43,7 @@ static POOL: Lazy = Lazy::new(|| { .name("async-std/executor".to_string()) .spawn(|| { let _ = PROCESSOR.with(|p| p.set(proc)); - abort_on_panic(|| main_loop()); + abort_on_panic(main_loop); }) .expect("cannot start a thread driving tasks"); } From a05b6a38104c26a1ffd2eb7ed6e4e32912c856fb Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:36:53 +0900 Subject: [PATCH 274/707] fix: mutable ref --- src/stream/stream/unzip.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index 4f5dfa198..e0832ff71 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -46,11 +46,9 @@ where match next { Some((a, b)) => { - let mut res = this.res.take().unwrap(); + let res = this.res.as_mut().unwrap(); res.0.extend(Some(a)); res.1.extend(Some(b)); - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From a69b3a8a9e215c689bfde3e07d5b50fe2ecc08e7 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sat, 16 Nov 2019 00:54:50 +0800 Subject: [PATCH 275/707] use `as_mut` for stream-partition --- src/stream/stream/partition.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index ba4938cf4..737445634 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -45,13 +45,11 @@ where match next { Some(v) => { - let mut res = this.res.take().unwrap(); + let res = this.res.as_mut().unwrap(); match (this.f)(&v) { true => res.0.extend(Some(v)), false => res.1.extend(Some(v)), }; - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From 7d616c695d627aa9465a8c8c17b7cadc3f3cb886 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:54:09 +0900 Subject: [PATCH 276/707] refactor: change to as_mut --- src/stream/stream/partition.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index 077d4ec28..74231ab72 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -44,15 +44,12 @@ where match next { Some(v) => { - let mut res = this.res.take().unwrap(); - + let res = this.res.as_mut().unwrap(); if (this.f)(&v) { res.0.extend(Some(v)) } else { res.1.extend(Some(v)) } - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From d68dc659b254569bae4bf4694d84ff5afaa46b1b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 18:08:00 +0100 Subject: [PATCH 277/707] remove pin_mut from successors test Signed-off-by: Yoshua Wuyts --- src/stream/successors.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index d5840eec5..4421564e2 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::mem; +use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -17,9 +17,8 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |&val| Some(val + 1) ); +/// let mut s = stream::successors(Some(22), |&val| Some(val + 1)); /// -/// pin_utils::pin_mut!(s); /// assert_eq!(s.next().await, Some(22)); /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); @@ -27,7 +26,6 @@ use pin_project_lite::pin_project; /// /// # /// # }) } -/// /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -35,10 +33,7 @@ pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Option, { - Successors { - succ, - slot: first, - } + Successors { succ, slot: first } } pin_project! { From 223fcc30eece3944dbff8ce0f61c7335b490451c Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sun, 17 Nov 2019 00:35:56 +0800 Subject: [PATCH 278/707] fix code style for stream --- src/stream/stream/all.rs | 11 +++++++++++ src/stream/stream/any.rs | 11 +++++++++++ src/stream/stream/chain.rs | 2 +- src/stream/stream/cmp.rs | 2 +- src/stream/stream/copied.rs | 2 +- src/stream/stream/count.rs | 2 +- src/stream/stream/cycle.rs | 4 ++-- src/stream/stream/enumerate.rs | 2 +- src/stream/stream/eq.rs | 2 +- src/stream/stream/filter.rs | 2 +- src/stream/stream/filter_map.rs | 2 +- src/stream/stream/find.rs | 2 +- src/stream/stream/find_map.rs | 2 +- src/stream/stream/flat_map.rs | 4 ++-- src/stream/stream/flatten.rs | 4 ++-- src/stream/stream/fold.rs | 2 +- src/stream/stream/for_each.rs | 2 +- src/stream/stream/fuse.rs | 9 +++++++++ src/stream/stream/ge.rs | 2 +- src/stream/stream/gt.rs | 2 +- src/stream/stream/inspect.rs | 2 +- src/stream/stream/last.rs | 2 +- src/stream/stream/le.rs | 2 +- src/stream/stream/lt.rs | 2 +- src/stream/stream/map.rs | 2 +- src/stream/stream/max_by.rs | 2 +- src/stream/stream/min_by.rs | 2 +- src/stream/stream/min_by_key.rs | 2 +- src/stream/stream/mod.rs | 27 +++++---------------------- src/stream/stream/nth.rs | 2 +- src/stream/stream/partial_cmp.rs | 2 +- src/stream/stream/position.rs | 2 +- src/stream/stream/skip.rs | 2 +- src/stream/stream/skip_while.rs | 2 +- src/stream/stream/step_by.rs | 2 +- src/stream/stream/take.rs | 9 +++++++++ src/stream/stream/take_while.rs | 2 +- src/stream/stream/throttle.rs | 2 +- src/stream/stream/timeout.rs | 4 ++-- src/stream/stream/try_fold.rs | 2 +- src/stream/stream/try_for_each.rs | 2 +- src/stream/stream/zip.rs | 2 +- 42 files changed, 86 insertions(+), 63 deletions(-) diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 7b84abe36..5adb68f33 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -14,6 +14,17 @@ pub struct AllFuture<'a, S, F, T> { pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AllFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + result: true, // the default if the empty stream + _marker: PhantomData, + } + } +} + impl Unpin for AllFuture<'_, S, F, T> {} impl Future for AllFuture<'_, S, F, S::Item> diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index c7fc76652..d6853a1cd 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -14,6 +14,17 @@ pub struct AnyFuture<'a, S, F, T> { pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AnyFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + result: false, // the default if the empty stream + _marker: PhantomData, + } + } +} + impl Unpin for AnyFuture<'_, S, F, T> {} impl Future for AnyFuture<'_, S, F, S::Item> diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index f6d9cf641..909fc19b1 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -25,7 +25,7 @@ pin_project! { impl Chain { pub(super) fn new(first: S, second: U) -> Self { - Chain { + Self { first: first.fuse(), second: second.fuse(), } diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index 19437e709..2be0c1a36 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -26,7 +26,7 @@ pin_project! { impl CmpFuture { pub(super) fn new(l: L, r: R) -> Self { - CmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs index e3c8367b6..651c31b60 100644 --- a/src/stream/stream/copied.rs +++ b/src/stream/stream/copied.rs @@ -14,7 +14,7 @@ pin_project! { impl Copied { pub(super) fn new(stream: S) -> Self { - Copied { stream } + Self { stream } } } diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 09657cfff..ebf2a2f17 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -20,7 +20,7 @@ pin_project! { impl CountFuture { pub(crate) fn new(stream: S) -> Self { - CountFuture { stream, count: 0 } + Self { stream, count: 0 } } } diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 7f01a61db..5f8eaa205 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -15,8 +15,8 @@ impl Cycle where S: Stream + Clone, { - pub fn new(source: S) -> Cycle { - Cycle { + pub(crate) fn new(source: S) -> Self { + Self { orig: source.clone(), source: ManuallyDrop::new(source), } diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index a758010ea..c4a37d6ed 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -16,7 +16,7 @@ pin_project! { impl Enumerate { pub(super) fn new(stream: S) -> Self { - Enumerate { stream, i: 0 } + Self { stream, i: 0 } } } diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index addcfa2eb..58ccc90e3 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -26,7 +26,7 @@ where L::Item: PartialEq, { pub(super) fn new(l: L, r: R) -> Self { - EqFuture { + Self { l: l.fuse(), r: r.fuse(), } diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 594b09497..00344b0e9 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -23,7 +23,7 @@ pin_project! { impl Filter { pub(super) fn new(stream: S, predicate: P) -> Self { - Filter { + Self { stream, predicate, } diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index e110f514f..3cd1e47a7 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -16,7 +16,7 @@ pin_project! { impl FilterMap { pub(crate) fn new(stream: S, f: F) -> Self { - FilterMap { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index 0c5ad62ff..4a0749b1a 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -13,7 +13,7 @@ pub struct FindFuture<'a, S, P> { impl<'a, S, P> FindFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, p: P) -> Self { - FindFuture { stream, p } + Self { stream, p } } } diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index b10bd9cad..c79494391 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -13,7 +13,7 @@ pub struct FindMapFuture<'a, S, F> { impl<'a, S, F> FindMapFuture<'a, S, F> { pub(super) fn new(stream: &'a mut S, f: F) -> Self { - FindMapFuture { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index ab45c9c72..6c828c920 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -30,8 +30,8 @@ where U: IntoStream, F: FnMut(S::Item) -> U, { - pub(super) fn new(stream: S, f: F) -> FlatMap { - FlatMap { + pub(super) fn new(stream: S, f: F) -> Self { + Self { stream: stream.map(f), inner_stream: None, } diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index edaffd044..1d6fcae6a 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -32,8 +32,8 @@ where S: Stream, S::Item: IntoStream, { - pub(super) fn new(stream: S) -> Flatten { - Flatten { + pub(super) fn new(stream: S) -> Self { + Self { stream, inner_stream: None, } diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index c4da59150..a346eb671 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -18,7 +18,7 @@ pin_project! { impl FoldFuture { pub(super) fn new(stream: S, init: B, f: F) -> Self { - FoldFuture { + Self { stream, f, acc: Some(init), diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 01833fd9e..dce5cdae9 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -18,7 +18,7 @@ pin_project! { impl ForEachFuture { pub(super) fn new(stream: S, f: F) -> Self { - ForEachFuture { + Self { stream, f, } diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 6297bef7d..c7449c273 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -21,6 +21,15 @@ pin_project! { } } +impl Fuse { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + done: false, + } + } +} + impl Stream for Fuse { type Item = S::Item; diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index f9012697b..67b20bed9 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - GeFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 81e95a1ab..1c1218910 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - GtFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index acf22465c..bb39662b9 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -23,7 +23,7 @@ pin_project! { impl Inspect { pub(super) fn new(stream: S, f: F) -> Self { - Inspect { + Self { stream, f, } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index 188da3c8f..3e0a0b38e 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -18,7 +18,7 @@ pin_project! { impl LastFuture { pub(crate) fn new(stream: S) -> Self { - LastFuture { stream, last: None } + Self { stream, last: None } } } diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 35b04bfb0..7b86161c6 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - LeFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index 86c31295c..100a00342 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - LtFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 7accb6fce..8e074a757 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -17,7 +17,7 @@ pin_project! { impl Map { pub(crate) fn new(stream: S, f: F) -> Self { - Map { + Self { stream, f, } diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index cfba9b93d..36b876bb4 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -20,7 +20,7 @@ pin_project! { impl MaxByFuture { pub(super) fn new(stream: S, compare: F) -> Self { - MaxByFuture { + Self { stream, compare, max: None, diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index fc332c265..e35719e6d 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -20,7 +20,7 @@ pin_project! { impl MinByFuture { pub(super) fn new(stream: S, compare: F) -> Self { - MinByFuture { + Self { stream, compare, min: None, diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 8179fb312..c515dad70 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -20,7 +20,7 @@ pin_project! { impl MinByKeyFuture { pub(super) fn new(stream: S, key_by: K) -> Self { - MinByKeyFuture { + Self { stream, min: None, key_by, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f8765762b..220e791af 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -111,7 +111,6 @@ pub use take_while::TakeWhile; pub use zip::Zip; use std::cmp::Ordering; -use std::marker::PhantomData; cfg_unstable! { use std::future::Future; @@ -288,10 +287,7 @@ extension_trait! { where Self: Sized, { - Take { - stream: self, - remaining: n, - } + Take::new(self, n) } #[doc = r#" @@ -714,10 +710,7 @@ extension_trait! { where Self: Sized, { - Fuse { - stream: self, - done: false, - } + Fuse::new(self) } #[doc = r#" @@ -1193,12 +1186,7 @@ extension_trait! { Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { - AllFuture { - stream: self, - result: true, // the default if the empty stream - _marker: PhantomData, - f, - } + AllFuture::new(self, f) } #[doc = r#" @@ -1438,12 +1426,7 @@ extension_trait! { Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { - AnyFuture { - stream: self, - result: false, // the default if the empty stream - _marker: PhantomData, - f, - } + AnyFuture::new(self, f) } #[doc = r#" @@ -1468,7 +1451,7 @@ extension_trait! { assert_eq!(sum, 6); // if we try to use stream again, it won't work. The following line - // gives "error: use of moved value: `stream` + // gives error: use of moved value: `stream` // assert_eq!(stream.next(), None); // let's try that again diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index 711287a38..267bd40a3 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -15,7 +15,7 @@ impl Unpin for NthFuture<'_, S> {} impl<'a, S> NthFuture<'a, S> { pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { - NthFuture { stream, n } + Self { stream, n } } } diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index 6bc28f78c..85587c999 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -26,7 +26,7 @@ pin_project! { impl PartialCmpFuture { pub(super) fn new(l: L, r: R) -> Self { - PartialCmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs index 5a51d7a73..df60eaae9 100644 --- a/src/stream/stream/position.rs +++ b/src/stream/stream/position.rs @@ -16,7 +16,7 @@ impl<'a, S, P> Unpin for PositionFuture<'a, S, P> {} impl<'a, S, P> PositionFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, predicate: P) -> Self { - PositionFuture { + Self { stream, predicate, index: 0, diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index cc2ba905b..bcff50d6c 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -23,7 +23,7 @@ pin_project! { impl Skip { pub(crate) fn new(stream: S, n: usize) -> Self { - Skip { stream, n } + Self { stream, n } } } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 5cb273eeb..23347132a 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -23,7 +23,7 @@ pin_project! { impl SkipWhile { pub(crate) fn new(stream: S, predicate: P) -> Self { - SkipWhile { + Self { stream, predicate: Some(predicate), } diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index 130209829..2149cdade 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -24,7 +24,7 @@ pin_project! { impl StepBy { pub(crate) fn new(stream: S, step: usize) -> Self { - StepBy { + Self { stream, step: step.checked_sub(1).unwrap(), i: 0, diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index e680b42ba..8c8522766 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -21,6 +21,15 @@ pin_project! { } } +impl Take { + pub(super) fn new(stream: S, remaining: usize) -> Self { + Self { + stream, + remaining, + } + } +} + impl Stream for Take { type Item = S::Item; diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 08b5a86c9..2ba8490e4 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -23,7 +23,7 @@ pin_project! { impl TakeWhile { pub(super) fn new(stream: S, predicate: P) -> Self { - TakeWhile { + Self { stream, predicate, } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 8896899ed..b2480bbd3 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -31,7 +31,7 @@ pin_project! { impl Throttle { pub(super) fn new(stream: S, duration: Duration) -> Self { - Throttle { + Self { stream, duration, blocked: false, diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 560a0e410..f580360de 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -22,10 +22,10 @@ pin_project! { } impl Timeout { - pub(crate) fn new(stream: S, dur: Duration) -> Timeout { + pub(crate) fn new(stream: S, dur: Duration) -> Self { let delay = Delay::new(dur); - Timeout { stream, delay } + Self { stream, delay } } } diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index efb9e339f..3b92d95ab 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -16,7 +16,7 @@ impl<'a, S, F, T> Unpin for TryFoldFuture<'a, S, F, T> {} impl<'a, S, F, T> TryFoldFuture<'a, S, F, T> { pub(super) fn new(stream: &'a mut S, init: T, f: F) -> Self { - TryFoldFuture { + Self { stream, f, acc: Some(init), diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 30e318502..86f1674a3 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -15,7 +15,7 @@ impl<'a, S, F> Unpin for TryForEachFuture<'a, S, F> {} impl<'a, S, F> TryForEachFuture<'a, S, F> { pub(crate) fn new(stream: &'a mut S, f: F) -> Self { - TryForEachFuture { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index f57d73590..597691b4e 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -34,7 +34,7 @@ impl fmt::Debug for Zip { impl Zip { pub(crate) fn new(first: A, second: B) -> Self { - Zip { + Self { item_slot: None, first, second, From b2aaa8b8259a1acbf09b656176b046fd3820f4bd Mon Sep 17 00:00:00 2001 From: sclaire-1 <54961957+sclaire-1@users.noreply.github.com> Date: Sat, 16 Nov 2019 13:02:17 -0800 Subject: [PATCH 279/707] Edit tutorial: implementing_a_client.md Edited to improve reading flow --- docs/src/tutorial/implementing_a_client.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index fd728555d..ba9d6f335 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -1,18 +1,16 @@ ## Implementing a client -Let's now implement the client for the chat. -Because the protocol is line-based, the implementation is pretty straightforward: +Since the protocol is line-based, implementing a client for the chat is straightforward: * Lines read from stdin should be sent over the socket. * Lines read from the socket should be echoed to stdout. -Unlike the server, the client needs only limited concurrency, as it interacts with only a single user. -For this reason, async doesn't bring a lot of performance benefits in this case. +Although async does not significantly affect client performance (as unlike the server, the client interacts solely with one user and only needs limited concurrency), async is still useful for managing concurrency! + +The client has to read from stdin and the socket *simultaneously*. +Programming this with threads is cumbersome, especially when implementing a clean shutdown. +With async, the `select!` macro is all that is needed. -However, async is still useful for managing concurrency! -Specifically, the client should *simultaneously* read from stdin and from the socket. -Programming this with threads is cumbersome, especially when implementing clean shutdown. -With async, we can just use the `select!` macro. ```rust,edition2018 # extern crate async_std; From 8ce3e78952bd8f1fcbf6098951fa4e3937018c8b Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sun, 17 Nov 2019 21:54:44 +0100 Subject: [PATCH 280/707] verbose errors feature This adds a new "verbose-errors" feature flag to async-std that enables wrapping certain errors in structures with more context. As an example, we use it in `fs::File::{open,create}` to add the given path to the error message (something that is lacking in std to annoyance of many). --- .github/workflows/ci.yml | 6 +++++ Cargo.toml | 1 + src/fs/file.rs | 13 ++++++++-- src/io/mod.rs | 1 + src/io/utils.rs | 51 ++++++++++++++++++++++++++++++++++++++++ src/utils.rs | 8 +++++++ tests/verbose_errors.rs | 20 ++++++++++++++++ 7 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/io/utils.rs create mode 100644 tests/verbose_errors.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99436b72b..5d5639c68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,12 @@ jobs: command: test args: --all --features unstable + - name: tests with verbose errors + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --features 'unstable verbose-errors' + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 7ffaae3a1..7886bcfd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ std = [ "pin-utils", "slab", ] +verbose-errors = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 8bc6c2cea..5186e96fe 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; +use crate::utils::VerboseErrorExt; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; @@ -112,7 +113,11 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = spawn_blocking(move || std::fs::File::open(&path)).await?; + let file = spawn_blocking(move || { + std::fs::File::open(&path) + .verbose_context(|| format!("Could not open {}", path.display())) + }) + .await?; Ok(File::new(file, true)) } @@ -147,7 +152,11 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = spawn_blocking(move || std::fs::File::create(&path)).await?; + let file = spawn_blocking(move || { + std::fs::File::create(&path) + .verbose_context(|| format!("Could not create {}", path.display())) + }) + .await?; Ok(File::new(file, true)) } diff --git a/src/io/mod.rs b/src/io/mod.rs index 4e8323052..d9660a724 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -291,6 +291,7 @@ cfg_std! { pub(crate) mod read; pub(crate) mod seek; pub(crate) mod write; + pub(crate) mod utils; mod buf_reader; mod buf_writer; diff --git a/src/io/utils.rs b/src/io/utils.rs new file mode 100644 index 000000000..ba6285f68 --- /dev/null +++ b/src/io/utils.rs @@ -0,0 +1,51 @@ +use std::{error::Error, fmt, io}; +use crate::utils::VerboseErrorExt; + +/// Wrap `std::io::Error` with additional message +/// +/// *Note* Only active when `verbose-errors` feature is enabled for this crate! +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl VerboseErrorExt for Result { + fn verbose_context(self, message: impl Fn() -> String) -> Self { + if cfg!(feature = "verbose-errors") { + self.map_err(|e| VerboseError::wrap(e, message())) + } else { + self + } + } +} + +#[derive(Debug)] +struct VerboseError { + source: io::Error, + message: String, +} + +impl VerboseError { + fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + VerboseError { + source, + message: message.into(), + }, + ) + } +} + +impl fmt::Display for VerboseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for VerboseError { + fn description(&self) -> &str { + self.source.description() + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) + } +} diff --git a/src/utils.rs b/src/utils.rs index 13dbe37d5..00dc7931b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -52,6 +52,14 @@ pub fn random(n: u32) -> u32 { }) } +/// Add additional context to errors +/// +/// *Note for implementors:* The given closure must only be executed when +/// `verbose-errors` feature is enabled for this crate! +pub(crate) trait VerboseErrorExt { + fn verbose_context(self, message: impl Fn() -> String) -> Self; +} + /// Defers evaluation of a block of code until the end of the scope. #[cfg(feature = "default")] #[doc(hidden)] diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs new file mode 100644 index 000000000..3d99ede77 --- /dev/null +++ b/tests/verbose_errors.rs @@ -0,0 +1,20 @@ +#[cfg(feature = "verbose-errors")] +mod verbose_tests { + use async_std::{fs, task}; + + #[test] + fn open_file() { + task::block_on(async { + let non_existing_file = + "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; + let res = fs::File::open(non_existing_file).await; + match res { + Ok(_) => panic!("Found file with random name: We live in a simulation"), + Err(e) => assert_eq!( + "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + &format!("{}", e) + ), + } + }) + } +} From 99ddfb3f9393273e401b94afee84004ae3e284cb Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sun, 17 Nov 2019 23:11:11 +0100 Subject: [PATCH 281/707] Wrap code more clearly in cfg blocks --- src/io/utils.rs | 63 +++++++++++++++++++++++++------------------------ src/utils.rs | 8 ++++++- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/io/utils.rs b/src/io/utils.rs index ba6285f68..ebd22149d 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -1,4 +1,3 @@ -use std::{error::Error, fmt, io}; use crate::utils::VerboseErrorExt; /// Wrap `std::io::Error` with additional message @@ -6,46 +5,48 @@ use crate::utils::VerboseErrorExt; /// *Note* Only active when `verbose-errors` feature is enabled for this crate! /// /// Keeps the original error kind and stores the original I/O error as `source`. -impl VerboseErrorExt for Result { +impl VerboseErrorExt for Result { + #[cfg(feature = "verbose-errors")] fn verbose_context(self, message: impl Fn() -> String) -> Self { - if cfg!(feature = "verbose-errors") { - self.map_err(|e| VerboseError::wrap(e, message())) - } else { - self - } + self.map_err(|e| verbose::Error::wrap(e, message())) } } -#[derive(Debug)] -struct VerboseError { - source: io::Error, - message: String, -} +#[cfg(feature = "verbose-errors")] +mod verbose { + use std::{error::Error as StdError, fmt, io}; -impl VerboseError { - fn wrap(source: io::Error, message: impl Into) -> io::Error { - io::Error::new( - source.kind(), - VerboseError { - source, - message: message.into(), - }, - ) + #[derive(Debug)] + pub(crate) struct Error { + source: io::Error, + message: String, } -} -impl fmt::Display for VerboseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) + impl Error { + pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + Error { + source, + message: message.into(), + }, + ) + } } -} -impl Error for VerboseError { - fn description(&self) -> &str { - self.source.description() + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } } - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.source) + impl StdError for Error { + fn description(&self) -> &str { + self.source.description() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } } } diff --git a/src/utils.rs b/src/utils.rs index 00dc7931b..ce4a85cc8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -56,8 +56,14 @@ pub fn random(n: u32) -> u32 { /// /// *Note for implementors:* The given closure must only be executed when /// `verbose-errors` feature is enabled for this crate! -pub(crate) trait VerboseErrorExt { +pub(crate) trait VerboseErrorExt: Sized { + #[cfg(feature = "verbose-errors")] fn verbose_context(self, message: impl Fn() -> String) -> Self; + + #[cfg(not(feature = "verbose-errors"))] + fn verbose_context(self, _: impl Fn() -> String) -> Self { + self + } } /// Defers evaluation of a block of code until the end of the scope. From 2c9b558d14d9125534953a596e08ed4f6d33bc5d Mon Sep 17 00:00:00 2001 From: hhggit Date: Mon, 18 Nov 2019 10:07:47 +0800 Subject: [PATCH 282/707] add os::windows::symlink_{dir,file} --- src/fs/mod.rs | 2 ++ src/os/windows/fs.rs | 55 +++++++++++++++++++++++++++++++++++++++++++ src/os/windows/mod.rs | 1 + 3 files changed, 58 insertions(+) create mode 100644 src/os/windows/fs.rs diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 4598ec849..5cf086def 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -3,11 +3,13 @@ //! This module is an async version of [`std::fs`]. //! //! [`os::unix::fs`]: ../os/unix/fs/index.html +//! [`os::windows::fs`]: ../os/windows/fs/index.html //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html //! //! # Platform-specific extensions //! //! * Unix: use the [`os::unix::fs`] module. +//! * Windows: use the [`os::windows::fs`] module. //! //! # Examples //! diff --git a/src/os/windows/fs.rs b/src/os/windows/fs.rs new file mode 100644 index 000000000..243f3819d --- /dev/null +++ b/src/os/windows/fs.rs @@ -0,0 +1,55 @@ +//! Windows-specific filesystem extensions. + +use crate::io; +use crate::path::Path; +use crate::task::spawn_blocking; + +/// Creates a new directory symbolic link on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_dir`]. +/// +/// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_dir; +/// +/// symlink_dir("a", "b").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_dir(&src, &dst)).await +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_file`]. +/// +/// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_file; +/// +/// symlink_file("a.txt", "b.txt").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await +} diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index f3350007b..5f0bc0e43 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -2,4 +2,5 @@ cfg_std! { pub mod io; + pub mod fs; } From f6829859fef56585498938f99ca2cec731abd1c1 Mon Sep 17 00:00:00 2001 From: Razican Date: Mon, 18 Nov 2019 16:39:21 +0100 Subject: [PATCH 283/707] Fixed deduplication of code --- src/stream/stream/merge.rs | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index b08b586e4..bdf7a29fd 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -45,25 +45,29 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if utils::random(1) == 1 { - match this.left.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.right.poll_next(cx), - Poll::Pending => match this.right.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } + poll_next_in_order(this.left, this.right, cx) } else { - match this.right.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.left.poll_next(cx), - Poll::Pending => match this.left.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } + poll_next_in_order(this.right, this.left, cx) } } } + +fn poll_next_in_order( + first: Pin<&mut F>, + second: Pin<&mut S>, + cx: &mut Context<'_>, +) -> Poll> +where + F: Stream, + S: Stream, +{ + match first.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } +} From d3e7f32a30dee9202f242f35b43e5f3d40429175 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 18 Nov 2019 15:47:45 +0000 Subject: [PATCH 284/707] Macro optimization to reduce compilation times --- src/utils.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 13dbe37d5..49e3d9933 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -221,6 +221,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)*); From 65afd41a33c32059fdd42575f8d4199fd98333ee Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 19 Nov 2019 05:04:18 +0100 Subject: [PATCH 285/707] Once doesn't need Unpin bound (#554) --- src/stream/once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/once.rs b/src/stream/once.rs index a33bd6ac3..e4ac682cc 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -39,7 +39,7 @@ pin_project! { } } -impl Stream for Once { +impl Stream for Once { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { From c7046432965b374ed7bc03dc35b6e465a706b215 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 18 Nov 2019 23:55:48 +0100 Subject: [PATCH 286/707] Remove verbose-errors cargo feature --- .github/workflows/ci.yml | 6 ---- Cargo.toml | 1 - src/fs/file.rs | 6 ++-- src/io/utils.rs | 66 ++++++++++++++++++---------------------- src/utils.rs | 13 ++------ tests/verbose_errors.rs | 32 +++++++++---------- 6 files changed, 49 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d5639c68..99436b72b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,12 +64,6 @@ jobs: command: test args: --all --features unstable - - name: tests with verbose errors - uses: actions-rs/cargo@v1 - with: - command: test - args: --all --features 'unstable verbose-errors' - check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 7886bcfd5..7ffaae3a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ std = [ "pin-utils", "slab", ] -verbose-errors = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 5186e96fe..f82428112 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::utils::VerboseErrorExt; +use crate::utils::Context as _; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; @@ -115,7 +115,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::open(&path) - .verbose_context(|| format!("Could not open {}", path.display())) + .context(|| format!("Could not open {}", path.display())) }) .await?; Ok(File::new(file, true)) @@ -154,7 +154,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::create(&path) - .verbose_context(|| format!("Could not create {}", path.display())) + .context(|| format!("Could not create {}", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/io/utils.rs b/src/io/utils.rs index ebd22149d..1b730645b 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -1,52 +1,46 @@ -use crate::utils::VerboseErrorExt; +use crate::utils::Context; /// Wrap `std::io::Error` with additional message /// -/// *Note* Only active when `verbose-errors` feature is enabled for this crate! -/// /// Keeps the original error kind and stores the original I/O error as `source`. -impl VerboseErrorExt for Result { - #[cfg(feature = "verbose-errors")] - fn verbose_context(self, message: impl Fn() -> String) -> Self { - self.map_err(|e| verbose::Error::wrap(e, message())) +impl Context for Result { + fn context(self, message: impl Fn() -> String) -> Self { + self.map_err(|e| VerboseError::wrap(e, message())) } } -#[cfg(feature = "verbose-errors")] -mod verbose { - use std::{error::Error as StdError, fmt, io}; +use std::{error::Error as StdError, fmt, io}; - #[derive(Debug)] - pub(crate) struct Error { - source: io::Error, - message: String, - } +#[derive(Debug)] +pub(crate) struct VerboseError { + source: io::Error, + message: String, +} - impl Error { - pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { - io::Error::new( - source.kind(), - Error { - source, - message: message.into(), - }, - ) - } +impl VerboseError { + pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + VerboseError { + source, + message: message.into(), + }, + ) } +} - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) - } +impl fmt::Display for VerboseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) } +} - impl StdError for Error { - fn description(&self) -> &str { - self.source.description() - } +impl StdError for VerboseError { + fn description(&self) -> &str { + self.source.description() + } - fn source(&self) -> Option<&(dyn StdError + 'static)> { - Some(&self.source) - } + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) } } diff --git a/src/utils.rs b/src/utils.rs index ce4a85cc8..645dc8495 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -53,17 +53,8 @@ pub fn random(n: u32) -> u32 { } /// Add additional context to errors -/// -/// *Note for implementors:* The given closure must only be executed when -/// `verbose-errors` feature is enabled for this crate! -pub(crate) trait VerboseErrorExt: Sized { - #[cfg(feature = "verbose-errors")] - fn verbose_context(self, message: impl Fn() -> String) -> Self; - - #[cfg(not(feature = "verbose-errors"))] - fn verbose_context(self, _: impl Fn() -> String) -> Self { - self - } +pub(crate) trait Context { + fn context(self, message: impl Fn() -> String) -> Self; } /// Defers evaluation of a block of code until the end of the scope. diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 3d99ede77..54d04f8d0 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,20 +1,16 @@ -#[cfg(feature = "verbose-errors")] -mod verbose_tests { - use async_std::{fs, task}; +use async_std::{fs, task}; - #[test] - fn open_file() { - task::block_on(async { - let non_existing_file = - "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; - let res = fs::File::open(non_existing_file).await; - match res { - Ok(_) => panic!("Found file with random name: We live in a simulation"), - Err(e) => assert_eq!( - "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", - &format!("{}", e) - ), - } - }) - } +#[test] +fn open_file() { + task::block_on(async { + let non_existing_file = "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; + let res = fs::File::open(non_existing_file).await; + match res { + Ok(_) => panic!("Found file with random name: We live in a simulation"), + Err(e) => assert_eq!( + "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + &format!("{}", e) + ), + } + }) } From 314a75da288367f92481f20424e728fe177e8fed Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:00:54 +0900 Subject: [PATCH 287/707] fix typo --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f8765762b..c61da554e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -916,7 +916,7 @@ extension_trait! { let max = s.clone().max_by_key(|x| x.abs()).await; assert_eq!(max, Some(3)); - let max = stream::empty::().min_by_key(|x| x.abs()).await; + let max = stream::empty::().max_by_key(|x| x.abs()).await; assert_eq!(max, None); # # }) } From 64b2e10b930caf1f5f047b81060f415b570b3768 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:17:29 +0900 Subject: [PATCH 288/707] fix max_by_key mistake --- src/stream/stream/max_by_key.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index b5bc7e0c2..5ebc25d73 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; @@ -13,7 +13,7 @@ pin_project! { pub struct MaxByKeyFuture { #[pin] stream: S, - max: Option, + max: Option<(T, T)>, key_by: K, } } @@ -37,24 +37,32 @@ where type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + let this = self.project(); let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { - let new = (this.key_by)(&new); + let (key, value) = key(this.key_by)(new); cx.waker().wake_by_ref(); + match this.max.take() { - None => *this.max = Some(new), + None => *this.max = Some((key, value)), - Some(old) => match new.cmp(&old) { - Ordering::Greater => *this.max = Some(new), + Some(old) => match key.cmp(&old.0) { + Ordering::Greater => *this.max = Some((key, value)), _ => *this.max = Some(old), }, } Poll::Pending } - None => Poll::Ready(this.max.take()), + None => Poll::Ready(match this.max.take() { + None => None, + Some(max) => Some(max.1), + }), } } } From 667bbc1019783d0af0d8424a31c59b178c503eab Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:18:11 +0900 Subject: [PATCH 289/707] doc: update doc test --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c61da554e..ca386e0ff 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -911,10 +911,10 @@ extension_trait! { use async_std::prelude::*; use async_std::stream; - let s = stream::from_iter(vec![-1isize, -2, -3]); + 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(3)); + assert_eq!(max, Some(-10)); let max = stream::empty::().max_by_key(|x| x.abs()).await; assert_eq!(max, None); From ca71ad073bba886f84aaf0fa14e3801f54b203f9 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:25:35 +0900 Subject: [PATCH 290/707] fix stream min_by_key mistake --- src/stream/stream/min_by_key.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 8179fb312..3cd001431 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; @@ -13,7 +13,7 @@ pin_project! { pub struct MinByKeyFuture { #[pin] stream: S, - min: Option, + min: Option<(T, T)>, key_by: K, } } @@ -37,24 +37,32 @@ where type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + let this = self.project(); let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { - let new = (this.key_by)(&new); + let (key, value) = key(this.key_by)(new); cx.waker().wake_by_ref(); + match this.min.take() { - None => *this.min = Some(new), + None => *this.min = Some((key, value)), - Some(old) => match new.cmp(&old) { - Ordering::Less => *this.min = Some(new), + Some(old) => match key.cmp(&old.0) { + Ordering::Less => *this.min = Some((key, value)), _ => *this.min = Some(old), }, } Poll::Pending } - None => Poll::Ready(this.min.take()), + None => Poll::Ready(match this.min.take() { + None => None, + Some(max) => Some(max.1), + }), } } } From 080875edc9effe5387b37b99d56b92302599c9ba Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:25:48 +0900 Subject: [PATCH 291/707] update min_by_key doc --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index ca386e0ff..48d865e05 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -875,10 +875,10 @@ extension_trait! { use async_std::prelude::*; use async_std::stream; - let s = stream::from_iter(vec![1isize, 2, -3]); + 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)); + assert_eq!(min, Some(-1)); let min = stream::empty::().min_by_key(|x| x.abs()).await; assert_eq!(min, None); From b5e66c4f93d699e986ab2681e94b6b79170cec4c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:38:51 +0900 Subject: [PATCH 292/707] refactor: Refactoring option type handling --- src/stream/stream/max_by_key.rs | 5 +---- src/stream/stream/min_by_key.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index 5ebc25d73..e421f94aa 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -59,10 +59,7 @@ where } Poll::Pending } - None => Poll::Ready(match this.max.take() { - None => None, - Some(max) => Some(max.1), - }), + None => Poll::Ready(this.max.take().map(|max| max.1)), } } } diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 3cd001431..142dfe194 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -59,10 +59,7 @@ where } Poll::Pending } - None => Poll::Ready(match this.min.take() { - None => None, - Some(max) => Some(max.1), - }), + None => Poll::Ready(this.min.take().map(|min| min.1)), } } } From 72ca2c1a24ea535752401fbfb9628f30ec209efe Mon Sep 17 00:00:00 2001 From: razican Date: Tue, 19 Nov 2019 21:14:56 +0100 Subject: [PATCH 293/707] Improved the code with some minor changes --- src/stream/stream/merge.rs | 7 +++---- src/stream/stream/mod.rs | 10 +++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index bdf7a29fd..d9d2b0a08 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -62,12 +62,11 @@ where S: Stream, { match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), Poll::Ready(None) => second.poll_next(cx), + Poll::Ready(item) => Poll::Ready(item), Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, + Poll::Ready(None) | Poll::Pending => Poll::Pending, + Poll::Ready(item) => Poll::Ready(item), }, } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7c4bceb0e..223aea5a0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1663,18 +1663,14 @@ extension_trait! { ``` # async_std::task::block_on(async { use async_std::prelude::*; - use async_std::stream; + use async_std::stream::{self, FromStream}; let a = stream::once(1u8); let b = stream::once(2u8); let c = stream::once(3u8); - let mut s = a.merge(b).merge(c); - let mut lst = Vec::new(); - - while let Some(n) = s.next().await { - lst.push(n) - } + let s = a.merge(b).merge(c); + let mut lst = Vec::from_stream(s).await; lst.sort_unstable(); assert_eq!(&lst, &[1u8, 2u8, 3u8]); From 72ed4eb4fde43ec89d7135f0d16b4df3b0d88fda Mon Sep 17 00:00:00 2001 From: hhggit Date: Wed, 20 Nov 2019 19:51:01 +0800 Subject: [PATCH 294/707] Update mod.rs --- src/os/windows/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 5f0bc0e43..cd8deb609 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -2,5 +2,8 @@ cfg_std! { pub mod io; +} + +cfg_default! { pub mod fs; } From 5fba3a09289ffd043e37b38622b1e3cc25a5125e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 20 Nov 2019 13:22:46 +0100 Subject: [PATCH 295/707] Fix rng use in Stream::merge --- src/stream/stream/merge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index d9d2b0a08..84ac43229 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -44,7 +44,7 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - if utils::random(1) == 1 { + if utils::random(2) == 0 { poll_next_in_order(this.left, this.right, cx) } else { poll_next_in_order(this.right, this.left, cx) From d146d95a3934ab214c8ec22c2cb029c562b60ef0 Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 20 Nov 2019 21:38:42 +0900 Subject: [PATCH 296/707] Update src/stream/stream/mod.rs Co-Authored-By: Taiki Endo --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e0e9b7e44..51c413900 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1722,7 +1722,7 @@ extension_trait! { #[doc = r#" Converts an stream of pairs into a pair of containers. - unzip() consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. + `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. This function is, in some sense, the opposite of [`zip`]. From b3d30de4a11d785929a086e458ec1da4aec34bf3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 20 Nov 2019 14:58:49 +0100 Subject: [PATCH 297/707] mark windows fs APIs as "unstable" (#567) Signed-off-by: Yoshua Wuyts --- src/os/windows/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index cd8deb609..6dac11b6e 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -4,6 +4,6 @@ cfg_std! { pub mod io; } -cfg_default! { +cfg_unstable! { pub mod fs; } From e01f07d72a224ff0427dac70db544a12c2c0bf8c Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 21 Nov 2019 00:22:06 +0100 Subject: [PATCH 298/707] Add context to more errors cc #569 --- src/fs/canonicalize.rs | 8 ++++- src/fs/copy.rs | 7 ++++- src/fs/create_dir.rs | 7 ++++- src/fs/create_dir_all.rs | 7 ++++- src/fs/file.rs | 7 ++--- src/fs/hard_link.rs | 12 +++++++- src/fs/read.rs | 6 +++- src/fs/read_dir.rs | 12 +++++--- src/fs/read_link.rs | 8 ++++- src/fs/read_to_string.rs | 7 ++++- src/fs/remove_dir.rs | 7 ++++- src/fs/remove_dir_all.rs | 7 ++++- src/fs/remove_file.rs | 7 ++++- src/fs/rename.rs | 12 +++++++- src/fs/write.rs | 7 ++++- src/io/copy.rs | 7 +++-- src/io/stdin.rs | 4 ++- src/net/tcp/listener.rs | 9 ++++-- src/net/tcp/stream.rs | 13 ++++++-- src/net/udp/mod.rs | 64 +++++++++++++++++++++++++++++++++++----- tests/verbose_errors.rs | 2 +- 21 files changed, 181 insertions(+), 39 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 6eb6977db..38a5a6b90 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Returns the canonical form of a path. /// @@ -32,5 +33,10 @@ use crate::task::spawn_blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::canonicalize(&path).map(Into::into)).await + spawn_blocking(move || { + std::fs::canonicalize(&path) + .map(Into::into) + .context(|| format!("could not canonicalize `{}`", path.display())) + }) + .await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 170b66ece..8fb447bb0 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Copies the contents and permissions of a file to a new location. /// @@ -41,5 +42,9 @@ use crate::task::spawn_blocking; pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::copy(&from, &to)).await + spawn_blocking(move || { + std::fs::copy(&from, &to) + .context(|| format!("could not copy `{}` to `{}`", from.display(), to.display())) + }) + .await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 03c24918c..37923c055 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory. /// @@ -34,5 +35,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::create_dir(path)).await + spawn_blocking(move || { + std::fs::create_dir(&path) + .context(|| format!("could not create directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 152419430..753dfd492 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory and all of its parents if they are missing. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::create_dir_all(path)).await + spawn_blocking(move || { + std::fs::create_dir_all(&path) + .context(|| format!("could not create directory path `{}`", path.display())) + }) + .await } diff --git a/src/fs/file.rs b/src/fs/file.rs index f82428112..24c72c6d1 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,11 +9,11 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::utils::Context as _; 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::utils::Context as _; /// An open file on the filesystem. /// @@ -114,8 +114,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { - std::fs::File::open(&path) - .context(|| format!("Could not open {}", path.display())) + std::fs::File::open(&path).context(|| format!("Could not open `{}`", path.display())) }) .await?; Ok(File::new(file, true)) @@ -154,7 +153,7 @@ 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())) + .context(|| format!("Could not create `{}`", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index e6e56cd53..a6a406989 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a hard link on the filesystem. /// @@ -32,5 +33,14 @@ use crate::task::spawn_blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::hard_link(&from, &to)).await + spawn_blocking(move || { + std::fs::hard_link(&from, &to).context(|| { + format!( + "could not create a hard link from `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/read.rs b/src/fs/read.rs index ab7d17566..3b568f7a6 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as raw bytes. /// @@ -36,5 +37,8 @@ use crate::task::spawn_blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read(path)).await + spawn_blocking(move || { + std::fs::read(&path).context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 5e51065b6..d8261a949 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,11 +1,12 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use crate::fs::DirEntry; use crate::io; use crate::path::Path; use crate::stream::Stream; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; /// Returns a stream of entries in a directory. /// @@ -45,9 +46,12 @@ use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_dir(path)) - .await - .map(ReadDir::new) + spawn_blocking(move || { + std::fs::read_dir(&path) + .context(|| format!("could not read directory `{}`", path.display())) + }) + .await + .map(ReadDir::new) } /// A stream of entries in a directory. diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 7ec18a45e..d8cabb725 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads a symbolic link and returns the path it points to. /// @@ -28,5 +29,10 @@ use crate::task::spawn_blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_link(path).map(Into::into)).await + spawn_blocking(move || { + std::fs::read_link(&path) + .map(Into::into) + .context(|| format!("could not read link `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index d06aa614c..2378aaedc 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as a string. /// @@ -37,5 +38,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_to_string(path)).await + spawn_blocking(move || { + std::fs::read_to_string(&path) + .context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index 1a62db2e7..8fdba18c1 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes an empty directory. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_dir(path)).await + spawn_blocking(move || { + std::fs::remove_dir(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 336674061..d4bad3a28 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a directory and all of its contents. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_dir_all(path)).await + spawn_blocking(move || { + std::fs::remove_dir_all(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 9a74ec111..b881f8be5 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a file. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_file(path)).await + spawn_blocking(move || { + std::fs::remove_file(&path) + .context(|| format!("could not remove file `{}`", path.display())) + }) + .await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index ed7f39c9c..25fc55fa2 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Renames a file or directory to a new location. /// @@ -34,5 +35,14 @@ use crate::task::spawn_blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::rename(&from, &to)).await + spawn_blocking(move || { + std::fs::rename(&from, &to).context(|| { + format!( + "could not rename `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 4e5d20bb1..7c14098b0 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Writes a slice of bytes as the new contents of a file. /// @@ -33,5 +34,9 @@ use crate::task::spawn_blocking; pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - spawn_blocking(move || std::fs::write(path, contents)).await + spawn_blocking(move || { + std::fs::write(&path, contents) + .context(|| format!("could not write to file `{}`", path.display())) + }) + .await } diff --git a/src/io/copy.rs b/src/io/copy.rs index 8ec3c1af1..f05ed0e17 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,10 +1,11 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; +use crate::utils::Context as _; /// Copies the entire contents of a reader into a writer. /// @@ -90,7 +91,7 @@ where writer, amt: 0, }; - future.await + future.await.context(|| String::from("io::copy failed")) } /// Copies the entire contents of a reader into a writer. @@ -177,5 +178,5 @@ where writer, amt: 0, }; - future.await + future.await.context(|| String::from("io::copy failed")) } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 167ea2dd3..618393af1 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,10 +1,11 @@ +use std::future::Future; use std::pin::Pin; use std::sync::Mutex; -use std::future::Future; 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; @@ -162,6 +163,7 @@ impl Stdin { } }) .await + .context(|| String::from("Could not read line on stdin")) } /// Locks this handle to the standard input stream, returning a readable guard. diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index f98bbdc75..9d944f4b6 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,5 +1,5 @@ -use std::net::SocketAddr; use std::future::Future; +use std::net::SocketAddr; use std::pin::Pin; use crate::future; @@ -8,6 +8,7 @@ use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::utils::Context as _; /// A TCP socket server, listening for connections. /// @@ -75,8 +76,12 @@ impl TcpListener { /// [`local_addr`]: #method.local_addr pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { match mio::net::TcpListener::bind(&addr) { Ok(mio_listener) => { return Ok(TcpListener { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1da9c7c2b..9d3c2ecd0 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -7,6 +7,7 @@ use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; use crate::task::{spawn_blocking, Context, Poll}; +use crate::utils::Context as _; /// A TCP stream between a local and a remote socket. /// @@ -71,11 +72,17 @@ impl TcpStream { /// ``` pub async fn connect(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { let res = spawn_blocking(move || { - let std_stream = std::net::TcpStream::connect(addr)?; - let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; + let std_stream = std::net::TcpStream::connect(addr) + .context(|| format!("could not connect to {}", addr))?; + let mio_stream = mio::net::TcpStream::from_stream(std_stream) + .context(|| format!("could not open async connection to {}", addr))?; Ok(TcpStream { watcher: Watcher::new(mio_stream), }) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 37c9d50ce..e6064136a 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -5,6 +5,7 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; +use crate::utils::Context as _; /// A UDP socket. /// @@ -66,10 +67,14 @@ impl UdpSocket { /// # /// # Ok(()) }) } /// ``` - pub async fn bind(addr: A) -> io::Result { + pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addr.to_socket_addrs().await? { + for addr in addrs { match mio::net::UdpSocket::bind(&addr) { Ok(mio_socket) => { return Ok(UdpSocket { @@ -106,7 +111,10 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.watcher.get_ref().local_addr() + self.watcher + .get_ref() + .local_addr() + .context(|| String::from("could not get local address")) } /// Sends data on the socket to the given address. @@ -151,6 +159,7 @@ impl UdpSocket { .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) }) .await + .context(|| format!("Could not send packet to {}", addr)) } /// Receives data from the socket. @@ -178,6 +187,17 @@ impl UdpSocket { .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 + }) } /// Connects the UDP socket to a remote address. @@ -195,7 +215,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?; /// socket.connect("127.0.0.1:8080").await?; @@ -204,8 +224,12 @@ impl UdpSocket { /// ``` pub async fn connect(&self, addrs: A) -> io::Result<()> { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { // TODO(stjepang): connect on the blocking pool match self.watcher.get_ref().connect(addr) { Ok(()) => return Ok(()), @@ -248,7 +272,19 @@ 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 + 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 + }) } /// Receives data from the socket. @@ -271,7 +307,19 @@ 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 + 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 + }) } /// Gets the value of the `SO_BROADCAST` option for this socket. @@ -415,7 +463,7 @@ impl UdpSocket { /// use async_std::net::UdpSocket; /// /// let socket_addr = SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(), 0); - /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123) ; + /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123); /// let socket = UdpSocket::bind(&socket_addr).await?; /// /// socket.join_multicast_v6(&mdns_addr, 0)?; diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 54d04f8d0..207d0b27b 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -8,7 +8,7 @@ fn open_file() { match res { Ok(_) => panic!("Found file with random name: We live in a simulation"), Err(e) => assert_eq!( - "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + "Could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`", &format!("{}", e) ), } From 16edec346498f3a6e32869b78b57c63ca826cc17 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 21 Nov 2019 17:50:30 +0100 Subject: [PATCH 299/707] Ignore seek errors in poll_unread --- src/fs/file.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index f82428112..94e2989b3 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -742,7 +742,10 @@ impl LockGuard { if n > 0 { // Seek `n` bytes backwards. This call should not block because it only changes // the internal offset into the file and doesn't touch the actual file on disk. - (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; + // + // We ignore errors here because special files like `/dev/random` are not + // seekable. + let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64))); } // Switch to idle mode. From ba1ee2d204f1e27f26d0cb98c004ce7676b6aae4 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 18:03:10 +0100 Subject: [PATCH 300/707] Fix a-chat tutorial issues (#573) * tutorial/receiving_messages: fix future output type bound * tutorial/receiving_messages: remove unneeded message trimming Trimming was done twice on messages, so one of the two instances can be removed. I personally think removing the first instance, in which we are splitting names from messages makes the code more readable than removing the second instance, but other examples further in the tutorial show the second instance removed. * tutorial/receiving_messages: declare use of TcpStream and io::BufReader Readers couldn't see the `use` lines corresponding to these two structures. * tutorial/connecting_readers_and_writers: typos and grammar fixes * tutorial/all_together: remove unneeded use async_std::io * tutorial: use SinkExt consistently from futures::sink::SinkExt * tutorial/handling_disconnection: hide mpsc use clause and remove empty lines The empty lines translate to the output making it look weird. * tutorial/handling_disconnection: fix typos * tutorial/handling_disconnection: use ? in broker_handle.await We were happy to return an Err variant from the broker_handle before and nothing has changed in this regard, so bubbling it up to run(). --- docs/src/tutorial/all_together.md | 4 ++-- docs/src/tutorial/clean_shutdown.md | 4 ++-- .../tutorial/connecting_readers_and_writers.md | 12 ++++++------ docs/src/tutorial/handling_disconnection.md | 17 ++++++++--------- docs/src/tutorial/receiving_messages.md | 12 ++++++++---- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 789283e66..8bb01e943 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -6,13 +6,13 @@ At this point, we only need to start the broker to get a fully-functioning (in t # extern crate async_std; # extern crate futures; use async_std::{ - io::{self, BufReader}, + io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; use futures::channel::mpsc; -use futures::SinkExt; +use futures::sink::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 5dcc7f263..bd112c934 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -30,7 +30,7 @@ Let's add waiting to the server: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -163,7 +163,7 @@ And to the broker: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index fcc42b63d..921cf90ca 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -2,12 +2,12 @@ ## Connecting Readers and Writers So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`? -We should somehow maintain an `peers: HashMap>` map which allows a client to find destination channels. +We should somehow maintain a `peers: HashMap>` map which allows a client to find destination channels. However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message. One trick to make reasoning about state simpler comes from the actor model. -We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels. -By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit. +We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels. +By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit. The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue. ```rust,edition2018 @@ -92,9 +92,9 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { } ``` -1. Broker should handle two types of events: a message or an arrival of a new peer. -2. Internal state of the broker is a `HashMap`. +1. The broker task should handle two types of events: a message or an arrival of a new peer. +2. The internal state of the broker is a `HashMap`. Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers 3. To handle a message, we send it over a channel to each destination -4. To handle new peer, we first register it in the peer's map ... +4. To handle a new peer, we first register it in the peer's map ... 5. ... and then spawn a dedicated task to actually write the messages to the socket. diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index acb744b09..9db9abd25 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -22,7 +22,7 @@ First, let's add a shutdown channel to the `connection_loop`: # extern crate futures; # use async_std::net::TcpStream; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -60,8 +60,8 @@ async fn connection_loop(mut broker: Sender, stream: Arc) -> R } ``` -1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type. -2. We pass the shutdown channel to the writer task +1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type. +2. We pass the shutdown channel to the writer task. 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped. In the `connection_writer_loop`, we now need to choose between shutdown and message channels. @@ -71,14 +71,12 @@ We use the `select` macro for this purpose: # extern crate async_std; # extern crate futures; # use async_std::{net::TcpStream, prelude::*}; -use futures::channel::mpsc; +# use futures::channel::mpsc; use futures::{select, FutureExt}; # use std::sync::Arc; - # type Receiver = mpsc::UnboundedReceiver; # type Result = std::result::Result>; # type Sender = mpsc::UnboundedSender; - # #[derive(Debug)] # enum Void {} // 1 @@ -112,7 +110,7 @@ async fn connection_writer_loop( 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. -This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable. +This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible. ## Final Code @@ -128,7 +126,8 @@ use async_std::{ task, }; use futures::channel::mpsc; -use futures::{select, FutureExt, SinkExt}; +use futures::sink::SinkExt; +use futures::{select, FutureExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, @@ -158,7 +157,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); - broker_handle.await; + broker_handle.await?; Ok(()) } diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 213589c07..4f705294e 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,14 +10,18 @@ We need to: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# io::BufReader, -# net::{TcpListener, TcpStream, ToSocketAddrs}, +# net::{TcpListener, ToSocketAddrs}, # prelude::*, # task, # }; # # type Result = std::result::Result>; # +use async_std::{ + io::BufReader, + net::TcpStream, +}; + async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); @@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> { Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), }; let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); - let msg: String = msg.trim().to_string(); + let msg: String = msg.to_string(); } Ok(()) } @@ -130,7 +134,7 @@ So let's use a helper function for this: # }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { task::spawn(async move { if let Err(e) = fut.await { From ec5415358f929a5ce92f7451503653c7a190893e Mon Sep 17 00:00:00 2001 From: laizy Date: Fri, 22 Nov 2019 01:03:23 +0800 Subject: [PATCH 301/707] simplify AllFuture and AnyFuture (#572) --- src/stream/stream/all.rs | 5 +---- src/stream/stream/any.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 5adb68f33..d2ac5cac8 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -10,7 +10,6 @@ use crate::task::{Context, Poll}; pub struct AllFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } @@ -19,7 +18,6 @@ impl<'a, S, F, T> AllFuture<'a, S, F, T> { Self { stream, f, - result: true, // the default if the empty stream _marker: PhantomData, } } @@ -40,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { // don't forget to wake this task again to pull the next item from stream @@ -50,7 +47,7 @@ where Poll::Ready(false) } } - None => Poll::Ready(self.result), + None => Poll::Ready(true), } } } diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index d6853a1cd..34168e672 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -10,7 +10,6 @@ use crate::task::{Context, Poll}; pub struct AnyFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } @@ -19,7 +18,6 @@ impl<'a, S, F, T> AnyFuture<'a, S, F, T> { Self { stream, f, - result: false, // the default if the empty stream _marker: PhantomData, } } @@ -40,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { Poll::Ready(true) @@ -50,7 +47,7 @@ where Poll::Pending } } - None => Poll::Ready(self.result), + None => Poll::Ready(false), } } } From cffacf7fa32d448a4b0707bfddd51fc0f06d2850 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 21 Nov 2019 21:21:19 +0100 Subject: [PATCH 302/707] feedback from review Signed-off-by: Yoshua Wuyts --- README.md | 6 ++++++ src/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c074fee5..34ebe6df7 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ syntax. ## Examples +All examples require the [`"attributes"` feature] to be enabled. This feature +is not enabled by default because it significantly impacts compile times. See +[`task::block_on`] for an alternative way to start executing tasks. + ```rust async fn say_hello() { println!("Hello, world!"); @@ -90,6 +94,8 @@ More examples, including networking and file access, can be found in our [`examples`]: https://github.com/async-rs/async-std/tree/master/examples [documentation]: https://docs.rs/async-std#examples +[`task::block_on`]: task/fn.block_on.html +[`"attributes"` feature]: https://docs.rs/async-std/#features ## Philosophy diff --git a/src/lib.rs b/src/lib.rs index 5442909f3..d0c87ff5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,8 +154,8 @@ //! ``` //! #[async_std::main] //! async fn main() { -//! let a = || async move { 1u8 }; -//! let b = || async move { 2u8 }; +//! let a = async { 1u8 }; +//! let b = async { 2u8 }; //! assert_eq!(a.join(b).await, (1u8, 2u8)) //! } //! ``` From 3780ff7b44d438d991a8253ee3cc1916106fea45 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 20 Nov 2019 02:25:00 +0100 Subject: [PATCH 303/707] 1.1.0 Signed-off-by: Yoshua Wuyts changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d19fddb8..7977be76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.1.0] - 2019-11-21 + +[API Documentation](https://docs.rs/async-std/1.1.0/async-std) + +This patch introduces a faster scheduler algorithm, `Stream::throttle`, and +stabilizes `task::yield_now`. Additionally we're introducing several more stream +APIs, bringing us to almost complete parity with the standard library. + +Furthermore our `path` submodule now returns more context in errors. So if +opening a file fails, async-std will tell you *which* file was failed to open, +making it easier to write and debug programs. + +## Examples + +```rust +let start = Instant::now(); + +let mut s = stream::interval(Duration::from_millis(5)) + .throttle(Duration::from_millis(10)) + .take(2); + +s.next().await; +assert!(start.elapsed().as_millis() >= 5); + +s.next().await; +assert!(start.elapsed().as_millis() >= 15); + +s.next().await; +assert!(start.elapsed().as_millis() >= 25); +``` + +## Added + +- Added `Stream::throttle` as "unstable". +- Added `Stream::count` as "unstable". +- Added `Stream::max` as "unstable". +- Added `Stream::successors` as "unstable". +- Added `Stream::by_ref` as "unstable". +- Added `Stream::partition` as "unstable". +- Added contextual errors to the `path` submodule. +- Added `os::windows::symlink_dir` as "unstable". +- Added `os::windows::symlink_file` as "unstable". +- Stabilized `task::yield_now`. + +## Fixes + +- We now ignore seek errors when rolling back failed `read` calls on `File`. +- Fixed a bug where `Stream::max_by_key` was returning the wrong result. +- Fixed a bug where `Stream::min_by_key` was returning the wrong result. + +## Changed + +- Applied various fixes to the tutorial. +- Fixed an issue with Clippy. +- Optimized an internal code generation macro, improving compilation speeds. +- Removed an `Unpin` bound from `stream::Once`. +- Removed various extra internal uses of `pin_mut!`. +- Simplified `Stream::any` and `Stream::all`'s internals. +- The `surf` example is now enabled again. +- Tweaked some streams internals. +- Updated `futures-timer` to 2.0.0, improving compilation speed. +- Upgraded `async-macros` to 2.0.0. +- `Stream::merge` now uses randomized ordering to reduce overall latency. +- The scheduler is now more efficient by keeping a slot for the next task to + run. This is similar to Go's scheduler, and Tokio's scheduler. +- Fixed the documentation of the `channel` types to link back to the `channel` + function. + # [1.0.1] - 2019-11-12 [API Documentation](https://docs.rs/async-std/1.0.1/async-std) @@ -442,7 +510,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 diff --git a/Cargo.toml b/Cargo.toml index 7ffaae3a1..9df056603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.0.1" +version = "1.1.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From c1f7be5d42f9fbeb63250e2b4f75986d2ae94fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Sat, 23 Nov 2019 11:40:07 -0600 Subject: [PATCH 304/707] Adding timeout extension method to Future trait --- src/future/future/mod.rs | 13 +++++++++++++ src/future/timeout.rs | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 5fdaf4b1a..fc800cb2d 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -15,6 +15,7 @@ cfg_unstable! { use try_race::TryRace; use join::Join; use try_join::TryJoin; + use crate::future::timeout::TimeoutFuture; } extension_trait! { @@ -355,6 +356,18 @@ extension_trait! { { TryJoin::new(self, other) } + + #[doc = r#" + Waits for both the future and a timeout, if the timeout completes before + the future, it returns an TimeoutError. + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> impl Future [TimeoutFuture] + where Self: Sized + { + TimeoutFuture::new(self, dur) + } } impl Future for Box { diff --git a/src/future/timeout.rs b/src/future/timeout.rs index ff87ae4f7..c2b6306c9 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -42,7 +42,7 @@ where pin_project! { /// A future that times out after a duration of time. - struct TimeoutFuture { + pub struct TimeoutFuture { #[pin] future: F, #[pin] @@ -50,6 +50,12 @@ pin_project! { } } +impl TimeoutFuture { + pub fn new(future: F, dur: Duration) -> TimeoutFuture { + TimeoutFuture { future: future, delay: Delay::new(dur) } + } +} + impl Future for TimeoutFuture { type Output = Result; From aa7d1c27a42b262ef47a68e0fb61f5078fb18a33 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 25 Nov 2019 21:18:40 +0100 Subject: [PATCH 305/707] Verbose errors: Apply suggestions Co-Authored-By: Yoshua Wuyts --- src/fs/file.rs | 4 ++-- src/io/stdin.rs | 2 +- src/net/udp/mod.rs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 24c72c6d1..c111e013c 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -114,7 +114,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { - std::fs::File::open(&path).context(|| format!("Could not open `{}`", path.display())) + std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display())) }) .await?; Ok(File::new(file, true)) @@ -153,7 +153,7 @@ 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())) + .context(|| format!("could not create `{}`", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 618393af1..369ccae4c 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -163,7 +163,7 @@ impl Stdin { } }) .await - .context(|| String::from("Could not read line on stdin")) + .context(|| String::from("could not read line on stdin")) } /// Locks this handle to the standard input stream, returning a readable guard. diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index e6064136a..a1ca03f33 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -159,7 +159,7 @@ impl UdpSocket { .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) }) .await - .context(|| format!("Could not send packet to {}", addr)) + .context(|| format!("could not send packet to {}", addr)) } /// Receives data from the socket. @@ -190,7 +190,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not receive data on "); + let mut error = String::from("could not receive data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { @@ -277,7 +277,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not send data on "); + let mut error = String::from("could not send data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { @@ -312,7 +312,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not receive data on "); + let mut error = String::from("could not receive data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { From 56538ebd9117402d2c8e69a67a08b4ea8b5e660f Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 25 Nov 2019 21:41:54 +0100 Subject: [PATCH 306/707] Improve verbose errors for socket addresses Moves the point of adding error context to the net::addr module so that we have access to the raw address input and can include it in the error message. --- src/net/addr.rs | 32 +++++++++++++++++++++++++++----- src/net/tcp/listener.rs | 4 +--- src/net/tcp/stream.rs | 3 +-- src/net/udp/mod.rs | 3 +-- tests/verbose_errors.rs | 17 ++++++++++++++++- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/net/addr.rs b/src/net/addr.rs index 2769dd5e2..ea839500e 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -1,11 +1,12 @@ +use std::future::Future; use std::mem; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use std::future::Future; use crate::io; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as ErrorContext; cfg_not_docs! { macro_rules! ret { @@ -67,6 +68,18 @@ pub enum ToSocketAddrsFuture { Done, } +/// Wrap `std::io::Error` with additional message +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl ErrorContext for ToSocketAddrsFuture { + fn context(self, message: impl Fn() -> String) -> Self { + match self { + ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)), + x => x, + } + } +} + impl> Future for ToSocketAddrsFuture { type Output = io::Result; @@ -110,7 +123,9 @@ impl ToSocketAddrs for SocketAddrV4 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V4(*self).to_socket_addrs() + SocketAddr::V4(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -123,7 +138,9 @@ impl ToSocketAddrs for SocketAddrV6 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V6(*self).to_socket_addrs() + SocketAddr::V6(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -195,7 +212,9 @@ impl ToSocketAddrs for (&str, u16) { let host = host.to_string(); let task = spawn_blocking(move || { - std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) + let addr = (host.as_str(), port); + std::net::ToSocketAddrs::to_socket_addrs(&addr) + .context(|| format!("could not resolve address `{:?}`", addr)) }); ToSocketAddrsFuture::Resolving(task) } @@ -215,7 +234,10 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + let task = spawn_blocking(move || { + std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) + .context(|| format!("could not resolve address `{:?}`", addr)) + }); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 9d944f4b6..fe06a96d6 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -8,7 +8,6 @@ use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Context as _; /// A TCP socket server, listening for connections. /// @@ -78,8 +77,7 @@ impl TcpListener { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::TcpListener::bind(&addr) { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 9d3c2ecd0..413178333 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -74,8 +74,7 @@ impl TcpStream { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { let res = spawn_blocking(move || { diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index a1ca03f33..7fef1ed5b 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -71,8 +71,7 @@ impl UdpSocket { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::UdpSocket::bind(&addr) { diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 207d0b27b..156309289 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,4 +1,4 @@ -use async_std::{fs, task}; +use async_std::{fs, io, net::ToSocketAddrs, task}; #[test] fn open_file() { @@ -14,3 +14,18 @@ fn open_file() { } }) } + +#[test] +fn resolve_address() { + task::block_on(async { + let non_existing_addr = "ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80"; + let res: Result<_, io::Error> = non_existing_addr.to_socket_addrs().await; + match res { + Ok(_) => panic!("Found address with random name: We live in a simulation"), + Err(e) => assert_eq!( + "could not resolve address `\"ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80\"`", + &format!("{}", e) + ), + } + }) +} From e66e2e2b8fc8332f84eabbab3256c7e1b1a6f69c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 26 Nov 2019 11:39:57 +0100 Subject: [PATCH 307/707] link to our contribution guidelines Signed-off-by: Yoshua Wuyts --- .github/CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..708d20212 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Our contribution policy can be found at [async.rs/contribute][policy]. + +[policy]: https://async.rs/contribute/ From 0f30ab8c0a67707e57ac238e7e6b42821b05ba8a Mon Sep 17 00:00:00 2001 From: boats Date: Tue, 26 Nov 2019 14:23:10 +0100 Subject: [PATCH 308/707] Fix the docs and Debug output of BufWriter. (#588) The BufWriter docs inaccurately stated that it flushes on drop, which it does not do. This PR changes the docs, as well as the example, to highlight that the user must explicitly flush a bufwriter. There were also two places where the BufWriter code referred to it as a BufReader: in the link to the std docs, and in the Debug output. Those have also been fixed. --- src/io/buf_writer.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index ce6a97b37..c527d0270 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -22,14 +22,14 @@ pin_project! { /// times. It also provides no advantage when writing to a destination that is /// in memory, like a `Vec`. /// - /// When the `BufWriter` is dropped, the contents of its buffer will be written - /// out. However, any errors that happen in the process of flushing the buffer - /// when the writer is dropped will be ignored. Code that wishes to handle such - /// errors must manually call [`flush`] before the writer is dropped. + /// Unlike the `BufWriter` type in `std`, this type does not write out the + /// contents of its buffer when it is dropped. Therefore, it is absolutely + /// critical that users explicitly flush the buffer before dropping a + /// `BufWriter`. /// - /// This type is an async version of [`std::io::BufReader`]. + /// This type is an async version of [`std::io::BufWriter`]. /// - /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html /// /// # Examples /// @@ -61,10 +61,13 @@ pin_project! { /// use async_std::prelude::*; /// /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); + /// /// for i in 0..10 { /// let arr = [i+1]; /// stream.write(&arr).await?; /// }; + /// + /// stream.flush().await?; /// # /// # Ok(()) }) } /// ``` @@ -325,7 +328,7 @@ impl Write for BufWriter { impl fmt::Debug for BufWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BufReader") + f.debug_struct("BufWriter") .field("writer", &self.inner) .field("buf", &self.buf) .finish() From 635c592950e1d70ebf96336b285c4c43a88e9357 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 27 Nov 2019 14:26:04 +0900 Subject: [PATCH 309/707] feat: Add stream::delay --- src/stream/stream/delay.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index f6de5f2d4..b04501047 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -2,22 +2,24 @@ use std::future::Future; use std::pin::Pin; use std::time::Duration; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; +pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct Delay { - stream: S, - delay: futures_timer::Delay, - delay_done: bool, + pub struct Delay { + #[pin] + stream: S, + #[pin] + delay: futures_timer::Delay, + delay_done: bool, + } } impl Delay { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_pinned!(delay: futures_timer::Delay); - pin_utils::unsafe_unpinned!(delay_done: bool); - pub(super) fn new(stream: S, dur: Duration) -> Self { Delay { stream, @@ -33,12 +35,14 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if !self.delay_done { - futures_core::ready!(self.as_mut().delay().poll(cx)); - *self.as_mut().delay_done() = true; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + if !*this.delay_done { + futures_core::ready!(this.delay.poll(cx)); + *this.delay_done = true; } - self.as_mut().stream().poll_next(cx) + this.stream.poll_next(cx) } } From 32765ece418f81b2e36f9f2ca73a60ea0b893531 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 27 Nov 2019 14:26:25 +0900 Subject: [PATCH 310/707] test: Add stream::delay test code --- src/stream/stream/mod.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index ef1e5ecc7..e847d6afe 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -587,16 +587,12 @@ extension_trait! { use async_std::stream; use std::time::Duration; - let a = stream::once(1).delay(Duration::from_millis(200)); - let b = stream::once(2).delay(Duration::from_millis(100)); - let c = stream::once(3).delay(Duration::from_millis(300)); + let mut s = stream::from_iter(vec![0u8, 1, 2]).delay(Duration::from_millis(200)); - let s = stream::join!(a, b, c); - - assert_eq!(stream.next().await, Some(1)); - assert_eq!(stream.next().await, Some(2)); - assert_eq!(stream.next().await, Some(3)); - assert_eq!(stream.next().await, None); + assert_eq!(s.next().await, Some(0)); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, None); # # }) } ``` From 794e331761634ef6c89f8f57fd409fa415e0521c Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 27 Nov 2019 21:38:38 +0900 Subject: [PATCH 311/707] Refactor join type (#577) * refactor: update future join type * test: update future join test * update future::try_join --- src/future/future/join.rs | 6 +++--- src/future/future/mod.rs | 20 ++++++++++---------- src/future/future/try_join.rs | 12 ++++++------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/future/future/join.rs b/src/future/future/join.rs index 5cfbd99af..0febcad0c 100644 --- a/src/future/future/join.rs +++ b/src/future/future/join.rs @@ -12,7 +12,7 @@ pin_project! { pub struct Join where L: Future, - R: Future + R: Future, { #[pin] left: MaybeDone, #[pin] right: MaybeDone, @@ -22,7 +22,7 @@ pin_project! { impl Join where L: Future, - R: Future, + R: Future, { pub(crate) fn new(left: L, right: R) -> Self { Self { @@ -35,7 +35,7 @@ where impl Future for Join where L: Future, - R: Future, + R: Future, { type Output = (L::Output, R::Output); diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 5fdaf4b1a..0f9ef5864 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -289,10 +289,10 @@ extension_trait! { use async_std::future; let a = future::ready(1u8); - let b = future::ready(2u8); + let b = future::ready(2u16); let f = a.join(b); - assert_eq!(f.await, (1u8, 2u8)); + assert_eq!(f.await, (1u8, 2u16)); # }); ``` "#] @@ -304,7 +304,7 @@ extension_trait! { ) -> impl Future::Output, ::Output)> [Join] where Self: std::future::Future + Sized, - F: std::future::Future::Output>, + F: std::future::Future, { Join::new(self, other) } @@ -328,30 +328,30 @@ extension_trait! { use async_std::prelude::*; use async_std::future; - let a = future::ready(Err("Error")); + 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::(2u8)); + let b = future::ready(Ok::(2u16)); let f = a.try_join(b); - assert_eq!(f.await, Ok((1u8, 2u8))); + assert_eq!(f.await, Ok((1u8, 2u16))); # # Ok(()) }) } ``` "#] #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn try_join( + fn try_join( self, other: F - ) -> impl Future> [TryJoin] + ) -> impl Future> [TryJoin] where - Self: std::future::Future> + Sized, - F: std::future::Future::Output>, + Self: std::future::Future> + Sized, + F: std::future::Future>, { TryJoin::new(self, other) } diff --git a/src/future/future/try_join.rs b/src/future/future/try_join.rs index 58ae6d620..f1667240f 100644 --- a/src/future/future/try_join.rs +++ b/src/future/future/try_join.rs @@ -12,7 +12,7 @@ pin_project! { pub struct TryJoin where L: Future, - R: Future + R: Future, { #[pin] left: MaybeDone, #[pin] right: MaybeDone, @@ -22,7 +22,7 @@ pin_project! { impl TryJoin where L: Future, - R: Future, + R: Future, { pub(crate) fn new(left: L, right: R) -> Self { Self { @@ -32,12 +32,12 @@ where } } -impl Future for TryJoin +impl Future for TryJoin where - L: Future>, - R: Future, + L: Future>, + R: Future>, { - type Output = Result<(T, T), E>; + type Output = Result<(A, B), E>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); From 68005661d9b83ab59b9894b516f823d41a29e0dc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 27 Nov 2019 15:47:27 +0100 Subject: [PATCH 312/707] fix Stream::throttle hot loop (#584) Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 23 +++++++---------------- src/stream/stream/throttle.rs | 5 +---- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e68e6acda..f7a1ba2c2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -336,28 +336,19 @@ extension_trait! { let start = Instant::now(); // emit value every 5 milliseconds - let s = stream::interval(Duration::from_millis(5)) - .enumerate() - .take(3); + let s = stream::interval(Duration::from_millis(5)).take(2); // throttle for 10 milliseconds let mut s = s.throttle(Duration::from_millis(10)); - assert_eq!(s.next().await, Some((0, ()))); - let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 5); + s.next().await; + assert!(start.elapsed().as_millis() >= 5); - assert_eq!(s.next().await, Some((1, ()))); - let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 15); + s.next().await; + assert!(start.elapsed().as_millis() >= 15); - assert_eq!(s.next().await, Some((2, ()))); - let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 25); - - assert_eq!(s.next().await, None); - let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 35); + s.next().await; + assert!(start.elapsed().as_millis() >= 35); # # }) } ``` diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index b2480bbd3..ce8c13b37 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -55,10 +55,7 @@ impl Stream for Throttle { } match this.stream.poll_next(cx) { - Poll::Pending => { - cx.waker().wake_by_ref(); // Continue driving even though emitting Pending - Poll::Pending - } + Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { *this.blocked = true; From dba416608a79b8aed65f364e2d11745c0651a80f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 27 Nov 2019 16:05:15 +0100 Subject: [PATCH 313/707] 1.2.0 (#589) Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7977be76e..5c9283f02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.2.0] - 2019-11-28 + +[API Documentation](https://docs.rs/async-std/1.2.0/async-std) + +This patch includes some minor quality-of-life improvements, introduces a +new `Stream::unzip` API, and adds verbose errors to our networking types. + +This means if you can't connect to a socket, you'll never have to wonder again +*which* address it was you couldn't connect to, instead of having to go through +the motions to debug what the address was. + +## Example + +Unzip a stream of tuples into two collections: + +```rust +use async_std::prelude::*; +use async_std::stream; + +let s = stream::from_iter(vec![(1,2), (3,4)]); + +let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; + +assert_eq!(left, [1, 3]); +assert_eq!(right, [2, 4]); +``` + +## Added + +- Added `Stream::unzip` as "unstable". +- Added verbose errors to the networking types. + +## Changed + +- Enabled CI on master branch. + +## Fixed + +- Fixed the docs and `Debug` output of `BufWriter`. + # [1.1.0] - 2019-11-21 [API Documentation](https://docs.rs/async-std/1.1.0/async-std) From 0165d7f6d11f03ff8d281492828e3f2146f1b8d3 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 27 Nov 2019 16:08:57 +0100 Subject: [PATCH 314/707] Add missing items to the changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c9283f02..533dcaf3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,10 +42,13 @@ assert_eq!(right, [2, 4]); ## Changed - Enabled CI on master branch. +- `Future::join` and `Future::try_join` can now join futures with different + output types. ## Fixed - Fixed the docs and `Debug` output of `BufWriter`. +- Fixed a bug in `Stream::throttle` that made it consume too much CPU. # [1.1.0] - 2019-11-21 From 4ed15d67c9c361c2fa576ac6cf89c98e5c3ed4b4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 27 Nov 2019 16:12:31 +0100 Subject: [PATCH 315/707] Fix links in the changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 533dcaf3f..e637d4d7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] -# [1.2.0] - 2019-11-28 +# [1.2.0] - 2019-11-27 [API Documentation](https://docs.rs/async-std/1.2.0/async-std) @@ -553,7 +553,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.1.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.2.0...HEAD +[1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 From 9627826756b658bbbcca3323d5b3865d2d040646 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 27 Nov 2019 16:14:43 +0100 Subject: [PATCH 316/707] Bump the version to 1.2.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9df056603..7c4613b8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.1.0" +version = "1.2.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From bf9ee8881542cc4b8e06e07145f3fd5ae2807216 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 27 Nov 2019 16:29:29 +0100 Subject: [PATCH 317/707] Fix a typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e637d4d7f..fe32794cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ Unzip a stream of tuples into two collections: use async_std::prelude::*; use async_std::stream; -let s = stream::from_iter(vec![(1,2), (3,4)]); +let s = stream::from_iter(vec![(1,2), (3,4)]); let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; From 55560ea9b4b9cb0af369f3f84880ddc22099a3ca Mon Sep 17 00:00:00 2001 From: linkmauve Date: Wed, 27 Nov 2019 19:35:27 +0100 Subject: [PATCH 318/707] docs: Replace mention of futures-preview crate It is now stable in 0.3. --- docs/src/overview/std-and-library-futures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md index 9b4801edb..c321d2119 100644 --- a/docs/src/overview/std-and-library-futures.md +++ b/docs/src/overview/std-and-library-futures.md @@ -10,7 +10,7 @@ The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelu 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. -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-preview` 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 `FuturesExt`. ## Interfaces and Stability From 9f7c1833dc7559f981d4071a8decb8e4eec322a2 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 10:37:04 +0900 Subject: [PATCH 319/707] fix module --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e847d6afe..cab0b39b5 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -28,7 +28,6 @@ mod cloned; mod cmp; mod copied; mod cycle; -mod delay; mod enumerate; mod eq; mod filter; @@ -99,7 +98,6 @@ use try_for_each::TryForEachFuture; 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; @@ -132,6 +130,7 @@ cfg_unstable! { pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; + pub use delay::Delay; mod count; mod merge; @@ -140,6 +139,7 @@ cfg_unstable! { mod partition; mod timeout; mod throttle; + mod delay; mod unzip; } From da965e9ba4bb189fe3202df0874973c4003120e0 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 15:54:13 +0900 Subject: [PATCH 320/707] fix indent --- src/stream/stream/delay.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index b04501047..576879293 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -8,8 +8,8 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { -#[doc(hidden)] -#[allow(missing_debug_implementations)] + #[doc(hidden)] + #[allow(missing_debug_implementations)] pub struct Delay { #[pin] stream: S, From 556d7992ce5566888678ed2fb2058e06259028bd Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 15:57:22 +0900 Subject: [PATCH 321/707] test: fix failed doc test --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d0c87ff5c..2cca5e36f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,6 +152,8 @@ //! Await two futures concurrently, and return a tuple of their output: //! //! ``` +//! use async_std::prelude::*; +//! //! #[async_std::main] //! async fn main() { //! let a = async { 1u8 }; @@ -167,7 +169,7 @@ //! //! #[async_std::main] //! async fn main() -> std::io::Result<()> { -//! let mut socket = UdpSocket::bind("127.0.0.1:8080")?; +//! let socket = UdpSocket::bind("127.0.0.1:8080").await?; //! println!("Listening on {}", socket.local_addr()?); //! //! let mut buf = vec![0u8; 1024]; From fe04cf26b6f9d09cf6ae09cf9f70160415c44986 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 16:16:39 +0900 Subject: [PATCH 322/707] test: fix stream::throttle doc test --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f7a1ba2c2..d77fdc876 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -348,7 +348,7 @@ extension_trait! { assert!(start.elapsed().as_millis() >= 15); s.next().await; - assert!(start.elapsed().as_millis() >= 35); + assert!(start.elapsed().as_millis() >= 25); # # }) } ``` From 44e38eae5950bc8d8803f010588d0c6e95ed12cb Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 19:52:46 +0900 Subject: [PATCH 323/707] fix open_file test code --- tests/verbose_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 156309289..17d42611c 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -8,7 +8,7 @@ fn open_file() { match res { Ok(_) => panic!("Found file with random name: We live in a simulation"), Err(e) => assert_eq!( - "Could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`", + "could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`", &format!("{}", e) ), } From 7d9a06300245b5a32e65dece153a0a12bed6d942 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 20:53:47 +0900 Subject: [PATCH 324/707] fix cargo test arguments on 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 27f031870..f1a71c199 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --features unstable attributes + args: --all --features "unstable attributes" check_fmt_and_docs: name: Checking fmt and docs From c85e2496b1345efbd66ecc648fa12ed151eecd4a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 20:54:07 +0900 Subject: [PATCH 325/707] Enable doc test on ci --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1a71c199..bee2562a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,12 @@ jobs: command: test args: --all --features "unstable attributes" + - name: documentation test + uses: actions-rs/cargo@v1 + with: + command: test + args: --doc --features "unstable attributes" + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest From fb1fb6c903fca2b68871706b873f3f548148b4b4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 28 Nov 2019 22:53:24 +0900 Subject: [PATCH 326/707] test: Test the delay time --- src/stream/stream/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cab0b39b5..4fefbad53 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -585,14 +585,24 @@ extension_trait! { # use async_std::prelude::*; use async_std::stream; - use std::time::Duration; + 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() <= 210); + assert_eq!(s.next().await, Some(2)); + assert!(start.elapsed().as_millis() <= 210); + assert_eq!(s.next().await, None); + assert!(start.elapsed().as_millis() <= 210); # # }) } ``` From 81e3c41826014269fd8b11cb16e41dd4ef614ffa Mon Sep 17 00:00:00 2001 From: Povilas Balciunas Date: Fri, 29 Nov 2019 11:52:54 +1300 Subject: [PATCH 327/707] Fix a link in the docs --- 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 198e57870..8e181d135 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -113,7 +113,7 @@ //! [`Builder::stack_size`]: struct.Builder.html#method.stack_size //! [`Builder::name`]: struct.Builder.html#method.name //! [`task::current`]: fn.current.html -//! [`Task`]: struct.Thread.html +//! [`Task`]: struct.Task.html //! [`Task::name`]: struct.Task.html#method.name //! [`task_local!`]: ../macro.task_local.html //! [`with`]: struct.LocalKey.html#method.with From fd86effb635b2bc5b3f2fe9bb5c506f3b62ee051 Mon Sep 17 00:00:00 2001 From: Bryant Luk Date: Mon, 2 Dec 2019 13:04:19 -0600 Subject: [PATCH 328/707] Change recv_from to recv in UdpSocket::recv doc --- src/net/udp/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 7fef1ed5b..418b4b60a 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -298,10 +298,11 @@ impl UdpSocket { /// 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, peer) = socket.recv_from(&mut buf).await?; - /// println!("Received {} bytes from {}", n, peer); + /// let n = socket.recv(&mut buf).await?; + /// println!("Received {} bytes", n); /// # /// # Ok(()) }) } /// ``` From 54fa559554d1374f4a9c40da90452cb8366d0aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 08:09:20 -0600 Subject: [PATCH 329/707] Changing scope of disclosure --- src/future/timeout.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/future/timeout.rs b/src/future/timeout.rs index c2b6306c9..05aaa4509 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -51,7 +51,8 @@ pin_project! { } impl TimeoutFuture { - pub fn new(future: F, dur: Duration) -> TimeoutFuture { + #[allow(dead_code)] + pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture { TimeoutFuture { future: future, delay: Delay::new(dur) } } } From c14c377974c6bdd17e6a118eb061b33bdac63bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 08:09:58 -0600 Subject: [PATCH 330/707] Changing method signature --- 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 fc800cb2d..569d03201 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -363,7 +363,7 @@ extension_trait! { "#] #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn timeout(self, dur: Duration) -> impl Future [TimeoutFuture] + fn timeout(self, dur: Duration) -> impl Future [TimeoutFuture] where Self: Sized { TimeoutFuture::new(self, dur) From 4670388a568b39f13d15c4d1f03bb15282ff3143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 08:10:06 -0600 Subject: [PATCH 331/707] Adding tests --- tests/timeout_future.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/timeout_future.rs diff --git a/tests/timeout_future.rs b/tests/timeout_future.rs new file mode 100644 index 000000000..e1dfb4e15 --- /dev/null +++ b/tests/timeout_future.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "unstable")] + +use std::time::Duration; + +use async_std::prelude::*; +use async_std::future; +use async_std::task; + +#[test] +fn should_timeout() { + task::block_on(async { + let fut = future::pending::<()>(); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_err()); + }); +} + +#[test] +fn should_not_timeout() { + task::block_on(async { + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()); + }); +} \ No newline at end of file From cc85533f7c50b9c15fdaac840001c057cb73b734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 21:15:32 -0600 Subject: [PATCH 332/707] fixing format --- tests/timeout_future.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/timeout_future.rs b/tests/timeout_future.rs index e1dfb4e15..e6e4b3446 100644 --- a/tests/timeout_future.rs +++ b/tests/timeout_future.rs @@ -2,13 +2,13 @@ use std::time::Duration; -use async_std::prelude::*; use async_std::future; +use async_std::prelude::*; use async_std::task; #[test] fn should_timeout() { - task::block_on(async { + task::block_on(async { let fut = future::pending::<()>(); let dur = Duration::from_millis(100); let res = fut.timeout(dur).await; @@ -18,10 +18,10 @@ fn should_timeout() { #[test] fn should_not_timeout() { - task::block_on(async { + task::block_on(async { let fut = future::ready(0); let dur = Duration::from_millis(100); let res = fut.timeout(dur).await; assert!(res.is_ok()); }); -} \ No newline at end of file +} From 33e7c87dfc6dba1c22080f3bfa334830c667ef2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 5 Dec 2019 21:19:02 -0600 Subject: [PATCH 333/707] Adding example to docs --- src/future/future/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 569d03201..efa832a7a 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -360,6 +360,21 @@ extension_trait! { #[doc = r#" Waits for both the future and a timeout, if the timeout completes before the future, it returns an TimeoutError. + + # Example + ``` + #async_std::task::block_on(async { + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()); + + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()) + # }); + ``` "#] #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] From f0bdcfec25a546c4ae60f89d9d92d49bf80a2cfe Mon Sep 17 00:00:00 2001 From: Dung Pham Date: Fri, 6 Dec 2019 13:06:44 +0700 Subject: [PATCH 334/707] fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34ebe6df7..769b42968 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ More examples, including networking and file access, can be found in our [`examples`]: https://github.com/async-rs/async-std/tree/master/examples [documentation]: https://docs.rs/async-std#examples -[`task::block_on`]: task/fn.block_on.html +[`task::block_on`]: https://docs.rs/async-std/*/async_std/task/fn.block_on.html [`"attributes"` feature]: https://docs.rs/async-std/#features ## Philosophy From a04157850bfa7fca3384c94543760c5a8c70a4e5 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 8 Dec 2019 15:15:29 +0900 Subject: [PATCH 335/707] fix readme --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 769b42968..46616390a 100644 --- a/README.md +++ b/README.md @@ -74,18 +74,15 @@ syntax. ## Examples -All examples require the [`"attributes"` feature] to be enabled. This feature -is not enabled by default because it significantly impacts compile times. See -[`task::block_on`] for an alternative way to start executing tasks. - ```rust +use async_std::task; + async fn say_hello() { println!("Hello, world!"); } -#[async_std::main] -async fn main() { - say_hello().await; +fn main() { + task::block_on(say_hello()) } ``` From 447c17128faf4cbcf0ca09f8f5999d53f0b63fb4 Mon Sep 17 00:00:00 2001 From: svengrim Date: Tue, 10 Dec 2019 14:37:32 +0100 Subject: [PATCH 336/707] fix: Fix typo in documentation --- docs/src/tutorial/handling_disconnection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 9db9abd25..87a6ac660 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -157,7 +157,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); - broker_handle.await?; + broker_handle.await; Ok(()) } From f06ab9fbc4bf0862fc2f091c8d63455242fb4ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Dec 2019 12:36:50 +0100 Subject: [PATCH 337/707] Remove mention of task stack size configuration (#612) --- src/task/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index 8e181d135..e66ed0d1b 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -58,7 +58,7 @@ //! ## Configuring tasks //! //! A new task can be configured before it is spawned via the [`Builder`] type, -//! which currently allows you to set the name and stack size for the child task: +//! which currently allows you to set the name for the child task: //! //! ``` //! # #![allow(unused_must_use)] @@ -110,7 +110,6 @@ //! [`join`]: struct.JoinHandle.html#method.join //! [`panic!`]: https://doc.rust-lang.org/std/macro.panic.html //! [`Builder`]: struct.Builder.html -//! [`Builder::stack_size`]: struct.Builder.html#method.stack_size //! [`Builder::name`]: struct.Builder.html#method.name //! [`task::current`]: fn.current.html //! [`Task`]: struct.Task.html From a0f3b3b753056d23b60adcb59f36932427154784 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Dec 2019 12:49:22 +0100 Subject: [PATCH 338/707] Remove unused macros (#610) * replace async-macros with internals only Signed-off-by: Yoshua Wuyts * clean up MaybeDone Signed-off-by: Yoshua Wuyts * inline futures_core::ready Signed-off-by: Yoshua Wuyts * remove big commented blob Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 - src/future/future/join.rs | 2 +- src/future/future/race.rs | 2 +- src/future/future/try_join.rs | 2 +- src/future/future/try_race.rs | 2 +- src/future/maybe_done.rs | 79 +++++++++++++++++++++++++++++++++++ src/future/mod.rs | 2 + src/task/mod.rs | 5 +-- src/task/ready.rs | 4 ++ 9 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 src/future/maybe_done.rs create mode 100644 src/task/ready.rs diff --git a/Cargo.toml b/Cargo.toml index 7c4613b8c..569dfb369 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ docs = ["attributes", "unstable"] unstable = ["default", "broadcaster"] attributes = ["async-attributes"] std = [ - "async-macros", "crossbeam-utils", "futures-core", "futures-io", @@ -51,7 +50,6 @@ std = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-macros = { version = "2.0.0", optional = true } 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 } diff --git a/src/future/future/join.rs b/src/future/future/join.rs index 0febcad0c..4a508ce8e 100644 --- a/src/future/future/join.rs +++ b/src/future/future/join.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use async_macros::MaybeDone; +use crate::future::MaybeDone; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; diff --git a/src/future/future/race.rs b/src/future/future/race.rs index ed034f05e..82165a0f1 100644 --- a/src/future/future/race.rs +++ b/src/future/future/race.rs @@ -1,7 +1,7 @@ use std::future::Future; use std::pin::Pin; -use async_macros::MaybeDone; +use crate::future::MaybeDone; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; diff --git a/src/future/future/try_join.rs b/src/future/future/try_join.rs index f1667240f..5277ae317 100644 --- a/src/future/future/try_join.rs +++ b/src/future/future/try_join.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use async_macros::MaybeDone; +use crate::future::MaybeDone; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; diff --git a/src/future/future/try_race.rs b/src/future/future/try_race.rs index d0ca4a90f..45199570f 100644 --- a/src/future/future/try_race.rs +++ b/src/future/future/try_race.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use async_macros::MaybeDone; +use crate::future::MaybeDone; use pin_project_lite::pin_project; use crate::task::{Context, Poll}; diff --git a/src/future/maybe_done.rs b/src/future/maybe_done.rs new file mode 100644 index 000000000..7a3a7fa33 --- /dev/null +++ b/src/future/maybe_done.rs @@ -0,0 +1,79 @@ +//! A type that wraps a future to keep track of its completion status. +//! +//! This implementation was taken from the original `macro_rules` `join/try_join` +//! macros in the `futures-preview` crate. + +use std::future::Future; +use std::mem; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use futures_core::ready; + +/// A future that may have completed. +#[derive(Debug)] +pub(crate) enum MaybeDone { + /// A not-yet-completed future + Future(Fut), + + /// The output of the completed future + Done(Fut::Output), + + /// The empty variant after the result of a [`MaybeDone`] has been + /// taken using the [`take`](MaybeDone::take) method. + Gone, +} + +impl MaybeDone { + /// Create a new instance of `MaybeDone`. + pub(crate) fn new(future: Fut) -> MaybeDone { + Self::Future(future) + } + + /// Returns an [`Option`] containing a reference to the output of the future. + /// The output of this method will be [`Some`] if and only if the inner + /// future has been completed and [`take`](MaybeDone::take) + /// has not yet been called. + #[inline] + pub(crate) fn output(self: Pin<&Self>) -> Option<&Fut::Output> { + let this = self.get_ref(); + match this { + MaybeDone::Done(res) => Some(res), + _ => None, + } + } + + /// Attempt to take the output of a `MaybeDone` without driving it + /// towards completion. + #[inline] + pub(crate) fn take(self: Pin<&mut Self>) -> Option { + unsafe { + let this = self.get_unchecked_mut(); + match this { + MaybeDone::Done(_) => {} + MaybeDone::Future(_) | MaybeDone::Gone => return None, + }; + if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) { + Some(output) + } else { + unreachable!() + } + } + } +} + +impl Future for MaybeDone { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let res = unsafe { + match Pin::as_mut(&mut self).get_unchecked_mut() { + MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)), + MaybeDone::Done(_) => return Poll::Ready(()), + MaybeDone::Gone => panic!("MaybeDone polled after value taken"), + } + }; + self.set(MaybeDone::Done(res)); + Poll::Ready(()) + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index 8b51a6a5f..993627652 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -63,5 +63,7 @@ cfg_default! { cfg_unstable! { pub use into_future::IntoFuture; + pub(crate) use maybe_done::MaybeDone; mod into_future; + mod maybe_done; } diff --git a/src/task/mod.rs b/src/task/mod.rs index e66ed0d1b..a73d5d240 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -121,10 +121,9 @@ cfg_std! { #[doc(inline)] pub use std::task::{Context, Poll, Waker}; - #[doc(inline)] - pub use async_macros::ready; - + pub use ready::ready; pub use yield_now::yield_now; + mod ready; mod yield_now; } diff --git a/src/task/ready.rs b/src/task/ready.rs new file mode 100644 index 000000000..aca04aa7b --- /dev/null +++ b/src/task/ready.rs @@ -0,0 +1,4 @@ +/// Extracts the successful type of a `Poll`. +/// +/// This macro bakes in propagation of `Pending` signals by returning early. +pub use futures_core::ready; From c90732a8058c0a68881a21093ce528901166d643 Mon Sep 17 00:00:00 2001 From: Toralf Wittner Date: Thu, 12 Dec 2019 17:37:38 +0100 Subject: [PATCH 339/707] TcpStream: Shutdown write direction in poll_close. Fixes #599. --- src/net/tcp/stream.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 413178333..ae8ca7dc8 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -356,6 +356,7 @@ impl Write for &TcpStream { } fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + self.shutdown(std::net::Shutdown::Write)?; Poll::Ready(Ok(())) } } From fa288931c681a4dd5210fc497f73068a3457fd3c Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 15 Nov 2019 21:15:50 +0100 Subject: [PATCH 340/707] Skeleton for DoubleEndedStreamExt trait --- src/stream/double_ended/mod.rs | 22 ++++++++++++++++++++++ src/stream/mod.rs | 1 + 2 files changed, 23 insertions(+) create mode 100644 src/stream/double_ended/mod.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs new file mode 100644 index 000000000..101ccbd38 --- /dev/null +++ b/src/stream/double_ended/mod.rs @@ -0,0 +1,22 @@ +extension_trait! { + use crate::stream::Stream; + + use std::pin::Pin; + use std::task::{Context, Poll}; + + #[doc = r#" + Something fancy + "#] + pub trait DoubleEndedStream { + type Item; + + fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } + + #[doc = r#" + Something else + "#] + pub trait DoubleEndedStreamExt: crate::stream::DoubleEndedStream { + } +} + diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d8b96ec22..e15d0818d 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,6 +318,7 @@ mod repeat; mod repeat_with; cfg_unstable! { + mod double_ended; mod double_ended_stream; mod exact_size_stream; mod extend; From d0ef48c75354445b7f6e89e0c34d587859140158 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 15 Nov 2019 22:07:25 +0100 Subject: [PATCH 341/707] Sketch out nth_back --- src/stream/double_ended/mod.rs | 37 ++++++++++++++++++++++++++- src/stream/double_ended/nth_back.rs | 39 +++++++++++++++++++++++++++++ src/stream/mod.rs | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/stream/double_ended/nth_back.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index 101ccbd38..982c2c716 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -1,9 +1,14 @@ +mod nth_back; + +use nth_back::NthBackFuture; + extension_trait! { use crate::stream::Stream; use std::pin::Pin; use std::task::{Context, Poll}; + #[doc = r#" Something fancy "#] @@ -17,6 +22,36 @@ extension_trait! { Something else "#] pub trait DoubleEndedStreamExt: crate::stream::DoubleEndedStream { + + #[doc = r#" + Returns the nth element from the back of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.nth_back(1).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] + fn nth_back( + &mut self, + n: usize, + ) -> impl Future> + '_ [NthBackFuture<'_, Self>] + where + Self: Unpin + Sized, + { + NthBackFuture::new(self, n) + } } } - diff --git a/src/stream/double_ended/nth_back.rs b/src/stream/double_ended/nth_back.rs new file mode 100644 index 000000000..e318e79ad --- /dev/null +++ b/src/stream/double_ended/nth_back.rs @@ -0,0 +1,39 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::future::Future; + +use crate::stream::DoubleEndedStream; + +pub struct NthBackFuture<'a, S> { + stream: &'a mut S, + n: usize, +} + +impl<'a, S> NthBackFuture<'a, S> { + pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { + NthBackFuture { stream, n } + } +} + +impl<'a, S> Future for NthBackFuture<'a, S> +where + S: DoubleEndedStream + Sized + Unpin, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx)); + match next { + Some(v) => match self.n { + 0 => Poll::Ready(Some(v)), + _ => { + self.n -= 1; + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} + diff --git a/src/stream/mod.rs b/src/stream/mod.rs index e15d0818d..318733b2f 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,7 +318,7 @@ mod repeat; mod repeat_with; cfg_unstable! { - mod double_ended; + pub mod double_ended; mod double_ended_stream; mod exact_size_stream; mod extend; From 78bafbb88f679749434e020f8163acd4285a5ac6 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 15 Nov 2019 22:15:28 +0100 Subject: [PATCH 342/707] Sketch outch rfind --- src/stream/double_ended/mod.rs | 14 ++++++++++++ src/stream/double_ended/rfind.rs | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/stream/double_ended/rfind.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index 982c2c716..18fcde3bf 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -1,6 +1,8 @@ mod nth_back; +mod rfind; use nth_back::NthBackFuture; +use rfind::RFindFuture; extension_trait! { use crate::stream::Stream; @@ -53,5 +55,17 @@ extension_trait! { { NthBackFuture::new(self, n) } + + fn rfind

( + &mut self, + p: P, + ) -> impl Future> + '_ [RFindFuture<'_, Self, P>] + where + Self: Unpin + Sized, + P: FnMut(&Self::Item) -> bool, + { + RFindFuture::new(self, p) + } + } } diff --git a/src/stream/double_ended/rfind.rs b/src/stream/double_ended/rfind.rs new file mode 100644 index 000000000..b05e14ee0 --- /dev/null +++ b/src/stream/double_ended/rfind.rs @@ -0,0 +1,39 @@ +use std::task::{Context, Poll}; +use std::future::Future; +use std::pin::Pin; + +use crate::stream::DoubleEndedStream; + +pub struct RFindFuture<'a, S, P> { + stream: &'a mut S, + p: P, +} + +impl<'a, S, P> RFindFuture<'a, S, P> { + pub(super) fn new(stream: &'a mut S, p: P) -> Self { + RFindFuture { stream, p } + } +} + +impl Unpin for RFindFuture<'_, S, P> {} + +impl<'a, S, P> Future for RFindFuture<'a, S, P> +where + S: DoubleEndedStream + Unpin + Sized, + P: FnMut(&S::Item) -> bool, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx)); + + match item { + Some(v) if (&mut self.p)(&v) => Poll::Ready(Some(v)), + Some(_) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + None => Poll::Ready(None), + } + } +} From cc493df433c1ca6bc16a3cd376e503696e26f91c Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 15 Nov 2019 22:29:00 +0100 Subject: [PATCH 343/707] Sketch out rfold --- src/stream/double_ended/mod.rs | 13 +++++++++ src/stream/double_ended/rfold.rs | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/stream/double_ended/rfold.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index 18fcde3bf..a2f9d2660 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -1,8 +1,10 @@ mod nth_back; mod rfind; +mod rfold; use nth_back::NthBackFuture; use rfind::RFindFuture; +use rfold::RFoldFuture; extension_trait! { use crate::stream::Stream; @@ -67,5 +69,16 @@ extension_trait! { RFindFuture::new(self, p) } + fn rfold( + self, + accum: B, + f: F, + ) -> impl Future> [RFoldFuture] + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + RFoldFuture::new(self, accum, f) + } } } diff --git a/src/stream/double_ended/rfold.rs b/src/stream/double_ended/rfold.rs new file mode 100644 index 000000000..4df7d9fbe --- /dev/null +++ b/src/stream/double_ended/rfold.rs @@ -0,0 +1,50 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use crate::stream::DoubleEndedStream; + +pin_project! { + pub struct RFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + } +} + +impl RFoldFuture { + pub(super) fn new(stream: S, init: B, f: F) -> Self { + RFoldFuture { + stream, + f, + acc: Some(init), + } + } +} + +impl Future for RFoldFuture +where + S: DoubleEndedStream + Sized, + F: FnMut(B, S::Item) -> B, +{ + type Output = B; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx)); + + match next { + Some(v) => { + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + *this.acc = Some(new); + } + None => return Poll::Ready(this.acc.take().unwrap()), + } + } + } +} From aabfefd0154a35173b1a705da7fdf06410c0f318 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sat, 16 Nov 2019 12:43:05 +0000 Subject: [PATCH 344/707] Add a sample implementation of a double ended stream --- src/stream/double_ended_stream.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 129bb1cdf..95e47633e 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -22,3 +22,31 @@ pub trait DoubleEndedStream: Stream { /// [trait-level]: trait.DoubleEndedStream.html fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } + +pub struct Sample { + inner: Vec, +} + +impl From> for Sample { + fn from(data: Vec) -> Self { + Sample { inner: data } + } +} + +impl Unpin for Sample {} + +impl Stream for Sample { + type Item = T; + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.inner.len() > 0 { + return Poll::Ready(Some(self.inner.remove(0))); + } + return Poll::Ready(None); + } +} + +impl DoubleEndedStream for Sample { + fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.pop()) + } +} From c4b9a7f680070c254ed6f9220ad58b7080a93b54 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Tue, 19 Nov 2019 22:20:36 +0000 Subject: [PATCH 345/707] Add samples for some of the functions --- src/stream/double_ended/mod.rs | 45 +++++++++++++++++++++++++++++-- src/stream/double_ended_stream.rs | 28 ------------------- src/stream/mod.rs | 4 ++- src/stream/sample.rs | 33 +++++++++++++++++++++++ 4 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 src/stream/sample.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index a2f9d2660..249177566 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -37,10 +37,10 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # + use async_std::stream::Sample; use async_std::stream::double_ended::DoubleEndedStreamExt; - use async_std::stream; - let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let mut s = Sample::from(vec![1u8, 2, 3, 4, 5]); let second = s.nth_back(1).await; assert_eq!(second, Some(4)); @@ -58,6 +58,27 @@ extension_trait! { NthBackFuture::new(self, n) } + #[doc = r#" + Returns the the frist element from the right that matches the predicate. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::Sample; + use async_std::stream::double_ended::DoubleEndedStreamExt; + + let mut s = Sample::from(vec![1u8, 2, 3, 4, 5]); + + let second = s.rfind(|v| v % 2 == 0).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] fn rfind

( &mut self, p: P, @@ -69,6 +90,26 @@ extension_trait! { RFindFuture::new(self, p) } + #[doc = r#" + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::Sample; + use async_std::stream::double_ended::DoubleEndedStreamExt; + + let s = Sample::from(vec![1, 2, 3, 4, 5]); + + let second = s.rfold(0, |acc, v| v + acc).await; + + assert_eq!(second, 15); + # + # }) } + ``` + "#] fn rfold( self, accum: B, diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 95e47633e..129bb1cdf 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -22,31 +22,3 @@ pub trait DoubleEndedStream: Stream { /// [trait-level]: trait.DoubleEndedStream.html fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } - -pub struct Sample { - inner: Vec, -} - -impl From> for Sample { - fn from(data: Vec) -> Self { - Sample { inner: data } - } -} - -impl Unpin for Sample {} - -impl Stream for Sample { - type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.inner.len() > 0 { - return Poll::Ready(Some(self.inner.remove(0))); - } - return Poll::Ready(None); - } -} - -impl DoubleEndedStream for Sample { - fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.inner.pop()) - } -} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 318733b2f..6ce9aac7e 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -307,8 +307,9 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; +pub use crate::stream::sample::Sample; -pub(crate) mod stream; +pub mod stream; mod empty; mod from_fn; @@ -316,6 +317,7 @@ mod from_iter; mod once; mod repeat; mod repeat_with; +mod sample; cfg_unstable! { pub mod double_ended; diff --git a/src/stream/sample.rs b/src/stream/sample.rs new file mode 100644 index 000000000..90caeed1f --- /dev/null +++ b/src/stream/sample.rs @@ -0,0 +1,33 @@ +use crate::stream::Stream; + +use std::pin::Pin; +use std::task::{Context, Poll}; +use crate::stream::DoubleEndedStream; + +pub struct Sample { + inner: Vec, +} + +impl From> for Sample { + fn from(data: Vec) -> Self { + Sample { inner: data } + } +} + +impl Unpin for Sample {} + +impl Stream for Sample { + type Item = T; + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.inner.len() > 0 { + return Poll::Ready(Some(self.inner.remove(0))); + } + return Poll::Ready(None); + } +} + +impl DoubleEndedStream for Sample { + fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.pop()) + } +} From 55194edbf736c5909b0f5d03d7f36379813f1d7e Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 20 Nov 2019 07:45:40 +0000 Subject: [PATCH 346/707] Add try_rfold --- src/stream/double_ended/mod.rs | 43 +++++++++++++++++++++ src/stream/double_ended/try_rfold.rs | 56 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/stream/double_ended/try_rfold.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index 249177566..c2372d2a8 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -1,10 +1,12 @@ mod nth_back; mod rfind; mod rfold; +mod try_rfold; use nth_back::NthBackFuture; use rfind::RFindFuture; use rfold::RFoldFuture; +use try_rfold::TryRFoldFuture; extension_trait! { use crate::stream::Stream; @@ -121,5 +123,46 @@ extension_trait! { { RFoldFuture::new(self, accum, f) } + + #[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. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::Sample; + use async_std::stream::double_ended::DoubleEndedStreamExt; + + let s = Sample::from(vec![1, 2, 3, 4, 5]); + let sum = s.try_rfold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") + } + }).await; + + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_rfold( + self, + accum: B, + f: F, + ) -> impl Future> [TryRFoldFuture] + where + Self: Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryRFoldFuture::new(self, accum, f) + } + } } diff --git a/src/stream/double_ended/try_rfold.rs b/src/stream/double_ended/try_rfold.rs new file mode 100644 index 000000000..4c97cea0d --- /dev/null +++ b/src/stream/double_ended/try_rfold.rs @@ -0,0 +1,56 @@ +use crate::future::Future; +use std::pin::Pin; +use crate::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use crate::stream::DoubleEndedStream; + +pin_project! { +#[doc(hidden)] +#[allow(missing_debug_implementations)] + pub struct TryRFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + } +} + +impl TryRFoldFuture { + pub(super) fn new(stream: S, init: T, f: F) -> Self { + TryRFoldFuture { + stream, + f, + acc: Some(init), + } + } +} + +impl Future for TryRFoldFuture +where + S: DoubleEndedStream + Unpin, + F: FnMut(T, S::Item) -> Result, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx)); + + match next { + Some(v) => { + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + + match new { + Ok(o) => *this.acc = Some(o), + Err(e) => return Poll::Ready(Err(e)), + } + } + None => return Poll::Ready(Ok(this.acc.take().unwrap())), + } + } + } +} From ee2f52f3cee2c86aaf8e27fdc22c25b116a70bf3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 20 Nov 2019 07:54:05 +0000 Subject: [PATCH 347/707] Add next_back --- src/stream/double_ended/mod.rs | 33 ++++++++++++++++++++++++++++ src/stream/double_ended/next_back.rs | 19 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/stream/double_ended/next_back.rs diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index c2372d2a8..a3dbafd60 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -1,8 +1,10 @@ +mod next_back; mod nth_back; mod rfind; mod rfold; mod try_rfold; +use next_back::NextBackFuture; use nth_back::NthBackFuture; use rfind::RFindFuture; use rfold::RFoldFuture; @@ -28,6 +30,37 @@ extension_trait! { Something else "#] pub trait DoubleEndedStreamExt: crate::stream::DoubleEndedStream { + #[doc = r#" + Advances the stream and returns the next value. + + 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::stream::Sample; + use async_std::stream::double_ended::DoubleEndedStreamExt; + + let mut s = Sample::from(vec![7u8]); + + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn next(&mut self) -> impl Future> + '_ [NextBackFuture<'_, Self>] + where + Self: Unpin, + { + NextBackFuture { stream: self } + } #[doc = r#" Returns the nth element from the back of the stream. diff --git a/src/stream/double_ended/next_back.rs b/src/stream/double_ended/next_back.rs new file mode 100644 index 000000000..aa642d094 --- /dev/null +++ b/src/stream/double_ended/next_back.rs @@ -0,0 +1,19 @@ +use std::pin::Pin; +use std::future::Future; + +use crate::stream::DoubleEndedStream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct NextBackFuture<'a, T: Unpin + ?Sized> { + pub(crate) stream: &'a mut T, +} + +impl Future for NextBackFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.stream).poll_next_back(cx) + } +} From 02aa2f3d2a918d5e50944d8b9c6b2e5620e78bc1 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 20 Nov 2019 21:48:04 +0000 Subject: [PATCH 348/707] Fix next_back --- src/stream/double_ended/mod.rs | 6 +++--- src/stream/double_ended/nth_back.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index a3dbafd60..ec9e6ae6b 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -49,13 +49,13 @@ extension_trait! { let mut s = Sample::from(vec![7u8]); - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, None); + assert_eq!(s.next_back().await, Some(7)); + assert_eq!(s.next_back().await, None); # # }) } ``` "#] - fn next(&mut self) -> impl Future> + '_ [NextBackFuture<'_, Self>] + fn next_back(&mut self) -> impl Future> + '_ [NextBackFuture<'_, Self>] where Self: Unpin, { diff --git a/src/stream/double_ended/nth_back.rs b/src/stream/double_ended/nth_back.rs index e318e79ad..8e1ed6371 100644 --- a/src/stream/double_ended/nth_back.rs +++ b/src/stream/double_ended/nth_back.rs @@ -1,6 +1,6 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use std::future::Future; use crate::stream::DoubleEndedStream; From 94893d29249c970038b9620ea886099e402e751a Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 20 Nov 2019 22:14:36 +0000 Subject: [PATCH 349/707] Move more of the documentation --- src/stream/double_ended/mod.rs | 73 +++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index ec9e6ae6b..3267ae582 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -18,16 +18,85 @@ extension_trait! { #[doc = r#" - Something fancy + A stream able to yield elements from both ends. + + Something that implements `DoubleEndedStream` has one extra capability + over something that implements [`Stream`]: the ability to also take + `Item`s from the back, as well as the front. + + It is important to note that both back and forth work on the same range, + and do not cross: iteration is over when they meet in the middle. + + In a similar fashion to the [`Stream`] protocol, once a + `DoubleEndedStream` returns `None` from a `next_back()`, calling it again + may or may not ever return `Some` again. `next()` and `next_back()` are + interchangeable for this purpose. + ``` "#] pub trait DoubleEndedStream { + #[doc = r#" + The type of items yielded by this stream. + "#] type Item; + #[doc = r#" + Attempts to receive the next item from the back of the stream. + + There are several possible return values: + + * `Poll::Pending` means this stream's next_back 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 DoubleEndedStream + Unpin, + ) -> impl DoubleEndedStream + Unpin { + struct Increment(S); + + impl + Unpin> Stream for Increment { + type Item = S::Item; + + fn poll_next_back( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.0).poll_next_back(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)); // will need to implement DoubleEndedStream + + assert_eq!(s.next_back().await, Some(8)); + assert_eq!(s.next_back().await, None); + # + # }) } + ``` + "#] fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } #[doc = r#" - Something else + Extension methods for [`DoubleEndedStreamExt`]. + + [`Stream`]: ../stream/trait.Stream.html "#] pub trait DoubleEndedStreamExt: crate::stream::DoubleEndedStream { #[doc = r#" From abd360893c45984fefad2fa97ae05b855c6fe02b Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 20 Nov 2019 22:16:39 +0000 Subject: [PATCH 350/707] Disable docs and Debug for unexposed structs --- src/stream/double_ended/nth_back.rs | 2 ++ src/stream/double_ended/rfind.rs | 2 ++ src/stream/double_ended/rfold.rs | 2 ++ src/stream/double_ended/try_rfold.rs | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/stream/double_ended/nth_back.rs b/src/stream/double_ended/nth_back.rs index 8e1ed6371..e32a28fd6 100644 --- a/src/stream/double_ended/nth_back.rs +++ b/src/stream/double_ended/nth_back.rs @@ -4,6 +4,8 @@ use std::task::{Context, Poll}; use crate::stream::DoubleEndedStream; +#[doc(hidden)] +#[allow(missing_debug_implementations)] pub struct NthBackFuture<'a, S> { stream: &'a mut S, n: usize, diff --git a/src/stream/double_ended/rfind.rs b/src/stream/double_ended/rfind.rs index b05e14ee0..947269342 100644 --- a/src/stream/double_ended/rfind.rs +++ b/src/stream/double_ended/rfind.rs @@ -4,6 +4,8 @@ use std::pin::Pin; use crate::stream::DoubleEndedStream; +#[doc(hidden)] +#[allow(missing_debug_implementations)] pub struct RFindFuture<'a, S, P> { stream: &'a mut S, p: P, diff --git a/src/stream/double_ended/rfold.rs b/src/stream/double_ended/rfold.rs index 4df7d9fbe..9002f8d9e 100644 --- a/src/stream/double_ended/rfold.rs +++ b/src/stream/double_ended/rfold.rs @@ -7,6 +7,8 @@ use pin_project_lite::pin_project; use crate::stream::DoubleEndedStream; pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] pub struct RFoldFuture { #[pin] stream: S, diff --git a/src/stream/double_ended/try_rfold.rs b/src/stream/double_ended/try_rfold.rs index 4c97cea0d..9e6295a74 100644 --- a/src/stream/double_ended/try_rfold.rs +++ b/src/stream/double_ended/try_rfold.rs @@ -7,8 +7,8 @@ use pin_project_lite::pin_project; use crate::stream::DoubleEndedStream; pin_project! { -#[doc(hidden)] -#[allow(missing_debug_implementations)] + #[doc(hidden)] + #[allow(missing_debug_implementations)] pub struct TryRFoldFuture { #[pin] stream: S, From 892c6008c24e7ec5c1ed1986204730240f8bb4e4 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 22 Nov 2019 16:53:42 +0000 Subject: [PATCH 351/707] Replace sample with a hidden from_iter implementation for double-ended-stream --- src/stream/double_ended/from_iter.rs | 38 ++++++++++++++++++++++++++++ src/stream/double_ended/mod.rs | 27 +++++++++----------- src/stream/mod.rs | 2 -- src/stream/sample.rs | 33 ------------------------ 4 files changed, 50 insertions(+), 50 deletions(-) create mode 100644 src/stream/double_ended/from_iter.rs delete mode 100644 src/stream/sample.rs diff --git a/src/stream/double_ended/from_iter.rs b/src/stream/double_ended/from_iter.rs new file mode 100644 index 000000000..616724f80 --- /dev/null +++ b/src/stream/double_ended/from_iter.rs @@ -0,0 +1,38 @@ +use crate::stream::Stream; + +use std::pin::Pin; +use std::task::{Context, Poll}; +use crate::stream::DoubleEndedStream; + +/// A double-ended stream that was created from iterator. +/// +/// This stream is created by the [`from_iter`] function. +/// See it documentation for more. +/// +/// [`from_iter`]: fn.from_iter.html +#[derive(Debug)] +pub struct FromIter { + inner: Vec, +} + +pub fn from_iter(iter: I) -> FromIter { + FromIter { inner: iter.into_iter().collect() } +} + +impl Unpin for FromIter {} + +impl Stream for FromIter { + type Item = T; + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + if self.inner.len() > 0 { + return Poll::Ready(Some(self.inner.remove(0))); + } + return Poll::Ready(None); + } +} + +impl DoubleEndedStream for FromIter { + fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.inner.pop()) + } +} diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs index 3267ae582..b8b78b276 100644 --- a/src/stream/double_ended/mod.rs +++ b/src/stream/double_ended/mod.rs @@ -3,12 +3,14 @@ mod nth_back; mod rfind; mod rfold; mod try_rfold; +mod from_iter; use next_back::NextBackFuture; use nth_back::NthBackFuture; use rfind::RFindFuture; use rfold::RFoldFuture; use try_rfold::TryRFoldFuture; +pub use from_iter::{from_iter, FromIter}; extension_trait! { use crate::stream::Stream; @@ -113,10 +115,9 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::Sample; - use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - let mut s = Sample::from(vec![7u8]); + let mut s = double_ended::from_iter(vec![7u8]); assert_eq!(s.next_back().await, Some(7)); assert_eq!(s.next_back().await, None); @@ -141,10 +142,9 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::Sample; - use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - let mut s = Sample::from(vec![1u8, 2, 3, 4, 5]); + let mut s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.nth_back(1).await; assert_eq!(second, Some(4)); @@ -172,10 +172,9 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::Sample; - use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - let mut s = Sample::from(vec![1u8, 2, 3, 4, 5]); + let mut s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.rfind(|v| v % 2 == 0).await; assert_eq!(second, Some(4)); @@ -202,10 +201,9 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::Sample; - use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - let s = Sample::from(vec![1, 2, 3, 4, 5]); + let s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.rfold(0, |acc, v| v + acc).await; @@ -237,10 +235,9 @@ extension_trait! { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::Sample; - use async_std::stream::double_ended::DoubleEndedStreamExt; + use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - let s = Sample::from(vec![1, 2, 3, 4, 5]); + let s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); let sum = s.try_rfold(0, |acc, v| { if (acc+v) % 2 == 1 { Ok(v+3) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 6ce9aac7e..f6b6497b3 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -307,7 +307,6 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; -pub use crate::stream::sample::Sample; pub mod stream; @@ -317,7 +316,6 @@ mod from_iter; mod once; mod repeat; mod repeat_with; -mod sample; cfg_unstable! { pub mod double_ended; diff --git a/src/stream/sample.rs b/src/stream/sample.rs deleted file mode 100644 index 90caeed1f..000000000 --- a/src/stream/sample.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::stream::Stream; - -use std::pin::Pin; -use std::task::{Context, Poll}; -use crate::stream::DoubleEndedStream; - -pub struct Sample { - inner: Vec, -} - -impl From> for Sample { - fn from(data: Vec) -> Self { - Sample { inner: data } - } -} - -impl Unpin for Sample {} - -impl Stream for Sample { - type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.inner.len() > 0 { - return Poll::Ready(Some(self.inner.remove(0))); - } - return Poll::Ready(None); - } -} - -impl DoubleEndedStream for Sample { - fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.inner.pop()) - } -} From 6e8236d0e17ade1493916f36c1c166c388cb6d4f Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 22 Nov 2019 17:24:52 +0000 Subject: [PATCH 352/707] Document from_iter for DoubleEndedStream --- src/stream/double_ended/from_iter.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/stream/double_ended/from_iter.rs b/src/stream/double_ended/from_iter.rs index 616724f80..ae424a50e 100644 --- a/src/stream/double_ended/from_iter.rs +++ b/src/stream/double_ended/from_iter.rs @@ -15,6 +15,25 @@ pub struct FromIter { inner: Vec, } +/// Converts an iterator into a double-ended stream. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; +/// +/// let mut s = double_ended::from_iter(vec![0, 1, 2, 3]); +/// +/// assert_eq!(s.next_back().await, Some(3)); +/// assert_eq!(s.next_back().await, Some(2)); +/// assert_eq!(s.next_back().await, Some(1)); +/// assert_eq!(s.next_back().await, Some(0)); +/// assert_eq!(s.next_back().await, None); +/// # +/// # }) +/// ``` pub fn from_iter(iter: I) -> FromIter { FromIter { inner: iter.into_iter().collect() } } From f9a4c35fd69c386393f1406f8e1c8d97f416afb3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sat, 23 Nov 2019 22:20:37 +0000 Subject: [PATCH 353/707] Silence warning about missing docs for the double_ended module --- src/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f6b6497b3..9b8ee8a3a 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,6 +318,7 @@ mod repeat; mod repeat_with; cfg_unstable! { + #[doc(hidden)] pub mod double_ended; mod double_ended_stream; mod exact_size_stream; From 41cf0f855b8de6860f3bff3125725bab77eee24f Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sat, 23 Nov 2019 22:27:08 +0000 Subject: [PATCH 354/707] Make Once a DoubleEndedStream --- src/stream/once.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/stream/once.rs b/src/stream/once.rs index e4ac682cc..9ce93aaf4 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -4,6 +4,7 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::stream::double_ended_stream::DoubleEndedStream; /// Creates a stream that yields a single item. /// @@ -46,3 +47,9 @@ impl Stream for Once { Poll::Ready(self.project().value.take()) } } + +impl DoubleEndedStream for Once { + fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.project().value.take()) + } +} From 8e5dedec34e50dd920a40eb8f9cea6bd90baf560 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 28 Nov 2019 12:33:37 +0100 Subject: [PATCH 355/707] Restructure package. No longer use a extension trait to match std. Still outstanding: How do I hide the concrete structs from the trait? --- src/stream/double_ended/mod.rs | 267 ------------------ src/stream/double_ended_stream.rs | 24 -- .../from_iter.rs | 4 +- src/stream/double_ended_stream/mod.rs | 243 ++++++++++++++++ .../next_back.rs | 0 .../nth_back.rs | 0 .../rfind.rs | 0 .../rfold.rs | 0 .../try_rfold.rs | 0 src/stream/mod.rs | 3 +- 10 files changed, 246 insertions(+), 295 deletions(-) delete mode 100644 src/stream/double_ended/mod.rs delete mode 100644 src/stream/double_ended_stream.rs rename src/stream/{double_ended => double_ended_stream}/from_iter.rs (90%) create mode 100644 src/stream/double_ended_stream/mod.rs rename src/stream/{double_ended => double_ended_stream}/next_back.rs (100%) rename src/stream/{double_ended => double_ended_stream}/nth_back.rs (100%) rename src/stream/{double_ended => double_ended_stream}/rfind.rs (100%) rename src/stream/{double_ended => double_ended_stream}/rfold.rs (100%) rename src/stream/{double_ended => double_ended_stream}/try_rfold.rs (100%) diff --git a/src/stream/double_ended/mod.rs b/src/stream/double_ended/mod.rs deleted file mode 100644 index b8b78b276..000000000 --- a/src/stream/double_ended/mod.rs +++ /dev/null @@ -1,267 +0,0 @@ -mod next_back; -mod nth_back; -mod rfind; -mod rfold; -mod try_rfold; -mod from_iter; - -use next_back::NextBackFuture; -use nth_back::NthBackFuture; -use rfind::RFindFuture; -use rfold::RFoldFuture; -use try_rfold::TryRFoldFuture; -pub use from_iter::{from_iter, FromIter}; - -extension_trait! { - use crate::stream::Stream; - - use std::pin::Pin; - use std::task::{Context, Poll}; - - - #[doc = r#" - A stream able to yield elements from both ends. - - Something that implements `DoubleEndedStream` has one extra capability - over something that implements [`Stream`]: the ability to also take - `Item`s from the back, as well as the front. - - It is important to note that both back and forth work on the same range, - and do not cross: iteration is over when they meet in the middle. - - In a similar fashion to the [`Stream`] protocol, once a - `DoubleEndedStream` returns `None` from a `next_back()`, calling it again - may or may not ever return `Some` again. `next()` and `next_back()` are - interchangeable for this purpose. - ``` - "#] - pub trait DoubleEndedStream { - #[doc = r#" - The type of items yielded by this stream. - "#] - type Item; - - #[doc = r#" - Attempts to receive the next item from the back of the stream. - - There are several possible return values: - - * `Poll::Pending` means this stream's next_back 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 DoubleEndedStream + Unpin, - ) -> impl DoubleEndedStream + Unpin { - struct Increment(S); - - impl + Unpin> Stream for Increment { - type Item = S::Item; - - fn poll_next_back( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - match Pin::new(&mut self.0).poll_next_back(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)); // will need to implement DoubleEndedStream - - assert_eq!(s.next_back().await, Some(8)); - assert_eq!(s.next_back().await, None); - # - # }) } - ``` - "#] - fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - } - - #[doc = r#" - Extension methods for [`DoubleEndedStreamExt`]. - - [`Stream`]: ../stream/trait.Stream.html - "#] - pub trait DoubleEndedStreamExt: crate::stream::DoubleEndedStream { - #[doc = r#" - Advances the stream and returns the next value. - - 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::stream::double_ended::{self, DoubleEndedStreamExt}; - - let mut s = double_ended::from_iter(vec![7u8]); - - assert_eq!(s.next_back().await, Some(7)); - assert_eq!(s.next_back().await, None); - # - # }) } - ``` - "#] - fn next_back(&mut self) -> impl Future> + '_ [NextBackFuture<'_, Self>] - where - Self: Unpin, - { - NextBackFuture { stream: self } - } - - #[doc = r#" - Returns the nth element from the back of the stream. - - # Examples - - Basic usage: - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - - let mut s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); - - let second = s.nth_back(1).await; - assert_eq!(second, Some(4)); - # - # }) } - ``` - "#] - fn nth_back( - &mut self, - n: usize, - ) -> impl Future> + '_ [NthBackFuture<'_, Self>] - where - Self: Unpin + Sized, - { - NthBackFuture::new(self, n) - } - - #[doc = r#" - Returns the the frist element from the right that matches the predicate. - - # Examples - - Basic usage: - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - - let mut s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); - - let second = s.rfind(|v| v % 2 == 0).await; - assert_eq!(second, Some(4)); - # - # }) } - ``` - "#] - fn rfind

( - &mut self, - p: P, - ) -> impl Future> + '_ [RFindFuture<'_, Self, P>] - where - Self: Unpin + Sized, - P: FnMut(&Self::Item) -> bool, - { - RFindFuture::new(self, p) - } - - #[doc = r#" - # Examples - - Basic usage: - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - - let s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); - - let second = s.rfold(0, |acc, v| v + acc).await; - - assert_eq!(second, 15); - # - # }) } - ``` - "#] - fn rfold( - self, - accum: B, - f: F, - ) -> impl Future> [RFoldFuture] - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - RFoldFuture::new(self, accum, f) - } - - #[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. - - # Examples - - Basic usage: - - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; - - let s = double_ended::from_iter(vec![1u8, 2, 3, 4, 5]); - let sum = s.try_rfold(0, |acc, v| { - if (acc+v) % 2 == 1 { - Ok(v+3) - } else { - Err("fail") - } - }).await; - - assert_eq!(sum, Err("fail")); - # - # }) } - ``` - "#] - fn try_rfold( - self, - accum: B, - f: F, - ) -> impl Future> [TryRFoldFuture] - where - Self: Sized, - F: FnMut(B, Self::Item) -> Result, - { - TryRFoldFuture::new(self, accum, f) - } - - } -} diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs deleted file mode 100644 index 129bb1cdf..000000000 --- a/src/stream/double_ended_stream.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::stream::Stream; - -use std::pin::Pin; -use std::task::{Context, Poll}; - -/// A stream able to yield elements from both ends. -/// -/// Something that implements `DoubleEndedStream` has one extra capability -/// over something that implements [`Stream`]: the ability to also take -/// `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 { - /// Removes and returns an element from the end of the stream. - /// - /// Returns `None` when there are no more elements. - /// - /// The [trait-level] docs contain more details. - /// - /// [trait-level]: trait.DoubleEndedStream.html - fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; -} diff --git a/src/stream/double_ended/from_iter.rs b/src/stream/double_ended_stream/from_iter.rs similarity index 90% rename from src/stream/double_ended/from_iter.rs rename to src/stream/double_ended_stream/from_iter.rs index ae424a50e..29a3e7d8d 100644 --- a/src/stream/double_ended/from_iter.rs +++ b/src/stream/double_ended_stream/from_iter.rs @@ -22,9 +22,9 @@ pub struct FromIter { /// ``` /// # async_std::task::block_on(async { /// # -/// use async_std::stream::double_ended::{self, DoubleEndedStreamExt}; +/// use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; /// -/// let mut s = double_ended::from_iter(vec![0, 1, 2, 3]); +/// let mut s = double_ended_stream::from_iter(vec![0, 1, 2, 3]); /// /// assert_eq!(s.next_back().await, Some(3)); /// assert_eq!(s.next_back().await, Some(2)); diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs new file mode 100644 index 000000000..921366158 --- /dev/null +++ b/src/stream/double_ended_stream/mod.rs @@ -0,0 +1,243 @@ +use crate::stream::Stream; + +use std::pin::Pin; +use std::task::{Context, Poll}; + +mod from_iter; +mod next_back; +mod nth_back; +mod rfind; +mod rfold; +mod try_rfold; + +pub use from_iter::{from_iter, FromIter}; +use next_back::NextBackFuture; +use nth_back::NthBackFuture; +use rfind::RFindFuture; +use rfold::RFoldFuture; +use try_rfold::TryRFoldFuture; + +/// A stream able to yield elements from both ends. +/// +/// Something that implements `DoubleEndedStream` has one extra capability +/// over something that implements [`Stream`]: the ability to also take +/// `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. + + There are several possible return values: + + * `Poll::Pending` means this stream's next_back 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 DoubleEndedStream + Unpin, + ) -> impl DoubleEndedStream + 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)), + } + } + } + + impl + Unpin> DoubleEndedStream for Increment { + fn poll_next_back( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.0).poll_next_back(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_back().await, Some(8)); + assert_eq!(s.next_back().await, None); + # + # }) } + ``` + "#] + fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + #[doc = r#" + Advances the stream and returns the next value. + + 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::stream::double_ended_stream::{self, DoubleEndedStream}; + + let mut s = double_ended_stream::from_iter(vec![7u8]); + + assert_eq!(s.next_back().await, Some(7)); + assert_eq!(s.next_back().await, None); + # + # }) } + ``` + "#] + fn next_back(&mut self) -> NextBackFuture<'_, Self> + where + Self: Unpin, + { + NextBackFuture { stream: self } + } + + #[doc = r#" + Returns the nth element from the back of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + + let mut s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.nth_back(1).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] + fn nth_back(&mut self, n: usize) -> NthBackFuture<'_, Self> + where + Self: Unpin + Sized, + { + NthBackFuture::new(self, n) + } + + #[doc = r#" + Returns the the frist element from the right that matches the predicate. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + + let mut s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.rfind(|v| v % 2 == 0).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] + fn rfind

(&mut self, p: P) -> RFindFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(&Self::Item) -> bool, + { + RFindFuture::new(self, p) + } + + #[doc = r#" + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + + let s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.rfold(0, |acc, v| v + acc).await; + + assert_eq!(second, 15); + # + # }) } + ``` + "#] + fn rfold(self, accum: B, f: F) -> RFoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + RFoldFuture::new(self, accum, f) + } + + #[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. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + + let s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let sum = s.try_rfold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") + } + }).await; + + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_rfold(self, accum: B, f: F) -> TryRFoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryRFoldFuture::new(self, accum, f) + } +} diff --git a/src/stream/double_ended/next_back.rs b/src/stream/double_ended_stream/next_back.rs similarity index 100% rename from src/stream/double_ended/next_back.rs rename to src/stream/double_ended_stream/next_back.rs diff --git a/src/stream/double_ended/nth_back.rs b/src/stream/double_ended_stream/nth_back.rs similarity index 100% rename from src/stream/double_ended/nth_back.rs rename to src/stream/double_ended_stream/nth_back.rs diff --git a/src/stream/double_ended/rfind.rs b/src/stream/double_ended_stream/rfind.rs similarity index 100% rename from src/stream/double_ended/rfind.rs rename to src/stream/double_ended_stream/rfind.rs diff --git a/src/stream/double_ended/rfold.rs b/src/stream/double_ended_stream/rfold.rs similarity index 100% rename from src/stream/double_ended/rfold.rs rename to src/stream/double_ended_stream/rfold.rs diff --git a/src/stream/double_ended/try_rfold.rs b/src/stream/double_ended_stream/try_rfold.rs similarity index 100% rename from src/stream/double_ended/try_rfold.rs rename to src/stream/double_ended_stream/try_rfold.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 9b8ee8a3a..ebce3a36b 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -319,8 +319,7 @@ mod repeat_with; cfg_unstable! { #[doc(hidden)] - pub mod double_ended; - mod double_ended_stream; + pub mod double_ended_stream; mod exact_size_stream; mod extend; mod from_stream; From b0038e11bed6d75f87b2c17a82da56b8534c98ce Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 28 Nov 2019 21:52:48 +0100 Subject: [PATCH 356/707] Only implement the DoubleEndedStream for once when the flag is on --- src/stream/once.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stream/once.rs b/src/stream/once.rs index 9ce93aaf4..939722d9e 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -4,7 +4,9 @@ use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::stream::double_ended_stream::DoubleEndedStream; + +#[cfg(feature = "unstable")] +use crate::stream::DoubleEndedStream; /// Creates a stream that yields a single item. /// @@ -48,6 +50,7 @@ impl Stream 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()) From 182fe6896f628efde8bdd7b5d00923c7ea0629e5 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 12 Dec 2019 20:46:03 +0100 Subject: [PATCH 357/707] No need for a custom impl for FromIter for DoubleEndedStream --- src/stream/double_ended_stream/from_iter.rs | 57 --------------------- src/stream/double_ended_stream/mod.rs | 2 - src/stream/from_iter.rs | 9 ++++ 3 files changed, 9 insertions(+), 59 deletions(-) delete mode 100644 src/stream/double_ended_stream/from_iter.rs diff --git a/src/stream/double_ended_stream/from_iter.rs b/src/stream/double_ended_stream/from_iter.rs deleted file mode 100644 index 29a3e7d8d..000000000 --- a/src/stream/double_ended_stream/from_iter.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::stream::Stream; - -use std::pin::Pin; -use std::task::{Context, Poll}; -use crate::stream::DoubleEndedStream; - -/// A double-ended stream that was created from iterator. -/// -/// This stream is created by the [`from_iter`] function. -/// See it documentation for more. -/// -/// [`from_iter`]: fn.from_iter.html -#[derive(Debug)] -pub struct FromIter { - inner: Vec, -} - -/// Converts an iterator into a double-ended stream. -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; -/// -/// let mut s = double_ended_stream::from_iter(vec![0, 1, 2, 3]); -/// -/// assert_eq!(s.next_back().await, Some(3)); -/// assert_eq!(s.next_back().await, Some(2)); -/// assert_eq!(s.next_back().await, Some(1)); -/// assert_eq!(s.next_back().await, Some(0)); -/// assert_eq!(s.next_back().await, None); -/// # -/// # }) -/// ``` -pub fn from_iter(iter: I) -> FromIter { - FromIter { inner: iter.into_iter().collect() } -} - -impl Unpin for FromIter {} - -impl Stream for FromIter { - type Item = T; - fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - if self.inner.len() > 0 { - return Poll::Ready(Some(self.inner.remove(0))); - } - return Poll::Ready(None); - } -} - -impl DoubleEndedStream for FromIter { - fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(self.inner.pop()) - } -} diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index 921366158..dc2a45c9f 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -3,14 +3,12 @@ use crate::stream::Stream; use std::pin::Pin; use std::task::{Context, Poll}; -mod from_iter; mod next_back; mod nth_back; mod rfind; mod rfold; mod try_rfold; -pub use from_iter::{from_iter, FromIter}; use next_back::NextBackFuture; use nth_back::NthBackFuture; use rfind::RFindFuture; diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index d7a31d6c4..705d15048 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -3,6 +3,8 @@ use std::pin::Pin; use pin_project_lite::pin_project; use crate::stream::Stream; +#[cfg(feature = "unstable")] +use crate::stream::double_ended_stream::DoubleEndedStream; use crate::task::{Context, Poll}; pin_project! { @@ -51,3 +53,10 @@ impl Stream for FromIter { Poll::Ready(self.iter.next()) } } + +#[cfg(feature = "unstable")] +impl DoubleEndedStream for FromIter { + fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.iter.next_back()) + } +} From 84b6d2b27632851469521ddf9554e0fd96714f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Thu, 12 Dec 2019 18:34:02 -0600 Subject: [PATCH 358/707] Removing duplicated tests --- tests/timeout_future.rs | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 tests/timeout_future.rs diff --git a/tests/timeout_future.rs b/tests/timeout_future.rs deleted file mode 100644 index e6e4b3446..000000000 --- a/tests/timeout_future.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![cfg(feature = "unstable")] - -use std::time::Duration; - -use async_std::future; -use async_std::prelude::*; -use async_std::task; - -#[test] -fn should_timeout() { - task::block_on(async { - let fut = future::pending::<()>(); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_err()); - }); -} - -#[test] -fn should_not_timeout() { - task::block_on(async { - let fut = future::ready(0); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_ok()); - }); -} From 055c64e8a73bf943bc5ebb296cd469b83c4d00e8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Dec 2019 11:55:29 +0100 Subject: [PATCH 359/707] 1.3.0 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe32794cf..fc2238cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.3.0] - 2019-12-12 + +[API Documentation](https://docs.rs/async-std/1.3.0/async-std) + +This patch introduces `Stream::delay`, more methods on `DoubleEndedStream`, +and improves compile times. `Stream::delay` is a new API that's similar to +[`task::sleep`](https://docs.rs/async-std/1.2.0/async_std/task/fn.sleep.html), +but can be passed as part of as stream, rather than as a separate block. This is +useful for examples, or when manually debugging race conditions. + +## Examples + +```rust +let start = Instant::now(); +let mut s = stream::from_iter(vec![0u8, 1]).delay(Duration::from_millis(200)); + +// The first time will take more than 200ms due to delay. +s.next().await; +assert!(start.elapsed().as_millis() >= 200); + +// There will be no delay after the first time. +s.next().await; +assert!(start.elapsed().as_millis() <= 210); +``` + +## Added + +- Added `Stream::delay` as "unstable" [(#309)](https://github.com/async-rs/async-std/pull/309) +- Added `DoubleEndedStream::next_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::nth_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::rfind` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::try_rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- `stream::Once` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562) +- `stream::FromIter` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562) + +## Changed + +- Removed our dependency on `async-macros`, speeding up compilation [(#610)](https://github.com/async-rs/async-std/pull/610) + +## Fixes + +- Fixed a link in the task docs [(#598)](https://github.com/async-rs/async-std/pull/598) +- Fixed the `UdpSocket::recv` example [(#603)](https://github.com/async-rs/async-std/pull/603) +- Fixed a link to `task::block_on` [(#608)](https://github.com/async-rs/async-std/pull/608) +- Fixed an incorrect API mention in `task::Builder` [(#612)](https://github.com/async-rs/async-std/pull/612) +- Fixed leftover mentions of `futures-preview` [(#595)](https://github.com/async-rs/async-std/pull/595) +- Fixed a typo in the tutorial [(#614)](https://github.com/async-rs/async-std/pull/614) +- `::poll_close` now closes the write half of the stream [(#618)](https://github.com/async-rs/async-std/pull/618) + # [1.2.0] - 2019-11-27 [API Documentation](https://docs.rs/async-std/1.2.0/async-std) @@ -553,7 +603,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.2.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.3.0...HEAD +[1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 diff --git a/Cargo.toml b/Cargo.toml index 569dfb369..ec2e5580b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.2.0" +version = "1.3.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 761029cd08901179e37e816c10b97c7f86c91679 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Dec 2019 15:28:09 +0100 Subject: [PATCH 360/707] fix stream doc hiccup Signed-off-by: Yoshua Wuyts --- 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 ebce3a36b..4e5422154 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -308,7 +308,7 @@ pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; -pub mod stream; +pub(crate) mod stream; mod empty; mod from_fn; From 499a44ab3bcbea116dd1025d90796faaa5dfc7fe Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 14 Dec 2019 23:34:55 +0800 Subject: [PATCH 361/707] Use ?Sized in Mutex and RwLock --- src/sync/mutex.rs | 30 ++++++++++++++++-------------- src/sync/rwlock.rs | 44 +++++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 4d2cf2512..3782bb63f 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -39,14 +39,14 @@ use crate::task::{Context, Poll}; /// # /// # }) /// ``` -pub struct Mutex { +pub struct Mutex { locked: AtomicBool, wakers: WakerSet, value: UnsafeCell, } -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} impl Mutex { /// Creates a new mutex. @@ -65,7 +65,9 @@ impl Mutex { value: UnsafeCell::new(t), } } +} +impl Mutex { /// Acquires the lock. /// /// Returns a guard that releases the lock when dropped. @@ -189,7 +191,7 @@ impl Mutex { /// let mutex = Mutex::new(10); /// assert_eq!(mutex.into_inner(), 10); /// ``` - pub fn into_inner(self) -> T { + pub fn into_inner(self) -> T where T: Sized { self.value.into_inner() } @@ -216,7 +218,7 @@ impl Mutex { } } -impl fmt::Debug for Mutex { +impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Locked; impl fmt::Debug for Locked { @@ -238,19 +240,19 @@ impl From for Mutex { } } -impl Default for Mutex { +impl Default for Mutex { fn default() -> Mutex { Mutex::new(Default::default()) } } /// A guard that releases the lock when dropped. -pub struct MutexGuard<'a, T>(&'a Mutex); +pub struct MutexGuard<'a, T: ?Sized>(&'a Mutex); -unsafe impl Send for MutexGuard<'_, T> {} -unsafe impl Sync for MutexGuard<'_, T> {} +unsafe impl Send for MutexGuard<'_, T> {} +unsafe impl Sync for MutexGuard<'_, T> {} -impl Drop 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); @@ -260,19 +262,19 @@ impl Drop for MutexGuard<'_, T> { } } -impl fmt::Debug for MutexGuard<'_, T> { +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> { +impl fmt::Display for MutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl Deref for MutexGuard<'_, T> { +impl Deref for MutexGuard<'_, T> { type Target = T; fn deref(&self) -> &T { @@ -280,7 +282,7 @@ impl Deref for MutexGuard<'_, T> { } } -impl DerefMut for MutexGuard<'_, T> { +impl DerefMut for MutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0.value.get() } } diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index bc3f64052..a748607fb 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -49,15 +49,15 @@ const READ_COUNT_MASK: usize = !(ONE_READ - 1); /// # /// # }) /// ``` -pub struct RwLock { +pub struct RwLock { state: AtomicUsize, read_wakers: WakerSet, write_wakers: WakerSet, value: UnsafeCell, } -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} impl RwLock { /// Creates a new reader-writer lock. @@ -77,7 +77,9 @@ impl RwLock { value: UnsafeCell::new(t), } } +} +impl RwLock { /// Acquires a read lock. /// /// Returns a guard that releases the lock when dropped. @@ -316,7 +318,7 @@ impl RwLock { /// let lock = RwLock::new(10); /// assert_eq!(lock.into_inner(), 10); /// ``` - pub fn into_inner(self) -> T { + pub fn into_inner(self) -> T where T: Sized { self.value.into_inner() } @@ -343,7 +345,7 @@ impl RwLock { } } -impl fmt::Debug for RwLock { +impl fmt::Debug for RwLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Locked; impl fmt::Debug for Locked { @@ -365,19 +367,19 @@ impl From for RwLock { } } -impl Default for RwLock { +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>(&'a RwLock); +pub struct RwLockReadGuard<'a, T: ?Sized>(&'a RwLock); -unsafe impl Send for RwLockReadGuard<'_, T> {} -unsafe impl Sync for RwLockReadGuard<'_, T> {} +unsafe impl Send for RwLockReadGuard<'_, T> {} +unsafe impl Sync for RwLockReadGuard<'_, T> {} -impl Drop for RwLockReadGuard<'_, T> { +impl Drop for RwLockReadGuard<'_, T> { fn drop(&mut self) { let state = self.0.state.fetch_sub(ONE_READ, Ordering::SeqCst); @@ -388,19 +390,19 @@ impl Drop for RwLockReadGuard<'_, T> { } } -impl fmt::Debug for RwLockReadGuard<'_, T> { +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> { +impl fmt::Display for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl Deref for RwLockReadGuard<'_, T> { +impl Deref for RwLockReadGuard<'_, T> { type Target = T; fn deref(&self) -> &T { @@ -409,12 +411,12 @@ impl Deref for RwLockReadGuard<'_, T> { } /// A guard that releases the write lock when dropped. -pub struct RwLockWriteGuard<'a, T>(&'a RwLock); +pub struct RwLockWriteGuard<'a, T: ?Sized>(&'a RwLock); -unsafe impl Send for RwLockWriteGuard<'_, T> {} -unsafe impl Sync for RwLockWriteGuard<'_, T> {} +unsafe impl Send for RwLockWriteGuard<'_, T> {} +unsafe impl Sync for RwLockWriteGuard<'_, T> {} -impl Drop for RwLockWriteGuard<'_, T> { +impl Drop for RwLockWriteGuard<'_, T> { fn drop(&mut self) { self.0.state.store(0, Ordering::SeqCst); @@ -427,19 +429,19 @@ impl Drop for RwLockWriteGuard<'_, T> { } } -impl fmt::Debug for RwLockWriteGuard<'_, T> { +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> { +impl fmt::Display for RwLockWriteGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl Deref for RwLockWriteGuard<'_, T> { +impl Deref for RwLockWriteGuard<'_, T> { type Target = T; fn deref(&self) -> &T { @@ -447,7 +449,7 @@ impl Deref for RwLockWriteGuard<'_, T> { } } -impl DerefMut for RwLockWriteGuard<'_, T> { +impl DerefMut for RwLockWriteGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0.value.get() } } From 732ef10f9812e1581132af5e077a7b27de414dd2 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 14 Dec 2019 23:42:14 +0800 Subject: [PATCH 362/707] Make code compile --- src/sync/mutex.rs | 6 +++--- src/sync/rwlock.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 3782bb63f..c62b5616a 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -93,12 +93,12 @@ impl Mutex { /// # }) /// ``` pub async fn lock(&self) -> MutexGuard<'_, T> { - pub struct LockFuture<'a, T> { + pub struct LockFuture<'a, T: ?Sized> { mutex: &'a Mutex, opt_key: Option, } - impl<'a, T> Future for LockFuture<'a, T> { + 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 { @@ -125,7 +125,7 @@ impl Mutex { } } - impl Drop for LockFuture<'_, T> { + 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 { diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index a748607fb..08d8ed849 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -101,12 +101,12 @@ impl RwLock { /// # }) /// ``` pub async fn read(&self) -> RwLockReadGuard<'_, T> { - pub struct ReadFuture<'a, T> { + pub struct ReadFuture<'a, T: ?Sized> { lock: &'a RwLock, opt_key: Option, } - impl<'a, T> Future for ReadFuture<'a, T> { + 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 { @@ -133,7 +133,7 @@ impl RwLock { } } - impl Drop for ReadFuture<'_, T> { + 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 { @@ -226,12 +226,12 @@ impl RwLock { /// # }) /// ``` pub async fn write(&self) -> RwLockWriteGuard<'_, T> { - pub struct WriteFuture<'a, T> { + pub struct WriteFuture<'a, T: ?Sized> { lock: &'a RwLock, opt_key: Option, } - impl<'a, T> Future for WriteFuture<'a, T> { + 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 { @@ -258,7 +258,7 @@ impl RwLock { } } - impl Drop for WriteFuture<'_, T> { + 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 { From 07eb2c1280708c6e9ec6f5a4e4aacb84d7e8b4b5 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Sat, 14 Dec 2019 17:43:22 +0000 Subject: [PATCH 363/707] Make WriteFmtFuture must_use Fixes #627. Thanks to @jebrosen for pointing out the location of the issue. --- src/io/write/write_fmt.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index ec7847f22..d20c41d8a 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -6,6 +6,7 @@ use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] +#[must_use] pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> { pub(crate) writer: &'a mut T, pub(crate) res: Option>>, From c70552ead53dbaff8f0d70008b4b2a6c3036bc83 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Dec 2019 09:37:14 +0100 Subject: [PATCH 364/707] unpub double_ended_stream Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 4e5422154..d8b96ec22 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,8 +318,7 @@ mod repeat; mod repeat_with; cfg_unstable! { - #[doc(hidden)] - pub mod double_ended_stream; + mod double_ended_stream; mod exact_size_stream; mod extend; mod from_stream; From b7e55762d8435ba3e06fd0c21cd8c82d8e4c57a9 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Dec 2019 10:04:39 +0100 Subject: [PATCH 365/707] upgrade log, remove kv-log-macro Signed-off-by: Yoshua Wuyts --- Cargo.toml | 4 +--- src/task/block_on.rs | 5 ++--- src/task/builder.rs | 5 ++--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ec2e5580b..6a5370864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ default = [ "crossbeam-channel", "crossbeam-deque", "futures-timer", - "kv-log-macro", "log", "mio", "mio-uds", @@ -58,8 +57,7 @@ crossbeam-utils = { version = "0.7.0", optional = true } futures-core = { version = "0.3.1", optional = true } futures-io = { version = "0.3.1", 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 } +log = { version = "0.4.10", features = ["kv_unstable"], optional = true } memchr = { version = "2.2.1", optional = true } mio = { version = "0.6.19", optional = true } mio-uds = { version = "0.6.7", optional = true } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 80259c579..8f58b2a90 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,7 +6,6 @@ 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}; @@ -43,7 +42,7 @@ where // Log this `block_on` operation. if log_enabled!(log::Level::Trace) { - trace!("block_on", { + log::trace!("block_on", { task_id: task.id().0, parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); @@ -59,7 +58,7 @@ where defer! { if log_enabled!(log::Level::Trace) { Task::get_current(|t| { - trace!("completed", { + log::trace!("completed", { task_id: t.id().0, }); }); diff --git a/src/task/builder.rs b/src/task/builder.rs index afd4c2c1c..41eb25633 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,4 +1,3 @@ -use kv_log_macro::trace; use log::log_enabled; use std::future::Future; @@ -38,7 +37,7 @@ impl Builder { // Log this `spawn` operation. if log_enabled!(log::Level::Trace) { - trace!("spawn", { + log::trace!("spawn", { task_id: task.id().0, parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); @@ -54,7 +53,7 @@ impl Builder { defer! { if log_enabled!(log::Level::Trace) { Task::get_current(|t| { - trace!("completed", { + log::trace!("completed", { task_id: t.id().0, }); }); From 8ad1d231165b7a035c50f8be8ffdfa1342e37425 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Dec 2019 10:43:19 +0100 Subject: [PATCH 366/707] fix ci Signed-off-by: Yoshua Wuyts --- src/stream/double_ended_stream/mod.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs index dc2a45c9f..a177865b6 100644 --- a/src/stream/double_ended_stream/mod.rs +++ b/src/stream/double_ended_stream/mod.rs @@ -105,9 +105,10 @@ pub trait DoubleEndedStream: Stream { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + use async_std::prelude::*; + use async_std::stream; - let mut s = double_ended_stream::from_iter(vec![7u8]); + let mut s = stream::from_iter(vec![7u8]); assert_eq!(s.next_back().await, Some(7)); assert_eq!(s.next_back().await, None); @@ -132,9 +133,10 @@ pub trait DoubleEndedStream: Stream { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + use async_std::prelude::*; + use async_std::stream; - let mut s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.nth_back(1).await; assert_eq!(second, Some(4)); @@ -159,9 +161,10 @@ pub trait DoubleEndedStream: Stream { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + use async_std::prelude::*; + use async_std::stream; - let mut s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.rfind(|v| v % 2 == 0).await; assert_eq!(second, Some(4)); @@ -185,9 +188,10 @@ pub trait DoubleEndedStream: Stream { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + use async_std::prelude::*; + use async_std::stream; - let s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); let second = s.rfold(0, |acc, v| v + acc).await; @@ -215,9 +219,10 @@ pub trait DoubleEndedStream: Stream { ``` # fn main() { async_std::task::block_on(async { # - use async_std::stream::double_ended_stream::{self, DoubleEndedStream}; + use async_std::prelude::*; + use async_std::stream; - let s = double_ended_stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); let sum = s.try_rfold(0, |acc, v| { if (acc+v) % 2 == 1 { Ok(v+3) From 60de8e1082676303de0171a66a404cdbdf9f574d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Dec 2019 11:08:59 +0100 Subject: [PATCH 367/707] up time limits Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f6822d241..7fac00db2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -587,13 +587,13 @@ extension_trait! { assert_eq!(s.next().await, Some(1)); // There will be no delay after the first time. - assert!(start.elapsed().as_millis() <= 210); + assert!(start.elapsed().as_millis() < 400); assert_eq!(s.next().await, Some(2)); - assert!(start.elapsed().as_millis() <= 210); + assert!(start.elapsed().as_millis() < 400); assert_eq!(s.next().await, None); - assert!(start.elapsed().as_millis() <= 210); + assert!(start.elapsed().as_millis() < 400); # # }) } ``` From 36d24cd0e1f5049be4caa2461a43c704eb61975e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 16 Dec 2019 13:57:27 +0100 Subject: [PATCH 368/707] 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 369/707] 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 43f4f393af2451e9377346f0b8b9120654dc0419 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 17 Dec 2019 22:48:14 +0900 Subject: [PATCH 370/707] fix missing export for the return value --- src/io/mod.rs | 6 +++--- src/io/read/mod.rs | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index ee1289d3a..056b05cad 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -105,8 +105,8 @@ //! //! ```no_run //! use async_std::fs::File; -//! use async_std::io::BufWriter; //! use async_std::io::prelude::*; +//! use async_std::io::BufWriter; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -116,8 +116,8 @@ //! //! // write a byte to the buffer //! writer.write(&[42]).await?; -//! //! } // the buffer is flushed once writer goes out of scope +//! // //! # //! # Ok(()) }) } //! ``` @@ -281,7 +281,7 @@ cfg_std! { pub use copy::copy; pub use cursor::Cursor; pub use empty::{empty, Empty}; - pub use read::Read; + pub use read::*; pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 0d7f4dcc5..8aade1894 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -17,6 +17,10 @@ use std::mem; use crate::io::IoSliceMut; +pub use take::Take; +pub use bytes::Bytes; +pub use chain::Chain; + extension_trait! { use std::pin::Pin; use std::ops::{Deref, DerefMut}; @@ -301,11 +305,11 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn take(self, limit: u64) -> take::Take + fn take(self, limit: u64) -> Take where Self: Sized, { - take::Take { inner: self, limit } + Take { inner: self, limit } } #[doc = r#" @@ -377,8 +381,8 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn bytes(self) -> bytes::Bytes where Self: Sized { - bytes::Bytes { inner: self } + fn bytes(self) -> Bytes where Self: Sized { + Bytes { inner: self } } #[doc = r#" @@ -413,8 +417,8 @@ extension_trait! { # Ok(()) }) } ``` "#] - fn chain(self, next: R) -> chain::Chain where Self: Sized { - chain::Chain { first: self, second: next, done_first: false } + fn chain(self, next: R) -> Chain where Self: Sized { + Chain { first: self, second: next, done_first: false } } } From d8befe24e80ad148832a9894169130d5f93bfaaa Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 18 Dec 2019 08:01:09 +0900 Subject: [PATCH 371/707] Revert "upgrade log, remove kv-log-macro" --- Cargo.toml | 4 +++- src/task/block_on.rs | 5 +++-- src/task/builder.rs | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a5370864..ec2e5580b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ default = [ "crossbeam-channel", "crossbeam-deque", "futures-timer", + "kv-log-macro", "log", "mio", "mio-uds", @@ -57,7 +58,8 @@ crossbeam-utils = { version = "0.7.0", optional = true } futures-core = { version = "0.3.1", optional = true } futures-io = { version = "0.3.1", optional = true } futures-timer = { version = "2.0.2", optional = true } -log = { version = "0.4.10", features = ["kv_unstable"], optional = true } +kv-log-macro = { version = "1.0.4", optional = true } +log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.2.1", optional = true } mio = { version = "0.6.19", optional = true } mio-uds = { version = "0.6.7", optional = true } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 8f58b2a90..80259c579 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,6 +6,7 @@ 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,7 +43,7 @@ where // Log this `block_on` operation. if log_enabled!(log::Level::Trace) { - log::trace!("block_on", { + trace!("block_on", { task_id: task.id().0, parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); @@ -58,7 +59,7 @@ where defer! { if log_enabled!(log::Level::Trace) { Task::get_current(|t| { - log::trace!("completed", { + trace!("completed", { task_id: t.id().0, }); }); diff --git a/src/task/builder.rs b/src/task/builder.rs index 41eb25633..afd4c2c1c 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,3 +1,4 @@ +use kv_log_macro::trace; use log::log_enabled; use std::future::Future; @@ -37,7 +38,7 @@ impl Builder { // Log this `spawn` operation. if log_enabled!(log::Level::Trace) { - log::trace!("spawn", { + trace!("spawn", { task_id: task.id().0, parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), }); @@ -53,7 +54,7 @@ impl Builder { defer! { if log_enabled!(log::Level::Trace) { Task::get_current(|t| { - log::trace!("completed", { + trace!("completed", { task_id: t.id().0, }); }); From 980a1f7834070b1ff7eddc1d8a710840315e98b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Tue, 17 Dec 2019 22:46:25 -0600 Subject: [PATCH 372/707] Correcting docs on function --- src/future/future/mod.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index e302175e2..bd9728995 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -363,16 +363,18 @@ extension_trait! { # Example ``` - #async_std::task::block_on(async { - let fut = future::ready(0); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_ok()); - - let fut = future::ready(0); - let dur = Duration::from_millis(100); - let res = fut.timeout(dur).await; - assert!(res.is_ok()) + # async_std::task::block_on(async { + # + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()); + + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()) + # # }); ``` "#] From 1eeb1019e90a71b0ee39a806959d818a0f91a9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Tue, 17 Dec 2019 23:05:06 -0600 Subject: [PATCH 373/707] Fixing example --- src/future/future/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index bd9728995..eea0ffb2c 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -364,7 +364,10 @@ extension_trait! { # Example ``` # async_std::task::block_on(async { - # + # + 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; From 97b4901b7528d3aae89d86438461408080c4bcc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Tue, 17 Dec 2019 23:12:09 -0600 Subject: [PATCH 374/707] Fixing tests --- src/future/future/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index eea0ffb2c..dea0ade03 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -364,10 +364,13 @@ extension_trait! { # Example ``` # async_std::task::block_on(async { - # + # + use std::time::Duration; + use async_std::prelude::*; use async_std::future; - + use async_std::task; + let fut = future::ready(0); let dur = Duration::from_millis(100); let res = fut.timeout(dur).await; From eedf1d33676c52ba7d2645ba3f55b4ab0b5ee0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Tue, 17 Dec 2019 23:17:02 -0600 Subject: [PATCH 375/707] Fixing docs --- src/future/future/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index dea0ade03..8d956fd75 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -369,7 +369,6 @@ extension_trait! { use async_std::prelude::*; use async_std::future; - use async_std::task; let fut = future::ready(0); let dur = Duration::from_millis(100); From ef021dcb2bac3d1825d8b8162ecbfa41e1740eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20P=C3=A9rez=20Garc=C3=ADa?= Date: Wed, 18 Dec 2019 07:18:57 -0600 Subject: [PATCH 376/707] Changing test condition --- src/future/future/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index 8d956fd75..b3efedb0d 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -375,10 +375,10 @@ extension_trait! { let res = fut.timeout(dur).await; assert!(res.is_ok()); - let fut = future::ready(0); + let fut = future::pending::<()>(); let dur = Duration::from_millis(100); let res = fut.timeout(dur).await; - assert!(res.is_ok()) + assert!(res.is_err()) # # }); ``` From 3fd6d8b02efab0f1eaf9d324944a854b3d5bc2a3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 20 Dec 2019 11:58:32 +0100 Subject: [PATCH 377/707] 1.4.0 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc2238cf6..831c45c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.4.0] - 2019-12-20 + +[API Documentation](https://docs.rs/async-std/1.4.0/async-std) + +This patch adds `Future::timeout`, providing a method counterpart to the +`future::timeout` free function. And includes several bug fixes around missing +APIs. Notably we're not shipping our new executor yet, first announced [on our +blog](https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/). + +## Examples + +```rust +use async_std::prelude::*; +use async_std::future; +use std::time::Duration; + +let fut = future::pending::<()>(); // This future will never resolve. +let res = fut.timeout(Duration::from_millis(100)).await; +assert!(res.is_err()); // The future timed out, returning an err. +``` + +## Added + +- Added `Future::timeout` as "unstable" [(#600)](https://github.com/async-rs/async-std/pull/600) + +## Fixes + +- Fixed a doc test and enabled it on CI [(#597)](https://github.com/async-rs/async-std/pull/597) +- Fixed a rendering issue with the `stream` submodule documentation [(#621)](https://github.com/async-rs/async-std/pull/621) +- `Write::write_fmt`'s future is now correctly marked as `#[must_use]` [(#628)](https://github.com/async-rs/async-std/pull/628) +- Fixed the missing `io::Bytes` export [(#633)](https://github.com/async-rs/async-std/pull/633) +- Fixed the missing `io::Chain` export [(#633)](https://github.com/async-rs/async-std/pull/633) +- Fixed the missing `io::Take` export [(#633)](https://github.com/async-rs/async-std/pull/633) + # [1.3.0] - 2019-12-12 [API Documentation](https://docs.rs/async-std/1.3.0/async-std) @@ -604,6 +638,7 @@ task::blocking(async { - Initial beta release [Unreleased]: https://github.com/async-rs/async-std/compare/v1.3.0...HEAD +[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 [1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 diff --git a/Cargo.toml b/Cargo.toml index ec2e5580b..1648e18c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.3.0" +version = "1.4.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From b3942ecfa8406987f88999f3a2b92f8a824e4ac6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 24 Dec 2019 14:39:55 +0100 Subject: [PATCH 378/707] remove tokio mention Signed-off-by: Yoshua Wuyts --- src/stream/interval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/interval.rs b/src/stream/interval.rs index b0df71419..f8a6ef64e 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -85,7 +85,7 @@ impl Stream for Interval { /// 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 -/// tokio. +/// async-std. fn duration_to_nanos(dur: Duration) -> Option { dur.as_secs() .checked_mul(1_000_000_000) From 081166f204d5fe9cc5288bb9a7f0b97380c7ae39 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Fri, 27 Dec 2019 03:06:41 +0100 Subject: [PATCH 379/707] Fixing inaccurate function description in udp::recv --- src/net/udp/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 418b4b60a..1b6d0d741 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -288,7 +288,7 @@ impl UdpSocket { /// Receives data from the socket. /// - /// On success, returns the number of bytes read and the origin. + /// On success, returns the number of bytes read. /// /// # Examples /// From c3d5dba1b570e55bbff023bf586ea29fb3c329e3 Mon Sep 17 00:00:00 2001 From: Stefano Probst Date: Sat, 28 Dec 2019 17:27:37 +0100 Subject: [PATCH 380/707] Fix typo in stream documentation --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7fac00db2..25b37450d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -695,7 +695,7 @@ extension_trait! { # }) } ``` - An empty stream will return `None: + An empty stream will return `None`: ``` # fn main() { async_std::task::block_on(async { # From 65d7950df1bff99010f911f89040afc50d63aac9 Mon Sep 17 00:00:00 2001 From: Artem Varaksa Date: Wed, 1 Jan 2020 15:36:47 +0300 Subject: [PATCH 381/707] Fix crate documentation typo --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2cca5e36f..a698c39f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! encouraged to read it. The `async-std` source is generally high //! quality and a peek behind the curtains is often enlightening. //! -//! Modules in this crate are organized in the same way as in `async-std`, except blocking +//! Modules in this crate are organized in the same way as in `std`, except blocking //! functions have been replaced with async functions and threads have been replaced with //! lightweight tasks. //! From af2d46d9b9242f2764e742731f9e031be0c53252 Mon Sep 17 00:00:00 2001 From: Alfie John Date: Tue, 7 Jan 2020 13:29:30 +1100 Subject: [PATCH 382/707] Tiny grammar fix --- 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 a73d5d240..f738fca40 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -37,7 +37,7 @@ //! outlive its parent (the task that spawned it), unless this parent is the root task. //! //! The root task can also wait on the completion of the child task; a call to [`spawn`] produces a -//! [`JoinHandle`], which provides implements `Future` and can be `await`ed: +//! [`JoinHandle`], which implements `Future` and can be `await`ed: //! //! ``` //! use async_std::task; From d806a095996ab60a6bfef4b5a2687d4fde1614ab Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 6 Jan 2020 23:00:56 -0800 Subject: [PATCH 383/707] Add a section on the async ecosystem to showcase crates that use async-std --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 46616390a..8cd3ac5da 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,20 @@ documentation] on how to enable them. [cargo-add]: https://github.com/killercup/cargo-edit [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. + ## License From dfb0c8124c085b66f58e439a14c46f0be3cf49c4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 7 Jan 2020 14:21:17 +0100 Subject: [PATCH 384/707] remove usage of deprecated method Signed-off-by: Yoshua Wuyts --- src/io/utils.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/io/utils.rs b/src/io/utils.rs index 1b730645b..0ba3ecb2d 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -1,5 +1,7 @@ use crate::utils::Context; +use std::{error::Error as StdError, fmt, io}; + /// Wrap `std::io::Error` with additional message /// /// Keeps the original error kind and stores the original I/O error as `source`. @@ -9,8 +11,6 @@ impl Context for Result { } } -use std::{error::Error as StdError, fmt, io}; - #[derive(Debug)] pub(crate) struct VerboseError { source: io::Error, @@ -36,10 +36,6 @@ impl fmt::Display for VerboseError { } impl StdError for VerboseError { - fn description(&self) -> &str { - self.source.description() - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.source) } From 5bf3d953136a2ddbf790d2fb54296ab5a684b3ae Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 26 Dec 2019 22:51:50 +0100 Subject: [PATCH 385/707] feat: do not require default feature for unstable --- Cargo.toml | 4 ++-- src/future/future/mod.rs | 12 +++++++----- src/io/mod.rs | 2 +- src/io/read/bytes.rs | 2 +- src/io/read/chain.rs | 2 +- src/lib.rs | 2 ++ src/stream/stream/count.rs | 2 +- src/stream/stream/mod.rs | 4 ++-- src/stream/stream/partition.rs | 2 +- src/stream/stream/unzip.rs | 2 +- src/utils.rs | 13 ++++++++++++- 11 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1648e18c5..63d599a5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,8 @@ default = [ "num_cpus", "pin-project-lite", ] -docs = ["attributes", "unstable"] -unstable = ["default", "broadcaster"] +docs = ["attributes", "unstable", "default"] +unstable = ["std", "broadcaster", "futures-timer"] attributes = ["async-attributes"] std = [ "crossbeam-utils", diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index b3efedb0d..d610096b8 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -7,7 +7,6 @@ cfg_unstable! { mod try_join; use std::time::Duration; - use delay::DelayFuture; use flatten::FlattenFuture; use crate::future::IntoFuture; @@ -15,6 +14,9 @@ cfg_unstable! { use try_race::TryRace; use join::Join; use try_join::TryJoin; +} + +cfg_unstable_default! { use crate::future::timeout::TimeoutFuture; } @@ -149,7 +151,7 @@ extension_trait! { /// dbg!(a.await); /// # }) /// ``` - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn delay(self, dur: Duration) -> impl Future [DelayFuture] where @@ -363,13 +365,13 @@ extension_trait! { # Example ``` - # async_std::task::block_on(async { + # 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; @@ -383,7 +385,7 @@ extension_trait! { # }); ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn timeout(self, dur: Duration) -> impl Future [TimeoutFuture] where Self: Sized diff --git a/src/io/mod.rs b/src/io/mod.rs index 056b05cad..51c473d02 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -321,7 +321,7 @@ cfg_default! { mod stdout; } -cfg_unstable! { +cfg_unstable_default! { pub use stderr::StderrLock; pub use stdin::StdinLock; pub use stdout::StdoutLock; diff --git a/src/io/read/bytes.rs b/src/io/read/bytes.rs index 422452433..ab9259611 100644 --- a/src/io/read/bytes.rs +++ b/src/io/read/bytes.rs @@ -32,7 +32,7 @@ impl Stream for Bytes { } } -#[cfg(test)] +#[cfg(all(test, default))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 335cac255..4fcdb0ec9 100644 --- a/src/io/read/chain.rs +++ b/src/io/read/chain.rs @@ -165,7 +165,7 @@ impl BufRead for Chain { } } -#[cfg(test)] +#[cfg(all(test, default))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/lib.rs b/src/lib.rs index a698c39f1..c9d127713 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,7 +266,9 @@ cfg_unstable! { mod option; mod string; mod collections; +} +cfg_unstable_default! { #[doc(inline)] pub use std::{write, writeln}; } diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index ebf2a2f17..1ca8aef18 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -9,7 +9,7 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct CountFuture { #[pin] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 25b37450d..b98950b31 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -355,7 +355,7 @@ extension_trait! { # }) } ``` "#] - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn throttle(self, d: Duration) -> Throttle where @@ -1507,7 +1507,7 @@ extension_trait! { # }) } ``` "#] - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn by_ref(&mut self) -> &mut Self { self diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index 7e2caad42..aaf58ac09 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -8,7 +8,7 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Debug)] - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct PartitionFuture { #[pin] diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index e0832ff71..cb5757805 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -8,7 +8,7 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Clone, Debug)] - #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct UnzipFuture { #[pin] diff --git a/src/utils.rs b/src/utils.rs index 7d253b49c..c5ec0ce92 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,7 +19,6 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { } /// Generates a random number in `0..n`. -#[cfg(feature = "default")] pub fn random(n: u32) -> u32 { use std::cell::Cell; use std::num::Wrapping; @@ -90,6 +89,18 @@ macro_rules! cfg_unstable { } } +/// Declares unstable and default items. +#[doc(hidden)] +macro_rules! cfg_unstable_default { + ($($item:item)*) => { + $( + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(unstable))] + $item + )* + } +} + /// Declares Unix-specific items. #[doc(hidden)] macro_rules! cfg_unix { From 9c9ab90da3e0f0933e08a173caff318b0613b1ad Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 11 Jan 2020 11:49:52 +0100 Subject: [PATCH 386/707] feature gate random --- src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.rs b/src/utils.rs index c5ec0ce92..cb831daaa 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,6 +19,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> 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; From 9c6ab5e7c3d4e79f2ce1c510339504df1ebd1257 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 11 Jan 2020 11:57:42 +0100 Subject: [PATCH 387/707] fix --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index cb831daaa..ef50ed028 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,7 +19,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { } /// Generates a random number in `0..n`. -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "default"))] pub fn random(n: u32) -> u32 { use std::cell::Cell; use std::num::Wrapping; From c8c075615c0a44ec10c210292caf25899d8b8b0a Mon Sep 17 00:00:00 2001 From: Paul Colomiets Date: Sun, 12 Jan 2020 03:45:41 +0200 Subject: [PATCH 388/707] book: Add Production-ready Accept Loop section Part of the #658 work --- docs/src/SUMMARY.md | 3 +- docs/src/patterns/accept-loop.md | 256 +++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 docs/src/patterns/accept-loop.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d679825e8..8b49ce7ab 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -19,8 +19,9 @@ - [Clean Shutdown](./tutorial/clean_shutdown.md) - [Handling Disconnection](./tutorial/handling_disconnection.md) - [Implementing a Client](./tutorial/implementing_a_client.md) -- [TODO: Async Patterns](./patterns.md) +- [Async Patterns](./patterns.md) - [TODO: Collected Small Patterns](./patterns/small-patterns.md) + - [Production-Ready Accept Loop](./patterns/accept-loop.md) - [Security practices](./security/index.md) - [Security Disclosures and Policy](./security/policy.md) - [Glossary](./glossary.md) diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md new file mode 100644 index 000000000..acc0d755f --- /dev/null +++ b/docs/src/patterns/accept-loop.md @@ -0,0 +1,256 @@ +# Production-Ready Accept Loop + +Production-ready accept loop needs the following things: +1. Handling errors +2. Limiting the number of simultanteous connections to avoid deny-of-service + (DoS) attacks + + +## Handling errors + +There are two kinds of errors in accept loop: +1. Per-connection errors. System uses them to notify that there was a + connection in the queue and it's dropped by peer. Subsequent connection + can be already queued so next connection must be accepted immediately. +2. Resource shortages. When these are encountered it doesn't make sense to + accept next socket immediately. But listener stays active, so you server + should try to accept socket later. + +Here is the example of per-connection error (printed in normal and debug mode): +``` +Error: Connection reset by peer (os error 104) +Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" } +``` + +And the following is the most common example of a resource shortage error: +``` +Error: Too many open files (os error 24) +Error: Os { code: 24, kind: Other, message: "Too many open files" } +``` + +### Testing Application + +To test your application on these errors try the following (this works +on unixes only). + +Lower limit and start the application: +``` +$ ulimit -n 100 +$ cargo run --example your_app + Compiling your_app v0.1.0 (/work) + Finished dev [unoptimized + debuginfo] target(s) in 5.47s + Running `target/debug/examples/your_app` +Server is listening on: http://127.0.0.1:1234 +``` +Then in another console run [`wrk`] benchmark tool: +``` +$ wrk -c 1000 http://127.0.0.1:1234 +Running 10s test @ http://localhost:8080/ + 2 threads and 1000 connections +$ telnet localhost 1234 +Trying ::1... +Connected to localhost. +``` + +Important is to check the following things: + +1. Application doesn't crash on error (but may log errors, see below) +2. It's possible to connect to the application again once load is stopped + (few seconds after `wrk`). This is what `telnet` does in example above, + make sure it prints `Connected to `. +3. The `Too many open files` error is logged in the appropriate log. This + requires to set "maximum number of simultaneous connections" parameter (see + below) of your application to a value greater that `100` for this example. +4. Check CPU usage of the app while doing a test. It should not occupy 100% + of a single CPU core (it's unlikely that you can exhaust CPU by 1000 + connections in Rust, so this means error handling is not right). + +#### Testing non-HTTP applications + +If it's possible, use the appropriate benchmark tool and set the appropriate +number of connections. For example `redis-benchmark` has `-c` parameter for +that, if you implement redis protocol. + +Alternatively, can still use `wrk`, just make sure that connection is not +immediately closed. If it is, put a temporary timeout before handing +connection to the protocol handler, like this: + +```rust,edition2018 +# extern crate async_std; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +#async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { +# let listener = TcpListener::bind(addr).await?; +# let mut incoming = listener.incoming(); +while let Some(stream) = incoming.next().await { + task::spawn(async { + task:sleep(Duration::from_secs(10)).await; // 1 + connection_loop(stream).await; + }); +} +# Ok(()) +# } +``` + +1. Make sure the sleep coroutine is inside the spawned task, not in the loop. + +[`wrk`]: https://github.com/wg/wrk + + +### Handling Errors Manually + +Here is how basic accept loop could look like: + +```rust,edition2018 +# extern crate async_std; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +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 { + Err(ref e) if is_connection_error(e) => continue, // 1 + Err(e) => { + eprintln!("Error: {}. Pausing for 500ms."); // 3 + task::sleep(Duration::from_millis(500)).await; // 2 + continue; + } + Ok(s) => s, + }; + // body + } + Ok(()) +} +``` + +1. Ignore per-connection errors. +2. Sleep and continue on resource shortage. +3. It's important to log the message, because these errors commonly mean the + misconfiguration of the system and are helpful for operations people running + the application. + +Be sure to [test your application](#testing-application). + + +### External Crates + +The crate [`async-listen`] have a helper to achieve this task: +```rust,edition2018 +# extern crate async_std; +# extern crate async_listen; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +use async_listen::ListenExt; + +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + + let listener = TcpListener::bind(addr).await?; + let mut incoming = listener + .incoming() + .log_warnings(|e| eprintln!("Error: {}. Pausing for 500ms.", e)) // 1 + .handle_errors(Duration::from_millis(500)); + while let Some(socket) = incoming.next().await { // 2 + // body + } + Ok(()) +} +``` + +1. Logs resource shortages (`async-listen` calls them warnings). If you use + `log` crate or any other in your app this should go to the log. +2. Stream yields sockets without `Result` wrapper after `handle_errors` because + all errors are already handled. + +[`async-listen`]: https://crates.io/crates/async-listen/ + +Be sure to [test your application](#testing-application). + + +## Connections Limit + +Even if you've applied everything described in +[Handling Errors](#handling-errors) section, there is still a problem. + +Let's imagine you have a server that needs to open a file to process +client request. At some point, you might encounter the following situation: + +1. There are as much client connection as max file descriptors allowed for + the application. +2. Listener gets `Too many open files` error so it sleeps. +3. Some client sends a request via the previously open connection. +4. Opening a file to serve request fails, because of the same + `Too many open files` error, until some other client drops a connection. + +There are many more possible situations, this is just a small illustation that +limiting number of connections is very useful. Generally, it's one of the ways +to control resources used by a server and avoiding some kinds of deny of +service (DoS) attacks. + +### `async-listen` crate + +Limiting maximum number of simultaneous connections with [`async-listen`] +looks like the following: + +```rust,edition2018 +# extern crate async_std; +# extern crate async_listen; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, TcpStream, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +use async_listen::{ListenExt, Token}; + +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + + let listener = TcpListener::bind(addr).await?; + let mut incoming = listener + .incoming() + .log_warnings(|e| eprintln!("Error: {}. Pausing for 500ms.", e)) + .handle_errors(Duration::from_millis(500)) // 1 + .backpressure(100); + while let Some((token, socket)) = incoming.next().await { // 2 + task::spawn(async move { + connection_loop(&token, stream).await; // 3 + }); + } + Ok(()) +} +async fn connection_loop(_token: &Token, stream: TcpStream) { // 4 + // ... +} +``` + +1. We need to handle errors first, because [`backpressure`] helper expects + stream of `TcpStream` rather than `Result`. +2. The token yielded by a new stream is what is counted by backpressure helper. + I.e. if you drop a token, new connection can be established. +3. We give connection loop a reference to token to bind token's lifetime to + the lifetime of the connection. +4. The token itsellf in the function can be ignored, hence `_token` + +[`backpressure`]: https://docs.rs/async-listen/0.1.2/async_listen/trait.ListenExt.html#method.backpressure + +Be sure to [test this behavior](#testing-application). From 83afbab2efbe35ae71b749bf53b550794dfdc77c Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 12 Jan 2020 17:32:59 +0300 Subject: [PATCH 389/707] Use `take_while` instead of `scan` for `Option` --- src/option/from_stream.rs | 23 ++++++++++++----------- src/option/product.rs | 25 +++++++++++++------------ src/option/sum.rs | 23 ++++++++++++----------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index 867911433..dc6d8416f 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -17,24 +17,25 @@ where let stream = stream.into_stream(); Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs - let mut found_error = false; + let mut found_none = false; let out: V = stream - .scan((), |_, elem| { - match elem { - Some(elem) => Some(elem), - None => { - found_error = true; - // Stop processing the stream on error - None - } + .take_while(|elem| { + elem.is_some() || { + found_none = true; + false } }) + .map(Option::unwrap) .collect() .await; - if found_error { None } else { Some(out) } + if found_none { + None + } else { + Some(out) + } }) } } diff --git a/src/option/product.rs b/src/option/product.rs index abaab73ed..ff61eaab3 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Stream, Product}; +use crate::stream::{Product, Stream}; impl Product> for Option where @@ -36,23 +36,24 @@ where ``` "#] fn product<'a, S>(stream: S) -> Pin> + 'a>> - where S: Stream> + 'a + where + S: Stream> + 'a, { Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; - let out = >::product(stream - .scan((), |_, elem| { - match elem { - Some(elem) => Some(elem), - None => { + let out = >::product( + stream + .take_while(|elem| { + elem.is_some() || { found_none = true; - // Stop processing the stream on error - None + false } - } - })).await; + }) + .map(Option::unwrap), + ) + .await; if found_none { None diff --git a/src/option/sum.rs b/src/option/sum.rs index d2e44830f..cd92f78d6 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -31,23 +31,24 @@ where ``` "#] fn sum<'a, S>(stream: S) -> Pin> + 'a>> - where S: Stream> + 'a + where + S: Stream> + 'a, { Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; - let out = >::sum(stream - .scan((), |_, elem| { - match elem { - Some(elem) => Some(elem), - None => { + let out = >::sum( + stream + .take_while(|elem| { + elem.is_some() || { found_none = true; - // Stop processing the stream on error - None + false } - } - })).await; + }) + .map(Option::unwrap), + ) + .await; if found_none { None From fb567a3a09da32569f05712bdb07b4f31f2f9526 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 12 Jan 2020 17:53:16 +0300 Subject: [PATCH 390/707] Recovered comments --- src/option/from_stream.rs | 1 + src/option/product.rs | 1 + src/option/sum.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index dc6d8416f..f0aafea6b 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -24,6 +24,7 @@ where .take_while(|elem| { elem.is_some() || { found_none = true; + // Stop processing the stream on `None` false } }) diff --git a/src/option/product.rs b/src/option/product.rs index ff61eaab3..2238a9101 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -48,6 +48,7 @@ where .take_while(|elem| { elem.is_some() || { found_none = true; + // Stop processing the stream on `None` false } }) diff --git a/src/option/sum.rs b/src/option/sum.rs index cd92f78d6..0570a1e15 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -43,6 +43,7 @@ where .take_while(|elem| { elem.is_some() || { found_none = true; + // Stop processing the stream on error false } }) From 002903788319328d2627a60da08d1c78db7b062e Mon Sep 17 00:00:00 2001 From: Paul Colomiets Date: Mon, 13 Jan 2020 08:46:32 +0200 Subject: [PATCH 391/707] async-listen crate: Add `error_hint()` invocation --- docs/src/patterns/accept-loop.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md index acc0d755f..46d6cc67b 100644 --- a/docs/src/patterns/accept-loop.md +++ b/docs/src/patterns/accept-loop.md @@ -159,26 +159,33 @@ The crate [`async-listen`] have a helper to achieve this task: # # type Result = std::result::Result>; # -use async_listen::ListenExt; +use async_listen::{ListenExt, error_hint}; async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener .incoming() - .log_warnings(|e| eprintln!("Error: {}. Pausing for 500ms.", e)) // 1 + .log_warnings(log_accept_error) // 1 .handle_errors(Duration::from_millis(500)); while let Some(socket) = incoming.next().await { // 2 // body } Ok(()) } + +fn log_accept_error(e: &io::Error) { + eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)) // 3 +} ``` 1. Logs resource shortages (`async-listen` calls them warnings). If you use `log` crate or any other in your app this should go to the log. 2. Stream yields sockets without `Result` wrapper after `handle_errors` because all errors are already handled. +3. Together with the error we print a hint, which explains some errors for end + users. For example, it recommends increasing open file limit and gives + a link. [`async-listen`]: https://crates.io/crates/async-listen/ @@ -221,14 +228,14 @@ looks like the following: # # type Result = std::result::Result>; # -use async_listen::{ListenExt, Token}; +use async_listen::{ListenExt, Token, error_hint}; async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener .incoming() - .log_warnings(|e| eprintln!("Error: {}. Pausing for 500ms.", e)) + .log_warnings(log_accept_error) .handle_errors(Duration::from_millis(500)) // 1 .backpressure(100); while let Some((token, socket)) = incoming.next().await { // 2 @@ -241,6 +248,9 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { async fn connection_loop(_token: &Token, stream: TcpStream) { // 4 // ... } +# fn log_accept_error(e: &io::Error) { +# eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)); +# } ``` 1. We need to handle errors first, because [`backpressure`] helper expects From 0ed0d63094c00abdebb9f027754974703248e419 Mon Sep 17 00:00:00 2001 From: nasa Date: Tue, 14 Jan 2020 03:49:52 +0900 Subject: [PATCH 392/707] Remove unnecessary trait bound on FlatMap (#651) * Remove unnecessary trait bound on FlatMap * test: upgrade test code --- src/stream/stream/flat_map.rs | 1 - src/stream/stream/mod.rs | 18 +++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index 6c828c920..9a7d921de 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -41,7 +41,6 @@ where impl Stream for FlatMap where S: Stream, - S::Item: IntoStream, U: Stream, F: FnMut(S::Item) -> U, { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b98950b31..4cf88c7b2 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -791,18 +791,22 @@ extension_trait! { # async_std::task::block_on(async { use async_std::prelude::*; - use async_std::stream::IntoStream; use async_std::stream; - let inner1 = stream::from_iter(vec![1,2,3]); - let inner2 = stream::from_iter(vec![4,5,6]); + let words = stream::from_iter(&["alpha", "beta", "gamma"]); - let s = stream::from_iter(vec![inner1, inner2]); + let merged: String = words + .flat_map(|s| stream::from_iter(s.chars())) + .collect().await; + assert_eq!(merged, "alphabetagamma"); - let v :Vec<_> = s.flat_map(|s| s.into_stream()).collect().await; - - assert_eq!(v, vec![1,2,3,4,5,6]); + 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]); # }); ``` "#] From 5d5064b871317b28e6a992650e350974acd36e25 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 13 Jan 2020 21:42:31 +0100 Subject: [PATCH 393/707] add FromStream Result example (#643) Signed-off-by: Yoshua Wuyts --- src/result/from_stream.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index a8490d691..d6e790e3e 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -10,6 +10,23 @@ where /// Takes each element in the stream: if it is an `Err`, no further /// elements are taken, and the `Err` is returned. Should no `Err` /// occur, a container with the values of each `Result` is returned. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let v = stream::from_iter(vec![1, 2]); + /// let res: Result, &'static str> = v.map(|x: u32| + /// x.checked_add(1).ok_or("Overflow!") + /// ).collect().await; + /// assert_eq!(res, Ok(vec![2, 3])); + /// # + /// # }) } + /// ``` #[inline] fn from_stream<'a, S: IntoStream> + 'a>( stream: S, From a4f6806605d86fcd9056beb8bfd462a4d9377f7d Mon Sep 17 00:00:00 2001 From: noah Date: Mon, 13 Jan 2020 17:47:51 -0600 Subject: [PATCH 394/707] Fixes https://github.com/async-rs/async-std/issues/652 --- src/net/udp/mod.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 1b6d0d741..4ff356313 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -244,9 +244,12 @@ impl UdpSocket { })) } - /// Sends data on the socket to the given address. + /// Sends data on the socket to the remote address to which it is connected. /// - /// On success, returns the number of bytes written. + /// The [`connect`] method will connect this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// [`connect`]: #method.connect /// /// # Examples /// @@ -297,12 +300,11 @@ impl UdpSocket { /// # /// use async_std::net::UdpSocket; /// - /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// let socket = UdpSocket::bind("127.0.0.1:7878").await?; /// socket.connect("127.0.0.1:8080").await?; + /// let bytes = socket.send(b"Hi there!").await?; /// - /// let mut buf = vec![0; 1024]; - /// let n = socket.recv(&mut buf).await?; - /// println!("Received {} bytes", n); + /// println!("Sent {} bytes", bytes); /// # /// # Ok(()) }) } /// ``` From f8dd3d98169ef5efca51d37e5995b626cf324b6a Mon Sep 17 00:00:00 2001 From: Qifan Lu Date: Tue, 10 Dec 2019 18:18:15 -0800 Subject: [PATCH 395/707] Add stream::pending::{pending, Pending} --- src/stream/mod.rs | 2 ++ src/stream/pending.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/stream/pending.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d8b96ec22..0bfd4e865 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -325,6 +325,7 @@ cfg_unstable! { mod fused_stream; mod interval; mod into_stream; + mod pending; mod product; mod successors; mod sum; @@ -336,6 +337,7 @@ cfg_unstable! { 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}; diff --git a/src/stream/pending.rs b/src/stream/pending.rs new file mode 100644 index 000000000..1fd085193 --- /dev/null +++ b/src/stream/pending.rs @@ -0,0 +1,49 @@ +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::stream::{Stream, DoubleEndedStream, ExactSizeStream, FusedStream}; + +/// A stream that never returns any items. +/// +/// This stream is created by the [`pending`] function. See its +/// documentation for more. +/// +/// [`pending`]: fn.pending.html +#[derive(Debug)] +pub struct Pending { + _marker: PhantomData +} + +/// Creates a stream that never returns any items. +/// +/// The returned stream will always return `Pending` when polled. +pub fn pending() -> Pending { + Pending { _marker: PhantomData } +} + +impl Stream for Pending { + type Item = T; + + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Pending + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +impl DoubleEndedStream for Pending { + fn poll_next_back(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} + +impl FusedStream for Pending {} + +impl ExactSizeStream for Pending { + fn len(&self) -> usize { + 0 + } +} From 879e14c6abf7d246871a7882075e8e8eb8c37d18 Mon Sep 17 00:00:00 2001 From: Qifan Lu Date: Tue, 10 Dec 2019 18:31:06 -0800 Subject: [PATCH 396/707] Remove size_hint from Stream impl --- src/stream/pending.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/stream/pending.rs b/src/stream/pending.rs index 1fd085193..943513e39 100644 --- a/src/stream/pending.rs +++ b/src/stream/pending.rs @@ -28,10 +28,6 @@ impl Stream for Pending { fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Pending } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(0)) - } } impl DoubleEndedStream for Pending { From e9357c0307b8ea6bfaccccb0bb10ed4406960573 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 14 Jan 2020 09:49:34 +0900 Subject: [PATCH 397/707] style: Run `cargo fmt` --- src/stream/pending.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/stream/pending.rs b/src/stream/pending.rs index 943513e39..5563d30da 100644 --- a/src/stream/pending.rs +++ b/src/stream/pending.rs @@ -2,24 +2,26 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use crate::stream::{Stream, DoubleEndedStream, ExactSizeStream, FusedStream}; +use crate::stream::{DoubleEndedStream, ExactSizeStream, FusedStream, Stream}; /// A stream that never returns any items. -/// +/// /// This stream is created by the [`pending`] function. See its /// documentation for more. -/// +/// /// [`pending`]: fn.pending.html #[derive(Debug)] pub struct Pending { - _marker: PhantomData + _marker: PhantomData, } /// Creates a stream that never returns any items. -/// +/// /// The returned stream will always return `Pending` when polled. pub fn pending() -> Pending { - Pending { _marker: PhantomData } + Pending { + _marker: PhantomData, + } } impl Stream for Pending { From f53fcbb7060390757b91edd1bc195ecc804be730 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 14 Jan 2020 10:18:14 +0900 Subject: [PATCH 398/707] test,docs: Add stream::pending example code --- src/stream/pending.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/stream/pending.rs b/src/stream/pending.rs index 5563d30da..922a54030 100644 --- a/src/stream/pending.rs +++ b/src/stream/pending.rs @@ -18,6 +18,27 @@ pub struct Pending { /// Creates a stream that never returns any items. /// /// The returned stream will always return `Pending` when polled. +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let dur = Duration::from_millis(100); +/// let mut s = stream::pending::<()>().timeout(dur); +/// +/// let item = s.next().await; +/// +/// assert!(item.is_some()); +/// assert!(item.unwrap().is_err()); +/// +/// # +/// # }) +/// ``` pub fn pending() -> Pending { Pending { _marker: PhantomData, From 76ed174fd5e3e60386dcbbd810613a27613207d1 Mon Sep 17 00:00:00 2001 From: nasa Date: Tue, 14 Jan 2020 23:26:22 +0900 Subject: [PATCH 399/707] Version up of dependent crate (#672) --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63d599a5e..60ea91aad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,18 +60,18 @@ futures-io = { version = "0.3.1", 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.2.1", optional = true } +memchr = { version = "2.3.0", 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.1", optional = true } +pin-project-lite = { version = "0.1.2", 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.2" +rand = "0.7.3" surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.1" From 76993dd7557ca0c0c11f73b5d4e10cc91abd33de Mon Sep 17 00:00:00 2001 From: noah Date: Tue, 14 Jan 2020 10:55:10 -0600 Subject: [PATCH 400/707] Revert "Fixes https://github.com/async-rs/async-std/issues/652" This reverts commit a4f68066 --- src/net/udp/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 4ff356313..1b6d0d741 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -244,12 +244,9 @@ impl UdpSocket { })) } - /// Sends data on the socket to the remote address to which it is connected. - /// - /// The [`connect`] method will connect this socket to a remote address. - /// This method will fail if the socket is not connected. + /// Sends data on the socket to the given address. /// - /// [`connect`]: #method.connect + /// On success, returns the number of bytes written. /// /// # Examples /// @@ -300,11 +297,12 @@ impl UdpSocket { /// # /// use async_std::net::UdpSocket; /// - /// let socket = UdpSocket::bind("127.0.0.1:7878").await?; + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// socket.connect("127.0.0.1:8080").await?; - /// let bytes = socket.send(b"Hi there!").await?; /// - /// println!("Sent {} bytes", bytes); + /// let mut buf = vec![0; 1024]; + /// let n = socket.recv(&mut buf).await?; + /// println!("Received {} bytes", n); /// # /// # Ok(()) }) } /// ``` From 0a528647649971453aa5a724023a2cba84638418 Mon Sep 17 00:00:00 2001 From: noah Date: Tue, 14 Jan 2020 10:59:17 -0600 Subject: [PATCH 401/707] Revert "Fixes https://github.com/async-rs/async-std/issues/652" This reverts commit a4f68066 --- src/net/udp/mod.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 1b6d0d741..e3bd71461 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -244,9 +244,12 @@ impl UdpSocket { })) } - /// Sends data on the socket to the given address. + /// Sends data on the socket to the remote address to which it is connected. /// - /// On success, returns the number of bytes written. + /// The [`connect`] method will connect this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// [`connect`]: #method.connect /// /// # Examples /// @@ -255,18 +258,11 @@ impl UdpSocket { /// # /// use async_std::net::UdpSocket; /// - /// const THE_MERCHANT_OF_VENICE: &[u8] = b" - /// If you prick us, do we not bleed? - /// If you tickle us, do we not laugh? - /// If you poison us, do we not die? - /// And if you wrong us, shall we not revenge? - /// "; - /// - /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// let bytes = socket.send(b"Hi there!").await?; /// - /// let addr = "127.0.0.1:7878"; - /// let sent = socket.send_to(THE_MERCHANT_OF_VENICE, &addr).await?; - /// println!("Sent {} bytes to {}", sent, addr); + /// println!("Sent {} bytes", bytes); /// # /// # Ok(()) }) } /// ``` From ee102dfc9e6d76593527524ecc36cfbbdb14f32d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 15 Jan 2020 10:41:39 +0900 Subject: [PATCH 402/707] docs: Add stream::timeout example when timeout error --- src/stream/stream/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4cf88c7b2..6c84ac2e3 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1645,6 +1645,13 @@ extension_trait! { while let Some(v) = s.next().await { assert_eq!(v, Ok(1)); } + + // 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(()) }) } ``` From b72dd83726f89a740f393389ff4c9ef0c4791ec4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 15 Jan 2020 11:00:03 +0900 Subject: [PATCH 403/707] update async-task to 1.2.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 63d599a5e..339db1fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ std = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } -async-task = { version = "1.0.0", optional = true } +async-task = { version = "1.2.1", 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 } From 134089af2c711e3786beb4174e0f1592d9de7752 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Wed, 15 Jan 2020 08:55:18 +0300 Subject: [PATCH 404/707] Use `filter_map(identity)` + other fixes --- src/option/from_stream.rs | 3 ++- src/option/product.rs | 3 ++- src/option/sum.rs | 5 +++-- src/result/from_stream.rs | 2 +- src/result/product.rs | 28 +++++++++++++++------------- src/result/sum.rs | 26 ++++++++++++++------------ src/unit/from_stream.rs | 2 +- src/utils.rs | 8 ++++++++ 8 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index f0aafea6b..2194f2944 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -2,6 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{FromStream, IntoStream}; +use crate::utils::identity; impl FromStream> for Option where @@ -28,7 +29,7 @@ where false } }) - .map(Option::unwrap) + .filter_map(identity) .collect() .await; diff --git a/src/option/product.rs b/src/option/product.rs index 2238a9101..f733befc9 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -2,6 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{Product, Stream}; +use crate::utils::identity; impl Product> for Option where @@ -52,7 +53,7 @@ where false } }) - .map(Option::unwrap), + .filter_map(identity), ) .await; diff --git a/src/option/sum.rs b/src/option/sum.rs index 0570a1e15..64fdb0735 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -2,6 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{Stream, Sum}; +use crate::utils::identity; impl Sum> for Option where @@ -43,11 +44,11 @@ where .take_while(|elem| { elem.is_some() || { found_none = true; - // Stop processing the stream on error + // Stop processing the stream on `None` false } }) - .map(Option::unwrap), + .filter_map(identity), ) .await; diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index a8490d691..424a53b96 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -21,7 +21,7 @@ where // if a failure occurs let mut found_error = None; let out: V = stream - .scan((), |_, elem| { + .scan((), |(), elem| { match elem { Ok(elem) => Some(elem), Err(err) => { diff --git a/src/result/product.rs b/src/result/product.rs index ec9d94a80..ad2e0149b 100644 --- a/src/result/product.rs +++ b/src/result/product.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Stream, Product}; +use crate::stream::{Product, Stream}; impl Product> for Result where @@ -36,26 +36,28 @@ where ``` "#] fn product<'a, S>(stream: S) -> Pin> + 'a>> - where S: Stream> + 'a + where + S: Stream> + 'a, { Box::pin(async move { // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; - let out = >::product(stream - .scan((), |_, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - found_error = Some(err); - // Stop processing the stream on error - None - } + let out = >::product(stream.scan((), |(), elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None } - })).await; + } + })) + .await; + match found_error { Some(err) => Err(err), - None => Ok(out) + None => Ok(out), } }) } diff --git a/src/result/sum.rs b/src/result/sum.rs index ccc4240e0..1cf5b7afb 100644 --- a/src/result/sum.rs +++ b/src/result/sum.rs @@ -36,26 +36,28 @@ where ``` "#] fn sum<'a, S>(stream: S) -> Pin> + 'a>> - where S: Stream> + 'a + where + S: Stream> + 'a, { Box::pin(async move { // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; - let out = >::sum(stream - .scan((), |_, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - found_error = Some(err); - // Stop processing the stream on error - None - } + let out = >::sum(stream.scan((), |(), elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None } - })).await; + } + })) + .await; + match found_error { Some(err) => Err(err), - None => Ok(out) + None => Ok(out), } }) } diff --git a/src/unit/from_stream.rs b/src/unit/from_stream.rs index da216e22c..7b784383d 100644 --- a/src/unit/from_stream.rs +++ b/src/unit/from_stream.rs @@ -8,6 +8,6 @@ impl FromStream<()> for () { fn from_stream<'a, S: IntoStream + 'a>( stream: S, ) -> Pin + 'a>> { - Box::pin(stream.into_stream().for_each(|_| ())) + Box::pin(stream.into_stream().for_each(drop)) } } diff --git a/src/utils.rs b/src/utils.rs index ef50ed028..780e733a8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -52,6 +52,14 @@ pub fn random(n: u32) -> u32 { }) } +/// Returns given argument without changes. +#[allow(dead_code)] +#[doc(hidden)] +#[inline(always)] +pub(crate) fn identity(arg: T) -> T { + arg +} + /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; From 38de0bfd2267cb299952046f129eb6cc896bde9a Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Wed, 15 Jan 2020 09:42:30 +0300 Subject: [PATCH 405/707] Use `std::convert::identity` --- src/option/from_stream.rs | 8 ++------ src/option/product.rs | 8 ++------ src/option/sum.rs | 8 ++------ src/utils.rs | 8 -------- 4 files changed, 6 insertions(+), 26 deletions(-) diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index 2194f2944..de929ca94 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -2,7 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{FromStream, IntoStream}; -use crate::utils::identity; +use std::convert::identity; impl FromStream> for Option where @@ -33,11 +33,7 @@ where .collect() .await; - if found_none { - None - } else { - Some(out) - } + if found_none { None } else { Some(out) } }) } } diff --git a/src/option/product.rs b/src/option/product.rs index f733befc9..b446c1ffe 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -2,7 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{Product, Stream}; -use crate::utils::identity; +use std::convert::identity; impl Product> for Option where @@ -57,11 +57,7 @@ where ) .await; - if found_none { - None - } else { - Some(out) - } + if found_none { None } else { Some(out) } }) } } diff --git a/src/option/sum.rs b/src/option/sum.rs index 64fdb0735..de404f42d 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -2,7 +2,7 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{Stream, Sum}; -use crate::utils::identity; +use std::convert::identity; impl Sum> for Option where @@ -52,11 +52,7 @@ where ) .await; - if found_none { - None - } else { - Some(out) - } + if found_none { None } else { Some(out) } }) } } diff --git a/src/utils.rs b/src/utils.rs index 780e733a8..ef50ed028 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -52,14 +52,6 @@ pub fn random(n: u32) -> u32 { }) } -/// Returns given argument without changes. -#[allow(dead_code)] -#[doc(hidden)] -#[inline(always)] -pub(crate) fn identity(arg: T) -> T { - arg -} - /// Add additional context to errors pub(crate) trait Context { fn context(self, message: impl Fn() -> String) -> Self; From ed248017b476f17334260db31daf82cc07c2a465 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Wed, 15 Jan 2020 12:06:50 +0300 Subject: [PATCH 406/707] Use internal `scan` state in `Result`s implementation --- src/result/from_stream.rs | 4 ++-- src/result/product.rs | 4 ++-- src/result/sum.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 424a53b96..86405ce01 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -21,11 +21,11 @@ where // if a failure occurs let mut found_error = None; let out: V = stream - .scan((), |(), elem| { + .scan(&mut found_error, |error, elem| { match elem { Ok(elem) => Some(elem), Err(err) => { - found_error = Some(err); + **error = Some(err); // Stop processing the stream on error None } diff --git a/src/result/product.rs b/src/result/product.rs index ad2e0149b..89ddacb97 100644 --- a/src/result/product.rs +++ b/src/result/product.rs @@ -43,11 +43,11 @@ where // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; - let out = >::product(stream.scan((), |(), elem| { + let out = >::product(stream.scan(&mut found_error, |error, elem| { match elem { Ok(elem) => Some(elem), Err(err) => { - found_error = Some(err); + **error = Some(err); // Stop processing the stream on error None } diff --git a/src/result/sum.rs b/src/result/sum.rs index 1cf5b7afb..c0ef4c26f 100644 --- a/src/result/sum.rs +++ b/src/result/sum.rs @@ -43,11 +43,11 @@ where // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; - let out = >::sum(stream.scan((), |(), elem| { + let out = >::sum(stream.scan(&mut found_error, |error, elem| { match elem { Ok(elem) => Some(elem), Err(err) => { - found_error = Some(err); + **error = Some(err); // Stop processing the stream on error None } From d283352a9add80f722c272238c6f9e122976aedb Mon Sep 17 00:00:00 2001 From: nasa Date: Fri, 17 Jan 2020 03:07:11 +0900 Subject: [PATCH 407/707] update broadcastor to 1.0.0 (#681) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 65541db44..b1155bb4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ std = [ [dependencies] async-attributes = { version = "1.1.1", optional = true } async-task = { version = "1.2.1", optional = true } -broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } +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 } From ed7ddacb28f1cb3a5c4b7b2a92afa6003dc320ef Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 17 Jan 2020 17:13:52 +0300 Subject: [PATCH 408/707] Rewrote `Result`s implementation using `take_while` and `filter_map` --- src/result/from_stream.rs | 32 ++++++++++++++++++++------------ src/result/product.rs | 39 +++++++++++++++++++++++++-------------- src/result/sum.rs | 39 +++++++++++++++++++++++++-------------- 3 files changed, 70 insertions(+), 40 deletions(-) diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 86405ce01..8ce4b8534 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -17,26 +17,34 @@ where let stream = stream.into_stream(); Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs + let mut is_error = false; let mut found_error = None; let out: V = stream - .scan(&mut found_error, |error, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - **error = Some(err); - // Stop processing the stream on error - None - } + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None } }) .collect() .await; - match found_error { - Some(err) => Err(err), - None => Ok(out), + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) } }) } diff --git a/src/result/product.rs b/src/result/product.rs index 89ddacb97..45782ff70 100644 --- a/src/result/product.rs +++ b/src/result/product.rs @@ -40,24 +40,35 @@ where S: Stream> + 'a, { Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs + let mut is_error = false; let mut found_error = None; - let out = >::product(stream.scan(&mut found_error, |error, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - **error = Some(err); - // Stop processing the stream on error - None - } - } - })) + let out = >::product( + stream + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None + } + }), + ) .await; - match found_error { - Some(err) => Err(err), - None => Ok(out), + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) } }) } diff --git a/src/result/sum.rs b/src/result/sum.rs index c0ef4c26f..b6d84a0c4 100644 --- a/src/result/sum.rs +++ b/src/result/sum.rs @@ -40,24 +40,35 @@ where S: Stream> + 'a, { Box::pin(async move { - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs + let mut is_error = false; let mut found_error = None; - let out = >::sum(stream.scan(&mut found_error, |error, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - **error = Some(err); - // Stop processing the stream on error - None - } - } - })) + let out = >::sum( + stream + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None + } + }), + ) .await; - match found_error { - Some(err) => Err(err), - None => Ok(out), + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) } }) } From 2221441a4c40ce5f4117d92d34f986d93a71a66b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 18 Jan 2020 08:36:54 +0900 Subject: [PATCH 409/707] feat: Implement Clone trait for DirEntry --- src/fs/dir_entry.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 527fab42e..e1622eec6 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -158,6 +158,12 @@ impl fmt::Debug for DirEntry { } } +impl Clone for DirEntry { + fn clone(&self) -> Self { + DirEntry(self.0.clone()) + } +} + cfg_unix! { use crate::os::unix::fs::DirEntryExt; From 81aa6d152a6be24f1ba19e63ad78318315a67c07 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 20 Jan 2020 20:40:01 +0100 Subject: [PATCH 410/707] Changing task::block_on to park after a single poll (#684) This was previously discussed in #605 and others as a source of high CPU load when sleeping tasks because of the overhead created by retrying a future in short succession. --- src/task/block_on.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 80259c579..a994ee7d3 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -3,7 +3,6 @@ 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; @@ -125,7 +124,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 +131,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(); } }) } From 6b860c370a182803bd39cc37867dcb8bafa5810e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 21 Jan 2020 05:41:48 +0900 Subject: [PATCH 411/707] Remove usage of unstable format_code_in_doc_comments option (#685) --- rustfmt.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rustfmt.toml b/rustfmt.toml index c6d404e21..1082fd888 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1 @@ version = "Two" -format_code_in_doc_comments = true From f9fe5c90cf704887e78b0487cfd44bc5f7d10a11 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 22 Jan 2020 12:47:18 +0100 Subject: [PATCH 412/707] Fix some typos in accept-loop pattern chapter --- docs/src/patterns/accept-loop.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md index 46d6cc67b..43c3f41d2 100644 --- a/docs/src/patterns/accept-loop.md +++ b/docs/src/patterns/accept-loop.md @@ -1,6 +1,6 @@ # Production-Ready Accept Loop -Production-ready accept loop needs the following things: +A production-ready accept loop needs the following things: 1. Handling errors 2. Limiting the number of simultanteous connections to avoid deny-of-service (DoS) attacks @@ -8,15 +8,15 @@ Production-ready accept loop needs the following things: ## Handling errors -There are two kinds of errors in accept loop: -1. Per-connection errors. System uses them to notify that there was a - connection in the queue and it's dropped by peer. Subsequent connection +There are two kinds of errors in an accept loop: +1. Per-connection errors. The system uses them to notify that there was a + connection in the queue and it's dropped by the peer. Subsequent connections can be already queued so next connection must be accepted immediately. 2. Resource shortages. When these are encountered it doesn't make sense to - accept next socket immediately. But listener stays active, so you server + accept the next socket immediately. But the listener stays active, so you server should try to accept socket later. -Here is the example of per-connection error (printed in normal and debug mode): +Here is the example of a per-connection error (printed in normal and debug mode): ``` Error: Connection reset by peer (os error 104) Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" } @@ -30,10 +30,10 @@ Error: Os { code: 24, kind: Other, message: "Too many open files" } ### Testing Application -To test your application on these errors try the following (this works +To test your application for these errors try the following (this works on unixes only). -Lower limit and start the application: +Lower limits and start the application: ``` $ ulimit -n 100 $ cargo run --example your_app @@ -42,7 +42,7 @@ $ cargo run --example your_app Running `target/debug/examples/your_app` Server is listening on: http://127.0.0.1:1234 ``` -Then in another console run [`wrk`] benchmark tool: +Then in another console run the [`wrk`] benchmark tool: ``` $ wrk -c 1000 http://127.0.0.1:1234 Running 10s test @ http://localhost:8080/ @@ -54,13 +54,13 @@ Connected to localhost. Important is to check the following things: -1. Application doesn't crash on error (but may log errors, see below) +1. The application doesn't crash on error (but may log errors, see below) 2. It's possible to connect to the application again once load is stopped (few seconds after `wrk`). This is what `telnet` does in example above, make sure it prints `Connected to `. 3. The `Too many open files` error is logged in the appropriate log. This requires to set "maximum number of simultaneous connections" parameter (see - below) of your application to a value greater that `100` for this example. + below) of your application to a value greater then `100` for this example. 4. Check CPU usage of the app while doing a test. It should not occupy 100% of a single CPU core (it's unlikely that you can exhaust CPU by 1000 connections in Rust, so this means error handling is not right). @@ -68,12 +68,12 @@ Important is to check the following things: #### Testing non-HTTP applications If it's possible, use the appropriate benchmark tool and set the appropriate -number of connections. For example `redis-benchmark` has `-c` parameter for +number of connections. For example `redis-benchmark` has a `-c` parameter for that, if you implement redis protocol. Alternatively, can still use `wrk`, just make sure that connection is not immediately closed. If it is, put a temporary timeout before handing -connection to the protocol handler, like this: +the connection to the protocol handler, like this: ```rust,edition2018 # extern crate async_std; @@ -147,7 +147,7 @@ Be sure to [test your application](#testing-application). ### External Crates -The crate [`async-listen`] have a helper to achieve this task: +The crate [`async-listen`] has a helper to achieve this task: ```rust,edition2018 # extern crate async_std; # extern crate async_listen; @@ -200,7 +200,7 @@ Even if you've applied everything described in Let's imagine you have a server that needs to open a file to process client request. At some point, you might encounter the following situation: -1. There are as much client connection as max file descriptors allowed for +1. There are as many client connection as max file descriptors allowed for the application. 2. Listener gets `Too many open files` error so it sleeps. 3. Some client sends a request via the previously open connection. @@ -257,7 +257,7 @@ async fn connection_loop(_token: &Token, stream: TcpStream) { // 4 stream of `TcpStream` rather than `Result`. 2. The token yielded by a new stream is what is counted by backpressure helper. I.e. if you drop a token, new connection can be established. -3. We give connection loop a reference to token to bind token's lifetime to +3. We give the connection loop a reference to token to bind token's lifetime to the lifetime of the connection. 4. The token itsellf in the function can be ignored, hence `_token` From b258215952b5819c08b05c838c285395b5f88ebd Mon Sep 17 00:00:00 2001 From: ninj Date: Sat, 25 Jan 2020 22:13:26 +0000 Subject: [PATCH 413/707] fix syntax problem for task::sleep --- 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 43c3f41d2..4508baf47 100644 --- a/docs/src/patterns/accept-loop.md +++ b/docs/src/patterns/accept-loop.md @@ -90,7 +90,7 @@ the connection to the protocol handler, like this: # let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { task::spawn(async { - task:sleep(Duration::from_secs(10)).await; // 1 + task::sleep(Duration::from_secs(10)).await; // 1 connection_loop(stream).await; }); } From 57974ae0b7be220d9e96e0262fd8bd7d8d1e851d Mon Sep 17 00:00:00 2001 From: Toralf Wittner Date: Mon, 27 Jan 2020 23:13:13 +0100 Subject: [PATCH 414/707] Use non-blocking connect for TcpStream. (#687) * Use non-blocking connect for TcpStream. Instead of spawning a background thread which is unaware of any timeouts but continues to run until the TCP stack decides that the remote is not reachable we use mio's non-blocking connect. mio's `TcpStream::connect` returns immediately but the actual connection is usually just in progress and we have to be sure the socket is writeable before we can consider the connection as established. * Add Watcher::{poll_read_ready, poll_write_ready}. Following a suggestion of @stjepang we offer methods to check for read/write readiness of a `Watcher` instead of the previous approach to accept a set of `Waker`s when registering an event source. The changes relative to master are smaller and both methods look more useful in other contexts. Also the code is more robust w.r.t. wakeups of the `Waker` from clones outside the `Reactor`. I am not sure if we need to add protection mechanisms against spurious wakeups from mio. Currently we treat the `Poll::Ready(())` of `Watcher::poll_write_ready` as proof that the non-blocking connect has finished, but if the event from mio was a spurious one, it might still be ongoing. --- src/net/driver/mod.rs | 89 +++++++++++++++++++++++++++++++++++++------ src/net/tcp/stream.rs | 33 ++++++++-------- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 7f33e8594..07ef2c7d2 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -16,10 +16,30 @@ struct Entry { token: mio::Token, /// Tasks that are blocked on reading from this I/O handle. - readers: Mutex>, + readers: Mutex, /// Thasks that are blocked on writing to this I/O handle. - writers: Mutex>, + writers: Mutex, +} + +/// 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 } /// The state of a networking driver. @@ -68,8 +88,8 @@ impl Reactor { // Allocate an entry and insert it into the slab. let entry = Arc::new(Entry { token, - readers: Mutex::new(Vec::new()), - writers: Mutex::new(Vec::new()), + readers: Mutex::new(Readers { ready: false, wakers: Vec::new() }), + writers: Mutex::new(Writers { ready: false, wakers: Vec::new() }), }); vacant.insert(entry.clone()); @@ -144,14 +164,18 @@ fn main_loop() -> io::Result<()> { // Wake up reader tasks blocked on this I/O handle. if !(readiness & reader_interests()).is_empty() { - for w in entry.readers.lock().unwrap().drain(..) { + let mut readers = entry.readers.lock().unwrap(); + readers.ready = true; + for w in readers.wakers.drain(..) { w.wake(); } } // Wake up writer tasks blocked on this I/O handle. if !(readiness & writer_interests()).is_empty() { - for w in entry.writers.lock().unwrap().drain(..) { + let mut writers = entry.writers.lock().unwrap(); + writers.ready = true; + for w in writers.wakers.drain(..) { w.wake(); } } @@ -207,7 +231,7 @@ impl Watcher { } // Lock the waker list. - let mut list = self.entry.readers.lock().unwrap(); + let mut readers = self.entry.readers.lock().unwrap(); // Try running the operation again. match f(self.source.as_ref().unwrap()) { @@ -216,10 +240,12 @@ impl Watcher { } // Register the task if it isn't registered already. - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); + if readers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { + readers.wakers.push(cx.waker().clone()); } + readers.ready = false; + Poll::Pending } @@ -242,7 +268,7 @@ impl Watcher { } // Lock the waker list. - let mut list = self.entry.writers.lock().unwrap(); + let mut writers = self.entry.writers.lock().unwrap(); // Try running the operation again. match f(self.source.as_ref().unwrap()) { @@ -251,10 +277,49 @@ impl Watcher { } // Register the task if it isn't registered already. - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); + if writers.wakers.iter().all(|w| !w.will_wake(cx.waker())) { + writers.wakers.push(cx.waker().clone()); } + writers.ready = false; + + 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 } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index ae8ca7dc8..537bd4cdc 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -6,8 +6,7 @@ use crate::future; use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; -use crate::task::{spawn_blocking, Context, Poll}; -use crate::utils::Context as _; +use crate::task::{Context, Poll}; /// A TCP stream between a local and a remote socket. /// @@ -77,20 +76,24 @@ impl TcpStream { .await?; for addr in addrs { - let res = spawn_blocking(move || { - let std_stream = std::net::TcpStream::connect(addr) - .context(|| format!("could not connect to {}", addr))?; - let mio_stream = mio::net::TcpStream::from_stream(std_stream) - .context(|| format!("could not open async connection to {}", addr))?; - Ok(TcpStream { - watcher: Watcher::new(mio_stream), - }) - }) - .await; + // 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), + Err(e) => { + last_err = Some(e); + continue + } + }; - match res { - Ok(stream) => return Ok(stream), - Err(err) => last_err = Some(err), + future::poll_fn(|cx| watcher.poll_write_ready(cx)).await; + + match watcher.get_ref().take_error() { + Ok(None) => return Ok(TcpStream { watcher }), + Ok(Some(e)) => last_err = Some(e), + Err(e) => last_err = Some(e) } } From 4996f297788542074d6fe37675d059a70cad85b8 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 10:30:27 +0900 Subject: [PATCH 415/707] feat: Add no-std feature --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b1155bb4e..fbcf5f4c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,9 @@ std = [ "pin-project-lite", "pin-utils", "slab", + "no-std", ] +no-std = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } From 51b84a7620823d8b1c081d0d1d994e95b2001f17 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 10:31:27 +0900 Subject: [PATCH 416/707] feat: Add no_std attribute when not std feature --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c9d127713..237d73f68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,6 +221,7 @@ //! features = ["std"] //! ``` +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![allow(clippy::mutex_atomic, clippy::module_inception)] From 3d32fd81f4a2993086c97da6dad8a529c7c597f4 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 10:33:00 +0900 Subject: [PATCH 417/707] feat: Make the utils module no_std --- src/utils.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index ef50ed028..ccf5e84af 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,9 @@ +#[cfg(feature = "no-std")] +extern crate alloc; + +#[cfg(feature = "no-std")] +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. @@ -104,6 +110,7 @@ macro_rules! cfg_unstable_default { /// Declares Unix-specific items. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_unix { ($($item:item)*) => { $( @@ -116,6 +123,7 @@ macro_rules! cfg_unix { /// Declares Windows-specific items. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_windows { ($($item:item)*) => { $( @@ -128,6 +136,7 @@ macro_rules! cfg_windows { /// Declares items when the "docs" feature is enabled. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_docs { ($($item:item)*) => { $( @@ -139,6 +148,7 @@ macro_rules! cfg_docs { /// Declares items when the "docs" feature is disabled. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_not_docs { ($($item:item)*) => { $( @@ -160,6 +170,18 @@ macro_rules! cfg_std { } } +/// Declares no-std items. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_no_std { + ($($item:item)*) => { + $( + #[cfg(feature = "no-std")] + $item + )* + } +} + /// Declares default items. #[allow(unused_macros)] #[doc(hidden)] @@ -180,6 +202,7 @@ macro_rules! cfg_default { /// /// 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 { ( @@ -204,14 +227,14 @@ macro_rules! extension_trait { #[allow(dead_code)] mod owned { #[doc(hidden)] - pub struct ImplFuture(std::marker::PhantomData); + 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>(std::marker::PhantomData<&'a T>); + 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. From 41f114d9fe17a9a0822570bfa4f90365b35f5d66 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 10:37:22 +0900 Subject: [PATCH 418/707] ci: Add no-std check --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bee2562a8..0b0e84fed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: with: command: check args: --features unstable --all --bins --examples --tests + - name: check bench uses: actions-rs/cargo@v1 if: matrix.rust == 'nightly' @@ -53,6 +54,12 @@ jobs: command: check args: --no-default-features --features std + - name: check no_std + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features --features no-std + - name: check attributes uses: actions-rs/cargo@v1 with: From 6aa55fde59bea45c1e5e7184782c882a4259813f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 11:18:09 +0900 Subject: [PATCH 419/707] feat: Make the task module no_std --- Cargo.toml | 5 +++-- src/lib.rs | 5 ++++- src/task/mod.rs | 11 +++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fbcf5f4c5..44920489a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ unstable = ["std", "broadcaster", "futures-timer"] attributes = ["async-attributes"] std = [ "crossbeam-utils", - "futures-core", "futures-io", "memchr", "once_cell", @@ -48,7 +47,9 @@ std = [ "slab", "no-std", ] -no-std = [] +no-std = [ + "futures-core", +] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 237d73f68..fc0755d28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,6 +241,10 @@ pub use async_attributes::{main, test}; #[cfg(feature = "std")] mod macros; +cfg_no_std! { + pub mod task; +} + cfg_std! { pub mod future; pub mod io; @@ -248,7 +252,6 @@ cfg_std! { pub mod prelude; pub mod stream; pub mod sync; - pub mod task; } cfg_default! { diff --git a/src/task/mod.rs b/src/task/mod.rs index f738fca40..680e2a5ce 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -117,13 +117,16 @@ //! [`task_local!`]: ../macro.task_local.html //! [`with`]: struct.LocalKey.html#method.with -cfg_std! { +cfg_no_std! { #[doc(inline)] - pub use std::task::{Context, Poll, Waker}; - + pub use core::task::{Context, Poll, Waker}; pub use ready::ready; - pub use yield_now::yield_now; + mod ready; +} + +cfg_std! { + pub use yield_now::yield_now; mod yield_now; } From 1762de285b8bd96eed278c6c0bf8b13edc5d0b62 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 15:40:28 +0900 Subject: [PATCH 420/707] feat: Make the future module no_std --- src/future/future/mod.rs | 6 +++--- src/future/mod.rs | 21 +++++++++++++-------- src/lib.rs | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs index d610096b8..24f3fb599 100644 --- a/src/future/future/mod.rs +++ b/src/future/future/mod.rs @@ -21,8 +21,8 @@ cfg_unstable_default! { } extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; + use core::pin::Pin; + use core::ops::{Deref, DerefMut}; use crate::task::{Context, Poll}; @@ -136,7 +136,7 @@ extension_trait! { [`Future`]: ../future/trait.Future.html "#] - pub trait FutureExt: std::future::Future { + pub trait FutureExt: core::future::Future { /// Returns a Future that delays execution for a specified time. /// /// # Examples diff --git a/src/future/mod.rs b/src/future/mod.rs index 993627652..3d325be83 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -46,15 +46,20 @@ //! [`Future::race`]: trait.Future.html#method.race //! [`Future::try_race`]: trait.Future.html#method.try_race -pub use future::Future; -pub use pending::pending; -pub use poll_fn::poll_fn; -pub use ready::ready; +cfg_no_std! { + pub use future::Future; + pub(crate) mod future; +} + +cfg_std! { + pub use pending::pending; + pub use poll_fn::poll_fn; + pub use ready::ready; -pub(crate) mod future; -mod pending; -mod poll_fn; -mod ready; + mod pending; + mod poll_fn; + mod ready; +} cfg_default! { pub use timeout::{timeout, TimeoutError}; diff --git a/src/lib.rs b/src/lib.rs index fc0755d28..6fe13e659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -243,10 +243,10 @@ mod macros; cfg_no_std! { pub mod task; + pub mod future; } cfg_std! { - pub mod future; pub mod io; pub mod os; pub mod prelude; From 880b7ee987576f24a6dceb955a96365479ae8101 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 20:34:51 +0900 Subject: [PATCH 421/707] remove crate::prelude import --- src/stream/extend.rs | 2 +- src/stream/interval.rs | 4 ++-- src/stream/stream/chain.rs | 3 ++- src/stream/stream/cmp.rs | 4 ++-- src/stream/stream/eq.rs | 4 ++-- src/stream/stream/flat_map.rs | 2 +- src/stream/stream/ge.rs | 4 ++-- src/stream/stream/gt.rs | 4 ++-- src/stream/stream/le.rs | 4 ++-- src/stream/stream/lt.rs | 4 ++-- src/stream/stream/merge.rs | 3 ++- src/stream/stream/ne.rs | 4 ++-- src/stream/stream/partial_cmp.rs | 2 +- 13 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/stream/extend.rs b/src/stream/extend.rs index c48fe1ed8..fab8e6ed5 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use crate::prelude::*; +use crate::future::Future; use crate::stream::IntoStream; /// Extends a collection with the contents of a stream. diff --git a/src/stream/interval.rs b/src/stream/interval.rs index f8a6ef64e..7a0c1740b 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -2,10 +2,10 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; +use crate::future::Future; +use crate::stream::Stream; use futures_timer::Delay; -use crate::prelude::*; - /// Creates a new stream that yields at a set interval. /// /// The stream first yields after `dur`, and continues to yield every diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 909fc19b1..1d62f386a 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -3,7 +3,8 @@ use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; +use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index 2be0c1a36..191219d50 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,11 +1,11 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index 58ccc90e3..54745af2d 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -1,10 +1,10 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index 9a7d921de..cc0f665ce 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -2,8 +2,8 @@ use std::pin::Pin; use pin_project_lite::pin_project; -use crate::prelude::*; use crate::stream::stream::map::Map; +use crate::stream::stream::StreamExt; use crate::stream::{IntoStream, Stream}; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index 67b20bed9..50ea21dd0 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,11 +1,11 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 1c1218910..d42b8d462 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,11 +1,11 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 7b86161c6..9ee44cb71 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,11 +1,11 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index 100a00342..4364ffea3 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,11 +1,11 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 84ac43229..ad697a809 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -3,8 +3,9 @@ use std::task::{Context, Poll}; use pin_project_lite::pin_project; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Fuse; +use crate::stream::Stream; use crate::utils; pin_project! { diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs index ec11d1fdc..7cad3b75f 100644 --- a/src/stream/stream/ne.rs +++ b/src/stream/stream/ne.rs @@ -1,10 +1,10 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index 85587c999..247413b8c 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -5,7 +5,7 @@ use std::pin::Pin; use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; From d622ec5d357ce8797fecdd480479ca92560e6c41 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 21:01:47 +0900 Subject: [PATCH 422/707] feat: Make the stream module no_std --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/stream/double_ended_stream/next_back.rs | 4 ++-- src/stream/double_ended_stream/nth_back.rs | 6 +++--- src/stream/double_ended_stream/rfind.rs | 6 +++--- src/stream/double_ended_stream/rfold.rs | 6 +++--- src/stream/double_ended_stream/try_rfold.rs | 2 +- src/stream/empty.rs | 4 ++-- src/stream/extend.rs | 2 +- src/stream/from_fn.rs | 2 +- src/stream/from_iter.rs | 2 +- src/stream/from_stream.rs | 4 ++-- src/stream/once.rs | 2 +- src/stream/pending.rs | 6 +++--- src/stream/product.rs | 4 ++-- src/stream/repeat.rs | 2 +- src/stream/repeat_with.rs | 2 +- src/stream/stream/all.rs | 6 +++--- src/stream/stream/any.rs | 6 +++--- src/stream/stream/chain.rs | 2 +- src/stream/stream/cloned.rs | 2 +- src/stream/stream/cmp.rs | 6 +++--- src/stream/stream/copied.rs | 2 +- src/stream/stream/count.rs | 4 ++-- src/stream/stream/cycle.rs | 4 ++-- src/stream/stream/delay.rs | 6 +++--- src/stream/stream/enumerate.rs | 2 +- src/stream/stream/eq.rs | 4 ++-- src/stream/stream/filter.rs | 2 +- src/stream/stream/filter_map.rs | 4 ++-- src/stream/stream/find.rs | 4 ++-- src/stream/stream/find_map.rs | 6 +++--- src/stream/stream/flat_map.rs | 2 +- src/stream/stream/flatten.rs | 4 ++-- src/stream/stream/fold.rs | 4 ++-- src/stream/stream/for_each.rs | 4 ++-- src/stream/stream/fuse.rs | 2 +- src/stream/stream/ge.rs | 6 +++--- src/stream/stream/gt.rs | 6 +++--- src/stream/stream/inspect.rs | 2 +- src/stream/stream/last.rs | 4 ++-- src/stream/stream/le.rs | 6 +++--- src/stream/stream/lt.rs | 6 +++--- src/stream/stream/map.rs | 2 +- src/stream/stream/max.rs | 8 ++++---- src/stream/stream/max_by.rs | 6 +++--- src/stream/stream/max_by_key.rs | 6 +++--- src/stream/stream/merge.rs | 4 ++-- src/stream/stream/min.rs | 8 ++++---- src/stream/stream/min_by.rs | 6 +++--- src/stream/stream/min_by_key.rs | 6 +++--- src/stream/stream/mod.rs | 8 ++++---- src/stream/stream/ne.rs | 4 ++-- src/stream/stream/next.rs | 4 ++-- src/stream/stream/nth.rs | 6 +++--- src/stream/stream/partial_cmp.rs | 6 +++--- src/stream/stream/partition.rs | 6 +++--- src/stream/stream/position.rs | 4 ++-- src/stream/stream/scan.rs | 2 +- src/stream/stream/skip.rs | 4 ++-- src/stream/stream/skip_while.rs | 2 +- src/stream/stream/step_by.rs | 2 +- src/stream/stream/take.rs | 2 +- src/stream/stream/take_while.rs | 2 +- src/stream/stream/timeout.rs | 2 +- src/stream/stream/try_fold.rs | 2 +- src/stream/stream/try_for_each.rs | 4 ++-- src/stream/stream/unzip.rs | 4 ++-- src/stream/stream/zip.rs | 4 ++-- src/stream/successors.rs | 4 ++-- src/stream/sum.rs | 4 ++-- 71 files changed, 143 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 44920489a..12061298e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,13 +42,13 @@ std = [ "futures-io", "memchr", "once_cell", - "pin-project-lite", "pin-utils", "slab", "no-std", ] no-std = [ "futures-core", + "pin-project-lite", ] [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 6fe13e659..f35d6cbce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -244,13 +244,13 @@ mod macros; cfg_no_std! { pub mod task; pub mod future; + pub mod stream; } cfg_std! { pub mod io; pub mod os; pub mod prelude; - pub mod stream; pub mod sync; } diff --git a/src/stream/double_ended_stream/next_back.rs b/src/stream/double_ended_stream/next_back.rs index aa642d094..9fb52b7b6 100644 --- a/src/stream/double_ended_stream/next_back.rs +++ b/src/stream/double_ended_stream/next_back.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::future::Future; +use core::pin::Pin; +use core::future::Future; use crate::stream::DoubleEndedStream; use crate::task::{Context, Poll}; diff --git a/src/stream/double_ended_stream/nth_back.rs b/src/stream/double_ended_stream/nth_back.rs index e32a28fd6..2701e9b69 100644 --- a/src/stream/double_ended_stream/nth_back.rs +++ b/src/stream/double_ended_stream/nth_back.rs @@ -1,6 +1,6 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; use crate::stream::DoubleEndedStream; diff --git a/src/stream/double_ended_stream/rfind.rs b/src/stream/double_ended_stream/rfind.rs index 947269342..366655169 100644 --- a/src/stream/double_ended_stream/rfind.rs +++ b/src/stream/double_ended_stream/rfind.rs @@ -1,6 +1,6 @@ -use std::task::{Context, Poll}; -use std::future::Future; -use std::pin::Pin; +use core::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; use crate::stream::DoubleEndedStream; diff --git a/src/stream/double_ended_stream/rfold.rs b/src/stream/double_ended_stream/rfold.rs index 9002f8d9e..9bc18783f 100644 --- a/src/stream/double_ended_stream/rfold.rs +++ b/src/stream/double_ended_stream/rfold.rs @@ -1,6 +1,6 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/src/stream/double_ended_stream/try_rfold.rs b/src/stream/double_ended_stream/try_rfold.rs index 9e6295a74..d67b6ecf8 100644 --- a/src/stream/double_ended_stream/try_rfold.rs +++ b/src/stream/double_ended_stream/try_rfold.rs @@ -1,5 +1,5 @@ use crate::future::Future; -use std::pin::Pin; +use core::pin::Pin; use crate::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/src/stream/empty.rs b/src/stream/empty.rs index 490907071..a11337af3 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -1,5 +1,5 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::marker::PhantomData; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/extend.rs b/src/stream/extend.rs index fab8e6ed5..dbf7911c7 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::future::Future; use crate::stream::IntoStream; diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 8067176e7..d3736454a 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs index 705d15048..ea2ed8efb 100644 --- a/src/stream/from_iter.rs +++ b/src/stream/from_iter.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 67b9b3df0..12d89e813 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use crate::stream::IntoStream; diff --git a/src/stream/once.rs b/src/stream/once.rs index 939722d9e..b86f181d9 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/pending.rs b/src/stream/pending.rs index 922a54030..edb6be4b1 100644 --- a/src/stream/pending.rs +++ b/src/stream/pending.rs @@ -1,6 +1,6 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; use crate::stream::{DoubleEndedStream, ExactSizeStream, FusedStream, Stream}; diff --git a/src/stream/product.rs b/src/stream/product.rs index 2f5bf4c39..15497e87c 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::future::Future; +use core::pin::Pin; +use core::future::Future; use crate::stream::Stream; diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index f3dfdbd85..e73a2f829 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index e183a77ca..39984a3a2 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index d2ac5cac8..06f4d7f80 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -1,6 +1,6 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index 34168e672..15154c506 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -1,6 +1,6 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 1d62f386a..c034a53e4 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs index 4c77c5c9e..eac992dd0 100644 --- a/src/stream/stream/cloned.rs +++ b/src/stream/stream/cloned.rs @@ -1,7 +1,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; use pin_project_lite::pin_project; -use std::pin::Pin; +use core::pin::Pin; pin_project! { /// A stream that clones the elements of an underlying stream. diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index 191219d50..9d2b0eccc 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs index 651c31b60..19296f5b9 100644 --- a/src/stream/stream/copied.rs +++ b/src/stream/stream/copied.rs @@ -1,7 +1,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; use pin_project_lite::pin_project; -use std::pin::Pin; +use core::pin::Pin; pin_project! { /// A stream that copies the elements of an underlying stream. diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 1ca8aef18..63e044977 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 5f8eaa205..ef46d1a77 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -1,5 +1,5 @@ -use std::mem::ManuallyDrop; -use std::pin::Pin; +use core::mem::ManuallyDrop; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs index 576879293..ff4c93738 100644 --- a/src/stream/stream/delay.rs +++ b/src/stream/stream/delay.rs @@ -1,6 +1,6 @@ -use std::future::Future; -use std::pin::Pin; -use std::time::Duration; +use core::future::Future; +use core::pin::Pin; +use core::time::Duration; use pin_project_lite::pin_project; diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index c4a37d6ed..093fefbda 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index 54745af2d..3d8307b0e 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 00344b0e9..2dc7dd486 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 3cd1e47a7..e43e8f09f 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index 4a0749b1a..8652ac095 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index c79494391..f7e3c1e04 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -1,6 +1,6 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; use crate::stream::Stream; diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index cc0f665ce..e07893a94 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index 1d6fcae6a..f2e275c29 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; diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index a346eb671..3938a3739 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index dce5cdae9..dbada101c 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::future::Future; +use core::pin::Pin; +use core::future::Future; use pin_project_lite::pin_project; diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index c7449c273..f3a059626 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index 50ea21dd0..1e1b70df5 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index d42b8d462..d58e7e9e6 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index bb39662b9..481810d72 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index d037efcb7..ebf1a484a 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 9ee44cb71..169f9eced 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index 4364ffea3..1851b8e4c 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 8e074a757..0eab3ce2b 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs index d8ff119d2..8a4d59447 100644 --- a/src/stream/stream/max.rs +++ b/src/stream/stream/max.rs @@ -1,7 +1,7 @@ -use std::cmp::{Ord, Ordering}; -use std::marker::PhantomData; -use std::pin::Pin; -use std::future::Future; +use core::cmp::{Ord, Ordering}; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; use pin_project_lite::pin_project; diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index 36b876bb4..8f986452d 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::pin::Pin; -use std::future::Future; +use core::cmp::Ordering; +use core::pin::Pin; +use core::future::Future; use pin_project_lite::pin_project; diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index e421f94aa..8fa91ab98 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index ad697a809..232097292 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs index 4ce52be9b..4fe2a6772 100644 --- a/src/stream/stream/min.rs +++ b/src/stream/stream/min.rs @@ -1,7 +1,7 @@ -use std::cmp::{Ord, Ordering}; -use std::marker::PhantomData; -use std::pin::Pin; -use std::future::Future; +use core::cmp::{Ord, Ordering}; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; use pin_project_lite::pin_project; diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index e35719e6d..fe1d40ea8 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::pin::Pin; -use std::future::Future; +use core::cmp::Ordering; +use core::pin::Pin; +use core::future::Future; use pin_project_lite::pin_project; diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 07c3642a9..549b7983b 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 6c84ac2e3..d0cc718e4 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -110,12 +110,12 @@ pub use take::Take; pub use take_while::TakeWhile; pub use zip::Zip; -use std::cmp::Ordering; +use core::cmp::Ordering; cfg_unstable! { - use std::future::Future; - use std::pin::Pin; - use std::time::Duration; + use core::future::Future; + use core::pin::Pin; + use core::time::Duration; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs index 7cad3b75f..c51ab31ef 100644 --- a/src/stream/stream/ne.rs +++ b/src/stream/stream/ne.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs index 23abb0b49..7bd208320 100644 --- a/src/stream/stream/next.rs +++ b/src/stream/stream/next.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::future::Future; +use core::pin::Pin; +use core::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index 267bd40a3..8cdabb6c4 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -1,6 +1,6 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::future::Future; use crate::stream::Stream; diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index 247413b8c..928a03b0f 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,6 +1,6 @@ -use std::cmp::Ordering; -use std::future::Future; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index aaf58ac09..69268ecb8 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -1,7 +1,7 @@ use pin_project_lite::pin_project; -use std::default::Default; -use std::future::Future; -use std::pin::Pin; +use core::default::Default; +use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs index df60eaae9..2811b6d86 100644 --- a/src/stream/stream/position.rs +++ b/src/stream/stream/position.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 385edf8ec..d72b0dc23 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index bcff50d6c..52b137d06 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 23347132a..d139de4d0 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index 2149cdade..3dd3d6259 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 8c8522766..ffc3e9935 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 2ba8490e4..60eb8c517 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index f580360de..ce658c83a 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -1,8 +1,8 @@ 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; diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index 3b92d95ab..73312ff78 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::future::Future; use crate::stream::Stream; diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 86f1674a3..917bfd16e 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index cb5757805..7771509a5 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 597691b4e..83d613c8c 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.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; diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 4421564e2..a9ce40ffe 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,5 +1,5 @@ -use std::mem; -use std::pin::Pin; +use core::mem; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/sum.rs b/src/stream/sum.rs index 9607bafa7..3b3144e5e 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,5 +1,5 @@ -use std::future::Future; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; use crate::stream::Stream; From 22d929d481b8748111a51907be1ca716a95c183c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 16 Jan 2020 21:13:23 +0900 Subject: [PATCH 423/707] fix import Future --- src/stream/extend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/extend.rs b/src/stream/extend.rs index dbf7911c7..702cbcac6 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -1,6 +1,6 @@ use core::pin::Pin; +use core::future::Future; -use crate::future::Future; use crate::stream::IntoStream; /// Extends a collection with the contents of a stream. From 7efe7caf66012ae6eb92e667f372f7a6ad6bd282 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 28 Jan 2020 15:58:06 +0900 Subject: [PATCH 424/707] fix: Change feature name no-std to alloc --- .github/workflows/ci.yml | 2 +- Cargo.toml | 4 ++-- src/future/mod.rs | 2 +- src/lib.rs | 2 +- src/task/mod.rs | 2 +- src/utils.rs | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b0e84fed..597c2b69b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --no-default-features --features no-std + args: --no-default-features --features alloc - name: check attributes uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index 12061298e..da6187ab7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,9 @@ std = [ "once_cell", "pin-utils", "slab", - "no-std", + "alloc", ] -no-std = [ +alloc = [ "futures-core", "pin-project-lite", ] diff --git a/src/future/mod.rs b/src/future/mod.rs index 3d325be83..9b75533d3 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -46,7 +46,7 @@ //! [`Future::race`]: trait.Future.html#method.race //! [`Future::try_race`]: trait.Future.html#method.try_race -cfg_no_std! { +cfg_alloc! { pub use future::Future; pub(crate) mod future; } diff --git a/src/lib.rs b/src/lib.rs index f35d6cbce..b5a3fff55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,7 @@ pub use async_attributes::{main, test}; #[cfg(feature = "std")] mod macros; -cfg_no_std! { +cfg_alloc! { pub mod task; pub mod future; pub mod stream; diff --git a/src/task/mod.rs b/src/task/mod.rs index 680e2a5ce..13fe9032d 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -117,7 +117,7 @@ //! [`task_local!`]: ../macro.task_local.html //! [`with`]: struct.LocalKey.html#method.with -cfg_no_std! { +cfg_alloc! { #[doc(inline)] pub use core::task::{Context, Poll, Waker}; pub use ready::ready; diff --git a/src/utils.rs b/src/utils.rs index ccf5e84af..e257d2f02 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "no-std")] +#[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "no-std")] +#[cfg(feature = "alloc")] use alloc::string::String; /// Calls a function and aborts if it panics. @@ -173,10 +173,10 @@ macro_rules! cfg_std { /// Declares no-std items. #[allow(unused_macros)] #[doc(hidden)] -macro_rules! cfg_no_std { +macro_rules! cfg_alloc { ($($item:item)*) => { $( - #[cfg(feature = "no-std")] + #[cfg(feature = "alloc")] $item )* } From 1d875836a2302681a395ee44512a518f0222da4a Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 28 Jan 2020 18:14:16 +0100 Subject: [PATCH 425/707] Implement Clone for TcpStream (#689) * Implement Clone for TcpStream * Update examples * Remove accidentally added examples --- examples/tcp-echo.rs | 5 +++-- examples/tcp-ipv4-and-6-echo.rs | 5 +++-- src/net/tcp/listener.rs | 7 +++---- src/net/tcp/stream.rs | 26 ++++++++++++++++---------- tests/tcp.rs | 22 ++++++++++++++++++++++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/examples/tcp-echo.rs b/examples/tcp-echo.rs index 7c50be016..c04f07765 100644 --- a/examples/tcp-echo.rs +++ b/examples/tcp-echo.rs @@ -14,8 +14,9 @@ use async_std::task; async fn process(stream: TcpStream) -> io::Result<()> { println!("Accepted from: {}", stream.peer_addr()?); - let (reader, writer) = &mut (&stream, &stream); - io::copy(reader, writer).await?; + let mut reader = stream.clone(); + let mut writer = stream; + io::copy(&mut reader, &mut writer).await?; Ok(()) } diff --git a/examples/tcp-ipv4-and-6-echo.rs b/examples/tcp-ipv4-and-6-echo.rs index aef5e15e5..a00e1f386 100644 --- a/examples/tcp-ipv4-and-6-echo.rs +++ b/examples/tcp-ipv4-and-6-echo.rs @@ -15,8 +15,9 @@ use async_std::task; async fn process(stream: TcpStream) -> io::Result<()> { println!("Accepted from: {}", stream.peer_addr()?); - let (reader, writer) = &mut (&stream, &stream); - io::copy(reader, writer).await?; + let mut reader = stream.clone(); + let mut writer = stream; + io::copy(&mut reader, &mut writer).await?; Ok(()) } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index fe06a96d6..1d7e91a27 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,6 +1,7 @@ use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; +use std::sync::Arc; use crate::future; use crate::io; @@ -75,9 +76,7 @@ impl TcpListener { /// [`local_addr`]: #method.local_addr 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::TcpListener::bind(&addr) { @@ -121,7 +120,7 @@ impl TcpListener { let mio_stream = mio::net::TcpStream::from_stream(io)?; let stream = TcpStream { - watcher: Watcher::new(mio_stream), + watcher: Arc::new(Watcher::new(mio_stream)), }; Ok((stream, addr)) } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 537bd4cdc..c9cdf5e6d 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,6 +1,7 @@ use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::net::SocketAddr; use std::pin::Pin; +use std::sync::Arc; use crate::future; use crate::io::{self, Read, Write}; @@ -44,9 +45,9 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TcpStream { - pub(super) watcher: Watcher, + pub(super) watcher: Arc>, } impl TcpStream { @@ -71,9 +72,7 @@ impl TcpStream { /// ``` pub async fn connect(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 { // mio's TcpStream::connect is non-blocking and may just be in progress @@ -84,16 +83,20 @@ impl TcpStream { Ok(s) => Watcher::new(s), Err(e) => { last_err = Some(e); - continue + continue; } }; future::poll_fn(|cx| watcher.poll_write_ready(cx)).await; match watcher.get_ref().take_error() { - Ok(None) => return Ok(TcpStream { watcher }), + Ok(None) => { + return Ok(TcpStream { + watcher: Arc::new(watcher), + }); + } Ok(Some(e)) => last_err = Some(e), - Err(e) => last_err = Some(e) + Err(e) => last_err = Some(e), } } @@ -369,7 +372,7 @@ impl From for TcpStream { fn from(stream: std::net::TcpStream) -> TcpStream { let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); TcpStream { - watcher: Watcher::new(mio_stream), + watcher: Arc::new(Watcher::new(mio_stream)), } } } @@ -391,7 +394,10 @@ cfg_unix! { impl IntoRawFd for TcpStream { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + // 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_fd() } } } diff --git a/tests/tcp.rs b/tests/tcp.rs index 00fa3a045..d92cff0db 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -94,3 +94,25 @@ fn smoke_async_stream_to_std_listener() -> io::Result<()> { Ok(()) } + +#[test] +fn cloned_streams() -> io::Result<()> { + task::block_on(async { + let listener = TcpListener::bind("127.0.0.1:0").await?; + let addr = listener.local_addr()?; + + let mut stream = TcpStream::connect(&addr).await?; + let mut cloned_stream = stream.clone(); + let mut incoming = listener.incoming(); + let mut write_stream = incoming.next().await.unwrap()?; + write_stream.write_all(b"Each your doing").await?; + + let mut buf = [0; 15]; + stream.read_exact(&mut buf[..8]).await?; + cloned_stream.read_exact(&mut buf[8..]).await?; + + assert_eq!(&buf[..15], b"Each your doing"); + + Ok(()) + }) +} From ef985bc72ef770b338480769cdf5ce42edcbc7b8 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 1 Feb 2020 09:45:41 +0900 Subject: [PATCH 426/707] ci: fix no_std ci --- .github/workflows/ci.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 597c2b69b..e99f508da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,12 +54,6 @@ jobs: command: check args: --no-default-features --features std - - name: check no_std - uses: actions-rs/cargo@v1 - with: - command: check - args: --no-default-features --features alloc - - name: check attributes uses: actions-rs/cargo@v1 with: @@ -78,6 +72,22 @@ jobs: command: test args: --doc --features "unstable attributes" + build__with_no_std: + name: Build with no-std + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + + - name: setup + run: rustup target add thumbv7m-none-eabi + + - name: check no_std + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features --features alloc --target thumbv7m-none-eabi -v + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest From f789f9d4f68af64eaee47c0db931de5632a294f3 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 1 Feb 2020 09:47:33 +0900 Subject: [PATCH 427/707] Select future-core featue according to feature --- Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index da6187ab7..6ae4d82ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,16 +38,17 @@ docs = ["attributes", "unstable", "default"] unstable = ["std", "broadcaster", "futures-timer"] attributes = ["async-attributes"] std = [ + "alloc", "crossbeam-utils", + "futures-core/std", "futures-io", "memchr", "once_cell", "pin-utils", "slab", - "alloc", ] alloc = [ - "futures-core", + "futures-core/alloc", "pin-project-lite", ] @@ -58,7 +59,7 @@ 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 } +futures-core = { version = "0.3.1", optional = true, default-features = false } futures-io = { version = "0.3.1", optional = true } futures-timer = { version = "2.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } From 0d90cb07b9afa8d741100483020be79b4acd6409 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 1 Feb 2020 09:49:54 +0900 Subject: [PATCH 428/707] fix: Move `extern crate alloc` to lib.rs --- src/lib.rs | 2 ++ src/utils.rs | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b5a3fff55..05073a2f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -230,6 +230,8 @@ #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "2048"] +extern crate alloc; + #[macro_use] mod utils; diff --git a/src/utils.rs b/src/utils.rs index e257d2f02..f18b74d19 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,3 @@ -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(feature = "alloc")] use alloc::string::String; /// Calls a function and aborts if it panics. From 3e24e0ba4ed99c185013a26eb0943cf00037429e Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 1 Feb 2020 16:43:12 +0900 Subject: [PATCH 429/707] ci: fix no-std check --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e99f508da..bcda99060 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,13 +80,15 @@ jobs: - uses: actions/checkout@master - name: setup - run: rustup target add thumbv7m-none-eabi + run: | + rustup default nightly + rustup target add thumbv7m-none-eabi - name: check no_std uses: actions-rs/cargo@v1 with: command: check - args: --no-default-features --features alloc --target thumbv7m-none-eabi -v + args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps check_fmt_and_docs: name: Checking fmt and docs From 39f2c6da784fdbf4c945f9541f77a8df96a90b6c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 3 Feb 2020 16:45:00 +0100 Subject: [PATCH 430/707] V1.5.0 (#694) * Update CHANGELOG.md * v1.5.0 * Update CHANGELOG.md --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 831c45c09..e464ed762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.5.0] - 2020-02-03 + +[API Documentation](https://docs.rs/async-std/1.5.0/async-std) + +This patch includes various quality of life improvements to async-std. +Including improved performance, stability, and the addition of various +`Clone` impls that replace the use of `Arc` in many cases. + +## Added + +- Added links to various ecosystem projects from the README ([#660](https://github.com/async-rs/async-std/pull/660)) +- Added an example on `FromStream` for `Result` ([#643](https://github.com/async-rs/async-std/pull/643)) +- Added `stream::pending` as "unstable" ([#615](https://github.com/async-rs/async-std/pull/615)) +- Added an example of `stream::timeout` to document the error flow ([#675](https://github.com/async-rs/async-std/pull/675)) +- Implement `Clone` for `DirEntry` ([#682](https://github.com/async-rs/async-std/pull/682)) +- Implement `Clone` for `TcpStream` ([#689](https://github.com/async-rs/async-std/pull/689)) + +## Changed + +- Removed internal comment on `stream::Interval` ([#645](https://github.com/async-rs/async-std/pull/645)) +- The "unstable" feature can now be used without requiring the "default" feature ([#647](https://github.com/async-rs/async-std/pull/647)) +- Removed unnecessary trait bound on `stream::FlatMap` ([#651](https://github.com/async-rs/async-std/pull/651)) +- Updated the "broadcaster" dependency used by "unstable" to `1.0.0` ([#681](https://github.com/async-rs/async-std/pull/681)) +- Updated `async-task` to 1.2.1 ([#676](https://github.com/async-rs/async-std/pull/676)) +- `task::block_on` now parks after a single poll, improving performance in many cases ([#684](https://github.com/async-rs/async-std/pull/684)) +- Improved reading flow of the "client" part of the async-std tutorial ([#550](https://github.com/async-rs/async-std/pull/550)) +- Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` ([#667](https://github.com/async-rs/async-std/pull/667)) +- `TcpStream::connect` no longer uses a thread from the threadpool, improving performance ([#687](https://github.com/async-rs/async-std/pull/687)) + +## Fixed + +- Fixed crate documentation typo ([#655](https://github.com/async-rs/async-std/pull/655)) +- Fixed documentation for `UdpSocket::recv` ([#648](https://github.com/async-rs/async-std/pull/648)) +- 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 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)) + # [1.4.0] - 2019-12-20 [API Documentation](https://docs.rs/async-std/1.4.0/async-std) @@ -637,7 +677,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.3.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.5.0...HEAD +[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 [1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 diff --git a/Cargo.toml b/Cargo.toml index b1155bb4e..9948e8e96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.4.0" +version = "1.5.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From d026c44ea344e14a64320df80064950843c03a30 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 4 Feb 2020 11:07:50 +0100 Subject: [PATCH 431/707] Document the core feature Follow-up to https://github.com/async-rs/async-std/pull/680 --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 05073a2f5..7eb305008 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,6 +220,16 @@ //! default-features = false //! features = ["std"] //! ``` +//! +//! And to use async-std on `no_std` targets that only support `alloc` only +//! enable the `core` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.0.0" +//! default-features = false +//! features = ["core"] +//! ``` #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "docs", feature(doc_cfg))] From 303ac90b7c3b22dbc5d04395d485d893ddd58d6c Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 7 Feb 2020 22:09:42 +0300 Subject: [PATCH 432/707] 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 433/707] 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 434/707] 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 435/707] 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 436/707] 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 437/707] `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 438/707] 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 439/707] 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 3719484ebae68e0ea1b917dfdc49234193974f89 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 17 Feb 2020 13:36:23 +0100 Subject: [PATCH 440/707] Update src/lib.rs Co-Authored-By: nasa --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7eb305008..0e7844bc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! [dependencies.async-std] //! version = "1.0.0" //! default-features = false -//! features = ["core"] +//! features = ["alloc"] //! ``` #![cfg_attr(not(feature = "std"), no_std)] From 283a54a1559b2660c82f4ddd0d03d7bd97a37f57 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 17 Feb 2020 13:36:59 +0100 Subject: [PATCH 441/707] Update src/lib.rs --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0e7844bc2..f87404504 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,7 +222,7 @@ //! ``` //! //! And to use async-std on `no_std` targets that only support `alloc` only -//! enable the `core` Cargo feature: +//! enable the `alloc` Cargo feature: //! //! ```toml //! [dependencies.async-std] From d87e2832150c249a6cce4210613f736819ecfe78 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 17 Feb 2020 13:38:24 +0100 Subject: [PATCH 442/707] Update src/lib.rs --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f87404504..a6a8f31c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,7 +226,7 @@ //! //! ```toml //! [dependencies.async-std] -//! version = "1.0.0" +//! version = "1.5.0" //! default-features = false //! features = ["alloc"] //! ``` From b9e4b6da3e541417143a8ee36608d8adfe39a800 Mon Sep 17 00:00:00 2001 From: sunli Date: Wed, 19 Feb 2020 14:36:07 +0800 Subject: [PATCH 443/707] Add Xactor to the ecosystems inside the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8cd3ac5da..4ebf5924a 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,8 @@ documentation] on how to enable them. * [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. + ## License From bd60cd9f81a16681761372fe57a8613c37e6adbd Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 20 Feb 2020 09:03:36 +0900 Subject: [PATCH 444/707] run `cargo fmt` --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a6a8f31c2..d49879275 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,7 +220,7 @@ //! default-features = false //! features = ["std"] //! ``` -//! +//! //! And to use async-std on `no_std` targets that only support `alloc` only //! enable the `alloc` Cargo feature: //! From 4742f461fe0d6e3219ed3d71d5d97468627a7d08 Mon Sep 17 00:00:00 2001 From: abhi Date: Sat, 22 Feb 2020 15:17:06 +0530 Subject: [PATCH 445/707] Add missing ? operator after handle.await According to line#118, there should be a `?` operator after `await`. --- 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 4f705294e..f62b65d9c 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -111,7 +111,7 @@ We can "fix" it by waiting for the task to be joined, like this: # # async move |stream| { let handle = task::spawn(connection_loop(stream)); -handle.await +handle.await? # }; ``` From 23b7c174f36f6632b0c29302f79094a23db325ec Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 3 Mar 2020 22:46:18 +0900 Subject: [PATCH 446/707] 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 447/707] 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 448/707] 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 449/707] 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 450/707] 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 451/707] 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 452/707] 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 453/707] 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 454/707] $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 455/707] 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 456/707] 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 457/707] 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 458/707] 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 459/707] 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 460/707] 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 461/707] 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 462/707] 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 463/707] 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 464/707] 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 465/707] 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 466/707] 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 467/707] 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 468/707] 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 469/707] 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 470/707] 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 471/707] 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 472/707] 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 473/707] 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 474/707] 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 475/707] 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 476/707] 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 477/707] 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 478/707] 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 479/707] 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 480/707] 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 481/707] 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 482/707] 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 483/707] 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 484/707] 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 485/707] 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 486/707] 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 487/707] 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 488/707] 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 489/707] 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 490/707] 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 491/707] 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 492/707] 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 493/707] 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 494/707] 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 495/707] 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 496/707] 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 497/707] 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 498/707] 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 499/707] 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 500/707] 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 501/707] 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 502/707] 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 503/707] 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 504/707] 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 505/707] 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 506/707] 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 507/707] 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 508/707] 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 509/707] 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 510/707] 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 511/707] 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 512/707] 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 513/707] 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 514/707] 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 515/707] 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 516/707] 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 517/707] 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 518/707] 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 519/707] 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 520/707] 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 521/707] 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 522/707] 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 523/707] 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 524/707] 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 525/707] 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 526/707] 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 527/707] 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 528/707] 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 529/707] 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 530/707] 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 531/707] 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 532/707] 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 533/707] 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 534/707] 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 535/707] 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 536/707] 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 537/707] 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 538/707] 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 539/707] 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 540/707] 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 541/707] 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 542/707] 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 543/707] 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 544/707] 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 545/707] 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 546/707] 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 547/707] 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 548/707] 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 549/707] 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 550/707] 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 551/707] 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 552/707] 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 553/707] 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 554/707] 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 555/707] 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 644/707] 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 645/707] 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 646/707] 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 647/707] 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 648/707] 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 649/707] 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 650/707] 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 651/707] 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 652/707] 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 653/707] 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 654/707] 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 655/707] 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 656/707] 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 657/707] 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 658/707] 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 659/707] 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 660/707] 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 661/707] 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 662/707] 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 663/707] 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 664/707] 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 665/707] 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 666/707] 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 667/707] 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 668/707] 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 669/707] 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 670/707] 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 671/707] 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 672/707] 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 673/707] 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 674/707] 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 675/707] 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 676/707] 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 677/707] 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 678/707] 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 679/707] 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 680/707] 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 681/707] 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 682/707] 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 683/707] 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 684/707] 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 685/707] 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 686/707] 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 687/707] 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 688/707] 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 689/707] 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 690/707] 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 691/707] 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 692/707] 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 693/707] 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 694/707] 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 695/707] 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 696/707] 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 697/707] 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 698/707] 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 699/707] 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 700/707] 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 701/707] 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 702/707] 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 703/707] 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 704/707] 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 705/707] 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 706/707] 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 707/707] 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 556/707] 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 557/707] 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 558/707] 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 559/707] 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 560/707] 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 561/707] 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 562/707] 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 563/707] 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 564/707] 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 565/707] 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 566/707] 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 567/707] 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 568/707] 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 569/707] 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 570/707] 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 571/707] 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 572/707] 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 573/707] 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 574/707] 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 575/707] 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 576/707] 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 577/707] 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 578/707] 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 579/707] 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 580/707] 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 581/707] (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 582/707] 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 583/707] 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 584/707] 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 585/707] 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 586/707] 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 587/707] 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 588/707] 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 589/707] 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 590/707] 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 591/707] 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 592/707] 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 593/707] 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 594/707] 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 595/707] 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 596/707] 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 597/707] 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 598/707] 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 599/707] 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 600/707] 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 601/707] 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 602/707] 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 603/707] 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 604/707] 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 605/707] 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 606/707] 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 607/707] 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 608/707] 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 609/707] 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 610/707] 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 611/707] 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 612/707] 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 613/707] 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 614/707] 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 615/707] 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 616/707] 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 617/707] 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 618/707] 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 619/707] 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 620/707] 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 621/707] 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 622/707] 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 623/707] 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 624/707] 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 625/707] 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 626/707] 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 627/707] 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 628/707] 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 629/707] 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 630/707] 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 631/707] 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 632/707] 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 633/707] 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 634/707] 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 635/707] 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 636/707] 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 637/707] 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 638/707] 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 639/707] 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 640/707] 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 641/707] 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 642/707] 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 643/707] 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