From f9741e7488eb8b0ba63fc50e8fa43732002bac7b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 15 Oct 2019 21:43:54 +0900 Subject: [PATCH 001/191] 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 5706aa2e..282d5e97 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 002/191] 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 95a77b80..f6c4a25e 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 003/191] 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 7849f1ce..31801381 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 004/191] 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 00000000..7a8cf477 --- /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 005/191] 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 d582d700..c44917d1 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 006/191] 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 00000000..723c7c19 --- /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 cc3b7a5d..d1a0f315 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 007/191] 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 556dc1ac..38f3d704 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 723c7c19..319b4ff8 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 d1a0f315..6bfd6303 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 008/191] 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 38f3d704..5254ac04 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 009/191] 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 7a8cf477..f73ae871 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 010/191] 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 c44917d1..47b3f835 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 011/191] 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 5254ac04..48497723 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 6bfd6303..cc3b7a5d 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 012/191] 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 48497723..abf7c183 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 013/191] 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 c44917d1..a881a7ae 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 f73ae871..042dc12b 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 014/191] 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 042dc12b..7e0270e3 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 a5a00d7b1465b728592ae0cff55fbefba853d528 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 16:29:23 +0900 Subject: [PATCH 015/191] 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 f6c4a25e..b4ccffbc 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 016/191] 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 b4ccffbc..4201a3a0 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 017/191] $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 4201a3a0..daba03e8 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 018/191] 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 e138c87d..afbad31e 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 00000000..fffda4dd --- /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 764dc978..7047b033 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 019/191] 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 3129e96a..745a5848 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 a2eb9e76..7f700734 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 020/191] 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 fffda4dd..7265d17f 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 021/191] 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 7265d17f..e922e94e 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 022/191] 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 7047b033..2f1a89f9 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 023/191] 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 e922e94e..06e9ec2e 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 024/191] 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 06e9ec2e..5c761672 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 025/191] 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 2f1a89f9..63d1d8cc 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 026/191] 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 5c761672..b9700876 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 6802de90..8ad8cdc0 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 0d4a907335beec5472f03c28c204e039fcc5e6fb Mon Sep 17 00:00:00 2001 From: Sunjay Varma Date: Sun, 20 Oct 2019 19:18:37 -0400 Subject: [PATCH 027/191] 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 64744e14..5d81f1c5 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 028/191] 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 67db720c..7d9cc636 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 d4037a3b..2142cac4 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 2b59ffb0..0086599f 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 84bb68d7..d84b64ab 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 029/191] 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 d84b64ab..8c14e20f 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 030/191] 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 afb29adc..76f096b3 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 00000000..8b66bc69 --- /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 00000000..25dc9209 --- /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 908f9c4d..cae0ebd9 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 00000000..17afa94b --- /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 00000000..caca4f65 --- /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 5799990d..71b14c70 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 501ece1b..f2d3c6eb 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 a87ade1a..dadbc347 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 031/191] 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 00000000..d25a869d --- /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 501ece1b..2ac4d70b 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 020eb85093d714b25015980ed0aeeb148ae25ec3 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 18:59:06 +0800 Subject: [PATCH 032/191] 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 00000000..2cc34a0d --- /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 501ece1b..56dda48a 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 033/191] 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 2ac4d70b..cccd8b27 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 034/191] 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 56dda48a..b5011c27 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 d97b3dfdf3a159608023aea7a197b522514d817a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 24 Oct 2019 08:29:05 +0900 Subject: [PATCH 035/191] 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 319b4ff8..53a4d75a 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 036/191] 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 7e0270e3..3c14811f 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 037/191] 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 b9700876..d44fab0e 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 038/191] 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 d44fab0e..bc1bee34 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 8ad8cdc0..c994a8ff 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 039/191] $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 bc1bee34..fc3592ee 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 040/191] 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 c994a8ff..596dc419 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 041/191] 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 7f04e082..1e33c8fc 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 042/191] 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 1e33c8fc..22481b53 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 043/191] 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 fc3592ee..56277330 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 044/191] 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 56277330..b244f035 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 045/191] 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 b244f035..1b21746e 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 046/191] 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 1b21746e..59330855 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 047/191] 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 59330855..3bf6fe84 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 37a7eadf17c55d0cb524080d34c2f30d848ecd7e Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sat, 26 Oct 2019 11:52:41 +0800 Subject: [PATCH 048/191] 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 d25a869d..d3d640b5 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 049/191] 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 d3d640b5..a41f49a7 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 050/191] 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 a41f49a7..6cd9e560 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 051/191] 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 6cd9e560..a626b284 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 052/191] 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 cac60733..d6dda2f7 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 053/191] 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 d6dda2f7..a482c632 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 054/191] 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 a482c632..6557f229 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 b5011c27..ca43e7d0 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 055/191] 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 d47eabce..c79630ad 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 056/191] 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 c79630ad..653834a7 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 7f888a14..ad5aa8fa 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 32a1eb31..8b6ba97d 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 ca3cac14..3d8e1080 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 13a08b0d54f5c983883ce3efca60278d38a8b6d7 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 27 Oct 2019 12:35:14 +0900 Subject: [PATCH 057/191] 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 3bf6fe84..3efb3523 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 058/191] 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 3efb3523..d81e79fb 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 059/191] 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 d81e79fb..2533f379 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 060/191] $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 ced7cac2..89e5e2b6 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 77ebfd1e..e049a490 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 ff25e862..370dcb9f 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 061/191] 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 2f7fe9e3..016f71a2 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 062/191] 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 cb2368aa..5df90e08 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 91622b0d..0c40f5a7 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 063/191] 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 b0adc387..c10303d7 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 064/191] 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 8193eeb7..1ea33616 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 93d91700..f869cbf6 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 cbe14b8b..360bfe86 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 065/191] 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 ceb91fea..49090707 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 7fee8926..5260d878 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 2f7fe9e3..c3b82945 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 ae90d639..d993c160 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 75fd6973..abccb431 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 15d4aa12..de53bc9d 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 df316150..5e0eeb48 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 eb4153f7..a2562e77 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 11629700..39af9cb0 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 5de60fb3..ba60b0ce 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 3ccc223d..d926ec4f 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 c4771d85..385edf8e 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 6562b99f..cc2ba905 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 0499df23..6435d81c 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 ab9e45b6..13020982 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 835bc447..e680b42b 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 bf89458b..35978b47 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 9b7c299b..27681f37 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 066/191] 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 d8d768e0..90442675 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 a82e21be..56368179 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 faa763c6..86aeb0ae 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 1ec28b29..0a8c4700 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 dd3991fd..22b9cf34 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 7945bfdd..1e9340fc 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 abccb431..aaaff0c6 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 067/191] 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 e796510d..a95e9185 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 068/191] 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 b3ae287f..29e43090 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 069/191] 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 3ad2776c..0fe73225 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 070/191] 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 e8075f1b..fe685176 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 071/191] 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 ad5aa8fa..b659c39e 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 072/191] 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 39af9cb0..6297bef7 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 2b237de6..f8640c8a 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 073/191] 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 53a4d75a..d672541e 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 074/191] 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 00000000..ed3268ea --- /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 2533f379..2ea0673e 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 596dc419..29e68fc4 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 075/191] 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 2ea0673e..f0049864 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 076/191] $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 f0049864..5e791cda 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 077/191] 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 a95e9185..6db0dbe9 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 078/191] 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 0fe73225..d10e6bdf 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 079/191] 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 f0e735a1..19af02ed 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 ad887303..ab74dd01 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 080/191] more Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19af02ed..c34fa24e 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 081/191] 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 c34fa24e..eda60387 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 082/191] 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 00000000..1ab56065 --- /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 e8190387..27090a5b 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 083/191] 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 27090a5b..5c42989f 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 084/191] 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 9a125b20..c81d82f9 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 7cd95aa5..4e727f21 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 82a0b00b..9fb28bab 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 8d4ba273..c314837b 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 085/191] 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 653834a7..dd8ec899 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 086/191] 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 00000000..8d3dba78 --- /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 6db0dbe9..07eecf28 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 087/191] 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 ab74dd01..dcf2c7d0 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 806acdbe..40e0abb2 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 3216012a..1f1a222a 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 bfaa17d4..3fd70470 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 c72937f6..e92f4f92 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 088/191] corrected a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7aeaed86..9af20a39 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 089/191] 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 00000000..42a37d84 --- /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 e8190387..07cd03a1 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 090/191] 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 e8190387..b8fac7e5 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 00000000..2f17ed0e --- /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 091/191] 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 2f17ed0e..ffeaca81 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 092/191] 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 42a37d84..5343c1a0 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 093/191] 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 e8190387..c4abe32f 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 00000000..3cd5b84c --- /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 094/191] 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 c8281d71..00fa3a04 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 095/191] 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 c4abe32f..0469c7ae 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 096/191] 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 4e727f21..7584dc1b 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 9fb28bab..359f2a34 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 c314837b..ccfd85b2 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 1ab56065..b4a8c7c1 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 097/191] 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 7584dc1b..334e50ad 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 359f2a34..8480c69d 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 ccfd85b2..aaa99ceb 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 098/191] 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 dcf2c7d0..63897053 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 601d477c..6eb6977d 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 733fb64b..170b66ec 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 740d303c..03c24918 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 76604de7..15241943 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 a55a9a92..9ee6b55a 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 959e2ada..527fab42 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 745a5848..8bc6c2ce 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 8b09b5d1..e6e56cd5 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 4afc5595..1383ec21 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 7f700734..91ad8cab 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 a0eb130b..ab7d1756 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 6e478019..fe12fa6d 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 eaa7b624..7ec18a45 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 40c4b6b8..d06aa614 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 d1fa7bf3..1a62db2e 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 0a0fceb7..33667406 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 5bc0608d..9a74ec11 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 c2aa77b7..ed7f39c9 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 d14ced94..60a6d6f1 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 bc5cce86..45be6d99 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 3df56042..4e5d20bb 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 0a8c4700..76ca5239 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 22b9cf34..c99a88db 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 1e9340fc..5455466a 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 519b1846..c17ff498 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 40e0abb2..7f33e859 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 5988194f..13a1752f 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 d3e85234..498b3a97 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 c96afd50..fc426b7c 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 b6e6a298..9bd86d38 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 b16f2a3c..647edc96 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 c10303d7..54a415f5 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 a43b42bc..a61d7859 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 00000000..0dc36991 --- /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 00000000..2a6a696e --- /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 00000000..1e743844 --- /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 00000000..9fefff2e --- /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 24eae081..72d559a7 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 3fd70470..00000000 --- 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 00000000..da2957b0 --- /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 1f1a222a..b6b5ea34 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 3d8e1080..bcec2e0e 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 00000000..67eee154 --- /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 e92f4f92..0869cab4 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 f7b08e16..00000000 --- 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 c11408ab..5cd0ce5b 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 60516d25..4f3ffe4f 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 0c40f5a7..f7938904 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 099/191] 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 00000000..b159ba12 --- /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 00000000..b3144713 --- /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 6751417a..403bee74 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 d10e6bdf..ea991fe6 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 cd7a3577..fcd030d8 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 ed1d2185..a0d0f07a 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 00000000..8fd1b621 --- /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 100/191] 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 dd8ec899..ac49bd6e 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 101/191] 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 63897053..af72b6f6 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 b659c39e..26856887 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 91432e0b..3e235cfa 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 102/191] 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 1ab56065..b4a8c7c1 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 71139f4c..f7905ed2 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 103/191] 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 8d3dba78..43bc9611 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 104/191] 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 af72b6f6..b117b113 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 26856887..ea05edf7 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 3e235cfa..73a5952e 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 105/191] Fix typo --- src/prelude.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index 73a5952e..91432e0b 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 106/191] 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 ac49bd6e..06712838 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 00000000..aef5e15e --- /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 107/191] 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 b3cc7113..8986d48d 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 a72d0559..d7b26909 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 99c636d6..c6281d77 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 108/191] 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 bda45766..322c49fa 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 109/191] =?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 b6b5ea34..3f4f18a1 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 110/191] 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 f7905ed2..e46ca9e5 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 578b854c..6b66d2ea 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 111/191] 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 8986d48d..a0d9b713 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 d7b26909..e5bb508d 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 c6281d77..bf22128f 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 112/191] 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 00000000..477d59d2 --- /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 e46ca9e5..51ef29d1 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 113/191] 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 00000000..b3fb65bf --- /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 51ef29d1..4a3875c4 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 114/191] 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 fe685176..dad94daa 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 00000000..2fd604a7 --- /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 00000000..d0ca4a90 --- /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 a45bf96c..dd28f284 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 4f3ffe4f..a1d85107 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 115/191] 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 c81d82f9..93753d10 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 a0d9b713..8bd2180a 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 e5bb508d..26b7ee00 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 bf22128f..c0565aa5 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 116/191] 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 b117b113..4e9dc09c 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 d926ec4f..f3505aca 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 4a3875c4..e182c033 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 00000000..42a6191f --- /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 117/191] 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 00000000..881377f2 --- /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 e182c033..4a845f11 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 118/191] 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 881377f2..7ed0774e 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 119/191] 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 00000000..94384693 --- /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 07eecf28..449c484e 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 7ed0774e..00000000 --- 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 4a845f11..d46ae879 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 120/191] 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 94384693..135a4307 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 121/191] 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 135a4307..b9dd87f9 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 122/191] 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 b9dd87f9..1d16f855 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 123/191] 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 1d16f855..92f31ec1 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 124/191] 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 92f31ec1..5210a69b 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 125/191] 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 5210a69b..df8b9563 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 126/191] 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 449c484e..07eecf28 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 df8b9563..518a804c 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 d46ae879..274992b4 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 127/191] 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 518a804c..9c145904 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 128/191] 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 9c145904..91f33f97 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 129/191] 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 274992b4..83b77308 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 130/191] 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 91f33f97..4d2dbf58 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 131/191] 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 83b77308..747c54e1 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 132/191] 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 4d2dbf58..c7781498 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 747c54e1..7b21bf07 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 133/191] 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 c7781498..4d2dbf58 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 7b21bf07..747c54e1 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 134/191] 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 4d2dbf58..8a31cc17 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 135/191] 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 fe685176..1b88430f 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 00000000..27ee2281 --- /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 136/191] 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 00000000..779e49d1 --- /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 e182c033..9595f80f 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 137/191] 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 25dc9209..5c154f42 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 17afa94b..fd242168 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 caca4f65..dd687723 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 43bc9611..5fd216db 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 e182c033..923c52bf 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 138/191] 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 403bee74..d4b64230 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 139/191] 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 8b66bc69..9b7274ff 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 923c52bf..4ab12a10 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 140/191] 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 a0d0f07a..81b0735a 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 8fd1b621..eb44a673 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 141/191] 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 779e49d1..1d7eef47 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 142/191] 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 1d7eef47..19dfbc87 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 143/191] 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 27ee2281..7b056142 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 144/191] 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 0f3ab280..8e6e90a6 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 145/191] 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 d4b64230..c3262808 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 fcd030d8..52c38985 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 81b0735a..65b9dcad 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 eb44a673..57fbaaa2 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 146/191] 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 57fbaaa2..a10f214f 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 147/191] 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 a10f214f..7e3d8e15 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 148/191] 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 01357b28..7112c381 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 149/191] 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 7112c381..12b258d6 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 150/191] 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 dad94daa..2cd5c7e9 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 ed61590b..f5c0a4c9 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 4ab12a10..b77019da 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 151/191] 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 07eecf28..29c3b7f1 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 152/191] 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 ea05edf7..c8d25d0a 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 07eecf28..29c3b7f1 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 153/191] 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 2cd5c7e9..548cccb8 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 b82971fe..45c5f28c 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 fb1b9456..c90b289a 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 f5c0a4c9..56f63235 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 ec2dd8f9..7dc30aee 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 26b7ee00..bd6580c2 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 a07c8968..eb114344 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 91432e0b..93218f11 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 54a22291..e8707cef 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 b77019da..990ef7b2 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 ea991fe6..ab551eca 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 a1d85107..7758cc74 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 154/191] 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 c8d25d0a..92cff5c5 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 155/191] 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 92cff5c5..4320089e 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 156/191] 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 93218f11..f8583fd2 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 157/191] 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 b77019da..c7e794a7 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 158/191] 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 29866be0..04c61c1d 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 60773e07..5f1a4d25 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 ea991fe6..56c5f811 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 54a415f5..d320cb2f 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 159/191] 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 4e9dc09c..9583cdee 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 dcc06616..789283e6 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 234067a3..5dcc7f26 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 c1ebe9b8..fcc42b63 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 a1f51d13..acb744b0 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 3aac67f3..fd728555 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 b381aacb..3f426d02 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 22dab769..43adbbbc 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 0163e3fc..2822d546 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 160/191] 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 7b056142..0e831442 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 161/191] 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 3a666e8a..d7bd75be 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 162/191] 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 4503fe39..439bf139 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 c8e44e93..99bca209 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 ae02c424..19d306ff 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 bd80c069..cc944029 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 ccf03378..422640b1 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 bd2a744b..6c88a8d4 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 c34bb9ed..0f4ce0c6 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 2b7bbc9b..c3445e15 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 123e844e..ba872b43 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 42447fef..8afc0db5 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 63a1a97c..b0dff009 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 3ffe149b..3d4c8265 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 17e396ab..dd2ddce3 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 8903de0e..3a5e5851 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 a90ebabb..12f5ac39 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 350418d8..5e39f198 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 e8707cef..6e5200ae 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 29c3b7f1..692c5de4 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 8572cc3c..769f1ec8 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 276d13a7..e0b2da95 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 00000000..27f5d4e9 --- /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 cb8063d0..bbf28987 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 ecf68c83..302fc7a8 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 b326b04c..7b6cda1a 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 163/191] 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 36b9f2f0..e9843d75 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 164/191] 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 06712838..5d5e7c7e 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 c3262808..dc7bee13 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 f7938904..34bd888f 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 165/191] 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 9583cdee..54ab0f1b 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 4320089e..4863cbbc 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 166/191] 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 3f4f18a1..6076d1bc 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 7758cc74..cfdf5fa9 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 167/191] 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 5d5e7c7e..031ffc96 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 54ab0f1b..7c860c03 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 d7bd75be..d712dc80 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 dd28f284..4e7d79a8 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 93753d10..c4711593 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 4863cbbc..04ed8fb6 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 f932e47e..b7811d2e 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 722cfe6b..c389d95a 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 30218f0e..f3350007 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 f8583fd2..2a1fa415 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 7e3d8e15..5ba4cfbd 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 72d559a7..bcdea72c 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 0869cab4..72e53d72 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 cfdf5fa9..13dbe37d 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 168/191] 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 eda60387..3568abf5 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 7c860c03..48b7d0b6 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 169/191] 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 5e39f198..0d26afab 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 170/191] 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 00000000..90ea3237 --- /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 d712dc80..729ace7c 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 00000000..58ae6d62 --- /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 171/191] 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 65b9dcad..e042bbd2 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 172/191] 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 48b7d0b6..bdbeafa6 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 173/191] 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 e9843d75..059e6050 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 12f5ac39..a1901115 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 174/191] 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 031ffc96..ca83f9b4 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 175/191] 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 f9a9e3a4..d0d69350 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 35978b47..9b2945fd 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 176/191] 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 4e7d79a8..fa5a7abd 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 177/191] 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 99bca209..148a57f4 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 cc944029..e0653ab5 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 6c88a8d4..c4197df4 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 c3445e15..bf47d8e7 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 8afc0db5..69b38538 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 3d4c8265..12262471 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 3a5e5851..767ec068 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 11f47d1c..d7ce2570 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 1383ec21..2948016e 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 1339a7c7..50aa45cd 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 fe12fa6d..5e51065b 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 d672541e..45658f45 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 0e831442..1d316440 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 42839a20..a9a81875 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 2138a301..39f7cab1 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 a808f97f..19452640 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 c745d732..ff87ae4f 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 04c61c1d..b66079bc 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 72385abb..bda1eee9 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 6327ca71..8fa9eba4 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 098df8d7..753f5e34 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 c46aff66..0ba04e57 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 c970f431..71cf004d 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 d76ee8c4..c7c47b8f 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 5f1a4d25..5b74389e 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 8e52ba2d..b4c61b8f 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 65743be2..74aa93e5 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 8bd2180a..5ff8a029 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 bd6580c2..167ea2dd 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 c0565aa5..1711c090 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 ec3668ea..6e22dbf2 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 08f2b5b4..590c12e8 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 da6e5c50..8f13091d 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 5353f7ab..f04c55d6 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 ad3e94ad..ec7847f2 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 5f8492b7..cdb49d42 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 c17ff498..2769dd5e 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 6fd27f0f..f98bbdc7 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 e4da809e..d2d53b60 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 9bd86d38..675ef481 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 12f5ac39..c95103f2 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 2a1fa415..a2a14a18 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 6033eb97..9296797d 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 0d26afab..7bdfd343 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 5260d878..a28a9014 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 5fd216db..a83afceb 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 6e5200ae..67b9b3df 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 efec4362..b0df7141 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 71b14c70..2f5bf4c3 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 de53bc9d..6e7cfa3b 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 3b65fc76..7b84abe3 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 a23adf4b..c7fc7665 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 df08e9db..19437e70 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 5343c1a0..addcfa2e 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 93624c03..b37a6a46 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 dfcf92d6..16993fc5 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 5b0eb124..66a76729 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 4696529b..6383ed78 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 3dc6031c..f9012697 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 513ca764..81e95a1a 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 eba01e5c..188da3c8 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 af727005..35b04bfb 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 524f2689..86c31295 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 a626b284..cfba9b93 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 b3fb65bf..b5bc7e0c 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 f3505aca..fe3579e9 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 b4a8c7c1..4ce52be9 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 ab12aa05..fc332c26 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 6557f229..8179fb31 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 d0d69350..76130bbb 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 ffeaca81..ec11d1fd 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 de75f5e9..23abb0b4 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 e7e042a9..711287a3 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 e30c6ea8..6bc28f78 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 3cd5b84c..3d8f40d5 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 3c14811f..636e406e 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 80392e10..bf92ff51 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 6b66d2ea..36198f9e 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 dadbc347..9607bafa 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 769f1ec8..55bec0c5 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 e0b2da95..eb6818c1 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 52c38985..5bec6a23 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 e042bbd2..bc3f6405 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 d320cb2f..f61a22b6 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 a61d7859..afd4c2c1 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 da2957b0..f81a483d 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 5cd0ce5b..03f83e2e 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 a238982d..da216e22 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 7b6cda1a..cdd4767d 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 178/191] 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 c95103f2..aae5de64 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 179/191] 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 031ffc96..cf8dee6a 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 180/191] 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 753f5e34..8ec3c1af 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 181/191] 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 76130bbb..8ea1459f 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 182/191] 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 fa5a7abd..8b51a6a5 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 183/191] 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 45658f45..641084ff 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 1d316440..a07b140c 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 729ace7c..5fdaf4b1 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 2fd604a7..ed034f05 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 a9a81875..8e5e5e04 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 39f7cab1..968972b5 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 184/191] 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 dc7bee13..392c8511 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 185/191] 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 00000000..51649c55 --- /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 00000000..b4061003 --- /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 059e6050..7ce9b62d 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 43adbbbc..dfe9426a 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 cf2e6bfe..56a63a47 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 352f18bc2a404b755dd7cd021ca50b3a79a7cc14 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 11:10:36 +0100 Subject: [PATCH 186/191] 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 a28a9014..f7a421fc 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 fdeb48c3..088c520b 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 5bec6a23..2c0ac0cc 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 c2f750d2882b4215ae6962d419056ba00b000f6d Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 10:21:42 +0100 Subject: [PATCH 187/191] 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 c4711593..4e832305 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 7bdfd343..c48fe1ed 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 a28a9014..24432c7e 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 a83afceb..d7a31d6c 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 692c5de4..f7828822 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 d993c160..a33bd6ac 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 aaaff0c6..f3dfdbd8 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 6e7cfa3b..e183a77c 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 5e0eeb48..f6d9cf64 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 19dfbc87..4c77c5c9 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 477d59d2..e3c8367b 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 8a31cc17..7f01a61d 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 2a3afa87..a758010e 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 a2562e77..594b0949 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 6a4593f9..e110f514 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 b37a6a46..0c5ad62f 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 16993fc5..b10bd9ca 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 ed3268ea..ab45c9c7 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 5e791cda..edaffd04 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 66a76729..c4da5915 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 6383ed78..01833fd9 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 ba60b0ce..acf22465 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 a1fafc30..7accb6fc 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 8ea1459f..5756a21e 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 3d8f40d5..5a51d7a7 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 6435d81c..5cb273ee 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 9b2945fd..08b5a86c 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 636e406e..560a0e41 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 bf92ff51..efb9e339 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 36198f9e..30e31850 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 27681f37..f57d7359 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 5438258ceec7932fa407c5c7e0697c7ccec7d6f2 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 13:19:59 +0100 Subject: [PATCH 188/191] 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 e4078662..24432c7e 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 189/191] 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 3568abf5..37471433 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 190/191] 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 37471433..c890f7a4 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 bdbeafa6..c1c68655 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 322c49fa..c384ec24 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 04ed8fb6..ddc6462c 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 191/191] 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 c384ec24..307c79e9 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" ```