From 735d604cd14422fcb7f974130d616993d73ce190 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 5 Oct 2019 22:17:21 +0300 Subject: [PATCH 001/407] Adds stream::repeat_with --- src/stream/mod.rs | 2 + src/stream/repeat_with.rs | 103 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/stream/repeat_with.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8aa12a2f..f2fcdebe 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,6 +26,7 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; 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, Zip}; pub(crate) mod stream; @@ -33,6 +34,7 @@ pub(crate) mod stream; mod empty; mod once; mod repeat; +mod repeat_with; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs new file mode 100644 index 00000000..b682dfa8 --- /dev/null +++ b/src/stream/repeat_with.rs @@ -0,0 +1,103 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that repeats elements of type `T` endlessly by applying a provided clousre. +/// +/// This stream is constructed by the [`repeat_with`] function. +/// +/// [`repeat_with`]: fn.repeat_with.html +#[derive(Debug)] +pub struct RepeatWith { + f: F, + future: Option, + __a: PhantomData, +} + +/// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::repeat_with(|| async { 1 }); +/// +/// +/// 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(1)); +/// assert_eq!(s.next().await, Some(1)); +/// +/// # }) } +/// ``` +/// +/// Going finite: +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::repeat_with(|| async { 1u8 }).take(2); +/// +/// +/// pin_utils::pin_mut!(s); +/// +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, None); +/// +/// # }) } +/// ``` +pub fn repeat_with(repeater: F) -> RepeatWith { + RepeatWith { + f: repeater, + future: None, + __a: PhantomData, + } +} + +impl RepeatWith { + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_pinned!(future: Option); +} + +impl Stream for RepeatWith +where + F: FnMut() -> Fut, + Fut: Future, +{ + type Item = A; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match self.future.is_some() { + true => { + let res = + futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + + self.as_mut().future().set(None); + + return Poll::Ready(Some(res)); + } + false => { + let fut = (self.as_mut().f())(); + + self.as_mut().future().set(Some(fut)); + } + } + } + } +} From 2384df11ed27e186ae7cab6db7938165db58898f Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 6 Oct 2019 08:27:44 +0300 Subject: [PATCH 002/407] Apply suggestions from code review Co-Authored-By: Yoshua Wuyts --- src/stream/repeat_with.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index b682dfa8..ce249ddb 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -5,7 +5,7 @@ use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that repeats elements of type `T` endlessly by applying a provided clousre. +/// A stream that repeats elements of type `T` endlessly by applying a provided closure. /// /// This stream is constructed by the [`repeat_with`] function. /// @@ -31,14 +31,12 @@ pub struct RepeatWith { /// /// let s = stream::repeat_with(|| async { 1 }); /// -/// /// 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(1)); /// assert_eq!(s.next().await, Some(1)); -/// /// # }) } /// ``` /// @@ -52,13 +50,11 @@ pub struct RepeatWith { /// /// let s = stream::repeat_with(|| async { 1u8 }).take(2); /// -/// /// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, None); -/// /// # }) } /// ``` pub fn repeat_with(repeater: F) -> RepeatWith { From 49d123c7f9b5b5a1d1e63310631dda7c57ac1100 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 6 Oct 2019 08:32:44 +0300 Subject: [PATCH 003/407] Fix review nits --- src/stream/repeat_with.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index ce249ddb..f38b323d 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -57,7 +57,11 @@ pub struct RepeatWith { /// 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, +{ RepeatWith { f: repeater, future: None, @@ -79,8 +83,8 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { - match self.future.is_some() { - true => { + match &self.future { + Some(_) => { let res = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); @@ -88,7 +92,7 @@ where return Poll::Ready(Some(res)); } - false => { + None => { let fut = (self.as_mut().f())(); self.as_mut().future().set(Some(fut)); From 58c3a06a14c70d34ff884c94142eac278032c238 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 01:24:50 +0200 Subject: [PATCH 004/407] init write_fmt Signed-off-by: Yoshua Wuyts --- src/io/fmt/mod.rs | 0 src/io/fmt/write.rs | 0 src/io/write/mod.rs | 47 +++++++++++++++++++++++++++++++++++++-- src/io/write/write_fmt.rs | 45 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/io/fmt/mod.rs create mode 100644 src/io/fmt/write.rs create mode 100644 src/io/write/write_fmt.rs diff --git a/src/io/fmt/mod.rs b/src/io/fmt/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/io/fmt/write.rs b/src/io/fmt/write.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 5e1ecc8b..169c9e1e 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -1,11 +1,13 @@ mod flush; mod write; mod write_all; +mod write_fmt; mod write_vectored; use flush::FlushFuture; use write::WriteFuture; use write_all::WriteAllFuture; +use write_fmt::WriteFmtFuture; use write_vectored::WriteVectoredFuture; use cfg_if::cfg_if; @@ -13,12 +15,12 @@ use cfg_if::cfg_if; use crate::io::IoSlice; use crate::utils::extension_trait; +use crate::io; + cfg_if! { if #[cfg(feature = "docs")] { use std::pin::Pin; use std::ops::{Deref, DerefMut}; - - use crate::io; use crate::task::{Context, Poll}; } } @@ -197,6 +199,47 @@ extension_trait! { { WriteAllFuture { writer: self, buf } } + + #[doc = r#" + Writes a formatted string into this writer, returning any error encountered. + + This method will continuously call [`write`] until there is no more data to be + written or an error is returned. This method will not return until the entire + buffer has been successfully written or such an error occurs. + + [`write`]: #tymethod.write + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::prelude::*; + use async_std::fs::File; + + let mut buffer = File::create("foo.txt").await?; + + // this call + write!(buffer, "{:.*}", 2, 1.234567).await?; + // turns into this: + buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?; + # + # Ok(()) }) } + ``` + "#] + fn write_fmt<'a>( + &'a mut self, + fmt: std::fmt::Arguments<'_>, + ) -> impl Future> + 'a [WriteFmtFuture<'a, Self>] + where + Self: Unpin, + { + let mut string = String::new(); + let res = std::fmt::write(&mut string, fmt) + .map(|_| string.into_bytes()) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error")); + WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } + } } impl Write for Box { diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs new file mode 100644 index 00000000..9c8187ab --- /dev/null +++ b/src/io/write/write_fmt.rs @@ -0,0 +1,45 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::io::{self, Write}; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> { + pub(crate) writer: &'a mut T, + pub(crate) res: Option>>, + pub(crate) buffer: Option>, + pub(crate) amt: u64, +} + +impl Future for WriteFmtFuture<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + + // Process the interal Result the first time we run. + if self.buffer.is_none() { + match self.res.take().unwrap() { + Err(err) => return Poll::Ready(Err(err)), + Ok(buffer) => self.buffer = Some(buffer), + }; + } + + let Self { writer, amt, buffer, .. } = &mut *self; + let mut buffer = buffer.as_mut().unwrap(); + + loop { + if buffer.is_empty() { + futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; + return Poll::Ready(Ok(())); + } + + let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &mut buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *amt += i as u64; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index fa4e946f..f1ed43e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,3 +77,6 @@ cfg_if! { } pub(crate) mod utils; + +#[doc(inline)] +pub use std::{write, writeln}; From 570dedd71269642b3e3b4a11ab7c5126292b2a49 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 01:31:51 +0200 Subject: [PATCH 005/407] cleanup Signed-off-by: Yoshua Wuyts --- src/io/fmt/mod.rs | 0 src/io/fmt/write.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/io/fmt/mod.rs delete mode 100644 src/io/fmt/write.rs diff --git a/src/io/fmt/mod.rs b/src/io/fmt/mod.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/io/fmt/write.rs b/src/io/fmt/write.rs deleted file mode 100644 index e69de29b..00000000 From f3eba1fb48641cc7c671c7cdd0e4683888714a13 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 01:34:13 +0200 Subject: [PATCH 006/407] comments Signed-off-by: Yoshua Wuyts --- src/io/write/mod.rs | 3 +++ src/io/write/write_fmt.rs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 169c9e1e..bb03d901 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -234,6 +234,9 @@ extension_trait! { where Self: Unpin, { + // In order to not have to implement an async version of `fmt` including private types + // and all, we convert `Arguments` to a `Result>` and pass that to the Future. + // Doing an owned conversion saves us from juggling references. let mut string = String::new(); let res = std::fmt::write(&mut string, fmt) .map(|_| string.into_bytes()) diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index 9c8187ab..bd2dd673 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -17,7 +17,6 @@ impl Future for WriteFmtFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Process the interal Result the first time we run. if self.buffer.is_none() { match self.res.take().unwrap() { @@ -26,15 +25,16 @@ impl Future for WriteFmtFuture<'_, T> { }; } + // Get the types from the future. let Self { writer, amt, buffer, .. } = &mut *self; let mut buffer = buffer.as_mut().unwrap(); + // Copy the data from the buffer into the writer until it's done. loop { if buffer.is_empty() { futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; return Poll::Ready(Ok(())); } - let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &mut buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); From a1cd76e24436117d43c85a91e6b9469ca0c58924 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 01:36:44 +0200 Subject: [PATCH 007/407] cargo fmt Signed-off-by: Yoshua Wuyts --- src/io/write/write_fmt.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index bd2dd673..f5942289 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -26,7 +26,12 @@ impl Future for WriteFmtFuture<'_, T> { } // Get the types from the future. - let Self { writer, amt, buffer, .. } = &mut *self; + let Self { + writer, + amt, + buffer, + .. + } = &mut *self; let mut buffer = buffer.as_mut().unwrap(); // Copy the data from the buffer into the writer until it's done. From b62e4a1e48cd9c58a09f7455e895fb234b0de2c6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 02:39:14 +0200 Subject: [PATCH 008/407] update desc Signed-off-by: Yoshua Wuyts --- src/io/write/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index bb03d901..7914ccc8 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -204,7 +204,7 @@ extension_trait! { Writes a formatted string into this writer, returning any error encountered. This method will continuously call [`write`] until there is no more data to be - written or an error is returned. This method will not return until the entire + written or an error is returned. This future will not resolve until the entire buffer has been successfully written or such an error occurs. [`write`]: #tymethod.write From ad156b1fce82ed441079bfe7f590686805d229a3 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 13 Oct 2019 15:55:32 +0900 Subject: [PATCH 009/407] feat: Add BufWriter::into_inner flush --- src/io/buf_writer.rs | 28 ++++++++++++++++++++++++++-- tests/buf_writer.rs | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 440e2705..ba3bf741 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use futures_core::ready; use crate::io::{self, Seek, SeekFrom, Write}; +use crate::io::write::WriteExt; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; @@ -83,6 +84,9 @@ pub struct BufWriter { written: usize, } +#[derive(Debug)] +pub struct IntoInnerError(W, std::io::Error); + impl BufWriter { pin_utils::unsafe_pinned!(inner: W); pin_utils::unsafe_unpinned!(buf: Vec); @@ -180,8 +184,28 @@ impl BufWriter { /// For method that will attempt to write before returning the writer see [`poll_into_inner`] /// /// [`poll_into_inner`]: #method.poll_into_inner - pub fn into_inner(self) -> W { - self.inner + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::io::BufWriter; + /// use async_std::net::TcpStream; + /// + /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buf_writer.into_inner().await.unwrap(); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn into_inner(mut self) -> Result>> + where + Self: Unpin + { + match self.flush().await { + Err(e) => Err(IntoInnerError(self, e)), + Ok(()) => Ok(self.inner), + } } /// Returns a reference to the internally buffered data. diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index 188cd8c8..fa0e1ed3 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -53,7 +53,7 @@ fn test_buffered_writer_inner_into_inner_does_not_flush() { let mut w = BufWriter::with_capacity(3, Vec::new()); w.write(&[0, 1]).await.unwrap(); assert_eq!(*w.get_ref(), []); - let w = w.into_inner(); + let w = w.into_inner().await.unwrap(); assert_eq!(w, []); }) } From 13ff627b09f872530b24b858420cc7f138554c6f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 13 Oct 2019 16:02:02 +0900 Subject: [PATCH 010/407] $cargo fmt --- src/io/buf_writer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index ba3bf741..f12aacbc 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -3,8 +3,8 @@ use std::pin::Pin; use futures_core::ready; -use crate::io::{self, Seek, SeekFrom, Write}; use crate::io::write::WriteExt; +use crate::io::{self, Seek, SeekFrom, Write}; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; @@ -200,7 +200,7 @@ impl BufWriter { /// ``` pub async fn into_inner(mut self) -> Result>> where - Self: Unpin + Self: Unpin, { match self.flush().await { Err(e) => Err(IntoInnerError(self, e)), From 9d9543c46b21e3fee7e2dca9fbe09b00539820bb Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sun, 13 Oct 2019 16:33:02 +0900 Subject: [PATCH 011/407] refactor: Remove needless main fn --- src/future/pending.rs | 4 ++-- src/future/poll_fn.rs | 4 ++-- src/future/ready.rs | 4 ++-- src/stream/empty.rs | 4 ++-- src/stream/mod.rs | 4 ++-- src/stream/once.rs | 4 ++-- src/stream/repeat.rs | 4 ++-- src/stream/stream/mod.rs | 4 ++-- src/sync/mod.rs | 4 ++-- src/sync/mutex.rs | 16 ++++++++-------- src/sync/rwlock.rs | 24 ++++++++++++------------ src/task/block_on.rs | 8 +++----- src/task/mod.rs | 8 ++++---- src/task/pool.rs | 4 ++-- src/task/sleep.rs | 4 ++-- src/task/task.rs | 4 ++-- src/task/worker.rs | 4 ++-- 17 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/future/pending.rs b/src/future/pending.rs index aaee7065..2138a301 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -9,7 +9,7 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use std::time::Duration; /// @@ -22,7 +22,7 @@ use crate::task::{Context, Poll}; /// let res: io::Result<()> = io::timeout(dur, fut).await; /// assert!(res.is_err()); /// # -/// # }) } +/// # }) /// ``` pub async fn pending() -> T { let fut = Pending { diff --git a/src/future/poll_fn.rs b/src/future/poll_fn.rs index 116e71c6..a808f97f 100644 --- a/src/future/poll_fn.rs +++ b/src/future/poll_fn.rs @@ -10,7 +10,7 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::future; /// use async_std::task::{Context, Poll}; @@ -21,7 +21,7 @@ use crate::task::{Context, Poll}; /// /// assert_eq!(future::poll_fn(poll_greeting).await, "hello world"); /// # -/// # }) } +/// # }) /// ``` pub async fn poll_fn(f: F) -> T where diff --git a/src/future/ready.rs b/src/future/ready.rs index 04f37b87..65cba563 100644 --- a/src/future/ready.rs +++ b/src/future/ready.rs @@ -7,13 +7,13 @@ /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::future; /// /// assert_eq!(future::ready(10).await, 10); /// # -/// # }) } +/// # }) /// ``` pub async fn ready(val: T) -> T { val diff --git a/src/stream/empty.rs b/src/stream/empty.rs index c9deea86..ceb91fea 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -9,7 +9,7 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -18,7 +18,7 @@ use crate::task::{Context, Poll}; /// /// assert_eq!(s.next().await, None); /// # -/// # }) } +/// # }) /// ``` pub fn empty() -> Empty { Empty { diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8aa12a2f..3d1b1036 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -7,7 +7,7 @@ //! # Examples //! //! ``` -//! # fn main() { async_std::task::block_on(async { +//! # async_std::task::block_on(async { //! # //! use async_std::prelude::*; //! use async_std::stream; @@ -18,7 +18,7 @@ //! assert_eq!(v, 9); //! } //! # -//! # }) } +//! # }) //! ``` use cfg_if::cfg_if; diff --git a/src/stream/once.rs b/src/stream/once.rs index 133a155c..be875e41 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -8,7 +8,7 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -18,7 +18,7 @@ use crate::task::{Context, Poll}; /// assert_eq!(s.next().await, Some(7)); /// assert_eq!(s.next().await, None); /// # -/// # }) } +/// # }) /// ``` pub fn once(t: T) -> Once { Once { value: Some(t) } diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index 1a6da411..75fd6973 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -8,7 +8,7 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -18,7 +18,7 @@ use crate::task::{Context, Poll}; /// assert_eq!(s.next().await, Some(7)); /// assert_eq!(s.next().await, Some(7)); /// # -/// # }) } +/// # }) /// ``` pub fn repeat(item: T) -> Repeat where diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8849605c..1bb0d319 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -7,7 +7,7 @@ //! # Examples //! //! ``` -//! # fn main() { async_std::task::block_on(async { +//! # async_std::task::block_on(async { //! # //! use async_std::prelude::*; //! use async_std::stream; @@ -18,7 +18,7 @@ //! assert_eq!(v, 9); //! } //! # -//! # }) } +//! # }) //! ``` mod all; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index df1d71ab..3d3b7b80 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -9,7 +9,7 @@ //! Spawn a task that updates an integer protected by a mutex: //! //! ``` -//! # fn main() { async_std::task::block_on(async { +//! # async_std::task::block_on(async { //! # //! use std::sync::Arc; //! @@ -26,7 +26,7 @@ //! //! assert_eq!(*m1.lock().await, 1); //! # -//! # }) } +//! # }) //! ``` #[doc(inline)] diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 673eb830..cd7a3577 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -24,7 +24,7 @@ const BLOCKED: usize = 1 << 1; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use std::sync::Arc; /// @@ -46,7 +46,7 @@ const BLOCKED: usize = 1 << 1; /// } /// assert_eq!(*m.lock().await, 10); /// # -/// # }) } +/// # }) /// ``` pub struct Mutex { state: AtomicUsize, @@ -82,7 +82,7 @@ impl Mutex { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use std::sync::Arc; /// @@ -99,7 +99,7 @@ impl Mutex { /// /// assert_eq!(*m2.lock().await, 20); /// # - /// # }) } + /// # }) /// ``` pub async fn lock(&self) -> MutexGuard<'_, T> { pub struct LockFuture<'a, T> { @@ -196,7 +196,7 @@ impl Mutex { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use std::sync::Arc; /// @@ -217,7 +217,7 @@ impl Mutex { /// /// assert_eq!(*m2.lock().await, 20); /// # - /// # }) } + /// # }) /// ``` pub fn try_lock(&self) -> Option> { if self.state.fetch_or(LOCK, Ordering::Acquire) & LOCK == 0 { @@ -249,7 +249,7 @@ impl Mutex { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::Mutex; /// @@ -257,7 +257,7 @@ impl Mutex { /// *mutex.get_mut() = 10; /// assert_eq!(*mutex.lock().await, 10); /// # - /// # }) } + /// # }) /// ``` pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.value.get() } diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 55a29fc4..ed1d2185 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -33,7 +33,7 @@ const READ_COUNT_MASK: usize = !(ONE_READ - 1); /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -51,7 +51,7 @@ const READ_COUNT_MASK: usize = !(ONE_READ - 1); /// *w += 1; /// assert_eq!(*w, 6); /// # -/// # }) } +/// # }) /// ``` pub struct RwLock { state: AtomicUsize, @@ -89,7 +89,7 @@ impl RwLock { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -100,7 +100,7 @@ impl RwLock { /// /// assert!(lock.try_read().is_some()); /// # - /// # }) } + /// # }) /// ``` pub async fn read(&self) -> RwLockReadGuard<'_, T> { pub struct LockFuture<'a, T> { @@ -211,7 +211,7 @@ impl RwLock { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -222,7 +222,7 @@ impl RwLock { /// /// assert!(lock.try_read().is_some()); /// # - /// # }) } + /// # }) /// ``` pub fn try_read(&self) -> Option> { let mut state = self.state.load(Ordering::Acquire); @@ -253,7 +253,7 @@ impl RwLock { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -264,7 +264,7 @@ impl RwLock { /// /// assert!(lock.try_read().is_none()); /// # - /// # }) } + /// # }) /// ``` pub async fn write(&self) -> RwLockWriteGuard<'_, T> { pub struct LockFuture<'a, T> { @@ -374,7 +374,7 @@ impl RwLock { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -385,7 +385,7 @@ impl RwLock { /// /// assert!(lock.try_write().is_none()); /// # - /// # }) } + /// # }) /// ``` pub fn try_write(&self) -> Option> { let mut state = self.state.load(Ordering::Acquire); @@ -431,7 +431,7 @@ impl RwLock { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; /// @@ -439,7 +439,7 @@ impl RwLock { /// *lock.get_mut() = 10; /// assert_eq!(*lock.write().await, 10); /// # - /// # }) } + /// # }) /// ``` pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.value.get() } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 11504002..31588c4d 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -31,11 +31,9 @@ use kv_log_macro::trace; /// ```no_run /// use async_std::task; /// -/// fn main() { -/// task::block_on(async { -/// println!("Hello, world!"); -/// }) -/// } +/// task::block_on(async { +/// println!("Hello, world!"); +/// }) /// ``` pub fn block_on(future: F) -> T where diff --git a/src/task/mod.rs b/src/task/mod.rs index a14e5d52..1f172c1c 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -10,7 +10,7 @@ //! Spawn a task and await its result: //! //! ``` -//! # fn main() { async_std::task::block_on(async { +//! # async_std::task::block_on(async { //! # //! use async_std::task; //! @@ -19,7 +19,7 @@ //! }); //! assert_eq!(handle.await, 3); //! # -//! # }) } +//! # }) //! ``` #[doc(inline)] @@ -64,7 +64,7 @@ pub(crate) mod blocking; /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::task; /// @@ -72,7 +72,7 @@ pub(crate) mod 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. diff --git a/src/task/pool.rs b/src/task/pool.rs index 49fbfe49..bfaa17d4 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -22,7 +22,7 @@ use crate::utils::abort_on_panic; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::task; /// @@ -32,7 +32,7 @@ use crate::utils::abort_on_panic; /// /// assert_eq!(handle.await, 3); /// # -/// # }) } +/// # }) /// ``` pub fn spawn(future: F) -> JoinHandle where diff --git a/src/task/sleep.rs b/src/task/sleep.rs index 9db09ffe..3e98755d 100644 --- a/src/task/sleep.rs +++ b/src/task/sleep.rs @@ -14,7 +14,7 @@ use crate::io; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use std::time::Duration; /// @@ -22,7 +22,7 @@ use crate::io; /// /// task::sleep(Duration::from_secs(1)).await; /// # -/// # }) } +/// # }) /// ``` pub async fn sleep(dur: Duration) { let _: io::Result<()> = io::timeout(dur, future::pending()).await; diff --git a/src/task/task.rs b/src/task/task.rs index ba808aa2..5100af44 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -68,7 +68,7 @@ impl JoinHandle { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::task; /// @@ -77,7 +77,7 @@ impl JoinHandle { /// }); /// println!("id = {}", handle.task().id()); /// # - /// # }) } + /// # }) pub fn task(&self) -> &Task { self.0.tag().task() } diff --git a/src/task/worker.rs b/src/task/worker.rs index fc2a6e7e..f7b08e16 100644 --- a/src/task/worker.rs +++ b/src/task/worker.rs @@ -22,13 +22,13 @@ use crate::utils::abort_on_panic; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # 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") From e27b578c27057478d01bdc3abe5fe48c2ace5cc7 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 13:06:39 +0200 Subject: [PATCH 012/407] WIP init Path and PathBuf async stubs --- src/fs/canonicalize.rs | 8 ++-- src/lib.rs | 3 +- src/path/mod.rs | 6 +++ src/path/path.rs | 105 +++++++++++++++++++++++++++++++++++++++++ src/path/pathbuf.rs | 34 +++++++++++++ 5 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/path/path.rs create mode 100644 src/path/pathbuf.rs diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index c484aeeb..84278cb5 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use crate::path::{Path, PathBuf}; use crate::io; use crate::task::blocking; @@ -32,6 +32,8 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn canonicalize>(path: P) -> io::Result { - let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::canonicalize(path) }).await + let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + Ok(blocking::spawn(async move { std::fs::canonicalize(&path) }) + .await? + .into()) } diff --git a/src/lib.rs b/src/lib.rs index fa4e946f..83453cd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ pub mod future; pub mod io; pub mod net; pub mod os; +pub mod path; pub mod prelude; pub mod stream; pub mod sync; @@ -62,8 +63,6 @@ pub mod task; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub mod path; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub mod pin; diff --git a/src/path/mod.rs b/src/path/mod.rs index 91532439..35a67179 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -4,6 +4,9 @@ //! //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html +mod path; +mod pathbuf; + // Structs re-export #[doc(inline)] pub use std::path::{Ancestors, Components, Display, Iter, PrefixComponent, StripPrefixError}; @@ -19,3 +22,6 @@ pub use std::path::MAIN_SEPARATOR; // Functions re-export #[doc(inline)] pub use std::path::is_separator; + +pub use path::Path; +pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs new file mode 100644 index 00000000..11dda640 --- /dev/null +++ b/src/path/path.rs @@ -0,0 +1,105 @@ +use std::ffi::OsStr; + +use crate::path::PathBuf; +use crate::{fs, io}; + +/// This struct is an async version of [`std::path::Path`]. +/// +/// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html +pub struct Path { + inner: OsStr, +} + +impl Path { + /// Yields the underlying [`OsStr`] slice. + /// + /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + pub fn as_os_str(&self) -> &OsStr { + &self.inner + } + + /// Returns the canonical, absolute form of the path with all intermediate + /// components normalized and symbolic links resolved. + /// + /// This is an alias to [`fs::canonicalize`]. + /// + /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html + /// + /// # Examples + /// + /// ```no_run + /// use crate::path::{Path, PathBuf}; + /// + /// let path = Path::new("/foo/test/../test/bar.rs"); + /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// ``` + pub async fn canonicalize(&self) -> io::Result { + fs::canonicalize(self).await + } + + /// Directly wraps a string slice as a `Path` slice. + /// + /// This is a cost-free conversion. + /// + /// # Examples + /// + /// ``` + /// use crate::path::Path; + /// + /// Path::new("foo.txt"); + /// ``` + /// + /// You can create `Path`s from `String`s, or even other `Path`s: + /// + /// ``` + /// use crate::path::Path; + /// + /// let string = String::from("foo.txt"); + /// let from_string = Path::new(&string); + /// let from_path = Path::new(&from_string); + /// assert_eq!(from_string, from_path); + /// ``` + pub fn new + ?Sized>(s: &S) -> &Path { + unsafe { &*(s.as_ref() as *const OsStr as *const Path) } + } + + /// Converts a `Path` to an owned [`PathBuf`]. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// + /// # Examples + /// + /// ``` + /// use crate::path::{Path, PathBuf}; + /// + /// let path_buf = Path::new("foo.txt").to_path_buf(); + /// assert_eq!(path_buf, PathBuf::from("foo.txt")); + /// ``` + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::from(self.inner.to_os_string()) + } +} + +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) -> &Path { + self + } +} + +impl std::fmt::Debug for Path { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.inner, formatter) + } +} diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs new file mode 100644 index 00000000..8e379adb --- /dev/null +++ b/src/path/pathbuf.rs @@ -0,0 +1,34 @@ +use std::ffi::OsString; + +/// This struct is an async version of [`std::path::PathBuf`]. +/// +/// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html +pub struct PathBuf { + inner: OsString, +} + +impl From for PathBuf { + fn from(path: std::path::PathBuf) -> PathBuf { + PathBuf { + inner: path.into_os_string(), + } + } +} + +impl Into for PathBuf { + fn into(self) -> std::path::PathBuf { + self.inner.into() + } +} + +impl From for PathBuf { + fn from(path: OsString) -> PathBuf { + PathBuf { inner: path } + } +} + +impl std::fmt::Debug for PathBuf { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.inner, formatter) + } +} From 3bd6a9df6d1ec8ad8252483c31568e3ca5eeefdc Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 13:39:12 +0200 Subject: [PATCH 013/407] Implemented components --- src/path/path.rs | 81 ++++++++++++++++++++++++++++++++++++++++++--- src/path/pathbuf.rs | 8 +++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/path/path.rs b/src/path/path.rs index 11dda640..f16c8c0b 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,6 @@ use std::ffi::OsStr; -use crate::path::PathBuf; +use crate::path::{Components, PathBuf}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -28,7 +28,7 @@ impl Path { /// # Examples /// /// ```no_run - /// use crate::path::{Path, PathBuf}; + /// use async_std::path::{Path, PathBuf}; /// /// let path = Path::new("/foo/test/../test/bar.rs"); /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); @@ -37,6 +37,45 @@ impl Path { fs::canonicalize(self).await } + /// Produces an iterator over the [`Component`]s of the path. + /// + /// When parsing the path, there is a small amount of normalization: + /// + /// * Repeated separators are ignored, so `a/b` and `a//b` both have + /// `a` and `b` as components. + /// + /// * Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and + /// `a/b` all have `a` and `b` as components, but `./a/b` starts with + /// an additional [`CurDir`] component. + /// + /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// + /// Note that no other normalization takes place; in particular, `a/c` + /// and `a/b/../c` are distinct, to account for the possibility that `b` + /// is a symbolic link (so its parent isn't `a`). + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, Component}; + /// use std::ffi::OsStr; + /// + /// 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) + /// ``` + /// + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + pub fn components(&self) -> Components<'_> { + let path: &std::path::Path = self.into(); + path.components() + } + /// Directly wraps a string slice as a `Path` slice. /// /// This is a cost-free conversion. @@ -44,7 +83,7 @@ impl Path { /// # Examples /// /// ``` - /// use crate::path::Path; + /// use async_std::path::Path; /// /// Path::new("foo.txt"); /// ``` @@ -52,7 +91,7 @@ impl Path { /// You can create `Path`s from `String`s, or even other `Path`s: /// /// ``` - /// use crate::path::Path; + /// use async_std::path::Path; /// /// let string = String::from("foo.txt"); /// let from_string = Path::new(&string); @@ -70,7 +109,7 @@ impl Path { /// # Examples /// /// ``` - /// use crate::path::{Path, PathBuf}; + /// use async_std::path::{Path, PathBuf}; /// /// let path_buf = Path::new("foo.txt").to_path_buf(); /// assert_eq!(path_buf, PathBuf::from("foo.txt")); @@ -98,8 +137,40 @@ impl AsRef for Path { } } +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +impl AsRef for str { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + impl std::fmt::Debug for Path { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.inner, formatter) } } + +impl std::cmp::PartialEq for Path { + fn eq(&self, other: &Path) -> bool { + self.components().eq(other.components()) + } +} + +impl std::cmp::Eq for Path {} + +impl std::cmp::PartialOrd for Path { + fn partial_cmp(&self, other: &Path) -> Option { + self.components().partial_cmp(other.components()) + } +} + +impl std::cmp::Ord for Path { + fn cmp(&self, other: &Path) -> std::cmp::Ordering { + self.components().cmp(other.components()) + } +} \ No newline at end of file diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 8e379adb..5ec0ed8f 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,5 +1,7 @@ use std::ffi::OsString; +use crate::path::Path; + /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html @@ -27,6 +29,12 @@ impl From for PathBuf { } } +impl AsRef for PathBuf { + fn as_ref(&self) -> &Path { + Path::new(&self.inner) + } +} + impl std::fmt::Debug for PathBuf { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.inner, formatter) From 930b81868d690e48d047b86470c254aba25fbae9 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 13:46:02 +0200 Subject: [PATCH 014/407] Use std variants of Path and PathBuf internally --- src/path/path.rs | 43 +++++-------------------------------------- src/path/pathbuf.rs | 10 +++++----- 2 files changed, 10 insertions(+), 43 deletions(-) diff --git a/src/path/path.rs b/src/path/path.rs index f16c8c0b..0a93ac6e 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -7,7 +7,7 @@ use crate::{fs, io}; /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html pub struct Path { - inner: OsStr, + inner: std::path::Path, } impl Path { @@ -15,7 +15,7 @@ impl Path { /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html pub fn as_os_str(&self) -> &OsStr { - &self.inner + self.inner.as_os_str() } /// Returns the canonical, absolute form of the path with all intermediate @@ -72,8 +72,7 @@ impl Path { /// [`Component`]: enum.Component.html /// [`CurDir`]: enum.Component.html#variant.CurDir pub fn components(&self) -> Components<'_> { - let path: &std::path::Path = self.into(); - path.components() + self.inner.components() } /// Directly wraps a string slice as a `Path` slice. @@ -99,7 +98,7 @@ impl Path { /// assert_eq!(from_string, from_path); /// ``` pub fn new + ?Sized>(s: &S) -> &Path { - unsafe { &*(s.as_ref() as *const OsStr as *const Path) } + unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } /// Converts a `Path` to an owned [`PathBuf`]. @@ -115,7 +114,7 @@ impl Path { /// assert_eq!(path_buf, PathBuf::from("foo.txt")); /// ``` pub fn to_path_buf(&self) -> PathBuf { - PathBuf::from(self.inner.to_os_string()) + PathBuf::from(self.inner.to_path_buf()) } } @@ -137,40 +136,8 @@ impl AsRef for Path { } } -impl AsRef for OsStr { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -impl AsRef for str { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - impl std::fmt::Debug for Path { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.inner, formatter) } } - -impl std::cmp::PartialEq for Path { - fn eq(&self, other: &Path) -> bool { - self.components().eq(other.components()) - } -} - -impl std::cmp::Eq for Path {} - -impl std::cmp::PartialOrd for Path { - fn partial_cmp(&self, other: &Path) -> Option { - self.components().partial_cmp(other.components()) - } -} - -impl std::cmp::Ord for Path { - fn cmp(&self, other: &Path) -> std::cmp::Ordering { - self.components().cmp(other.components()) - } -} \ No newline at end of file diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 5ec0ed8f..c3665e92 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -6,14 +6,12 @@ use crate::path::Path; /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html pub struct PathBuf { - inner: OsString, + inner: std::path::PathBuf, } impl From for PathBuf { fn from(path: std::path::PathBuf) -> PathBuf { - PathBuf { - inner: path.into_os_string(), - } + PathBuf { inner: path } } } @@ -25,7 +23,9 @@ impl Into for PathBuf { impl From for PathBuf { fn from(path: OsString) -> PathBuf { - PathBuf { inner: path } + PathBuf { + inner: std::path::PathBuf::from(path), + } } } From e690b55b18f086fa4aa1adf9833bb3603e0e7872 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 13:52:51 +0200 Subject: [PATCH 015/407] Implemented fs::metadata and Path::exists --- src/fs/metadata.rs | 5 ++--- src/path/path.rs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 2c9e41ec..ef99a568 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,8 +1,7 @@ -use std::path::Path; - use cfg_if::cfg_if; use crate::io; +use crate::path::Path; use crate::task::blocking; /// Reads metadata for a path. @@ -36,7 +35,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn metadata>(path: P) -> io::Result { - let path = path.as_ref().to_owned(); + let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); blocking::spawn(async move { std::fs::metadata(path) }).await } diff --git a/src/path/path.rs b/src/path/path.rs index 0a93ac6e..e7353e9b 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -75,6 +75,31 @@ impl Path { self.inner.components() } + /// Returns `true` if the path points at an existing entity. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// use async_std::path::Path; + /// assert_eq!(Path::new("does_not_exist.txt").exists(), false); + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata]. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + pub async fn exists(&self) -> bool { + fs::metadata(self).await.is_ok() + } + /// Directly wraps a string slice as a `Path` slice. /// /// This is a cost-free conversion. From 6bbfd039b1a69a70f7ecb57456036055120739c6 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 14:11:41 +0200 Subject: [PATCH 016/407] Fixed various tests --- src/path/path.rs | 31 ++++++++++++++++++++++++++----- src/path/pathbuf.rs | 17 ++++++++--------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/path/path.rs b/src/path/path.rs index e7353e9b..196935a2 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -6,6 +6,7 @@ use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html +#[derive(Debug, PartialEq)] pub struct Path { inner: std::path::Path, } @@ -28,10 +29,14 @@ impl Path { /// # Examples /// /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::path::{Path, PathBuf}; /// /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// assert_eq!(path.canonicalize().await.unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// # + /// # Ok(()) }) } /// ``` pub async fn canonicalize(&self) -> io::Result { fs::canonicalize(self).await @@ -86,8 +91,12 @@ impl Path { /// # Examples /// /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::path::Path; - /// assert_eq!(Path::new("does_not_exist.txt").exists(), false); + /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false); + /// # + /// # Ok(()) }) } /// ``` /// /// # See Also @@ -155,14 +164,26 @@ impl<'a> Into<&'a std::path::Path> for &'a Path { } } +impl AsRef for Path { + fn as_ref(&self) -> &OsStr { + self.inner.as_ref() + } +} + impl AsRef for Path { fn as_ref(&self) -> &Path { self } } -impl std::fmt::Debug for Path { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&self.inner, formatter) +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) } } + +impl AsRef for str { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} \ No newline at end of file diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index c3665e92..308200ee 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -5,6 +5,7 @@ use crate::path::Path; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html +#[derive(Debug, PartialEq)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -23,20 +24,18 @@ impl Into for PathBuf { impl From for PathBuf { fn from(path: OsString) -> PathBuf { - PathBuf { - inner: std::path::PathBuf::from(path), - } + std::path::PathBuf::from(path).into() } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &Path { - Path::new(&self.inner) +impl From<&str> for PathBuf { + fn from(path: &str) -> PathBuf { + std::path::PathBuf::from(path).into() } } -impl std::fmt::Debug for PathBuf { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&self.inner, formatter) +impl AsRef for PathBuf { + fn as_ref(&self) -> &Path { + Path::new(&self.inner) } } From 6c6106a292556cd34fcce346d4e2237dbf897aa6 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 14:17:33 +0200 Subject: [PATCH 017/407] Implemented Path::{metadata, symlink_metadata} --- src/fs/symlink_metadata.rs | 5 ++-- src/path/path.rs | 51 +++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index 6f1b9d50..7ccc5963 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -1,7 +1,6 @@ -use std::path::Path; - use crate::fs::Metadata; use crate::io; +use crate::path::Path; use crate::task::blocking; /// Reads metadata for a path without following symbolic links. @@ -34,6 +33,6 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { - let path = path.as_ref().to_owned(); + let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); blocking::spawn(async move { std::fs::symlink_metadata(path) }).await } diff --git a/src/path/path.rs b/src/path/path.rs index 196935a2..ffe80d94 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -109,6 +109,55 @@ impl Path { fs::metadata(self).await.is_ok() } + /// Queries the file system to get information about a file, directory, etc. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. + /// + /// This is an alias to [`fs::metadata`]. + /// + /// [`fs::metadata`]: ../fs/fn.metadata.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.metadata().await.expect("metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn metadata(&self) -> io::Result { + fs::metadata(self).await + } + + /// Queries the metadata about a file without following symlinks. + /// + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn symlink_metadata(&self) -> io::Result { + fs::symlink_metadata(self).await + } + /// Directly wraps a string slice as a `Path` slice. /// /// This is a cost-free conversion. @@ -186,4 +235,4 @@ impl AsRef for str { fn as_ref(&self) -> &Path { Path::new(self) } -} \ No newline at end of file +} From a57ba7ece0f99936e011774833470963604b093b Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 18:49:52 +0200 Subject: [PATCH 018/407] Implemented Path::into_path_buf --- src/path/path.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index ffe80d94..32e59b20 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -109,6 +109,17 @@ impl Path { fs::metadata(self).await.is_ok() } + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or + /// allocating. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html + /// [`PathBuf`]: struct.PathBuf.html + 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) }; + inner.into_path_buf().into() + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From 759e357bea1a90dc662da0c02062c7bbd382c4b3 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 18:52:59 +0200 Subject: [PATCH 019/407] Implemented Path::ancestors --- src/path/path.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/path/path.rs b/src/path/path.rs index 32e59b20..6e76028c 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,6 @@ use std::ffi::OsStr; -use crate::path::{Components, PathBuf}; +use crate::path::{Ancestors, Components, PathBuf}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -12,6 +12,32 @@ pub struct Path { } impl Path { + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, + /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns + /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, + /// namely `&self`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into())); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into())); + /// 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<'_> { + self.inner.ancestors() + } + /// Yields the underlying [`OsStr`] slice. /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html From 5235cd58be2852bb496caee919ae6d203a0c6296 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 18:56:26 +0200 Subject: [PATCH 020/407] Implemented Path::display --- src/path/path.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/path/path.rs b/src/path/path.rs index 6e76028c..dcda43aa 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,6 @@ use std::ffi::OsStr; -use crate::path::{Ancestors, Components, PathBuf}; +use crate::path::{Ancestors, Components, Display, PathBuf}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -106,6 +106,24 @@ impl Path { self.inner.components() } + /// Returns an object that implements [`Display`] for safely printing paths + /// that may contain non-Unicode data. + /// + /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/tmp/foo.rs"); + /// + /// println!("{}", path.display()); + /// ``` + pub fn display(&self) -> Display<'_> { + self.inner.display() + } + /// Returns `true` if the path points at an existing entity. /// /// This function will traverse symbolic links to query information about the From 40708334820cf9cfcaf164d3a14cc6746c070bab Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 18:58:36 +0200 Subject: [PATCH 021/407] Implemented Path::ends_with --- src/path/path.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index dcda43aa..fffe4c1d 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -124,6 +124,26 @@ impl Path { self.inner.display() } + /// Determines whether `child` is a suffix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.ends_with("passwd")); + /// ``` + pub fn ends_with>(&self, child: P) -> bool + where + P: std::convert::AsRef, + { + self.inner.ends_with(child) + } + /// Returns `true` if the path points at an existing entity. /// /// This function will traverse symbolic links to query information about the From a7eaae91ae2c192774f465c1e2e03b141987c08d Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:00:34 +0200 Subject: [PATCH 022/407] Implemented Path::extension --- src/path/path.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index fffe4c1d..c1b902bf 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -173,6 +173,31 @@ impl Path { fs::metadata(self).await.is_ok() } + /// Extracts the extension of [`self.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; + /// * Otherwise, the portion of the file name after the final `.` + /// + /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.rs"); + /// + /// assert_eq!("rs", path.extension().unwrap()); + /// ``` + pub fn extension(&self) -> Option<&OsStr> { + self.inner.extension() + } + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or /// allocating. /// From a6e1abecfceaf4a5ac26ae8a1f266217745f442e Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:03:33 +0200 Subject: [PATCH 023/407] Implemented Path::file_name --- src/path/path.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/path/path.rs b/src/path/path.rs index c1b902bf..83af6326 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -183,7 +183,7 @@ impl Path { /// * Otherwise, the portion of the file name after the final `.` /// /// [`self.file_name`]: struct.Path.html#method.file_name - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples /// @@ -198,6 +198,32 @@ impl Path { self.inner.extension() } + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of a directory, this + /// is the directory name. + /// + /// Returns [`None`] if the path terminates in `..`. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// use std::ffi::OsStr; + /// + /// 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()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); + /// ``` + pub fn file_name(&self) -> Option<&OsStr> { + self.inner.file_name() + } + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or /// allocating. /// From 28e936f6fec1d1a43df00c6452a36b1512052d28 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:05:18 +0200 Subject: [PATCH 024/407] Implemented Path::file_stem --- src/path/path.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 83af6326..2c0f718e 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -224,6 +224,32 @@ impl Path { self.inner.file_name() } + /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// + /// [`self.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; + /// * Otherwise, the portion of the file name before the final `.` + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.rs"); + /// + /// assert_eq!("foo", path.file_stem().unwrap()); + /// ``` + pub fn file_stem(&self) -> Option<&OsStr> { + self.inner.file_stem() + } + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or /// allocating. /// From 3a9597cd32a4e95b3a4cd2aa06bc241cc2f29e96 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:07:12 +0200 Subject: [PATCH 025/407] Implemented Path::has_root --- src/path/path.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 2c0f718e..be34172d 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -250,6 +250,26 @@ impl Path { self.inner.file_stem() } + /// Returns `true` if the `Path` has a root. + /// + /// * 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` + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// assert!(Path::new("/etc/passwd").has_root()); + /// ``` + pub fn has_root(&self) -> bool { + self.inner.has_root() + } + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or /// allocating. /// From 20f58ea1c1427ca19fddcc8fa063f9663863c0e7 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:10:16 +0200 Subject: [PATCH 026/407] Implemented Path::is_absolute --- src/path/path.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index be34172d..2f7e1c62 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -281,6 +281,28 @@ impl Path { inner.into_path_buf().into() } + /// 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. + /// + /// * 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. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// assert!(!Path::new("foo.txt").is_absolute()); + /// ``` + /// + /// [`has_root`]: #method.has_root + pub fn is_absolute(&self) -> bool { + self.inner.is_absolute() + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From df9a01f534adef469a3ba809177d96f9256a8e9b Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:12:14 +0200 Subject: [PATCH 027/407] Implemented Path::is_file --- src/path/path.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 2f7e1c62..7249f9d5 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -303,6 +303,76 @@ impl Path { self.inner.is_absolute() } + /// Returns `true` if the path exists on disk and is pointing at a directory. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// # 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); + /// # + /// # Ok(()) }) } + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_dir] if it was Ok. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir + pub async fn is_dir(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_dir()) + .unwrap_or(false) + } + + /// Returns `true` if the path exists on disk and is pointing at a regular file. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false); + /// assert_eq!(Path::new("a_file.txt").is_file().await, true); + /// # + /// # Ok(()) }) } + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_file] if it was Ok. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file + pub async fn is_file(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_file()) + .unwrap_or(false) + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From 5d87006006953f74b2125c5fc75a551805ca2299 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:16:52 +0200 Subject: [PATCH 028/407] Implemented Path::is_relative --- src/path/path.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 7249f9d5..5146bc38 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -373,6 +373,23 @@ impl Path { .unwrap_or(false) } + /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// + /// See [`is_absolute`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// assert!(Path::new("foo.txt").is_relative()); + /// ``` + /// + /// [`is_absolute`]: #method.is_absolute + pub fn is_relative(&self) -> bool { + self.inner.is_relative() + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From 0c03b923736eab63bf0d019fa855e4081548fa3a Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:31:17 +0200 Subject: [PATCH 029/407] Implemented Path::iter --- src/path/path.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/path/path.rs b/src/path/path.rs index 5146bc38..038fd24d 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,6 @@ use std::ffi::OsStr; -use crate::path::{Ancestors, Components, Display, PathBuf}; +use crate::path::{Ancestors, Components, Display, Iter, PathBuf}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -390,6 +390,31 @@ impl Path { self.inner.is_relative() } + /// Produces an iterator over the path's components viewed as [`OsStr`] + /// slices. + /// + /// For more information about the particulars of how the path is separated + /// into components, see [`components`]. + /// + /// [`components`]: #method.components + /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{self, Path}; + /// use std::ffi::OsStr; + /// + /// 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"))); + /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); + /// assert_eq!(it.next(), None) + /// ``` + pub fn iter(&self) -> Iter<'_> { + self.inner.iter() + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From cc57db02a3a41480592a07b5f8544d7aa0c2c2d7 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:33:55 +0200 Subject: [PATCH 030/407] Implemented Path::join --- src/path/path.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 038fd24d..2c4a97a6 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -415,6 +415,27 @@ impl Path { self.inner.iter() } + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. + /// + /// See [`PathBuf::push`] for more details on what it means to adjoin a path. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::push`]: struct.PathBuf.html#method.push + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// ``` + pub fn join>(&self, path: P) -> PathBuf + where + P: std::convert::AsRef, + { + self.inner.join(path).into() + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the From 141954d205c4a5e5da4c5fbf551e88ce7a7c2b53 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:38:33 +0200 Subject: [PATCH 031/407] Implemented Path::parent --- src/path/path.rs | 69 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/src/path/path.rs b/src/path/path.rs index 2c4a97a6..55a7922c 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -462,29 +462,6 @@ impl Path { fs::metadata(self).await } - /// Queries the metadata about a file without following symlinks. - /// - /// This is an alias to [`fs::symlink_metadata`]. - /// - /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); - /// println!("{:?}", metadata.file_type()); - /// # - /// # Ok(()) }) } - /// ``` - pub async fn symlink_metadata(&self) -> io::Result { - fs::symlink_metadata(self).await - } - /// Directly wraps a string slice as a `Path` slice. /// /// This is a cost-free conversion. @@ -511,6 +488,52 @@ impl Path { unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } + /// Returns the `Path` without its final component, if there is one. + /// + /// Returns [`None`] if the path terminates in a root or prefix. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); + /// + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); + /// ``` + pub fn parent(&self) -> Option<&Path> { + self.inner.parent().map(|p| p.into()) + } + + /// Queries the metadata about a file without following symlinks. + /// + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn symlink_metadata(&self) -> io::Result { + fs::symlink_metadata(self).await + } + /// Converts a `Path` to an owned [`PathBuf`]. /// /// [`PathBuf`]: struct.PathBuf.html From 04479b13c3a80895fc642079dfd465a640be58a9 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 12:13:29 +0200 Subject: [PATCH 032/407] add io::stdio Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 1 + src/io/stdio.rs | 29 +++++++++++++++++++++++++++++ src/lib.rs | 1 + src/macros.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 src/io/stdio.rs create mode 100644 src/macros.rs diff --git a/src/io/mod.rs b/src/io/mod.rs index 7a942854..eef6d731 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -55,5 +55,6 @@ mod repeat; mod sink; mod stderr; mod stdin; +mod stdio; mod stdout; mod timeout; diff --git a/src/io/stdio.rs b/src/io/stdio.rs new file mode 100644 index 00000000..2a4a3665 --- /dev/null +++ b/src/io/stdio.rs @@ -0,0 +1,29 @@ +//! Internal types for stdio. +//! +//! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`. + +use crate::io::{stderr, stdout, Write}; +use std::fmt; + +/// Write `args` `global_s`. `label` identifies the stream in a panic message. +async fn print_to( + args: fmt::Arguments<'_>, + global_s: fn() -> T, + label: &str, +) where + T: Write, +{ + if let Err(e) = global_s().write_fmt(args).await { + panic!("failed printing to {}: {}", label, e); + } +} + +#[doc(hidden)] +pub async fn _print(args: fmt::Arguments<'_>) { + print_to(args, stdout, "stdout"); +} + +#[doc(hidden)] +pub async fn _eprint(args: fmt::Arguments<'_>) { + print_to(args, stderr, "stderr"); +} diff --git a/src/lib.rs b/src/lib.rs index f1ed43e1..f69d1f89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ cfg_if! { } } +mod macros; pub(crate) mod utils; #[doc(inline)] diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..80d784da --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,43 @@ +/// Prints to the standard output. +/// +/// Equivalent to the [`println!`] macro except that a newline is not printed at +/// the end of the message. +/// +/// Note that stdout is frequently line-buffered by default so it may be +/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted +/// immediately. +/// +/// Use `print!` only for the primary output of your program. Use +/// [`eprint!`] instead to print error and progress messages. +/// +/// [`println!`]: macro.println.html +/// [flush]: io/trait.Write.html#tymethod.flush +/// [`eprint!`]: macro.eprint.html +/// +/// # Panics +/// +/// Panics if writing to `io::stdout()` fails. +/// +/// # Examples +/// +/// ``` +/// use std::io::{self, Write}; +/// +/// print!("this "); +/// print!("will "); +/// print!("be "); +/// print!("on "); +/// print!("the "); +/// print!("same "); +/// print!("line "); +/// +/// io::stdout().flush().unwrap(); +/// +/// print!("this string has a newline, why not choose println! instead?\n"); +/// +/// io::stdout().flush().unwrap(); +/// ``` +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); +} From 296d0d9d317a569f7cdf621809ceee356f5de578 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 12:48:41 +0200 Subject: [PATCH 033/407] add print macros Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 4 ++ src/io/stdio.rs | 24 +++---- src/io/write/write_fmt.rs | 2 +- src/macros.rs | 136 ++++++++++++++++++++++++++++++++++---- 4 files changed, 137 insertions(+), 29 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index eef6d731..812c97ab 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -39,6 +39,10 @@ pub use stdout::{stdout, Stdout}; pub use timeout::timeout; pub use write::Write; +// For use in the print macros. +#[doc(hidden)] +pub use stdio::{_eprint, _print}; + pub mod prelude; pub(crate) mod buf_read; diff --git a/src/io/stdio.rs b/src/io/stdio.rs index 2a4a3665..33d9e404 100644 --- a/src/io/stdio.rs +++ b/src/io/stdio.rs @@ -2,28 +2,20 @@ //! //! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`. -use crate::io::{stderr, stdout, Write}; +use crate::prelude::*; +use crate::io::{stderr, stdout}; use std::fmt; -/// Write `args` `global_s`. `label` identifies the stream in a panic message. -async fn print_to( - args: fmt::Arguments<'_>, - global_s: fn() -> T, - label: &str, -) where - T: Write, -{ - if let Err(e) = global_s().write_fmt(args).await { - panic!("failed printing to {}: {}", label, e); - } -} - #[doc(hidden)] pub async fn _print(args: fmt::Arguments<'_>) { - print_to(args, stdout, "stdout"); + if let Err(e) = stdout().write_fmt(args).await { + panic!("failed printing to stdout: {}", e); + } } #[doc(hidden)] pub async fn _eprint(args: fmt::Arguments<'_>) { - print_to(args, stderr, "stderr"); + if let Err(e) = stderr().write_fmt(args).await { + panic!("failed printing to stderr: {}", e); + } } diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index f5942289..a1149cde 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -36,7 +36,7 @@ impl Future for WriteFmtFuture<'_, T> { // Copy the data from the buffer into the writer until it's done. loop { - if buffer.is_empty() { + if *amt == buffer.len() as u64 { futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; return Poll::Ready(Ok(())); } diff --git a/src/macros.rs b/src/macros.rs index 80d784da..d06238c3 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -21,23 +21,135 @@ /// # Examples /// /// ``` -/// use std::io::{self, Write}; +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::io; +/// use async_std::print; /// -/// print!("this "); -/// print!("will "); -/// print!("be "); -/// print!("on "); -/// print!("the "); -/// print!("same "); -/// print!("line "); +/// print!("this ").await; +/// print!("will ").await; +/// print!("be ").await; +/// print!("on ").await; +/// print!("the ").await; +/// print!("same ").await; +/// print!("line ").await; /// -/// io::stdout().flush().unwrap(); +/// io::stdout().flush().await.unwrap(); /// -/// print!("this string has a newline, why not choose println! instead?\n"); +/// print!("this string has a newline, why not choose println! instead?\n").await; /// -/// io::stdout().flush().unwrap(); +/// io::stdout().flush().await.unwrap(); +/// # +/// # Ok(()) }) } /// ``` #[macro_export] macro_rules! print { - ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) +} + +/// Prints to the standard output, with a newline. +/// +/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone +/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). +/// +/// Use the [`format!`] syntax to write data to the standard output. +/// See [`std::fmt`] for more information. +/// +/// Use `println!` only for the primary output of your program. Use +/// [`eprintln!`] instead to print error and progress messages. +/// +/// [`format!`]: macro.format.html +/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html +/// [`eprintln!`]: macro.eprintln.html +/// # Panics +/// +/// Panics if writing to `io::stdout` fails. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::println; +/// +/// println!().await; // prints just a newline +/// println!("hello there!").await; +/// println!("format {} arguments", "some").await; +/// # +/// # Ok(()) }) } +/// ``` +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) +} + +/// Prints to the standard error. +/// +/// Equivalent to the [`print!`] macro, except that output goes to +/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for +/// example usage. +/// +/// Use `eprint!` only for error and progress messages. Use `print!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: io/struct.Stderr.html +/// [`print!`]: macro.print.html +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::eprint; +/// +/// eprint!("Error: Could not complete task").await; +/// # +/// # Ok(()) }) } +/// ``` +#[macro_export] +macro_rules! eprint { + ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))) +} + +/// Prints to the standard error, with a newline. +/// +/// Equivalent to the [`println!`] macro, except that output goes to +/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for +/// example usage. +/// +/// Use `eprintln!` only for error and progress messages. Use `println!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: io/struct.Stderr.html +/// [`println!`]: macro.println.html +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::eprintln; +/// +/// eprintln!("Error: Could not complete task").await; +/// # +/// # Ok(()) }) } +/// ``` +#[macro_export] +macro_rules! eprintln { + () => (async { $crate::eprint!("\n").await; }); + ($($arg:tt)*) => ( + async { + $crate::io::_eprint(format_args!($($arg)*)).await; + } + ); } From 467b64b6e75cac797dde8f63ea8133b99f9a7c76 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 12:51:21 +0200 Subject: [PATCH 034/407] doc fmt Signed-off-by: Yoshua Wuyts --- src/macros.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d06238c3..d7e4ae44 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -21,7 +21,7 @@ /// # Examples /// /// ``` -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::io; @@ -41,7 +41,7 @@ /// /// io::stdout().flush().await.unwrap(); /// # -/// # Ok(()) }) } +/// # }) /// ``` #[macro_export] macro_rules! print { @@ -69,7 +69,7 @@ macro_rules! print { /// # Examples /// /// ``` -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::println; /// @@ -77,7 +77,7 @@ macro_rules! print { /// println!("hello there!").await; /// println!("format {} arguments", "some").await; /// # -/// # Ok(()) }) } +/// # }) /// ``` #[macro_export] macro_rules! println { @@ -104,13 +104,13 @@ macro_rules! println { /// # Examples /// /// ``` -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::eprint; /// /// eprint!("Error: Could not complete task").await; /// # -/// # Ok(()) }) } +/// # }) /// ``` #[macro_export] macro_rules! eprint { @@ -136,13 +136,13 @@ macro_rules! eprint { /// # Examples /// /// ``` -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::eprintln; /// /// eprintln!("Error: Could not complete task").await; /// # -/// # Ok(()) }) } +/// # }) /// ``` #[macro_export] macro_rules! eprintln { From fef2e32a3c39e129fafce92c21396bf351d6a165 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 13:07:52 +0200 Subject: [PATCH 035/407] cargo fmt Signed-off-by: Yoshua Wuyts --- src/io/stdio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/stdio.rs b/src/io/stdio.rs index 33d9e404..0e11e1a9 100644 --- a/src/io/stdio.rs +++ b/src/io/stdio.rs @@ -2,8 +2,8 @@ //! //! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`. -use crate::prelude::*; use crate::io::{stderr, stdout}; +use crate::prelude::*; use std::fmt; #[doc(hidden)] From 89f73d3edac31e2dc6284ff39bf7b4fa298c65c9 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 19:57:46 +0200 Subject: [PATCH 036/407] Implemented Path::read_dir --- src/fs/read_dir.rs | 4 ++-- src/path/path.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 9b4269df..fe3ff2e5 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,9 +1,9 @@ -use std::path::Path; use std::pin::Pin; use crate::fs::DirEntry; use crate::future::Future; use crate::io; +use crate::path::Path; use crate::stream::Stream; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -44,7 +44,7 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// # Ok(()) }) } /// ``` pub async fn read_dir>(path: P) -> io::Result { - let path = path.as_ref().to_owned(); + let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); blocking::spawn(async move { std::fs::read_dir(path) }) .await .map(ReadDir::new) diff --git a/src/path/path.rs b/src/path/path.rs index 55a7922c..88d7ba84 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -511,6 +511,38 @@ impl Path { self.inner.parent().map(|p| p.into()) } + /// Returns an iterator over the entries within a directory. + /// + /// The iterator 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`]. + /// + /// [`io::Result`]: ../io/type.Result.html + /// [`DirEntry`]: ../fs/struct.DirEntry.html + /// [`fs::read_dir`]: ../fs/fn.read_dir.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// use async_std::fs; + /// + /// let path = Path::new("/laputa"); + /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); + /// while let Some(res) = dir.next().await { + /// let entry = res?; + /// println!("{}", entry.file_name().to_string_lossy()); + /// } + /// # + /// # Ok(()) }) } + /// ``` + pub async fn read_dir(&self) -> io::Result { + fs::read_dir(self).await + } + /// Queries the metadata about a file without following symlinks. /// /// This is an alias to [`fs::symlink_metadata`]. @@ -586,3 +618,9 @@ impl AsRef for str { Path::new(self) } } + +impl AsRef for String { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} From d349333a4377b0cd71ed0c808a572d0f3a0814b5 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:12:57 +0200 Subject: [PATCH 037/407] Implemented Path::read_link --- src/fs/read_link.rs | 9 +++++---- src/path/path.rs | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index aede99bc..b7ef69ac 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,6 +1,5 @@ -use std::path::{Path, PathBuf}; - use crate::io; +use crate::path::{Path, PathBuf}; use crate::task::blocking; /// Reads a symbolic link and returns the path it points to. @@ -28,6 +27,8 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn read_link>(path: P) -> io::Result { - let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::read_link(path) }).await + let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + Ok(blocking::spawn(async move { std::fs::read_link(path) }) + .await? + .into()) } diff --git a/src/path/path.rs b/src/path/path.rs index 88d7ba84..e159ca1b 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -529,6 +529,7 @@ impl Path { /// # /// use async_std::path::Path; /// use async_std::fs; + /// use futures_util::stream::StreamExt; /// /// let path = Path::new("/laputa"); /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); @@ -543,6 +544,28 @@ impl Path { fs::read_dir(self).await } + /// Reads a symbolic link, returning the file that the link points to. + /// + /// This is an alias to [`fs::read_link`]. + /// + /// [`fs::read_link`]: ../fs/fn.read_link.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// 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"); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn read_link(&self) -> io::Result { + fs::read_link(self).await + } + /// Queries the metadata about a file without following symlinks. /// /// This is an alias to [`fs::symlink_metadata`]. From 942403c52ca16870f3ca11625f61f8c2473fd132 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:14:07 +0200 Subject: [PATCH 038/407] Implemented Path::starts_with --- src/path/path.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index e159ca1b..f4cae23b 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -566,6 +566,31 @@ impl Path { fs::read_link(self).await } + /// Determines whether `base` is a prefix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.starts_with("/etc")); + /// assert!(path.starts_with("/etc/")); + /// assert!(path.starts_with("/etc/passwd")); + /// assert!(path.starts_with("/etc/passwd/")); + /// + /// assert!(!path.starts_with("/e")); + /// ``` + pub fn starts_with>(&self, base: P) -> bool + where + P: std::convert::AsRef, + { + self.inner.starts_with(base) + } + /// Queries the metadata about a file without following symlinks. /// /// This is an alias to [`fs::symlink_metadata`]. From df53a07fc5492519eb02e0e379d80ee39dbbd7f4 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:17:56 +0200 Subject: [PATCH 039/407] Implemented Path::strip_prefix --- src/path/path.rs | 37 ++++++++++++++++++++++++++++++++++++- src/path/pathbuf.rs | 6 ++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/path/path.rs b/src/path/path.rs index f4cae23b..c43ed049 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,6 @@ use std::ffi::OsStr; -use crate::path::{Ancestors, Components, Display, Iter, PathBuf}; +use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -591,6 +591,41 @@ impl Path { self.inner.starts_with(base) } + /// Returns a path that, when joined onto `base`, yields `self`. + /// + /// # Errors + /// + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] + /// returns `false`), returns [`Err`]. + /// + /// [`starts_with`]: #method.starts_with + /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/test/haha/foo.txt"); + /// + /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("test").is_ok(), false); + /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); + /// + /// let prefix = PathBuf::from("/test/"); + /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); + /// ``` + pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> + where + P: AsRef, + { + Ok(self.inner.strip_prefix(base)?.into()) + } + /// Queries the metadata about a file without following symlinks. /// /// This is an alias to [`fs::symlink_metadata`]. diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 308200ee..2f333286 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -39,3 +39,9 @@ impl AsRef for PathBuf { Path::new(&self.inner) } } + +impl AsRef for PathBuf { + fn as_ref(&self) -> &std::path::Path { + self.inner.as_ref() + } +} From ea43d7fd29f21cec0b2661c164a833a5aac14337 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:46:51 +0200 Subject: [PATCH 040/407] Implemented Path::to_str --- src/path/path.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index c43ed049..4bf53c3f 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -664,6 +664,26 @@ impl Path { pub fn to_path_buf(&self) -> PathBuf { PathBuf::from(self.inner.to_path_buf()) } + + /// Yields 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 + /// perfectly valid for some OS. + /// + /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_str(), Some("foo.txt")); + /// ``` + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } } impl<'a> From<&'a std::path::Path> for &'a Path { From a17b017e01ec96adce54a34a5aee98997564be27 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:49:57 +0200 Subject: [PATCH 041/407] Implemented Path::to_string_lossy --- src/path/path.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 4bf53c3f..a0ba8ef6 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -684,6 +684,31 @@ impl Path { pub fn to_str(&self) -> Option<&str> { self.inner.to_str() } + + /// Converts a `Path` to a [`Cow`]. + /// + /// Any non-Unicode sequences are replaced with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. + /// + /// [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html + /// [U+FFFD]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html + /// + /// # Examples + /// + /// Calling `to_string_lossy` on a `Path` with valid unicode: + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_string_lossy(), "foo.txt"); + /// ``` + /// + /// 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> { + self.inner.to_string_lossy() + } } impl<'a> From<&'a std::path::Path> for &'a Path { From 3c24b1891b7895be2fa0714f5c92b210737fbe1d Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:54:08 +0200 Subject: [PATCH 042/407] Implemented Path::with_extension --- src/path/path.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index a0ba8ef6..2179c00d 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -709,6 +709,25 @@ impl Path { pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> { self.inner.to_string_lossy() } + + /// Creates an owned [`PathBuf`] like `self` but with the given extension. + /// + /// See [`PathBuf::set_extension`] for more details. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// ``` + pub fn with_extension>(&self, extension: S) -> PathBuf { + self.inner.with_extension(extension).into() + } } impl<'a> From<&'a std::path::Path> for &'a Path { From 409a10a8b5eb2791fa855b3fc913ddabfb8595dd Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 20:55:48 +0200 Subject: [PATCH 043/407] Implemented Path::with_file_name --- src/path/path.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 2179c00d..21ab5e28 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -728,6 +728,28 @@ impl Path { pub fn with_extension>(&self, extension: S) -> PathBuf { self.inner.with_extension(extension).into() } + + /// Creates an owned [`PathBuf`] like `self` but with the given file name. + /// + /// See [`PathBuf::set_file_name`] for more details. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/tmp/foo.txt"); + /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); + /// + /// let path = Path::new("/tmp"); + /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); + /// ``` + pub fn with_file_name>(&self, file_name: S) -> PathBuf { + self.inner.with_file_name(file_name).into() + } } impl<'a> From<&'a std::path::Path> for &'a Path { From 1bd17f11f2809a1132fea2469af5b03437ecafe9 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:04:47 +0200 Subject: [PATCH 044/407] Implemented PathBuf::as_path --- src/path/pathbuf.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 2f333286..59f27460 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -10,6 +10,24 @@ pub struct PathBuf { inner: std::path::PathBuf, } +impl PathBuf { + /// Coerces to a [`Path`] slice. + /// + /// [`Path`]: struct.Path.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let p = PathBuf::from("/test"); + /// assert_eq!(Path::new("/test"), p.as_path()); + /// ``` + pub fn as_path(&self) -> &Path { + self.inner.as_path().into() + } +} + impl From for PathBuf { fn from(path: std::path::PathBuf) -> PathBuf { PathBuf { inner: path } From 80eaa285525c1526df71a1922f5b9f6244477ed5 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:11:39 +0200 Subject: [PATCH 045/407] Implemented PathBuf::into_boxed_path --- src/path/pathbuf.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 59f27460..c41de4a8 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -26,6 +26,15 @@ impl PathBuf { pub fn as_path(&self) -> &Path { self.inner.as_path().into() } + + /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html + /// [`Path`]: struct.Path.html + pub fn into_boxed_path(self) -> Box { + let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; + unsafe { Box::from_raw(rw) } + } } impl From for PathBuf { From a2baa1d8e0991b786504f5f10bdc2e853c201773 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 21:12:05 +0200 Subject: [PATCH 046/407] rename stream::join to stream::merge Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 3d1b1036..d165e874 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -48,6 +48,6 @@ cfg_if! { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] - pub use async_macros::{join_stream as join, JoinStream as Join}; + pub use async_macros::{join_stream as merge, JoinStream as Merge}; } } From 47ef222dab80059e5d8054bfcb21e6ed69fb18fb Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:13:01 +0200 Subject: [PATCH 047/407] Implemented PathBuf::into_os_string --- src/path/pathbuf.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index c41de4a8..5403967d 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -35,6 +35,22 @@ impl PathBuf { let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; unsafe { Box::from_raw(rw) } } + + /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// + /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let p = PathBuf::from("/the/head"); + /// let os_str = p.into_os_string(); + /// ``` + pub fn into_os_string(self) -> OsString { + self.inner.into_os_string() + } } impl From for PathBuf { From 71125d5c3b280f9e1aad4b7e2fd527e6e58256e7 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:19:23 +0200 Subject: [PATCH 048/407] Implemented PathBuf::new --- src/path/pathbuf.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 5403967d..2d6ba19e 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -51,6 +51,19 @@ impl PathBuf { pub fn into_os_string(self) -> OsString { self.inner.into_os_string() } + + /// Allocates an empty `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let path = PathBuf::new(); + /// ``` + pub fn new() -> PathBuf { + std::path::PathBuf::new().into() + } } impl From for PathBuf { From 07f9e485796c4fcc344d990554bde25751688398 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:20:43 +0200 Subject: [PATCH 049/407] Implemented PathBuf::pop --- src/path/pathbuf.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 2d6ba19e..b3a8d0a7 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -64,6 +64,30 @@ impl PathBuf { pub fn new() -> PathBuf { std::path::PathBuf::new().into() } + + /// Truncates `self` to [`self.parent`]. + /// + /// Returns `false` and does nothing if [`self.parent`] is [`None`]. + /// Otherwise, returns `true`. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`self.parent`]: struct.PathBuf.html#method.parent + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/test/test.rs"); + /// + /// p.pop(); + /// assert_eq!(Path::new("/test"), p.as_ref()); + /// p.pop(); + /// assert_eq!(Path::new("/"), p.as_ref()); + /// ``` + pub fn pop(&mut self) -> bool { + self.inner.pop() + } } impl From for PathBuf { From cc417cc0015a99d390526722c2066c6f1fc18693 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:38:47 +0200 Subject: [PATCH 050/407] Implemented PathBuf::push --- src/path/pathbuf.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index b3a8d0a7..9907cd39 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -88,6 +88,41 @@ impl PathBuf { pub fn pop(&mut self) -> bool { self.inner.pop() } + + /// Extends `self` with `path`. + /// + /// If `path` is absolute, it replaces the current path. + /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, it replaces `self`. + /// + /// # Examples + /// + /// Pushing a relative path extends the existing path: + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("file.bk"); + /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); + /// ``` + /// + /// Pushing an absolute path replaces the existing path: + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("/etc"); + /// assert_eq!(path, PathBuf::from("/etc")); + /// ``` + pub fn push>(&mut self, path: P) { + self.inner.push(path) + } } impl From for PathBuf { From 54c94b717c25a554f2f51f34da6df35b7aea23da Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:41:16 +0200 Subject: [PATCH 051/407] Implemented PathBuf::set_extension --- src/path/pathbuf.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 9907cd39..b4b781f5 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,4 +1,4 @@ -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use crate::path::Path; @@ -123,6 +123,35 @@ impl PathBuf { pub fn push>(&mut self, path: P) { self.inner.push(path) } + + /// Updates [`self.extension`] to `extension`. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// If [`self.extension`] is [`None`], the extension is added; otherwise + /// it is replaced. + /// + /// [`self.file_name`]: struct.PathBuf.html#method.file_name + /// [`self.extension`]: struct.PathBuf.html#method.extension + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.set_extension("force"); + /// assert_eq!(Path::new("/feel/the.force"), p.as_path()); + /// + /// p.set_extension("dark_side"); + /// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path()); + /// ``` + pub fn set_extension>(&mut self, extension: S) -> bool { + self.inner.set_extension(extension) + } } impl From for PathBuf { From 8df55dd015acc9baccd3141b47fda4e21f718b57 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Sun, 13 Oct 2019 21:42:38 +0200 Subject: [PATCH 052/407] Implemented PathBuf::set_file_name --- src/path/pathbuf.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index b4b781f5..928a65ab 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -152,6 +152,44 @@ impl PathBuf { pub fn set_extension>(&mut self, extension: S) -> bool { self.inner.set_extension(extension) } + + /// Updates [`self.file_name`] to `file_name`. + /// + /// If [`self.file_name`] was [`None`], this is equivalent to pushing + /// `file_name`. + /// + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) + /// + /// [`self.file_name`]: struct.PathBuf.html#method.file_name + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`pop`]: struct.PathBuf.html#method.pop + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let mut buf = PathBuf::from("/"); + /// assert!(buf.file_name() == None); + /// buf.set_file_name("bar"); + /// assert!(buf == PathBuf::from("/bar")); + /// assert!(buf.file_name().is_some()); + /// buf.set_file_name("baz.txt"); + /// assert!(buf == PathBuf::from("/baz.txt")); + /// ``` + pub fn set_file_name>(&mut self, file_name: S) { + self.inner.set_file_name(file_name) + } +} + +impl std::ops::Deref for PathBuf { + type Target = Path; + + fn deref(&self) -> &Path { + self.as_ref() + } } impl From for PathBuf { From 84a148ddae8e967bd4167d2b64c5d065115783d3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 21:48:53 +0200 Subject: [PATCH 053/407] rename stream::join to Stream::merge Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 4 ---- src/stream/stream/merge.rs | 42 ++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/stream/stream/merge.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d165e874..2e35f2da 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -45,9 +45,5 @@ cfg_if! { pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream; - - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[doc(inline)] - pub use async_macros::{join_stream as merge, JoinStream as Merge}; } } diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs new file mode 100644 index 00000000..6c213f27 --- /dev/null +++ b/src/stream/stream/merge.rs @@ -0,0 +1,42 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + +use futures_core::Stream; + +/// A stream merging two streams. +/// +/// This stream is returned by [`Stream::merge`]. +/// +/// [`Stream::merge`]: +#[derive(Debug)] +pub struct Merge { + left: L, + right: R, +} + +impl Unpin for Merge {} + +impl Merge { + pub(crate) fn new(left: L, right: R) -> Self { + Self { left, right } + } +} + +impl Stream for Merge +where + L: Stream + Unpin, + R: Stream + Unpin, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Poll::Ready(Some(item)) = Pin::new(&mut self.left).poll_next(cx) { + // The first stream made progress. The Merge needs to be polled + // again to check the progress of the second stream. + cx.waker().wake_by_ref(); + Poll::Ready(Some(item)) + } else { + Pin::new(&mut self.right).poll_next(cx) + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 1bb0d319..22ceabb9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -34,6 +34,7 @@ mod for_each; mod fuse; mod inspect; mod map; +mod merge; mod min_by; mod next; mod nth; @@ -91,6 +92,8 @@ cfg_if! { use crate::future::Future; use crate::stream::FromStream; + + pub use merge::Merge; } } @@ -1147,6 +1150,41 @@ extension_trait! { { FromStream::from_stream(self) } + + #[doc = r#" + Combines multiple streams into a single stream of all their outputs. + + 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::stream; + + let a = stream::once(1u8); + let b = stream::once(2u8); + let c = stream::once(3u8); + + let mut s = a.merge(b).merge(c); + + assert_eq!(s.next().await, Some(1u8)); + assert_eq!(s.next().await, Some(2u8)); + assert_eq!(s.next().await, Some(3u8)); + assert_eq!(s.next().await, None); + # }); + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn merge(self, other: U) -> Merge + where + Self: Sized, + U: Stream + Sized, + { + Merge::new(self, other) + } } impl Stream for Box { From b601bcfcb870023f7fda353109fe8b50be81c718 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 21:55:19 +0200 Subject: [PATCH 054/407] polish Signed-off-by: Yoshua Wuyts --- src/stream/stream/merge.rs | 4 ++-- src/stream/stream/mod.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 6c213f27..5e7b226e 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -3,11 +3,11 @@ use std::task::{Context, Poll}; use futures_core::Stream; -/// A stream merging two streams. +/// A stream that merges two other streams into a single stream. /// /// This stream is returned by [`Stream::merge`]. /// -/// [`Stream::merge`]: +/// [`Stream::merge`]: trait.Stream.html#method.merge #[derive(Debug)] pub struct Merge { left: L, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 22ceabb9..cf0eea16 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -34,7 +34,6 @@ mod for_each; mod fuse; mod inspect; mod map; -mod merge; mod min_by; mod next; mod nth; @@ -88,6 +87,8 @@ cfg_if! { cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod merge; + use std::pin::Pin; use crate::future::Future; From 04342c7b5dd6af770b013935fc7bb7bbed2341d4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 13 Oct 2019 22:05:11 +0200 Subject: [PATCH 055/407] docs Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 2 ++ src/stream/stream/mod.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 2e35f2da..359cb7aa 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -45,5 +45,7 @@ cfg_if! { pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream; + + pub use stream::Merge; } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cf0eea16..054d81b6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1155,7 +1155,8 @@ extension_trait! { #[doc = r#" Combines multiple streams into a single stream of all their outputs. - This macro is only usable inside of async functions, closures, and blocks. + Items are yielded as soon as they're received, and the stream continues yield until both + streams have been exhausted. # Examples From 05ba07daf884588517228bc5450ac3a5cc49d1ef Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 01:52:10 +0200 Subject: [PATCH 056/407] stabilize future::{join,try_join} Signed-off-by: Yoshua Wuyts --- src/future/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index 1f67e1aa..e5e696dd 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -44,9 +44,12 @@ #[doc(inline)] pub use std::future::Future; +#[doc(inline)] +pub use async_macros::{join, try_join}; + #[doc(inline)] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub use async_macros::{join, select, try_join, try_select}; +pub use async_macros::{select, try_select}; use cfg_if::cfg_if; From 3ac4575d9451f4b95e50e60e448b08cd0805fc60 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 02:21:27 +0200 Subject: [PATCH 057/407] add stream::FusedStream Signed-off-by: Yoshua Wuyts --- src/stream/fused_stream.rs | 21 +++++++++++++++++++++ src/stream/mod.rs | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 src/stream/fused_stream.rs diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs new file mode 100644 index 00000000..42e7e6f3 --- /dev/null +++ b/src/stream/fused_stream.rs @@ -0,0 +1,21 @@ +use crate::stream::Stream; + +/// A stream that always continues to yield `None` when exhausted. +/// +/// Calling next on a fused stream that has returned `None` once is guaranteed +/// to return [`None`] again. This trait should be implemented by all streams +/// that behave this way because it allows optimizing [`Stream::fuse`]. +/// +/// Note: In general, you should not use `FusedStream` in generic bounds if +/// you need a fused stream. Instead, you should just call [`Stream::fuse`] +/// on the stream. If the stream is already fused, the additional [`Fuse`] +/// wrapper will be a no-op with no performance penalty. +/// +/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None +/// [`Stream::fuse`]: trait.Stream.html#method.fuse +/// [`Fuse`]: struct.Fuse.html +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub trait FusedStream: Stream {} + +impl FusedStream for &mut S {} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 359cb7aa..9f5097d0 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -37,6 +37,7 @@ mod repeat; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; + mod fused_stream; mod extend; mod from_stream; mod into_stream; @@ -45,6 +46,7 @@ cfg_if! { pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream; + pub use fused_stream::FusedStream; pub use stream::Merge; } From d6aa1fb501e09babb5b2479dc27061e0f248897b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 13:58:26 +0200 Subject: [PATCH 058/407] Add task::yield_now as "unstable" (#300) Signed-off-by: Yoshua Wuyts --- src/task/mod.rs | 7 ++++++ src/task/yield_now.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/task/yield_now.rs diff --git a/src/task/mod.rs b/src/task/mod.rs index 1f172c1c..578a545f 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -49,6 +49,13 @@ mod worker; pub(crate) mod blocking; +cfg_if::cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + 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 diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs new file mode 100644 index 00000000..6f596388 --- /dev/null +++ b/src/task/yield_now.rs @@ -0,0 +1,53 @@ +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 +/// of the execution queue, making room for other futures to execute. This is +/// especially useful after running CPU-intensive operations inside a future. +/// +/// See also [`task::spawn_blocking`]. +/// +/// [`task::spawn_blocking`]: fn.spawn_blocking.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// task::yield_now().await; +/// # +/// # }) } +/// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[inline] +pub async fn yield_now() { + YieldNow(false).await +} + +struct YieldNow(bool); + +impl Future for YieldNow { + type Output = (); + + // The futures executor is implemented as a FIFO queue, so all this future + // does is re-schedule the future back to the end of the queue, giving room + // for other futures to progress. + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.0 { + self.0 = true; + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + } +} From fe88da4e640dd42c217d1cd429770cd1641e859c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 14:48:12 +0200 Subject: [PATCH 059/407] make all print macros unstable (#322) Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 ++ src/macros.rs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f69d1f89..cef50ae2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,5 +79,7 @@ cfg_if! { mod macros; pub(crate) mod utils; +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub use std::{write, writeln}; diff --git a/src/macros.rs b/src/macros.rs index d7e4ae44..d70b779f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -43,6 +43,8 @@ /// # /// # }) /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) @@ -79,6 +81,8 @@ macro_rules! print { /// # /// # }) /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! println { () => ($crate::print!("\n")); @@ -112,6 +116,8 @@ macro_rules! println { /// # /// # }) /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprint { ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))) @@ -144,6 +150,8 @@ macro_rules! eprint { /// # /// # }) /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprintln { () => (async { $crate::eprint!("\n").await; }); From a9950c5c9ff9d607e9b4ab0ba227f28fac47e605 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 15:06:24 +0200 Subject: [PATCH 060/407] stabilize task::ready! (#325) Signed-off-by: Yoshua Wuyts --- src/task/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index 578a545f..727dc593 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -25,8 +25,6 @@ #[doc(inline)] pub use std::task::{Context, Poll, Waker}; -#[cfg(any(feature = "unstable", feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub use async_macros::ready; From e1deaa58d8e7db68defeec3dcc9a88d184d8665d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 15:49:54 +0200 Subject: [PATCH 061/407] Add BufRead::split (#312) * add BufRead::split Signed-off-by: Yoshua Wuyts * fix docs Signed-off-by: Yoshua Wuyts * Update src/io/buf_read/mod.rs Co-Authored-By: Stjepan Glavina --- src/io/buf_read/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++++ src/io/buf_read/split.rs | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/io/buf_read/split.rs diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index d34b2ae2..987522b9 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -1,8 +1,11 @@ mod lines; mod read_line; mod read_until; +mod split; pub use lines::Lines; +pub use split::Split; + use read_line::ReadLineFuture; use read_until::ReadUntilFuture; @@ -226,6 +229,57 @@ extension_trait! { read: 0, } } + + #[doc = r#" + Returns a stream over the contents of this reader split on the byte `byte`. + + The stream returned from this function will return instances of + [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have + the delimiter byte at the end. + + This function will yield errors whenever [`read_until`] would have + also yielded an error. + + [`io::Result`]: type.Result.html + [`Vec`]: ../vec/struct.Vec.html + [`read_until`]: #method.read_until + + # Examples + + [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + this example, we use [`Cursor`] to iterate over all hyphen delimited + segments in a byte slice + + [`Cursor`]: struct.Cursor.html + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::io; + + let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + + let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + assert_eq!(split_iter.next().await, Some(b"lorem".to_vec())); + assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec())); + assert_eq!(split_iter.next().await, Some(b"dolor".to_vec())); + assert_eq!(split_iter.next().await, None); + # + # Ok(()) }) } + ``` + "#] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { + reader: self, + buf: Vec::new(), + delim: byte, + read: 0, + } + } } impl BufRead for Box { diff --git a/src/io/buf_read/split.rs b/src/io/buf_read/split.rs new file mode 100644 index 00000000..aa3b6fb6 --- /dev/null +++ b/src/io/buf_read/split.rs @@ -0,0 +1,46 @@ +use std::mem; +use std::pin::Pin; + +use super::read_until_internal; +use crate::io::{self, BufRead}; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. +/// +/// This stream is created by the [`split`] method on types that implement [`BufRead`]. +/// +/// This type is an async version of [`std::io::Split`]. +/// +/// [`split`]: trait.BufRead.html#method.lines +/// [`BufRead`]: trait.BufRead.html +/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html +#[derive(Debug)] +pub struct Split { + pub(crate) reader: R, + pub(crate) buf: Vec, + pub(crate) read: usize, + pub(crate) delim: u8, +} + +impl Stream for Split { + type Item = io::Result>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let Self { + reader, + buf, + read, + delim, + } = unsafe { self.get_unchecked_mut() }; + let reader = unsafe { Pin::new_unchecked(reader) }; + let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; + if n == 0 && buf.is_empty() { + return Poll::Ready(None); + } + if buf[buf.len() - 1] == *delim { + buf.pop(); + } + Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) + } +} From 612a94b31e74c5924dfbbe8c6779896eeede54ee Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 14 Oct 2019 15:51:47 +0200 Subject: [PATCH 062/407] Add process submodule as unstable (#310) Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 ++ src/process/mod.rs | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/process/mod.rs diff --git a/src/lib.rs b/src/lib.rs index cef50ae2..3ad178cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,8 @@ cfg_if! { pub mod path; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub mod pin; + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub mod process; mod unit; mod vec; diff --git a/src/process/mod.rs b/src/process/mod.rs new file mode 100644 index 00000000..630c5b9b --- /dev/null +++ b/src/process/mod.rs @@ -0,0 +1,14 @@ +//! A module for working with processes. +//! +//! This module is mostly concerned with spawning and interacting with child processes, but it also +//! provides abort and exit for terminating the current process. +//! +//! This is an async version of [`std::process`]. +//! +//! [`std::process`]: https://doc.rust-lang.org/std/process/index.html + +// Re-export structs. +pub use std::process::{ExitStatus, Output}; + +// Re-export functions. +pub use std::process::{abort, exit, id}; From 80bee9a215bf574d5137f58b5d946751b0eb16c8 Mon Sep 17 00:00:00 2001 From: assemblaj Date: Mon, 14 Oct 2019 11:43:00 -0400 Subject: [PATCH 063/407] Adds Stream::partial_cmp --- src/stream/stream/mod.rs | 38 +++++++++++++ src/stream/stream/partial_cmp.rs | 92 ++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/stream/stream/partial_cmp.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 054d81b6..27539af3 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -37,6 +37,7 @@ mod map; mod min_by; mod next; mod nth; +mod partial_cmp; mod scan; mod skip; mod skip_while; @@ -56,6 +57,7 @@ use for_each::ForEachFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; +use partial_cmp::PartialCmpFuture; use try_for_each::TryForEeachFuture; pub use chain::Chain; @@ -1187,6 +1189,42 @@ extension_trait! { { Merge::new(self, other) } + + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another. + + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + 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]); + assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); + assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); + assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); + assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); + assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); + # + # }) } + ``` + "#] + fn partial_cmp( + self, + other: S + ) -> impl Future> [PartialCmpFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + PartialCmpFuture::new(self, other) + } } impl Stream for Box { diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs new file mode 100644 index 00000000..fac2705d --- /dev/null +++ b/src/stream/stream/partial_cmp.rs @@ -0,0 +1,92 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::fuse::Fuse; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +// Lexicographically compares the elements of this `Stream` with those +// of another. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct PartialCmpFuture { + l: Fuse, + r: Fuse, + l_cache: Option, + r_cache: Option, +} + +impl PartialCmpFuture { + pin_utils::unsafe_pinned!(l: Fuse); + pin_utils::unsafe_pinned!(r: Fuse); + pin_utils::unsafe_unpinned!(l_cache: Option); + pin_utils::unsafe_unpinned!(r_cache: Option); + + pub(super) fn new(l: L, r: R) -> Self { + PartialCmpFuture { + l: l.fuse(), + r: r.fuse(), + l_cache: None, + r_cache: None, + } + } +} + +impl Future for PartialCmpFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialOrd, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + // Short circuit logic + // Stream that completes earliest can be considered Less, etc + let l_complete = self.l.done && self.as_mut().l_cache.is_none(); + let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + + if l_complete && r_complete { + return Poll::Ready(Some(Ordering::Equal)); + } else if l_complete { + return Poll::Ready(Some(Ordering::Less)); + } else if r_complete { + return Poll::Ready(Some(Ordering::Greater)); + } + + // Get next value if possible and necesary + if !self.l.done && self.as_mut().l_cache.is_none() { + let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + if let Some(item) = l_next { + *self.as_mut().l_cache() = Some(item); + } + } + + if !self.r.done && self.as_mut().r_cache.is_none() { + let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if let Some(item) = r_next { + *self.as_mut().r_cache() = Some(item); + } + } + + // Compare if both values are available. + if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { + let l_value = self.as_mut().l_cache().take().unwrap(); + let r_value = self.as_mut().r_cache().take().unwrap(); + let result = l_value.partial_cmp(&r_value); + + if let Some(Ordering::Equal) = result { + // Reset cache to prepare for next comparison + *self.as_mut().l_cache() = None; + *self.as_mut().r_cache() = None; + } else { + // Return non equal value + return Poll::Ready(result); + } + } + } + } +} From ba87048db559b3b892bf8ebc3901c65ddd352259 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 14 Oct 2019 22:00:45 +0200 Subject: [PATCH 064/407] Implemented our own Path::ancestors iterator --- src/path/mod.rs | 4 ++-- src/path/path.rs | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/path/mod.rs b/src/path/mod.rs index 35a67179..9b8cdc99 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -9,7 +9,7 @@ mod pathbuf; // Structs re-export #[doc(inline)] -pub use std::path::{Ancestors, Components, Display, Iter, PrefixComponent, StripPrefixError}; +pub use std::path::{Components, Display, Iter, PrefixComponent, StripPrefixError}; // Enums re-export #[doc(inline)] @@ -23,5 +23,5 @@ pub use std::path::MAIN_SEPARATOR; #[doc(inline)] pub use std::path::is_separator; -pub use path::Path; +pub use path::{Ancestors, Path}; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index 21ab5e28..e66c6c2c 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,6 +1,7 @@ use std::ffi::OsStr; +use std::iter::FusedIterator; -use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; +use crate::path::{Components, Display, Iter, PathBuf, StripPrefixError}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -35,7 +36,7 @@ impl Path { /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html /// [`parent`]: struct.Path.html#method.parent pub fn ancestors(&self) -> Ancestors<'_> { - self.inner.ancestors() + Ancestors { next: Some(&self) } } /// Yields the underlying [`OsStr`] slice. @@ -752,6 +753,42 @@ impl Path { } } +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: struct.Path.html#method.ancestors +/// [`Path`]: struct.Path.html +#[derive(Copy, Clone, Debug)] +pub struct Ancestors<'a> { + next: Option<&'a Path>, +} + +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + let next = self.next; + self.next = next.and_then(Path::parent); + next + } +} + +impl FusedIterator for Ancestors<'_> {} + 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()) From 0adcb50f584b1da7e1bd926ebaef0993392e6dd9 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 01:08:12 +0200 Subject: [PATCH 065/407] Add ToOwned and Borrow impls --- src/fs/canonicalize.rs | 2 +- src/fs/metadata.rs | 2 +- src/fs/read_dir.rs | 2 +- src/fs/read_link.rs | 2 +- src/fs/symlink_metadata.rs | 2 +- src/path/path.rs | 8 ++++++++ src/path/pathbuf.rs | 6 ++++++ 7 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 84278cb5..a671abae 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -32,7 +32,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn canonicalize>(path: P) -> io::Result { - let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + let path: PathBuf = path.as_ref().to_owned(); Ok(blocking::spawn(async move { std::fs::canonicalize(&path) }) .await? .into()) diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index ef99a568..8c923814 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -35,7 +35,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn metadata>(path: P) -> io::Result { - let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + let path = path.as_ref().to_owned(); blocking::spawn(async move { std::fs::metadata(path) }).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index fe3ff2e5..fd1a21f4 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -44,7 +44,7 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// # Ok(()) }) } /// ``` pub async fn read_dir>(path: P) -> io::Result { - let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + let path = path.as_ref().to_owned(); blocking::spawn(async move { std::fs::read_dir(path) }) .await .map(ReadDir::new) diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index b7ef69ac..37ef2c6f 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -27,7 +27,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn read_link>(path: P) -> io::Result { - let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + let path = path.as_ref().to_owned(); Ok(blocking::spawn(async move { std::fs::read_link(path) }) .await? .into()) diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index 7ccc5963..e2bc12dd 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -33,6 +33,6 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { - let path: std::path::PathBuf = path.as_ref().to_path_buf().into(); + let path = path.as_ref().to_owned(); blocking::spawn(async move { std::fs::symlink_metadata(path) }).await } diff --git a/src/path/path.rs b/src/path/path.rs index e66c6c2c..4cb40834 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -830,3 +830,11 @@ impl AsRef for String { Path::new(self) } } + +impl std::borrow::ToOwned for Path { + type Owned = PathBuf; + + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } +} diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 928a65ab..28062b27 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -192,6 +192,12 @@ impl std::ops::Deref for PathBuf { } } +impl std::borrow::Borrow for PathBuf { + fn borrow(&self) -> &Path { + &**self + } +} + impl From for PathBuf { fn from(path: std::path::PathBuf) -> PathBuf { PathBuf { inner: path } From f9cfee9e2cd1884f68cfa3ce2729c64981fb05a4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 01:11:32 +0200 Subject: [PATCH 066/407] Formatting --- src/fs/canonicalize.rs | 9 +++------ src/fs/read_link.rs | 4 +--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index a671abae..a763e4e4 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,6 +1,5 @@ -use crate::path::{Path, PathBuf}; - use crate::io; +use crate::path::{Path, PathBuf}; use crate::task::blocking; /// Returns the canonical form of a path. @@ -32,8 +31,6 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub async fn canonicalize>(path: P) -> io::Result { - let path: PathBuf = path.as_ref().to_owned(); - Ok(blocking::spawn(async move { std::fs::canonicalize(&path) }) - .await? - .into()) + let path = path.as_ref().to_owned(); + blocking::spawn(async move { std::fs::canonicalize(&path).map(Into::into) }).await } diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 37ef2c6f..53080ddc 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -28,7 +28,5 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - Ok(blocking::spawn(async move { std::fs::read_link(path) }) - .await? - .into()) + blocking::spawn(async move { std::fs::read_link(path).map(Into::into) }).await } From 504f8cb13741a7949a5016ec0f46fb7dbcc93424 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 01:25:20 +0200 Subject: [PATCH 067/407] Use crate::path everywhere --- src/fs/copy.rs | 3 +-- src/fs/create_dir.rs | 3 +-- src/fs/create_dir_all.rs | 3 +-- src/fs/dir_builder.rs | 3 +-- src/fs/dir_entry.rs | 4 ++-- src/fs/file.rs | 2 +- src/fs/hard_link.rs | 3 +-- src/fs/open_options.rs | 3 +-- src/fs/read.rs | 3 +-- src/fs/read_to_string.rs | 3 +-- src/fs/remove_dir.rs | 3 +-- src/fs/remove_dir_all.rs | 3 +-- src/fs/remove_file.rs | 3 +-- src/fs/rename.rs | 3 +-- src/fs/set_permissions.rs | 3 +-- src/fs/write.rs | 3 +-- src/os/unix/fs.rs | 3 +-- src/os/unix/net/datagram.rs | 2 +- src/os/unix/net/listener.rs | 2 +- src/os/unix/net/mod.rs | 6 +++--- src/os/unix/net/stream.rs | 2 +- src/path/path.rs | 18 +++++++++++++++--- 22 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/fs/copy.rs b/src/fs/copy.rs index fc3fd3e6..c0c6b9e9 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Copies the contents and permissions of a file to a new location. diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index aa2d5724..99f4ac45 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Creates a new directory. diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 33176f75..0dc446e1 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Creates a new directory and all of its parents if they are missing. diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 3064f7a3..1fb08502 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,9 +1,8 @@ -use std::path::Path; - use cfg_if::cfg_if; use crate::future::Future; use crate::io; +use crate::path::Path; use crate::task::blocking; /// A builder for creating directories with configurable options. diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 66b3cb7c..3d42f83b 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -1,12 +1,12 @@ use std::ffi::OsString; use std::fmt; -use std::path::PathBuf; use std::sync::Arc; use cfg_if::cfg_if; use crate::fs::{FileType, Metadata}; use crate::io; +use crate::path::PathBuf; use crate::task::blocking; /// An entry in a directory. @@ -50,7 +50,7 @@ impl DirEntry { /// # Ok(()) }) } /// ``` pub fn path(&self) -> PathBuf { - self.0.path() + self.0.path().into() } /// Reads the metadata for this entry. diff --git a/src/fs/file.rs b/src/fs/file.rs index bdf93474..b52bcd07 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -3,7 +3,6 @@ use std::cmp; use std::fmt; use std::io::{Read as _, Seek as _, Write as _}; use std::ops::{Deref, DerefMut}; -use std::path::Path; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -13,6 +12,7 @@ use cfg_if::cfg_if; use crate::fs::{Metadata, Permissions}; 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}; diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 5f950cde..2ae2cad4 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Creates a hard link on the filesystem. diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 252873cd..d7e0454f 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,10 +1,9 @@ -use std::path::Path; - use cfg_if::cfg_if; use crate::fs::File; use crate::future::Future; use crate::io; +use crate::path::Path; use crate::task::blocking; /// A builder for opening files with configurable options. diff --git a/src/fs/read.rs b/src/fs/read.rs index 6b3560db..b562ea3c 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Reads the entire contents of a file as raw bytes. diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index 345f76ef..a4d175fa 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Reads the entire contents of a file as a string. diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index a176edc8..f4571246 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Removes an empty directory. diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 9db0c31f..3b12d264 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Removes a directory and all of its contents. diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index cc0eeb25..216209fe 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Removes a file. diff --git a/src/fs/rename.rs b/src/fs/rename.rs index 72cd227f..f517a266 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Renames a file or directory to a new location. diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 6fa6306f..68dd8d40 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,7 +1,6 @@ -use std::path::Path; - use crate::fs::Permissions; use crate::io; +use crate::path::Path; use crate::task::blocking; /// Changes the permissions of a file or directory. diff --git a/src/fs/write.rs b/src/fs/write.rs index b0d7abcc..4db52212 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use crate::io; +use crate::path::Path; use crate::task::blocking; /// Writes a slice of bytes as the new contents of a file. diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index be8932c0..f00aaec6 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -1,10 +1,9 @@ //! Unix-specific filesystem extensions. -use std::path::Path; - use cfg_if::cfg_if; use crate::io; +use crate::path::Path; use crate::task::blocking; /// Creates a new symbolic link on the filesystem. diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 1f971f7f..61adc09e 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -2,7 +2,6 @@ use std::fmt; use std::net::Shutdown; -use std::path::Path; use mio_uds; @@ -11,6 +10,7 @@ use crate::future; 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; /// A Unix datagram socket. diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index eba2fadc..2d68a6b9 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -1,7 +1,6 @@ //! Unix-specific networking extensions. use std::fmt; -use std::path::Path; use std::pin::Pin; use mio_uds; @@ -12,6 +11,7 @@ use crate::future::{self, Future}; use crate::io; 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}; diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs index a719a484..2c79e8c3 100644 --- a/src/os/unix/net/mod.rs +++ b/src/os/unix/net/mod.rs @@ -13,7 +13,8 @@ mod stream; cfg_if! { if #[cfg(feature = "docs")] { use std::fmt; - use std::path::Path; + + use crate::path::Path; /// An address associated with a Unix socket. /// @@ -65,9 +66,8 @@ cfg_if! { /// With a pathname: /// /// ```no_run - /// use std::path::Path; - /// /// use async_std::os::unix::net::UnixListener; + /// use async_std::path::Path; /// /// let socket = UnixListener::bind("/tmp/socket").await?; /// let addr = socket.local_addr()?; diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index ae30b5bf..8245e638 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -3,7 +3,6 @@ use std::fmt; use std::io::{Read as _, Write as _}; use std::net::Shutdown; -use std::path::Path; use std::pin::Pin; use mio_uds; @@ -12,6 +11,7 @@ use super::SocketAddr; 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}; /// A Unix stream socket. diff --git a/src/path/path.rs b/src/path/path.rs index 4cb40834..4bc07573 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -801,9 +801,15 @@ impl<'a> Into<&'a std::path::Path> for &'a Path { } } -impl AsRef for Path { - fn as_ref(&self) -> &OsStr { - self.inner.as_ref() +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() } } @@ -813,6 +819,12 @@ 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) From 5c1e0522b755ea61ccb703561b8b8e8c362551c0 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 01:33:36 +0200 Subject: [PATCH 068/407] Fix failing tests --- src/path/path.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/path/path.rs b/src/path/path.rs index 4bc07573..63c86592 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -843,6 +843,12 @@ impl AsRef for String { } } +impl AsRef for std::path::PathBuf { + fn as_ref(&self) -> &Path { + Path::new(self.into()) + } +} + impl std::borrow::ToOwned for Path { type Owned = PathBuf; From aa13ba758ba17577f347a6da721f261b4e854409 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 02:05:23 +0200 Subject: [PATCH 069/407] Refactor --- src/path/ancestors.rs | 39 ++ src/path/mod.rs | 4 +- src/path/path.rs | 822 ++++++++++++++++++++---------------------- src/path/pathbuf.rs | 128 +++---- 4 files changed, 494 insertions(+), 499 deletions(-) create mode 100644 src/path/ancestors.rs diff --git a/src/path/ancestors.rs b/src/path/ancestors.rs new file mode 100644 index 00000000..c7237ffd --- /dev/null +++ b/src/path/ancestors.rs @@ -0,0 +1,39 @@ +use std::iter::FusedIterator; + +use crate::path::Path; + +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: struct.Path.html#method.ancestors +/// [`Path`]: struct.Path.html +#[derive(Copy, Clone, Debug)] +pub struct Ancestors<'a> { + pub(crate) next: Option<&'a Path>, +} + +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + let next = self.next; + self.next = next.and_then(Path::parent); + next + } +} + +impl FusedIterator for Ancestors<'_> {} diff --git a/src/path/mod.rs b/src/path/mod.rs index 9b8cdc99..36b9f2f0 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -4,6 +4,7 @@ //! //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html +mod ancestors; mod path; mod pathbuf; @@ -23,5 +24,6 @@ pub use std::path::MAIN_SEPARATOR; #[doc(inline)] pub use std::path::is_separator; -pub use path::{Ancestors, Path}; +use ancestors::Ancestors; +pub use path::Path; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index 63c86592..aa15ab22 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,7 +1,6 @@ use std::ffi::OsStr; -use std::iter::FusedIterator; -use crate::path::{Components, Display, Iter, PathBuf, StripPrefixError}; +use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -13,30 +12,30 @@ pub struct Path { } impl Path { - /// Produces an iterator over `Path` and its ancestors. + /// Directly wraps a string slice as a `Path` slice. /// - /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero - /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, - /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns - /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, - /// namely `&self`. + /// This is a cost-free conversion. /// /// # Examples /// /// ``` /// use async_std::path::Path; /// - /// let mut ancestors = Path::new("/foo/bar").ancestors(); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into())); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into())); - /// assert_eq!(ancestors.next(), Some(Path::new("/").into())); - /// assert_eq!(ancestors.next(), None); + /// Path::new("foo.txt"); /// ``` /// - /// [`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) } + /// You can create `Path`s from `String`s, or even other `Path`s: + /// + /// ``` + /// use async_std::path::Path; + /// + /// let string = String::from("foo.txt"); + /// let from_string = Path::new(&string); + /// let from_path = Path::new(&from_string); + /// assert_eq!(from_string, from_path); + /// ``` + pub fn new + ?Sized>(s: &S) -> &Path { + unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } /// Yields the underlying [`OsStr`] slice. @@ -46,144 +45,130 @@ impl Path { self.inner.as_os_str() } - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. + /// Yields a [`&str`] slice if the `Path` is valid unicode. /// - /// This is an alias to [`fs::canonicalize`]. + /// This conversion may entail doing a check for UTF-8 validity. + /// Note that validation is performed because non-UTF-8 strings are + /// perfectly valid for some OS. /// - /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html + /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html /// /// # Examples /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::{Path, PathBuf}; + /// ``` + /// use async_std::path::Path; /// - /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().await.unwrap(), PathBuf::from("/foo/test/bar.rs")); - /// # - /// # Ok(()) }) } + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_str(), Some("foo.txt")); /// ``` - pub async fn canonicalize(&self) -> io::Result { - fs::canonicalize(self).await + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() } - /// Produces an iterator over the [`Component`]s of the path. - /// - /// When parsing the path, there is a small amount of normalization: + /// Converts a `Path` to a [`Cow`]. /// - /// * Repeated separators are ignored, so `a/b` and `a//b` both have - /// `a` and `b` as components. + /// Any non-Unicode sequences are replaced with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// - /// * Occurrences of `.` are normalized away, except if they are at the - /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and - /// `a/b` all have `a` and `b` as components, but `./a/b` starts with - /// an additional [`CurDir`] component. + /// [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html + /// [U+FFFD]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html /// - /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// # Examples /// - /// Note that no other normalization takes place; in particular, `a/c` - /// and `a/b/../c` are distinct, to account for the possibility that `b` - /// is a symbolic link (so its parent isn't `a`). + /// Calling `to_string_lossy` on a `Path` with valid unicode: /// - /// # Examples + /// ``` + /// use async_std::path::Path; /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_string_lossy(), "foo.txt"); /// ``` - /// use async_std::path::{Path, Component}; - /// use std::ffi::OsStr; /// - /// let mut components = Path::new("/tmp/foo.txt").components(); + /// 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> { + self.inner.to_string_lossy() + } + + /// Converts a `Path` to an owned [`PathBuf`]. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// + /// # Examples /// - /// 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) /// ``` + /// use async_std::path::{Path, PathBuf}; /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir - pub fn components(&self) -> Components<'_> { - self.inner.components() + /// let path_buf = Path::new("foo.txt").to_path_buf(); + /// assert_eq!(path_buf, PathBuf::from("foo.txt")); + /// ``` + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::from(self.inner.to_path_buf()) } - /// Returns an object that implements [`Display`] for safely printing paths - /// that may contain non-Unicode data. + /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// the current directory. /// - /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html + /// * On Unix, a path is absolute if it starts with the root, so + /// `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. /// /// # Examples /// /// ``` /// use async_std::path::Path; /// - /// let path = Path::new("/tmp/foo.rs"); - /// - /// println!("{}", path.display()); + /// assert!(!Path::new("foo.txt").is_absolute()); /// ``` - pub fn display(&self) -> Display<'_> { - self.inner.display() + /// + /// [`has_root`]: #method.has_root + pub fn is_absolute(&self) -> bool { + self.inner.is_absolute() } - /// Determines whether `child` is a suffix of `self`. + /// Returns `true` if the `Path` is relative, i.e., not absolute. /// - /// Only considers whole path components to match. + /// See [`is_absolute`]'s documentation for more details. /// /// # Examples /// /// ``` /// use async_std::path::Path; /// - /// let path = Path::new("/etc/passwd"); - /// - /// assert!(path.ends_with("passwd")); + /// assert!(Path::new("foo.txt").is_relative()); /// ``` - pub fn ends_with>(&self, child: P) -> bool - where - P: std::convert::AsRef, - { - self.inner.ends_with(child) + /// + /// [`is_absolute`]: #method.is_absolute + pub fn is_relative(&self) -> bool { + self.inner.is_relative() } - /// Returns `true` if the path points at an existing entity. + /// Returns `true` if the `Path` has a root. /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. + /// * On Unix, a path has a root if it begins with `/`. /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. + /// * 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` /// /// # Examples /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false); - /// # - /// # Ok(()) }) } /// ``` + /// use async_std::path::Path; /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata]. - /// - /// [fs::metadata]: ../fs/fn.metadata.html - pub async fn exists(&self) -> bool { - fs::metadata(self).await.is_ok() + /// assert!(Path::new("/etc/passwd").has_root()); + /// ``` + pub fn has_root(&self) -> bool { + self.inner.has_root() } - /// Extracts the extension of [`self.file_name`], if possible. - /// - /// The extension is: + /// Returns the `Path` without its final component, if there is one. /// - /// * [`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 `.` + /// Returns [`None`] if the path terminates in a root or prefix. /// - /// [`self.file_name`]: struct.Path.html#method.file_name /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples @@ -191,12 +176,42 @@ impl Path { /// ``` /// use async_std::path::Path; /// - /// let path = Path::new("foo.rs"); + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); /// - /// assert_eq!("rs", path.extension().unwrap()); + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); /// ``` - pub fn extension(&self) -> Option<&OsStr> { - self.inner.extension() + pub fn parent(&self) -> Option<&Path> { + self.inner.parent().map(|p| p.into()) + } + + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, + /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns + /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, + /// namely `&self`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into())); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into())); + /// 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) } } /// Returns the final component of the `Path`, if there is one. @@ -225,170 +240,226 @@ impl Path { self.inner.file_name() } - /// Extracts the stem (non-extension) portion of [`self.file_name`]. - /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// Returns a path that, when joined onto `base`, yields `self`. /// - /// The stem is: + /// # Errors /// - /// * [`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 `.` + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] + /// returns `false`), returns [`Err`]. /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`starts_with`]: #method.starts_with + /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err /// /// # Examples /// /// ``` - /// use async_std::path::Path; + /// use async_std::path::{Path, PathBuf}; /// - /// let path = Path::new("foo.rs"); + /// let path = Path::new("/test/haha/foo.txt"); /// - /// assert_eq!("foo", path.file_stem().unwrap()); + /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("test").is_ok(), false); + /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); + /// + /// let prefix = PathBuf::from("/test/"); + /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); /// ``` - pub fn file_stem(&self) -> Option<&OsStr> { - self.inner.file_stem() + pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> + where + P: AsRef, + { + Ok(self.inner.strip_prefix(base.as_ref())?.into()) } - /// Returns `true` if the `Path` has a root. - /// - /// * On Unix, a path has a root if it begins with `/`. + /// Determines whether `base` is a prefix of `self`. /// - /// * 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` + /// Only considers whole path components to match. /// /// # Examples /// /// ``` /// use async_std::path::Path; /// - /// assert!(Path::new("/etc/passwd").has_root()); + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.starts_with("/etc")); + /// assert!(path.starts_with("/etc/")); + /// assert!(path.starts_with("/etc/passwd")); + /// assert!(path.starts_with("/etc/passwd/")); + /// + /// assert!(!path.starts_with("/e")); /// ``` - pub fn has_root(&self) -> bool { - self.inner.has_root() + pub fn starts_with>(&self, base: P) -> bool { + self.inner.starts_with(base.as_ref()) } - /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or - /// allocating. + /// Determines whether `child` is a suffix of `self`. /// - /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html - /// [`PathBuf`]: struct.PathBuf.html - 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) }; - inner.into_path_buf().into() + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.ends_with("passwd")); + /// ``` + pub fn ends_with>(&self, child: P) -> bool { + self.inner.ends_with(child.as_ref()) } - /// Returns `true` if the `Path` is absolute, i.e., if it is independent of - /// the current directory. + /// Extracts the stem (non-extension) portion of [`self.file_name`]. /// - /// * On Unix, a path is absolute if it starts with the root, so - /// `is_absolute` and [`has_root`] are equivalent. + /// [`self.file_name`]: struct.Path.html#method.file_name /// - /// * 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. + /// 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; + /// * Otherwise, the portion of the file name before the final `.` + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples /// /// ``` /// use async_std::path::Path; /// - /// assert!(!Path::new("foo.txt").is_absolute()); - /// ``` + /// let path = Path::new("foo.rs"); /// - /// [`has_root`]: #method.has_root - pub fn is_absolute(&self) -> bool { - self.inner.is_absolute() + /// assert_eq!("foo", path.file_stem().unwrap()); + /// ``` + pub fn file_stem(&self) -> Option<&OsStr> { + self.inner.file_stem() } - /// Returns `true` if the path exists on disk and is pointing at a directory. + /// Extracts the extension of [`self.file_name`], if possible. /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. + /// The extension is: /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. + /// * [`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 + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples /// - /// ```no_run - /// # 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); - /// # - /// # Ok(()) }) } + /// + /// let path = Path::new("foo.rs"); + /// + /// assert_eq!("rs", path.extension().unwrap()); /// ``` + pub fn extension(&self) -> Option<&OsStr> { + self.inner.extension() + } + + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// - /// # See Also + /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata] and handle its Result. Then call - /// [fs::Metadata::is_dir] if it was Ok. + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::push`]: struct.PathBuf.html#method.push /// - /// [fs::metadata]: ../fs/fn.metadata.html - /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir - pub async fn is_dir(&self) -> bool { - fs::metadata(self) - .await - .map(|m| m.is_dir()) - .unwrap_or(false) + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// ``` + pub fn join>(&self, path: P) -> PathBuf { + self.inner.join(path.as_ref()).into() } - /// Returns `true` if the path exists on disk and is pointing at a regular file. + /// Creates an owned [`PathBuf`] like `self` but with the given file name. /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. + /// See [`PathBuf::set_file_name`] for more details. /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name /// /// # Examples /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false); - /// assert_eq!(Path::new("a_file.txt").is_file().await, true); - /// # - /// # Ok(()) }) } /// ``` + /// use async_std::path::{Path, PathBuf}; /// - /// # See Also + /// let path = Path::new("/tmp/foo.txt"); + /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata] and handle its Result. Then call - /// [fs::Metadata::is_file] if it was Ok. + /// let path = Path::new("/tmp"); + /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); + /// ``` + pub fn with_file_name>(&self, file_name: S) -> PathBuf { + self.inner.with_file_name(file_name).into() + } + + /// Creates an owned [`PathBuf`] like `self` but with the given extension. /// - /// [fs::metadata]: ../fs/fn.metadata.html - /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file - pub async fn is_file(&self) -> bool { - fs::metadata(self) - .await - .map(|m| m.is_file()) - .unwrap_or(false) + /// See [`PathBuf::set_extension`] for more details. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// ``` + pub fn with_extension>(&self, extension: S) -> PathBuf { + self.inner.with_extension(extension).into() } - /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// Produces an iterator over the [`Component`]s of the path. /// - /// See [`is_absolute`]'s documentation for more details. + /// When parsing the path, there is a small amount of normalization: + /// + /// * Repeated separators are ignored, so `a/b` and `a//b` both have + /// `a` and `b` as components. + /// + /// * Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and + /// `a/b` all have `a` and `b` as components, but `./a/b` starts with + /// an additional [`CurDir`] component. + /// + /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// + /// Note that no other normalization takes place; in particular, `a/c` + /// and `a/b/../c` are distinct, to account for the possibility that `b` + /// is a symbolic link (so its parent isn't `a`). /// /// # Examples /// /// ``` - /// use async_std::path::Path; + /// use async_std::path::{Path, Component}; + /// use std::ffi::OsStr; /// - /// assert!(Path::new("foo.txt").is_relative()); + /// 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) /// ``` /// - /// [`is_absolute`]: #method.is_absolute - pub fn is_relative(&self) -> bool { - self.inner.is_relative() + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + pub fn components(&self) -> Components<'_> { + self.inner.components() } /// Produces an iterator over the path's components viewed as [`OsStr`] @@ -416,25 +487,22 @@ impl Path { self.inner.iter() } - /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. - /// - /// See [`PathBuf::push`] for more details on what it means to adjoin a path. + /// Returns an object that implements [`Display`] for safely printing paths + /// that may contain non-Unicode data. /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::push`]: struct.PathBuf.html#method.push + /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html /// /// # Examples /// /// ``` - /// use async_std::path::{Path, PathBuf}; + /// use async_std::path::Path; /// - /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// let path = Path::new("/tmp/foo.rs"); + /// + /// println!("{}", path.display()); /// ``` - pub fn join>(&self, path: P) -> PathBuf - where - P: std::convert::AsRef, - { - self.inner.join(path).into() + pub fn display(&self) -> Display<'_> { + self.inner.display() } /// Queries the file system to get information about a file, directory, etc. @@ -463,53 +531,72 @@ impl Path { fs::metadata(self).await } - /// Directly wraps a string slice as a `Path` slice. + /// Queries the metadata about a file without following symlinks. /// - /// This is a cost-free conversion. + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html /// /// # Examples /// - /// ``` + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::path::Path; /// - /// Path::new("foo.txt"); + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } /// ``` + pub async fn symlink_metadata(&self) -> io::Result { + fs::symlink_metadata(self).await + } + + /// Returns the canonical, absolute form of the path with all intermediate + /// components normalized and symbolic links resolved. /// - /// You can create `Path`s from `String`s, or even other `Path`s: + /// This is an alias to [`fs::canonicalize`]. /// - /// ``` - /// use async_std::path::Path; + /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html /// - /// let string = String::from("foo.txt"); - /// let from_string = Path::new(&string); - /// let from_path = Path::new(&from_string); - /// assert_eq!(from_string, from_path); + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// 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")); + /// # + /// # Ok(()) }) } /// ``` - pub fn new + ?Sized>(s: &S) -> &Path { - unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } + pub async fn canonicalize(&self) -> io::Result { + fs::canonicalize(self).await } - /// Returns the `Path` without its final component, if there is one. + /// Reads a symbolic link, returning the file that the link points to. /// - /// Returns [`None`] if the path terminates in a root or prefix. + /// This is an alias to [`fs::read_link`]. /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`fs::read_link`]: ../fs/fn.read_link.html /// /// # Examples /// - /// ``` + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::path::Path; /// - /// let path = Path::new("/foo/bar"); - /// let parent = path.parent().unwrap(); - /// assert_eq!(parent, Path::new("/foo")); - /// - /// let grand_parent = parent.parent().unwrap(); - /// assert_eq!(grand_parent, Path::new("/")); - /// assert_eq!(grand_parent.parent(), None); + /// let path = Path::new("/laputa/sky_castle.rs"); + /// let path_link = path.read_link().await.expect("read_link call failed"); + /// # + /// # Ok(()) }) } /// ``` - pub fn parent(&self) -> Option<&Path> { - self.inner.parent().map(|p| p.into()) + pub async fn read_link(&self) -> io::Result { + fs::read_link(self).await } /// Returns an iterator over the entries within a directory. @@ -545,11 +632,13 @@ impl Path { fs::read_dir(self).await } - /// Reads a symbolic link, returning the file that the link points to. + /// Returns `true` if the path points at an existing entity. /// - /// This is an alias to [`fs::read_link`]. + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. /// - /// [`fs::read_link`]: ../fs/fn.read_link.html + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. /// /// # Examples /// @@ -557,81 +646,28 @@ impl Path { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// 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"); + /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false); /// # /// # Ok(()) }) } /// ``` - pub async fn read_link(&self) -> io::Result { - fs::read_link(self).await - } - - /// Determines whether `base` is a prefix of `self`. - /// - /// Only considers whole path components to match. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("/etc/passwd"); /// - /// assert!(path.starts_with("/etc")); - /// assert!(path.starts_with("/etc/")); - /// assert!(path.starts_with("/etc/passwd")); - /// assert!(path.starts_with("/etc/passwd/")); - /// - /// assert!(!path.starts_with("/e")); - /// ``` - pub fn starts_with>(&self, base: P) -> bool - where - P: std::convert::AsRef, - { - self.inner.starts_with(base) - } - - /// Returns a path that, when joined onto `base`, yields `self`. - /// - /// # Errors - /// - /// If `base` is not a prefix of `self` (i.e., [`starts_with`] - /// returns `false`), returns [`Err`]. - /// - /// [`starts_with`]: #method.starts_with - /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/test/haha/foo.txt"); + /// # See Also /// - /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("test").is_ok(), false); - /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata]. /// - /// let prefix = PathBuf::from("/test/"); - /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); - /// ``` - pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> - where - P: AsRef, - { - Ok(self.inner.strip_prefix(base)?.into()) + /// [fs::metadata]: ../fs/fn.metadata.html + pub async fn exists(&self) -> bool { + fs::metadata(self).await.is_ok() } - /// Queries the metadata about a file without following symlinks. + /// Returns `true` if the path exists on disk and is pointing at a regular file. /// - /// This is an alias to [`fs::symlink_metadata`]. + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. /// - /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. /// /// # Examples /// @@ -639,156 +675,74 @@ impl Path { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); - /// println!("{:?}", metadata.file_type()); + /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false); + /// assert_eq!(Path::new("a_file.txt").is_file().await, true); /// # /// # Ok(()) }) } /// ``` - pub async fn symlink_metadata(&self) -> io::Result { - fs::symlink_metadata(self).await - } - - /// Converts a `Path` to an owned [`PathBuf`]. - /// - /// [`PathBuf`]: struct.PathBuf.html /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let path_buf = Path::new("foo.txt").to_path_buf(); - /// assert_eq!(path_buf, PathBuf::from("foo.txt")); - /// ``` - pub fn to_path_buf(&self) -> PathBuf { - PathBuf::from(self.inner.to_path_buf()) - } - - /// Yields 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 - /// perfectly valid for some OS. - /// - /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html - /// - /// # Examples + /// # See Also /// - /// ``` - /// use async_std::path::Path; + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_file] if it was Ok. /// - /// let path = Path::new("foo.txt"); - /// assert_eq!(path.to_str(), Some("foo.txt")); - /// ``` - pub fn to_str(&self) -> Option<&str> { - self.inner.to_str() + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file + pub async fn is_file(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_file()) + .unwrap_or(false) } - /// Converts a `Path` to a [`Cow`]. + /// Returns `true` if the path exists on disk and is pointing at a directory. /// - /// Any non-Unicode sequences are replaced with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. /// - /// [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html - /// [U+FFFD]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. /// /// # Examples /// - /// Calling `to_string_lossy` on a `Path` with valid unicode: - /// - /// ``` + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::path::Path; - /// - /// let path = Path::new("foo.txt"); - /// assert_eq!(path.to_string_lossy(), "foo.txt"); + /// assert_eq!(Path::new("./is_a_directory/").is_dir().await, true); + /// assert_eq!(Path::new("a_file.txt").is_dir().await, false); + /// # + /// # Ok(()) }) } /// ``` /// - /// 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> { - self.inner.to_string_lossy() - } - - /// Creates an owned [`PathBuf`] like `self` but with the given extension. - /// - /// See [`PathBuf::set_extension`] for more details. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension - /// - /// # Examples + /// # See Also /// - /// ``` - /// use async_std::path::{Path, PathBuf}; + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_dir] if it was Ok. /// - /// let path = Path::new("foo.rs"); - /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); - /// ``` - pub fn with_extension>(&self, extension: S) -> PathBuf { - self.inner.with_extension(extension).into() + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir + pub async fn is_dir(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_dir()) + .unwrap_or(false) } - /// Creates an owned [`PathBuf`] like `self` but with the given file name. - /// - /// See [`PathBuf::set_file_name`] for more details. + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or + /// allocating. /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/tmp/foo.txt"); - /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); - /// - /// let path = Path::new("/tmp"); - /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); - /// ``` - pub fn with_file_name>(&self, file_name: S) -> PathBuf { - self.inner.with_file_name(file_name).into() - } -} - -/// An iterator over [`Path`] and its ancestors. -/// -/// This `struct` is created by the [`ancestors`] method on [`Path`]. -/// See its documentation for more. -/// -/// # Examples -/// -/// ``` -/// use async_std::path::Path; -/// -/// let path = Path::new("/foo/bar"); -/// -/// for ancestor in path.ancestors() { -/// println!("{}", ancestor.display()); -/// } -/// ``` -/// -/// [`ancestors`]: struct.Path.html#method.ancestors -/// [`Path`]: struct.Path.html -#[derive(Copy, Clone, Debug)] -pub struct Ancestors<'a> { - next: Option<&'a Path>, -} - -impl<'a> Iterator for Ancestors<'a> { - type Item = &'a Path; - - fn next(&mut self) -> Option { - let next = self.next; - self.next = next.and_then(Path::parent); - next + 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) }; + inner.into_path_buf().into() } } -impl FusedIterator for Ancestors<'_> {} - 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()) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 28062b27..64744e14 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -11,6 +11,19 @@ pub struct PathBuf { } impl PathBuf { + /// Allocates an empty `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let path = PathBuf::new(); + /// ``` + pub fn new() -> PathBuf { + std::path::PathBuf::new().into() + } + /// Coerces to a [`Path`] slice. /// /// [`Path`]: struct.Path.html @@ -27,42 +40,39 @@ impl PathBuf { self.inner.as_path().into() } - /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + /// Extends `self` with `path`. /// - /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html - /// [`Path`]: struct.Path.html - pub fn into_boxed_path(self) -> Box { - let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; - unsafe { Box::from_raw(rw) } - } - - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// If `path` is absolute, it replaces the current path. /// - /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, it replaces `self`. /// /// # Examples /// + /// Pushing a relative path extends the existing path: + /// /// ``` /// use async_std::path::PathBuf; /// - /// let p = PathBuf::from("/the/head"); - /// let os_str = p.into_os_string(); + /// let mut path = PathBuf::from("/tmp"); + /// path.push("file.bk"); + /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); /// ``` - pub fn into_os_string(self) -> OsString { - self.inner.into_os_string() - } - - /// Allocates an empty `PathBuf`. /// - /// # Examples + /// Pushing an absolute path replaces the existing path: /// /// ``` /// use async_std::path::PathBuf; /// - /// let path = PathBuf::new(); + /// let mut path = PathBuf::from("/tmp"); + /// path.push("/etc"); + /// assert_eq!(path, PathBuf::from("/etc")); /// ``` - pub fn new() -> PathBuf { - std::path::PathBuf::new().into() + pub fn push>(&mut self, path: P) { + self.inner.push(path.as_ref()) } /// Truncates `self` to [`self.parent`]. @@ -89,39 +99,34 @@ impl PathBuf { self.inner.pop() } - /// Extends `self` with `path`. + /// Updates [`self.file_name`] to `file_name`. /// - /// If `path` is absolute, it replaces the current path. + /// If [`self.file_name`] was [`None`], this is equivalent to pushing + /// `file_name`. /// - /// On Windows: + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) /// - /// * if `path` has a root but no prefix (e.g., `\windows`), it - /// replaces everything except for the prefix (if any) of `self`. - /// * if `path` has a prefix but no root, it replaces `self`. + /// [`self.file_name`]: struct.PathBuf.html#method.file_name + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`pop`]: struct.PathBuf.html#method.pop /// /// # Examples /// - /// Pushing a relative path extends the existing path: - /// - /// ``` - /// use async_std::path::PathBuf; - /// - /// let mut path = PathBuf::from("/tmp"); - /// path.push("file.bk"); - /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); - /// ``` - /// - /// Pushing an absolute path replaces the existing path: - /// /// ``` /// use async_std::path::PathBuf; /// - /// let mut path = PathBuf::from("/tmp"); - /// path.push("/etc"); - /// assert_eq!(path, PathBuf::from("/etc")); + /// let mut buf = PathBuf::from("/"); + /// assert!(buf.file_name() == None); + /// buf.set_file_name("bar"); + /// assert!(buf == PathBuf::from("/bar")); + /// assert!(buf.file_name().is_some()); + /// buf.set_file_name("baz.txt"); + /// assert!(buf == PathBuf::from("/baz.txt")); /// ``` - pub fn push>(&mut self, path: P) { - self.inner.push(path) + pub fn set_file_name>(&mut self, file_name: S) { + self.inner.set_file_name(file_name) } /// Updates [`self.extension`] to `extension`. @@ -153,34 +158,29 @@ impl PathBuf { self.inner.set_extension(extension) } - /// Updates [`self.file_name`] to `file_name`. - /// - /// If [`self.file_name`] was [`None`], this is equivalent to pushing - /// `file_name`. - /// - /// Otherwise it is equivalent to calling [`pop`] and then pushing - /// `file_name`. The new path will be a sibling of the original path. - /// (That is, it will have the same parent.) + /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. /// - /// [`self.file_name`]: struct.PathBuf.html#method.file_name - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// [`pop`]: struct.PathBuf.html#method.pop + /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html /// /// # Examples /// /// ``` /// use async_std::path::PathBuf; /// - /// let mut buf = PathBuf::from("/"); - /// assert!(buf.file_name() == None); - /// buf.set_file_name("bar"); - /// assert!(buf == PathBuf::from("/bar")); - /// assert!(buf.file_name().is_some()); - /// buf.set_file_name("baz.txt"); - /// assert!(buf == PathBuf::from("/baz.txt")); + /// let p = PathBuf::from("/the/head"); + /// let os_str = p.into_os_string(); /// ``` - pub fn set_file_name>(&mut self, file_name: S) { - self.inner.set_file_name(file_name) + pub fn into_os_string(self) -> OsString { + self.inner.into_os_string() + } + + /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html + /// [`Path`]: struct.Path.html + pub fn into_boxed_path(self) -> Box { + let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; + unsafe { Box::from_raw(rw) } } } From 6be8467cdcf1706c1705df351c34bb67c2efb3f2 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Tue, 15 Oct 2019 09:50:03 +0200 Subject: [PATCH 070/407] impl Stream::take_while adapter (#332) * impl take_while stream adapter * fmt * add comment * unindent where clauses --- src/stream/mod.rs | 4 ++- src/stream/stream/mod.rs | 31 ++++++++++++++++++++++ src/stream/stream/take_while.rs | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/stream/stream/take_while.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 9f5097d0..6372d818 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,7 +26,9 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, Zip}; +pub use stream::{ + Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, TakeWhile, Zip, +}; pub(crate) mod stream; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 27539af3..0e563f6d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -43,6 +43,7 @@ mod skip; mod skip_while; mod step_by; mod take; +mod take_while; mod try_for_each; mod zip; @@ -70,6 +71,7 @@ pub use skip::Skip; pub use skip_while::SkipWhile; pub use step_by::StepBy; pub use take::Take; +pub use take_while::TakeWhile; pub use zip::Zip; use std::cmp::Ordering; @@ -241,6 +243,35 @@ extension_trait! { } } + #[doc = r#" + Creates a stream that yields elements based on a predicate. + + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); + let mut s = s.take_while(|x| x < &3 ); + + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, None); + + # + # }) } + "#] + fn take_while

(self, predicate: P) -> TakeWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + TakeWhile::new(self, predicate) + } + #[doc = r#" Creates a stream that yields each `step`th element. diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs new file mode 100644 index 00000000..6f3cc8f2 --- /dev/null +++ b/src/stream/stream/take_while.rs @@ -0,0 +1,47 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that yields elements based on a predicate. +#[derive(Debug)] +pub struct TakeWhile { + stream: S, + predicate: P, + __t: PhantomData, +} + +impl TakeWhile { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(predicate: P); + + pub(super) fn new(stream: S, predicate: P) -> Self { + TakeWhile { + stream, + predicate, + __t: PhantomData, + } + } +} + +impl Stream for TakeWhile +where + S: Stream, + P: FnMut(&S::Item) -> bool, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(_) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + None => Poll::Ready(None), + } + } +} From 1819408b466caf00fef36a4211dade815c3f1b19 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 09:51:53 +0200 Subject: [PATCH 071/407] add stream::ExactSizeStream as unstable (#330) Signed-off-by: Yoshua Wuyts --- src/prelude.rs | 12 ++++ src/stream/exact_size_stream.rs | 120 ++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 + 3 files changed, 134 insertions(+) create mode 100644 src/stream/exact_size_stream.rs diff --git a/src/prelude.rs b/src/prelude.rs index 645d7a24..f808da06 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,6 +11,8 @@ //! use async_std::prelude::*; //! ``` +use cfg_if::cfg_if; + #[doc(no_inline)] pub use crate::future::Future; #[doc(no_inline)] @@ -36,3 +38,13 @@ pub use crate::io::seek::SeekExt as _; pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + #[doc(no_inline)] + pub use crate::stream::DoubleEndedStream; + + #[doc(no_inline)] + pub use crate::stream::ExactSizeStream; + } +} diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs new file mode 100644 index 00000000..ef236910 --- /dev/null +++ b/src/stream/exact_size_stream.rs @@ -0,0 +1,120 @@ +pub use crate::stream::Stream; + +/// A stream that knows its exact length. +/// +/// Many [`Stream`]s don't know how many times they will iterate, but some do. +/// If a stream knows how many times it can iterate, providing access to +/// that information can be useful. For example, if you want to iterate +/// backwards, a good start is to know where the end is. +/// +/// When implementing an `ExactSizeStream`, you must also implement +/// [`Stream`]. When doing so, the implementation of [`size_hint`] *must* +/// return the exact size of the stream. +/// +/// [`Stream`]: trait.Stream.html +/// [`size_hint`]: trait.Stream.html#method.size_hint +/// +/// The [`len`] method has a default implementation, so you usually shouldn't +/// implement it. However, you may be able to provide a more performant +/// implementation than the default, so overriding it in this case makes sense. +/// +/// [`len`]: #method.len +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // a finite range knows exactly how many times it will iterate +/// let five = 0..5; +/// +/// assert_eq!(5, five.len()); +/// ``` +/// +/// In the [module level docs][moddocs], we implemented an [`Stream`], +/// `Counter`. Let's implement `ExactSizeStream` for it as well: +/// +/// [moddocs]: index.html +/// +/// ``` +/// # use std::task::{Context, Poll}; +/// # use std::pin::Pin; +/// # use async_std::prelude::*; +/// # struct Counter { +/// # count: usize, +/// # } +/// # impl Counter { +/// # fn new() -> Counter { +/// # Counter { count: 0 } +/// # } +/// # } +/// # impl Stream for Counter { +/// # type Item = usize; +/// # fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { +/// # self.count += 1; +/// # if self.count < 6 { +/// # Poll::Ready(Some(self.count)) +/// # } else { +/// # Poll::Ready(None) +/// # } +/// # } +/// # } +/// # fn main() { async_std::task::block_on(async { +/// # +/// impl ExactSizeStream for Counter { +/// // We can easily calculate the remaining number of iterations. +/// fn len(&self) -> usize { +/// 5 - self.count +/// } +/// } +/// +/// // And now we can use it! +/// +/// let counter = Counter::new(); +/// +/// assert_eq!(5, counter.len()); +/// # }); +/// # } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +pub trait ExactSizeStream: Stream { + /// Returns the exact number of times the stream will iterate. + /// + /// This method has a default implementation, so you usually should not + /// implement it directly. However, if you can provide a more efficient + /// implementation, you can do so. See the [trait-level] docs for an + /// example. + /// + /// This function has the same safety guarantees as the [`size_hint`] + /// function. + /// + /// [trait-level]: trait.ExactSizeStream.html + /// [`size_hint`]: trait.Stream.html#method.size_hint + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // a finite range knows exactly how many times it will iterate + /// let five = 0..5; + /// + /// assert_eq!(5, five.len()); + /// ``` + fn len(&self) -> usize { + let (lower, upper) = self.size_hint(); + // Note: This assertion is overly defensive, but it checks the invariant + // guaranteed by the trait. If this trait were rust-internal, + // we could use debug_assert!; assert_eq! will check all Rust user + // implementations too. + assert_eq!(upper, Some(lower)); + lower + } +} + +impl ExactSizeStream for &mut I { + fn len(&self) -> usize { + (**self).len() + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 6372d818..070983ed 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -39,12 +39,14 @@ mod repeat; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; + mod exact_size_stream; mod fused_stream; mod extend; mod from_stream; mod into_stream; pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream; From c7f654350202dddd7978fe1b36611a94d6e54868 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 15 Oct 2019 09:56:39 +0200 Subject: [PATCH 072/407] Inline TryFutureExt logic for src/io/timeout.rs (#317) --- src/io/timeout.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 2d1b29e7..69b6892c 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -1,6 +1,9 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::Duration; -use futures_timer::TryFutureExt; +use futures_core::future::TryFuture; +use futures_timer::Delay; use crate::future::Future; use crate::io; @@ -33,5 +36,48 @@ pub async fn timeout(dur: Duration, f: F) -> io::Result where F: Future>, { - f.timeout(dur).await + let f = TimeoutFuture { + timeout: Delay::new(dur), + future: f, + }; + f.await +} + +// Future returned by the [`io::timeout`](./fn.timeout.html) function. +#[derive(Debug)] +pub struct TimeoutFuture +where + F: Future>, +{ + future: F, + timeout: Delay, +} + +impl TimeoutFuture +where + F: Future>, +{ + pin_utils::unsafe_pinned!(future: F); + pin_utils::unsafe_pinned!(timeout: Delay); +} + +impl Future for TimeoutFuture +where + F: Future>, +{ + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.as_mut().future().try_poll(cx) { + Poll::Pending => {} + other => return other, + } + + if self.timeout().poll(cx).is_ready() { + let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); + Poll::Ready(err) + } else { + Poll::Pending + } + } } From f9741e7488eb8b0ba63fc50e8fa43732002bac7b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 15 Oct 2019 21:43:54 +0900 Subject: [PATCH 073/407] 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 074/407] 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 075/407] 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 aed9e2efdf779f2127fbd8350a7b51c856e68d04 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:27:27 +0200 Subject: [PATCH 076/407] removes Travis CI (#333) Signed-off-by: Yoshua Wuyts --- .travis.yml | 68 ----------------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bb3c836f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,68 +0,0 @@ -language: rust - -env: - - RUSTFLAGS="-D warnings" - -# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`. -cache: - directories: - - /home/travis/.cargo - -# Don't cache the cargo registry because it's too big. -before_cache: - - rm -rf /home/travis/.cargo/registry - - -branches: - only: - - master - - staging - - trying - -matrix: - fast_finish: true - include: - - rust: nightly - os: linux - - - rust: nightly - os: osx - osx_image: xcode9.2 - - - rust: nightly-x86_64-pc-windows-msvc - os: windows - - - name: fmt - rust: nightly - os: linux - before_script: | - if ! rustup component add rustfmt; then - target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`; - echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead"; - rustup toolchain install nightly-$target; - rustup default nightly-$target; - rustup component add rustfmt; - fi - script: - - cargo fmt --all -- --check - - - name: docs - rust: nightly - os: linux - script: - - cargo doc --features docs - - - name: book - rust: nightly - os: linux - before_script: - - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh - - cargo build # to find 'extern crate async_std' by `mdbook test` - script: - - mdbook build docs - - mdbook test -L ./target/debug/deps docs - -script: - - cargo check --all --benches --bins --examples --tests - - cargo check --features unstable --all --benches --bins --examples --tests - - cargo test --all --doc --features unstable From 35fc85a157b961aade8ee1d985af2174083fc0bf Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:27:41 +0200 Subject: [PATCH 077/407] clean readme (#331) * clean readme Signed-off-by: Yoshua Wuyts * add back features Signed-off-by: Yoshua Wuyts * Update README.md Co-Authored-By: Stjepan Glavina --- README.md | 203 ++++++++++++++++++++++++++---------------------------- 1 file changed, 96 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index ae129aa1..7aeaed86 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,78 @@ -# Async version of the Rust standard library - -[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std) -[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std) -[![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std) -[![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std) -[![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe) - -This crate provides an async version of [`std`]. It provides all the interfaces you -are used to, but in an async version and ready for Rust's `async`/`await` syntax. +

async-std

+
+ + Async version of the Rust standard library + +
+ +
+ +
+ + + +
+ +This crate provides an async version of [`std`]. It provides all the interfaces +you are used to, but in an async version and ready for Rust's `async`/`await` +syntax. [`std`]: https://doc.rust-lang.org/std/index.html -## Documentation +## Features -`async-std` comes with [extensive API documentation][docs] and a [book][book]. +- __Modern:__ Built from the ground up for `std::future` and `async/await` with + blazing fast compilation times. +- __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 + APIs once. +- __Clear:__ [Detailed documentation][docs] and [accessible guides][book] mean + using async Rust was never easier. [docs]: https://docs.rs/async-std [book]: https://book.async.rs -## Quickstart - -Add the following lines to your `Cargo.toml`: - -```toml -[dependencies] -async-std = "0.99" -``` - -Or use [cargo add][cargo-add] if you have it installed: - -```sh -$ cargo add async-std -``` - -[cargo-add]: https://github.com/killercup/cargo-edit - -## Hello world +## Examples ```rust use async_std::task; @@ -47,96 +84,48 @@ fn main() { } ``` -## Low-Friction Sockets with Built-In Timeouts - -```rust -use std::time::Duration; - -use async_std::{ - prelude::*, - task, - io, - net::TcpStream, -}; - -async fn get() -> io::Result> { - let mut stream = TcpStream::connect("example.com:80").await?; - stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?; - - let mut buf = vec![]; - - io::timeout(Duration::from_secs(5), async { - stream.read_to_end(&mut buf).await?; - Ok(buf) - }).await -} - -fn main() { - task::block_on(async { - let raw_response = get().await.expect("request"); - let response = String::from_utf8(raw_response) - .expect("utf8 conversion"); - println!("received: {}", response); - }); -} -``` - -## Features - -`async-std` is strongly commited to following semver. This means your code won't -break unless _you_ decide to upgrade. - -However every now and then we come up with something that we think will work -_great_ for `async-std`, and we want to provide a sneak-peek so you can try it -out. This is what we call _"unstable"_ features. You can try out the unstable -features by enabling the `unstable` feature in your `Cargo.toml` file: - -```toml -[dependencies.async-std] -version = "0.99" -features = ["unstable"] -``` - -Just be careful when using these features, as they may change between -versions. +More examples, including networking and file access, can be found in our +[`examples`] directory. -## Take a look around +[`examples`]: https://github.com/async-rs/async-std/tree/master/examples -Clone the repo: +## Philosophy -``` -git clone git@github.com:async-rs/async-std.git && cd async-std -``` +We believe Async Rust should be as easy to pick up as Sync Rust. We also believe +that the best API is the one you already know. And finally, we believe that +providing an asynchronous counterpart to the standard library is the best way +stdlib provides a reliable basis for both performance and productivity. -Generate docs: +Async-std is the embodiment of that vision. It combines single-allocation task +creation, with an adaptive lock-free executor, threadpool and network driver to +create a smooth system that processes work at a high pace with low latency, +using Rust's familiar stdlib API. -``` -cargo +nightly doc --features docs --open -``` +## Installation -Check out the [examples](examples). To run an example: +With [cargo add][cargo-add] installed run: +```sh +$ cargo add async-std ``` -cargo +nightly run --example hello-world -``` - -## Contributing -See [our contribution document][contribution]. +We also provide a set of "unstable" features with async-std. See the [features +documentation] on how to enable them. -[contribution]: https://async.rs/contribute +[cargo-add]: https://github.com/killercup/cargo-edit +[features documentation]: https://docs.rs/async-std/#features ## License -Licensed under either of - - * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + -#### Contribution +
+ Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + From e938527f660647ab0efecf5c9e1ff71191f33901 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:30:24 +0200 Subject: [PATCH 078/407] add stream::interval (#298) * add stream::interval Signed-off-by: Yoshua Wuyts * fix tests Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts * cross-docs Signed-off-by: Yoshua Wuyts * update deps Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- src/io/timeout.rs | 22 ++--- src/stream/interval.rs | 198 +++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 4 +- src/task/sleep.rs | 4 + 5 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 src/stream/interval.rs diff --git a/Cargo.toml b/Cargo.toml index 0c652988..dc319f71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" futures-core-preview = "=0.3.0-alpha.19" futures-io-preview = "=0.3.0-alpha.19" -futures-timer = "0.4.0" +futures-timer = "1.0.2" lazy_static = "1.4.0" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 69b6892c..9fcc15ef 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -2,8 +2,8 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use futures_core::future::TryFuture; use futures_timer::Delay; +use pin_utils::unsafe_pinned; use crate::future::Future; use crate::io; @@ -36,16 +36,16 @@ pub async fn timeout(dur: Duration, f: F) -> io::Result where F: Future>, { - let f = TimeoutFuture { + Timeout { timeout: Delay::new(dur), future: f, - }; - f.await + } + .await } -// Future returned by the [`io::timeout`](./fn.timeout.html) function. +/// Future returned by the `FutureExt::timeout` method. #[derive(Debug)] -pub struct TimeoutFuture +pub struct Timeout where F: Future>, { @@ -53,22 +53,22 @@ where timeout: Delay, } -impl TimeoutFuture +impl Timeout where F: Future>, { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(timeout: Delay); + unsafe_pinned!(future: F); + unsafe_pinned!(timeout: Delay); } -impl Future for TimeoutFuture +impl Future for Timeout where F: Future>, { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().try_poll(cx) { + match self.as_mut().future().poll(cx) { Poll::Pending => {} other => return other, } diff --git a/src/stream/interval.rs b/src/stream/interval.rs new file mode 100644 index 00000000..271cd81d --- /dev/null +++ b/src/stream/interval.rs @@ -0,0 +1,198 @@ +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 pin_utils::unsafe_pinned; + +use futures_timer::Delay; + +/// Creates a new stream that yields at a set interval. +/// +/// The stream first yields after `dur`, and continues to yield every +/// `dur` after that. The stream accounts for time elapsed between calls, and +/// will adjust accordingly to prevent time skews. +/// +/// Each interval may be slightly longer than the specified duration, but never +/// less. +/// +/// Note that intervals are not intended for high resolution timers, but rather +/// they will likely fire some granularity after the exact instant that they're +/// otherwise indicated to fire at. +/// +/// See also: [`task::sleep`]. +/// +/// [`task::sleep`]: ../task/fn.sleep.html +/// +/// # Examples +/// +/// Basic example: +/// +/// ```no_run +/// use async_std::prelude::*; +/// use async_std::stream; +/// use std::time::Duration; +/// +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// let mut interval = stream::interval(Duration::from_secs(4)); +/// while let Some(_) = interval.next().await { +/// println!("prints every four seconds"); +/// } +/// # +/// # Ok(()) }) } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub fn interval(dur: Duration) -> Interval { + Interval { + delay: Delay::new(dur), + interval: dur, + } +} + +/// A stream representing notifications at fixed interval +/// +#[derive(Debug)] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub struct Interval { + delay: Delay, + interval: Duration, +} + +impl Interval { + unsafe_pinned!(delay: Delay); +} + +impl Stream for Interval { + type Item = (); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if Pin::new(&mut *self).delay().poll(cx).is_pending() { + return Poll::Pending; + } + let when = Instant::now(); + let next = next_interval(when, Instant::now(), self.interval); + self.delay.reset(next); + Poll::Ready(Some(())) + } +} + +/// Converts Duration object to raw nanoseconds if possible +/// +/// This is useful to divide intervals. +/// +/// While technically for large duration it's impossible to represent any +/// duration as nanoseconds, the largest duration we can represent is about +/// 427_000 years. Large enough for any interval we would use or calculate in +/// tokio. +fn duration_to_nanos(dur: Duration) -> Option { + dur.as_secs() + .checked_mul(1_000_000_000) + .and_then(|v| v.checked_add(u64::from(dur.subsec_nanos()))) +} + +fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant { + let new = prev + interval; + if new > now { + return new; + } + + let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired"); + let interval_ns = + duration_to_nanos(interval).expect("interval is less that 427 thousand years"); + let mult = spent_ns / interval_ns + 1; + assert!( + mult < (1 << 32), + "can't skip more than 4 billion intervals of {:?} \ + (trying to skip {})", + interval, + mult + ); + prev + interval * (mult as u32) +} + +#[cfg(test)] +mod test { + use super::next_interval; + use std::time::{Duration, Instant}; + + struct Timeline(Instant); + + impl Timeline { + fn new() -> Timeline { + Timeline(Instant::now()) + } + fn at(&self, millis: u64) -> Instant { + self.0 + Duration::from_millis(millis) + } + fn at_ns(&self, sec: u64, nanos: u32) -> Instant { + self.0 + Duration::new(sec, nanos) + } + } + + fn dur(millis: u64) -> Duration { + Duration::from_millis(millis) + } + + // The math around Instant/Duration isn't 100% precise due to rounding + // errors, see #249 for more info + fn almost_eq(a: Instant, b: Instant) -> bool { + if a == b { + true + } else if a > b { + a - b < Duration::from_millis(1) + } else { + b - a < Duration::from_millis(1) + } + } + + #[test] + fn norm_next() { + let tm = Timeline::new(); + assert!(almost_eq( + next_interval(tm.at(1), tm.at(2), dur(10)), + tm.at(11) + )); + assert!(almost_eq( + next_interval(tm.at(7777), tm.at(7788), dur(100)), + tm.at(7877) + )); + assert!(almost_eq( + next_interval(tm.at(1), tm.at(1000), dur(2100)), + tm.at(2101) + )); + } + + #[test] + fn fast_forward() { + let tm = Timeline::new(); + assert!(almost_eq( + next_interval(tm.at(1), tm.at(1000), dur(10)), + tm.at(1001) + )); + assert!(almost_eq( + next_interval(tm.at(7777), tm.at(8888), dur(100)), + tm.at(8977) + )); + assert!(almost_eq( + next_interval(tm.at(1), tm.at(10000), dur(2100)), + tm.at(10501) + )); + } + + /// TODO: this test actually should be successful, but since we can't + /// multiply Duration on anything larger than u32 easily we decided + /// to allow it to fail for now + #[test] + #[should_panic(expected = "can't skip more than 4 billion intervals")] + fn large_skip() { + let tm = Timeline::new(); + assert_eq!( + next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)), + tm.at_ns(25, 1) + ); + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 070983ed..f34e9dc9 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -44,13 +44,15 @@ cfg_if! { mod extend; mod from_stream; mod into_stream; + mod interval; pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; - pub use into_stream::IntoStream; pub use fused_stream::FusedStream; + pub use into_stream::IntoStream; + pub use interval::{interval, Interval}; pub use stream::Merge; } diff --git a/src/task/sleep.rs b/src/task/sleep.rs index 3e98755d..380ee3e6 100644 --- a/src/task/sleep.rs +++ b/src/task/sleep.rs @@ -11,6 +11,10 @@ use crate::io; /// /// [`std::thread::sleep`]: https://doc.rust-lang.org/std/thread/fn.sleep.html /// +/// See also: [`stream::interval`]. +/// +/// [`stream::interval`]: ../stream/fn.interval.html +/// /// # Examples /// /// ``` From 23beab412506dd441acce18b3599fb4cd5abbb24 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 15 Oct 2019 16:50:17 +0300 Subject: [PATCH 079/407] Adds a from_fn stream implementation (#277) * Adds a from_fn stream implementation * Update src/stream/from_fn.rs Co-Authored-By: Yoshua Wuyts * Fix review nits * Use async_std Mutex --- src/stream/from_fn.rs | 100 ++++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 + 2 files changed, 102 insertions(+) create mode 100644 src/stream/from_fn.rs diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs new file mode 100644 index 00000000..c1cb97af --- /dev/null +++ b/src/stream/from_fn.rs @@ -0,0 +1,100 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that yields elements by calling a closure. +/// +/// This stream is constructed by [`from_fn`] function. +/// +/// [`from_fn`]: fn.from_fn.html +#[derive(Debug)] +pub struct FromFn { + f: F, + future: Option, + __t: PhantomData, +} + +/// 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 +/// syntax of creating a dedicated type and implementing a `Stream` trait for it. +/// +/// # Examples +/// +/// ``` +/// # fn main() { 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 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) +/// } +/// } +/// }); +/// +/// pin_utils::pin_mut!(s); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(3)); +/// assert_eq!(s.next().await, None); +/// # +/// # }) } +/// +/// ``` +pub fn from_fn(f: F) -> FromFn +where + F: FnMut() -> Fut, + Fut: Future>, +{ + FromFn { + f, + future: None, + __t: PhantomData, + } +} + +impl FromFn { + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_pinned!(future: Option); +} + +impl Stream for FromFn +where + F: FnMut() -> Fut, + Fut: Future>, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match &self.future { + Some(_) => { + let next = + futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + self.as_mut().future().set(None); + + return Poll::Ready(next); + } + None => { + let fut = (self.as_mut().f())(); + self.as_mut().future().set(Some(fut)); + } + } + } + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f34e9dc9..87f3f841 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -24,6 +24,7 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; +pub use from_fn::{from_fn, FromFn}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{ @@ -33,6 +34,7 @@ pub use stream::{ pub(crate) mod stream; mod empty; +mod from_fn; mod once; mod repeat; From 1a3429655c4f9208891756483e6ca87ba71d08bb Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:03:28 +0200 Subject: [PATCH 080/407] init blocking-updates Signed-off-by: Yoshua Wuyts --- src/task/blocking.rs | 5 +++-- src/task/mod.rs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 53b52c82..012158de 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -96,12 +96,13 @@ fn schedule(t: async_task::Task) { /// Spawns a blocking task. /// /// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. -pub(crate) fn spawn(future: F) -> JoinHandle +pub(crate) fn spawn(f: F) -> JoinHandle where - F: Future + Send + 'static, + 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) diff --git a/src/task/mod.rs b/src/task/mod.rs index 727dc593..21b85fd9 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -73,7 +73,7 @@ cfg_if::cfg_if! { /// # /// use async_std::task; /// -/// task::blocking(async { +/// task::blocking(|| { /// println!("long-running task here"); /// }).await; /// # @@ -84,10 +84,10 @@ cfg_if::cfg_if! { #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] -pub fn blocking(future: F) -> task::JoinHandle +pub fn blocking(f: F) -> task::JoinHandle where - F: crate::future::Future + Send + 'static, + F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - blocking::spawn(future) + blocking::spawn_blocking(future) } From b4c1c63fd27f3ce6350d9ba11689fe801fb8312e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:21:49 +0200 Subject: [PATCH 081/407] task::blocking async closure -> FnOnce Signed-off-by: Yoshua Wuyts --- src/fs/canonicalize.rs | 2 +- src/fs/copy.rs | 2 +- src/fs/create_dir.rs | 2 +- src/fs/create_dir_all.rs | 2 +- src/fs/dir_builder.rs | 2 +- src/fs/dir_entry.rs | 4 ++-- src/fs/file.rs | 20 ++++++++++---------- src/fs/hard_link.rs | 2 +- src/fs/metadata.rs | 2 +- src/fs/open_options.rs | 2 +- src/fs/read.rs | 2 +- src/fs/read_dir.rs | 4 ++-- src/fs/read_link.rs | 2 +- src/fs/read_to_string.rs | 2 +- src/fs/remove_dir.rs | 2 +- src/fs/remove_dir_all.rs | 2 +- src/fs/remove_file.rs | 2 +- src/fs/rename.rs | 2 +- src/fs/set_permissions.rs | 2 +- src/fs/symlink_metadata.rs | 2 +- src/fs/write.rs | 2 +- src/io/stderr.rs | 4 ++-- src/io/stdin.rs | 4 ++-- src/io/stdout.rs | 4 ++-- src/net/addr.rs | 5 ++--- src/net/tcp/stream.rs | 2 +- src/os/unix/fs.rs | 2 +- src/os/unix/net/datagram.rs | 2 +- src/os/unix/net/listener.rs | 2 +- src/os/unix/net/stream.rs | 2 +- src/task/blocking.rs | 1 - 31 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index a763e4e4..601d477c 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -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(async move { std::fs::canonicalize(&path).map(Into::into) }).await + blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index c0c6b9e9..733fb64b 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -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(async move { std::fs::copy(&from, &to) }).await + blocking::spawn(move || std::fs::copy(&from, &to)).await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 99f4ac45..740d303c 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -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(async move { std::fs::create_dir(path) }).await + blocking::spawn(move || std::fs::create_dir(path)).await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 0dc446e1..76604de7 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -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(async move { std::fs::create_dir_all(path) }).await + blocking::spawn(move || std::fs::create_dir_all(path)).await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 1fb08502..738ddbca 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -108,7 +108,7 @@ impl DirBuilder { } let path = path.as_ref().to_owned(); - async move { blocking::spawn(async move { builder.create(path) }).await } + async move { blocking::spawn(move || builder.create(path)).await } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 3d42f83b..80d4bbdf 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -89,7 +89,7 @@ impl DirEntry { /// ``` pub async fn metadata(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(async move { inner.metadata() }).await + blocking::spawn(move || inner.metadata()).await } /// Reads the file type for this entry. @@ -127,7 +127,7 @@ impl DirEntry { /// ``` pub async fn file_type(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(async move { inner.file_type() }).await + blocking::spawn(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 b52bcd07..39162345 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -97,7 +97,7 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?; + let file = blocking::spawn(move || std::fs::File::open(&path)).await?; Ok(file.into()) } @@ -132,7 +132,7 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?; + let file = blocking::spawn(move || std::fs::File::create(&path)).await?; Ok(file.into()) } @@ -165,7 +165,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.sync_all() }).await + blocking::spawn(move || state.file.sync_all()).await } /// Synchronizes OS-internal buffered contents to disk. @@ -201,7 +201,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.sync_data() }).await + blocking::spawn(move || state.file.sync_data()).await } /// Truncates or extends the file. @@ -234,7 +234,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.set_len(size) }).await + blocking::spawn(move || state.file.set_len(size)).await } /// Reads the file's metadata. @@ -253,7 +253,7 @@ impl File { /// ``` pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); - blocking::spawn(async move { file.metadata() }).await + blocking::spawn(move || file.metadata()).await } /// Changes the permissions on the file. @@ -282,7 +282,7 @@ impl File { /// ``` pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); - blocking::spawn(async move { file.set_permissions(perm) }).await + blocking::spawn(move || file.set_permissions(perm)).await } } @@ -702,7 +702,7 @@ impl LockGuard { self.register(cx); // Start a read operation asynchronously. - blocking::spawn(async move { + blocking::spawn(move || { // Read some data from the file into the cache. let res = { let State { file, cache, .. } = &mut *self; @@ -811,7 +811,7 @@ impl LockGuard { self.register(cx); // Start a write operation asynchronously. - blocking::spawn(async move { + blocking::spawn(move || { match (&*self.file).write_all(&self.cache) { Ok(_) => { // Switch to idle mode. @@ -844,7 +844,7 @@ impl LockGuard { self.register(cx); // Start a flush operation asynchronously. - blocking::spawn(async move { + blocking::spawn(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 2ae2cad4..8b09b5d1 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -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(async move { std::fs::hard_link(&from, &to) }).await + blocking::spawn(move || std::fs::hard_link(&from, &to)).await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 8c923814..65f494b5 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -36,7 +36,7 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::metadata(path) }).await + blocking::spawn(move || std::fs::metadata(path)).await } cfg_if! { diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index d7e0454f..a9511f1a 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -285,7 +285,7 @@ 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(async move { options.open(path).map(|f| f.into()) }).await } + async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await } } } diff --git a/src/fs/read.rs b/src/fs/read.rs index b562ea3c..a0eb130b 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -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(async move { std::fs::read(path) }).await + blocking::spawn(move || std::fs::read(path)).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index fd1a21f4..6e478019 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -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(async move { std::fs::read_dir(path) }) + blocking::spawn(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(async move { + self.0 = State::Busy(blocking::spawn(move || { let next = inner.next(); (inner, next) })); diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 53080ddc..eaa7b624 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -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(async move { std::fs::read_link(path).map(Into::into) }).await + blocking::spawn(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 a4d175fa..40c4b6b8 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -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(async move { std::fs::read_to_string(path) }).await + blocking::spawn(move || std::fs::read_to_string(path)).await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index f4571246..d1fa7bf3 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -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(async move { std::fs::remove_dir(path) }).await + blocking::spawn(move || std::fs::remove_dir(path)).await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 3b12d264..0a0fceb7 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -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(async move { std::fs::remove_dir_all(path) }).await + blocking::spawn(move || std::fs::remove_dir_all(path)).await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 216209fe..5bc0608d 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -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(async move { std::fs::remove_file(path) }).await + blocking::spawn(move || std::fs::remove_file(path)).await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index f517a266..c2aa77b7 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -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(async move { std::fs::rename(&from, &to) }).await + blocking::spawn(move || std::fs::rename(&from, &to)).await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 68dd8d40..d14ced94 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -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(async move { std::fs::set_permissions(path, perm) }).await + blocking::spawn(move || std::fs::set_permissions(path, perm)).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index e2bc12dd..bc5cce86 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -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(async move { std::fs::symlink_metadata(path) }).await + blocking::spawn(move || std::fs::symlink_metadata(path)).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 4db52212..3df56042 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -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(async move { std::fs::write(path, contents) }).await + blocking::spawn(move || std::fs::write(path, contents)).await } diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5706aa2e..34d4d1dc 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -116,7 +116,7 @@ impl Write for Stderr { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::write(&mut inner.stderr, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -144,7 +144,7 @@ impl Write for Stderr { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(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 95a77b80..178e819d 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -119,7 +119,7 @@ impl Stdin { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { inner.line.clear(); let res = inner.stdin.read_line(&mut inner.line); inner.last_op = Some(Operation::ReadLine(res)); @@ -172,7 +172,7 @@ impl Read for Stdin { } // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(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 7849f1ce..4128aae0 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -116,7 +116,7 @@ impl Write for Stdout { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::write(&mut inner.stdout, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -144,7 +144,7 @@ impl Write for Stdout { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(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 64b22cfc..adc24083 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -196,7 +196,7 @@ impl ToSocketAddrs for (&str, u16) { } let host = host.to_string(); - let task = blocking::spawn(async move { + let task = blocking::spawn(move || { std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) }); ToSocketAddrsFuture::Resolving(task) @@ -217,8 +217,7 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = - blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) }); + let task = blocking::spawn(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 10569434..1ffd6363 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -76,7 +76,7 @@ impl TcpStream { let mut last_err = None; for addr in addrs.to_socket_addrs().await? { - let res = blocking::spawn(async move { + let res = blocking::spawn(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 f00aaec6..4a155106 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -28,7 +28,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(async move { std::os::unix::fs::symlink(&src, &dst) }).await + blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await } cfg_if! { diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 61adc09e..c96afd50 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -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(async move { mio_uds::UnixDatagram::bind(path) }).await?; + let socket = blocking::spawn(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 2d68a6b9..b6e6a298 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -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(async move { mio_uds::UnixListener::bind(path) }).await?; + let listener = blocking::spawn(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 8245e638..b16f2a3c 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -58,7 +58,7 @@ impl UnixStream { pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { + blocking::spawn(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/blocking.rs b/src/task/blocking.rs index 012158de..3216012a 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -7,7 +7,6 @@ use std::time::Duration; use crossbeam_channel::{bounded, Receiver, Sender}; use lazy_static::lazy_static; -use crate::future::Future; use crate::task::task::{JoinHandle, Tag}; use crate::utils::abort_on_panic; From 12fdc1232ddd83f43e46659eca95229cc346873f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:24:38 +0200 Subject: [PATCH 082/407] rename task::blocking to task::spawn_blocking Signed-off-by: Yoshua Wuyts --- src/task/block_on.rs | 2 +- src/task/mod.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 31588c4d..e1b6d024 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -21,7 +21,7 @@ use kv_log_macro::trace; /// /// See also: [`task::blocking`]. /// -/// [`task::blocking`]: fn.blocking.html +/// [`task::spawn_blocking`]: fn.spawn_blocking.html /// /// [spawning]: https://doc.rust-lang.org/std/thread/fn.spawn.html /// [joining]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join diff --git a/src/task/mod.rs b/src/task/mod.rs index 21b85fd9..fc702dfc 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -60,9 +60,10 @@ cfg_if::cfg_if! { /// is useful to prevent long-running synchronous operations from blocking the main futures /// executor. /// -/// See also: [`task::block_on`]. +/// See also: [`task::block_on`], [`task::spawn`]. /// /// [`task::block_on`]: fn.block_on.html +/// [`task::spawn`]: fn.spawn.html /// /// # Examples /// @@ -73,7 +74,7 @@ cfg_if::cfg_if! { /// # /// use async_std::task; /// -/// task::blocking(|| { +/// task::spawn_blocking(|| { /// println!("long-running task here"); /// }).await; /// # @@ -84,10 +85,10 @@ cfg_if::cfg_if! { #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] -pub fn blocking(f: F) -> task::JoinHandle +pub fn spawn_blocking(f: F) -> task::JoinHandle where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - blocking::spawn_blocking(future) + blocking::spawn(f) } From 33806ad44ce7a80ae49dd2da57f60ad9680d4293 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 23:44:26 +0200 Subject: [PATCH 083/407] fix warning Signed-off-by: Yoshua Wuyts --- src/task/block_on.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index e1b6d024..db46f025 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -19,7 +19,7 @@ use kv_log_macro::trace; /// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an /// asynchronous task will be spawned. /// -/// See also: [`task::blocking`]. +/// See also: [`task::spawn_blocking`]. /// /// [`task::spawn_blocking`]: fn.spawn_blocking.html /// From 237cfa0315185b339dc23a14faf96cebde478743 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:03:49 +0200 Subject: [PATCH 084/407] add IntoFuture (#259) * add IntoFuture Signed-off-by: Yoshua Wuyts * blanket impl for IntoFuture Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts * example Signed-off-by: Yoshua Wuyts * mark as unstable Signed-off-by: Yoshua Wuyts --- src/future/into_future.rs | 54 +++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 3 +++ 2 files changed, 57 insertions(+) create mode 100644 src/future/into_future.rs diff --git a/src/future/into_future.rs b/src/future/into_future.rs new file mode 100644 index 00000000..58b67661 --- /dev/null +++ b/src/future/into_future.rs @@ -0,0 +1,54 @@ +use crate::future::Future; + +/// Convert a type into a `Future`. +/// +/// # Examples +/// +/// ``` +/// use async_std::future::{Future, IntoFuture}; +/// use async_std::io; +/// use async_std::pin::Pin; +/// +/// struct Client; +/// +/// impl Client { +/// pub async fn send(self) -> io::Result<()> { +/// // Send a request +/// Ok(()) +/// } +/// } +/// +/// impl IntoFuture for Client { +/// type Output = io::Result<()>; +/// +/// type Future = Pin>>; +/// +/// fn into_future(self) -> Self::Future { +/// Box::pin(async { +/// self.send().await +/// }) +/// } +/// } +/// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub trait IntoFuture { + /// The type of value produced on completion. + type Output; + + /// Which kind of future are we turning this into? + type Future: Future; + + /// Create a future from a value + fn into_future(self) -> Self::Future; +} + +impl IntoFuture for T { + type Output = T::Output; + + type Future = T; + + fn into_future(self) -> Self::Future { + self + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index e5e696dd..dc9b4662 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -63,7 +63,10 @@ mod ready; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod into_future; mod timeout; + + pub use into_future::IntoFuture; pub use timeout::{timeout, TimeoutError}; } } From 49faea2023396c240b91b6b4cffc56de038ce336 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:27:06 +0200 Subject: [PATCH 085/407] init FutureExt (#308) * init FutureExt Signed-off-by: Yoshua Wuyts * prelude Signed-off-by: Yoshua Wuyts * Refactor extension_trait Signed-off-by: Yoshua Wuyts * Fix rustdoc Signed-off-by: Yoshua Wuyts --- src/fs/dir_builder.rs | 3 +- src/fs/open_options.rs | 3 +- src/future/future.rs | 145 +++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 5 +- src/io/buf_read/mod.rs | 4 +- src/io/read/mod.rs | 4 +- src/io/seek.rs | 6 +- src/io/write/mod.rs | 4 +- src/prelude.rs | 2 + src/stream/stream/mod.rs | 4 +- src/task/blocking.rs | 1 + src/task/task.rs | 2 +- src/task/task_local.rs | 2 +- src/utils.rs | 56 +++++---------- 14 files changed, 190 insertions(+), 51 deletions(-) create mode 100644 src/future/future.rs diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 738ddbca..6dfddcb3 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,6 +1,7 @@ +use std::future::Future; + use cfg_if::cfg_if; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::task::blocking; diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index a9511f1a..fe65e2f4 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,7 +1,8 @@ +use std::future::Future; + use cfg_if::cfg_if; use crate::fs::File; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::task::blocking; diff --git a/src/future/future.rs b/src/future/future.rs new file mode 100644 index 00000000..556dc1ac --- /dev/null +++ b/src/future/future.rs @@ -0,0 +1,145 @@ +use crate::utils::extension_trait; + +cfg_if::cfg_if! { + if #[cfg(feature = "docs")] { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; + + use crate::task::{Context, Poll}; + } +} + +extension_trait! { + #[doc = r#" + A future represents an asynchronous computation. + + A future is a value that may not have finished computing yet. This kind of + "asynchronous value" makes it possible for a thread to continue doing useful + work while it waits for the value to become available. + + # The `poll` method + + The core method of future, `poll`, *attempts* to resolve the future into a + final value. This method does not block if the value is not ready. Instead, + the current task is scheduled to be woken up when it's possible to make + further progress by `poll`ing again. The `context` passed to the `poll` + method can provide a [`Waker`], which is a handle for waking up the current + task. + + When using a future, you generally won't call `poll` directly, but instead + `.await` the value. + + [`Waker`]: ../task/struct.Waker.html + "#] + pub trait Future { + #[doc = r#" + The type of value produced on completion. + "#] + type Output; + + #[doc = r#" + Attempt to resolve the future to a final value, registering + the current task for wakeup if the value is not yet available. + + # Return value + + This function returns: + + - [`Poll::Pending`] if the future is not ready yet + - [`Poll::Ready(val)`] with the result `val` of this future if it + finished successfully. + + Once a future has finished, clients should not `poll` it again. + + When a future is not ready yet, `poll` returns `Poll::Pending` and + stores a clone of the [`Waker`] copied from the current [`Context`]. + This [`Waker`] is then woken once the future can make progress. + For example, a future waiting for a socket to become + readable would call `.clone()` on the [`Waker`] and store it. + When a signal arrives elsewhere indicating that the socket is readable, + [`Waker::wake`] is called and the socket future's task is awoken. + Once a task has been woken up, it should attempt to `poll` the future + again, which may or may not produce a final value. + + Note that on multiple calls to `poll`, only the [`Waker`] from the + [`Context`] passed to the most recent call should be scheduled to + receive a wakeup. + + # Runtime characteristics + + Futures alone are *inert*; they must be *actively* `poll`ed to make + progress, meaning that each time the current task is woken up, it should + actively re-`poll` pending futures that it still has an interest in. + + The `poll` function is not called repeatedly in a tight loop -- instead, + it should only be called when the future indicates that it is ready to + make progress (by calling `wake()`). If you're familiar with the + `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures + typically do *not* suffer the same problems of "all wakeups must poll + all events"; they are more like `epoll(4)`. + + An implementation of `poll` should strive to return quickly, and should + not block. Returning quickly prevents unnecessarily clogging up + threads or event loops. If it is known ahead of time that a call to + `poll` may end up taking awhile, the work should be offloaded to a + thread pool (or something similar) to ensure that `poll` can return + quickly. + + # Panics + + Once a future has completed (returned `Ready` from `poll`), calling its + `poll` method again may panic, block forever, or cause other kinds of + problems; the `Future` trait places no requirements on the effects of + such a call. However, as the `poll` method is not marked `unsafe`, + Rust's usual rules apply: calls must never cause undefined behavior + (memory corruption, incorrect use of `unsafe` functions, or the like), + regardless of the future's state. + + [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending + [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready + [`Context`]: ../task/struct.Context.html + [`Waker`]: ../task/struct.Waker.html + [`Waker::wake`]: ../task/struct.Waker.html#method.wake + "#] + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll; + } + + pub trait FutureExt: std::future::Future { + } + + impl Future for Box { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unreachable!("this impl only appears in the rendered docs") + } + } + + impl Future for &mut F { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unreachable!("this impl only appears in the rendered docs") + } + } + + impl

Future for Pin

+ where + P: DerefMut + Unpin, +

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

::Target as Future>::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unreachable!("this impl only appears in the rendered docs") + } + } + + impl Future for std::panic::AssertUnwindSafe { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unreachable!("this impl only appears in the rendered docs") + } + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index dc9b4662..06d06336 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -41,9 +41,6 @@ //! | `future::select` | `Result` | Return on first value //! | `future::try_select` | `Result` | Return on first `Ok`, reject on last Err -#[doc(inline)] -pub use std::future::Future; - #[doc(inline)] pub use async_macros::{join, try_join}; @@ -53,10 +50,12 @@ pub use async_macros::{select, try_select}; use cfg_if::cfg_if; +pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; pub use ready::ready; +pub(crate) mod future; mod pending; mod poll_fn; mod ready; diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 987522b9..dff10fab 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -44,7 +44,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html [provided methods]: #provided-methods "#] - pub trait BufRead [BufReadExt: futures_io::AsyncBufRead] { + pub trait BufRead { #[doc = r#" Returns the contents of the internal buffer, filling it with more data from the inner reader if it is empty. @@ -67,7 +67,9 @@ extension_trait! { should no longer be returned in calls to `read`. "#] fn consume(self: Pin<&mut Self>, amt: usize); + } + 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/read/mod.rs b/src/io/read/mod.rs index 6fd95c12..40cb3ca0 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -50,7 +50,7 @@ extension_trait! { [`poll_read`]: #tymethod.poll_read [`poll_read_vectored`]: #method.poll_read_vectored "#] - pub trait Read [ReadExt: futures_io::AsyncRead] { + pub trait Read { #[doc = r#" Attempt to read from the `AsyncRead` into `buf`. "#] @@ -70,7 +70,9 @@ extension_trait! { ) -> Poll> { unreachable!("this impl only appears in the rendered docs") } + } + pub trait ReadExt: futures_io::AsyncRead { #[doc = r#" Reads some bytes from the byte stream. diff --git a/src/io/seek.rs b/src/io/seek.rs index 274eee73..2d1c4d37 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -33,7 +33,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html [provided methods]: #provided-methods "#] - pub trait Seek [SeekExt: futures_io::AsyncSeek] { + pub trait Seek { #[doc = r#" Attempt to seek to an offset, in bytes, in a stream. "#] @@ -42,7 +42,9 @@ extension_trait! { cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll>; + } + pub trait SeekExt: futures_io::AsyncSeek { #[doc = r#" Seeks to a new position in a byte stream. @@ -70,7 +72,7 @@ extension_trait! { fn seek( &mut self, pos: SeekFrom, - ) -> impl Future> [SeekFuture<'_, Self>] + ) -> impl Future> + '_ [SeekFuture<'_, Self>] where Self: Unpin, { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 7914ccc8..be69b7e2 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -49,7 +49,7 @@ extension_trait! { [`poll_flush`]: #tymethod.poll_flush [`poll_close`]: #tymethod.poll_close "#] - pub trait Write [WriteExt: futures_io::AsyncWrite] { + pub trait Write { #[doc = r#" Attempt to write bytes from `buf` into the object. "#] @@ -80,7 +80,9 @@ extension_trait! { Attempt to close the object. "#] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } + 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 f808da06..6c670cc7 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -28,6 +28,8 @@ pub use crate::stream::Stream; #[doc(no_inline)] pub use crate::task_local; +#[doc(hidden)] +pub use crate::future::future::FutureExt as _; #[doc(hidden)] pub use crate::io::buf_read::BufReadExt as _; #[doc(hidden)] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 0e563f6d..2ee01d78 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -122,7 +122,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html [provided methods]: #provided-methods "#] - pub trait Stream [StreamExt: futures_core::stream::Stream] { + pub trait Stream { #[doc = r#" The type of items yielded by this stream. "#] @@ -180,7 +180,9 @@ extension_trait! { ``` "#] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } + pub trait StreamExt: futures_core::stream::Stream { #[doc = r#" Advances the stream and returns the next value. diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 3216012a..5c183b3f 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -1,5 +1,6 @@ //! A thread pool for running blocking functions asynchronously. +use std::future::Future; use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; diff --git a/src/task/task.rs b/src/task/task.rs index 5100af44..ca3cac14 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::future::Future; use std::i64; use std::mem; use std::num::NonZeroU64; @@ -7,7 +8,6 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; use super::task_local; -use crate::future::Future; use crate::task::{Context, Poll}; /// A handle to a task. diff --git a/src/task/task_local.rs b/src/task/task_local.rs index 8347e34b..c72937f6 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -1,13 +1,13 @@ 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 lazy_static::lazy_static; use super::worker; -use crate::future::Future; use crate::utils::abort_on_panic; /// Declares task-local values. diff --git a/src/utils.rs b/src/utils.rs index bdf0f3b5..76db50b8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,7 +20,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } -/// Defines an extension trait for a base trait from the `futures` crate. +/// Defines an extension trait for a base trait. /// /// In generated docs, the base trait will contain methods from the extension trait. In actual /// code, the base trait will be re-exported and the extension trait will be hidden. We then @@ -35,10 +35,14 @@ macro_rules! extension_trait { // Interesting patterns: // - `$name`: trait name that gets rendered in the docs // - `$ext`: name of the hidden extension trait - // - `$base`: base trait from the `futures` crate - $(#[$attr:meta])* - pub trait $name:ident [$ext:ident: $base:path] { - $($body:tt)* + // - `$base`: base trait + #[doc = $doc:tt] + pub trait $name:ident { + $($body_base:tt)* + } + + pub trait $ext:ident: $base:path { + $($body_ext:tt)* } // Shim trait impls that only appear in docs. @@ -58,11 +62,11 @@ macro_rules! extension_trait { pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); } - // Render a fake trait containing all methods from the base trait and the extension trait. + // Render a fake trait combining the bodies of the base trait and the extension trait. #[cfg(feature = "docs")] - $(#[$attr])* + #[doc = $doc] pub trait $name { - extension_trait!(@doc () $($body)*); + extension_trait!(@doc () $($body_base)* $($body_ext)*); } // When not rendering docs, re-export the base trait from the futures crate. @@ -70,9 +74,9 @@ macro_rules! extension_trait { pub use $base as $name; // The extension trait that adds methods to any type implementing the base trait. - $(#[$attr])* + /// Extension trait. pub trait $ext: $base { - extension_trait!(@ext () $($body)*); + extension_trait!(@ext () $($body_ext)*); } // Blanket implementation of the extension trait for any type implementing the base trait. @@ -82,39 +86,15 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; - // Parse an associated type. - (@doc ($($head:tt)*) type $name:ident; $($tail:tt)*) => { - extension_trait!(@doc ($($head)* type $name;) $($tail)*); - }; - (@ext ($($head:tt)*) type $ident:ty; $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse a required method. - (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => { - extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)?;) $($tail)*); - }; - (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse a provided method that exists in the base trait. - (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => { - extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)? { $($body)* }) $($tail)*); - }; - (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse the return type in an extension method where the future doesn't borrow. + // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); }; - (@ext ($($head:tt)*) -> impl Future [$f:ty] $($tail:tt)*) => { + (@ext ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@ext ($($head)* -> $f) $($tail)*); }; - // Parse the return type in an extension method where the future borrows its environment. + // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future + $lt:lifetime [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> borrowed::ImplFuture<$lt, $out>) $($tail)*); }; @@ -122,7 +102,7 @@ macro_rules! extension_trait { extension_trait!(@ext ($($head)* -> $f) $($tail)*); }; - // Parse a token that doesn't fit into any of the previous patterns. + // Parse a token. (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => { extension_trait!(@doc ($($head)* $token) $($tail)*); }; From 00d936488b8909fdfc8896f485d7520ef0f3b0a4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:33:23 +0200 Subject: [PATCH 086/407] stabilize future::timeout (#335) Signed-off-by: Yoshua Wuyts --- src/future/mod.rs | 4 ++-- src/future/timeout.rs | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index 06d06336..cc3b7a5d 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -54,18 +54,18 @@ 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_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod into_future; - mod timeout; pub use into_future::IntoFuture; - pub use timeout::{timeout, TimeoutError}; } } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index aa88f646..a8338fba 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -28,8 +28,6 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, @@ -42,8 +40,6 @@ where } /// A future that times out after a duration of time. -#[doc(hidden)] -#[allow(missing_debug_implementations)] struct TimeoutFuture { future: F, delay: Delay, @@ -69,8 +65,6 @@ impl Future for TimeoutFuture { } /// An error returned when a future times out. -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { _private: (), From 4911f4599b02dbc3e08c9cbf2b583f267752e6b1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 17:50:24 +0200 Subject: [PATCH 087/407] init changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ab025a..6979d514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,93 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [0.99.10] - 2019-10-16 + +This patch stabilizes several core concurrency macros, introduces async versions +of `Path` and `PathBuf`, and adds almost 100 other commits. + +## Examples + +__Asynchronously read directories from the filesystem__ +```rust +use async_std::fs; +use async_std::path::Path; +use async_std::prelude::*; + +let path = Path::new("/laputa"); +let mut dir = fs::read_dir(&path).await.unwrap(); +while let Some(entry) = dir.next().await { + if let Ok(entry) = entry { + println!("{:?}", entry.path()); + } +} +``` + +__Cooperatively reschedule the current task on the executor__ +```rust +use async_std::prelude::*; +use async_std::task; + +task::spawn(async { + let x = fibonnacci(1000); // Do expensive work + task::yield_now().await; // Allow other tasks to run + x + fibonnacci(100) // Do more work +}) +``` + +__Create an interval stream__ +```rust +use async_std::prelude::*; +use async_std::stream; +use std::time::Duration; + +let mut interval = stream::interval(Duration::from_secs(4)); +while let Some(_) = interval.next().await { + println!("prints every four seconds"); +} +``` + +## Added + +- Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::merge` as "unstable", replacing `stream::join!` +- Added `Stream::partial_cmp` +- Added `Stream::take_while` +- Added `future::IntoFuture` as "unstable" +- Added `io::BufRead::split` +- Added `io::Write::write_fmt` +- Added `stream::from_fn` +- Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable" +- Added `process` as "unstable", re-exporting std types only for now +- Added `std::path::PathBuf` with all associated methods +- Added `std::path::Path` with all associated methods +- Added `stream::ExactSizeStream` as "unstable" +- Added `stream::FusedStream` as "unstable" +- Added `stream::interval` as "unstable" +- Added `task::spawn_blocking` as "unstable", replacing `task::blocking` +- Added `task::yield_now` +- Added `write!` and `writeln!` macros as "unstable" +- Added `std::net` re-exports to the `net` submodule +- Stabilized `future::join!` and `future::try_join!` +- Stabilized `future::timeout` +- Stabilized `path` +- Stabilized `task::ready!` + +## Changed + +- Fixed `BufWriter::into_inner` so it calls `flush` before yielding +- Refactored `io::BufWriter` internals +- Refactored `net::ToSocketAddrs` internals +- Removed Travis CI entirely +- Rewrote the README.md +- Stabilized `io::Cursor` +- Switched bors over to use GitHub actions + +## Removed + +- Removed the "unstable" `stream::join!` in favor of `Stream::merge` +- Removed the "unstable" `task::blocking` in favor of `task::spawn_blocking` + # [0.99.9] - 2019-10-08 This patch upgrades our `futures-rs` version, allowing us to build on the 1.39 @@ -183,7 +270,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.9...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD +[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 [0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7 From 609a5780a22c4c7b8d8080c04ef76f93dff47b83 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 17:51:32 +0200 Subject: [PATCH 088/407] 0.99.10 Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dc319f71..d1c10a62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.9" +version = "0.99.10" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 24cdb2d4899f63d41726a8c32c32b2727eb28e42 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 00:31:49 +0200 Subject: [PATCH 089/407] add stream::{Sum,Product} (#343) Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 10 +++++++--- src/stream/product.rs | 23 +++++++++++++++++++++++ src/stream/sum.rs | 23 +++++++++++++++++++++++ src/task/blocking.rs | 1 - 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/stream/product.rs create mode 100644 src/stream/sum.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 87f3f841..c41ceb68 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -42,19 +42,23 @@ cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; mod exact_size_stream; - mod fused_stream; mod extend; mod from_stream; - mod into_stream; + mod fused_stream; mod interval; + mod into_stream; + mod product; + mod sum; pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; pub use fused_stream::FusedStream; - pub use into_stream::IntoStream; pub use interval::{interval, Interval}; + pub use into_stream::IntoStream; + pub use product::Product; + pub use sum::Sum; pub use stream::Merge; } diff --git a/src/stream/product.rs b/src/stream/product.rs new file mode 100644 index 00000000..b3227761 --- /dev/null +++ b/src/stream/product.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::stream::Stream; + +/// Trait to represent types that can be created by productming up 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 +/// [`FromStream`] this trait should rarely be called directly and instead +/// interacted with through [`Stream::product`]. +/// +/// [`product`]: trait.Product.html#tymethod.product +/// [`FromStream`]: trait.FromStream.html +/// [`Stream::product`]: trait.Stream.html#method.product +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +pub trait Product: Sized { + /// Method which takes a stream and generates `Self` from the elements by + /// multiplying the items. + fn product(stream: S) -> F + where + S: Stream, + F: Future; +} diff --git a/src/stream/sum.rs b/src/stream/sum.rs new file mode 100644 index 00000000..fd5d7d5e --- /dev/null +++ b/src/stream/sum.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::stream::Stream; + +/// Trait to represent types that can be created by summing up a stream. +/// +/// This trait is used to implement the [`sum`] method on streams. Types which +/// implement the trait can be generated by the [`sum`] method. Like +/// [`FromStream`] this trait should rarely be called directly and instead +/// interacted with through [`Stream::sum`]. +/// +/// [`sum`]: trait.Sum.html#tymethod.sum +/// [`FromStream`]: trait.FromStream.html +/// [`Stream::sum`]: trait.Stream.html#method.sum +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +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 + where + S: Stream, + F: Future; +} diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 5c183b3f..3216012a 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -1,6 +1,5 @@ //! A thread pool for running blocking functions asynchronously. -use std::future::Future; use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; From a8dc2c6f9b8516109d7e301ca9de22e1aee41d4c Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 18:32:54 -0400 Subject: [PATCH 090/407] Adds Stream::lt (#337) --- src/stream/stream/lt.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/stream/stream/lt.rs diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs new file mode 100644 index 00000000..b774d7b4 --- /dev/null +++ b/src/stream/stream/lt.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +// Determines if the elements of this `Stream` are lexicographically +// less than those of another. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct LtFuture { + partial_cmp: PartialCmpFuture, +} + +impl LtFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + LtFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for LtFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialOrd, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + + match result { + Some(Ordering::Less) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2ee01d78..dfd8d49f 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod fold; mod for_each; mod fuse; mod inspect; +mod lt; mod map; mod min_by; mod next; @@ -55,6 +56,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use lt::LtFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; @@ -1258,6 +1260,43 @@ extension_trait! { { PartialCmpFuture::new(self, other) } + + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less than those of another. + + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + 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().lt(single.clone()).await, false); + assert_eq!(single.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); + + # + # }) } + ``` + "#] + fn lt( + self, + other: S + ) -> impl Future [LtFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + LtFuture::new(self, other) + } } impl Stream for Box { From f0f279ec04bf025f5e9aa6c57002245c2f1bb013 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 18:46:06 -0400 Subject: [PATCH 091/407] Adds Stream::le (#336) --- src/stream/stream/le.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 37 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/stream/stream/le.rs diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs new file mode 100644 index 00000000..37b62d83 --- /dev/null +++ b/src/stream/stream/le.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// Determines if the elements of this `Stream` are lexicographically +/// less or equal to those of another. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct LeFuture { + partial_cmp: PartialCmpFuture, +} + +impl LeFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + LeFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for LeFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialOrd, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + + match result { + Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index dfd8d49f..30b6031e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod fold; mod for_each; mod fuse; mod inspect; +mod le; mod lt; mod map; mod min_by; @@ -56,6 +57,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; use next::NextFuture; @@ -1261,6 +1263,41 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less or equal to those of another. + + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + 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); + assert_eq!(multi_gt.clone().le(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn le( + self, + other: S + ) -> impl Future [LeFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + LeFuture::new(self, other) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically less than those of another. From 5f7238eec6bdc921ce32f79da4afeec8ed28a3a8 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 19:11:48 -0400 Subject: [PATCH 092/407] [Draft PR] Adds Stream::gt (#304) * [Draft PR] Adds Stream::gt * Applies cargo format and fixes incorrect comment * cargo fmt * fixes rustdoc related issues --- src/stream/stream/gt.rs | 47 +++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 53 +++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/stream/stream/gt.rs diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs new file mode 100644 index 00000000..6c480a25 --- /dev/null +++ b/src/stream/stream/gt.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +// Determines if the elements of this `Stream` are lexicographically +// greater than those of another. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct GtFuture { + partial_cmp: PartialCmpFuture, +} + +impl GtFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + GtFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for GtFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialOrd, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + + match result { + Some(Ordering::Greater) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 30b6031e..90fde156 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -32,6 +32,7 @@ mod find_map; mod fold; mod for_each; mod fuse; +mod gt; mod inspect; mod le; mod lt; @@ -57,6 +58,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use gt::GtFuture; use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; @@ -1230,14 +1232,16 @@ extension_trait! { #[doc = r#" Lexicographically compares the elements of this `Stream` with those of another. - + # Examples + ``` # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; use std::collections::VecDeque; 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]); @@ -1263,17 +1267,54 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + 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); + assert_eq!(multi_gt.clone().gt(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn gt( + self, + other: S + ) -> impl Future [GtFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GtFuture::new(self, other) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically less or equal to those of another. - + # Examples + ``` # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; use std::collections::VecDeque; - + let single = VecDeque::from(vec![1]); let single_gt = VecDeque::from(vec![10]); let multi = VecDeque::from(vec![1,2]); @@ -1301,14 +1342,15 @@ extension_trait! { #[doc = r#" Determines if the elements of this `Stream` are lexicographically less than those of another. - + # Examples + ``` # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; use std::collections::VecDeque; - + let single = VecDeque::from(vec![1]); let single_gt = VecDeque::from(vec![10]); let multi = VecDeque::from(vec![1,2]); @@ -1318,7 +1360,6 @@ extension_trait! { assert_eq!(single.clone().lt(single_gt.clone()).await, true); assert_eq!(multi.clone().lt(single_gt.clone()).await, true); assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); - # # }) } ``` From a7041be6f291db6f465e9ac8a823ed387c7066fe Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 19:24:27 -0400 Subject: [PATCH 093/407] Adds Stream:ge (#285) * Adds partial_cmp.rs file and partial_cmp signature to mod.rs * adds tests that compare streams of same length * Adds Stream::ge * cargo fmt * fixes rustdoc error --- src/stream/stream/ge.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/stream/stream/ge.rs diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs new file mode 100644 index 00000000..eb9786b5 --- /dev/null +++ b/src/stream/stream/ge.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +// Determines if the elements of this `Stream` are lexicographically +// greater than or equal to those of another. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct GeFuture { + partial_cmp: PartialCmpFuture, +} + +impl GeFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + GeFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for GeFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialOrd, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + + match result { + Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 90fde156..fbbd8f66 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -32,6 +32,7 @@ mod find_map; mod fold; mod for_each; mod fuse; +mod ge; mod gt; mod inspect; mod le; @@ -58,6 +59,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use ge::GeFuture; use gt::GtFuture; use le::LeFuture; use lt::LtFuture; @@ -1240,6 +1242,7 @@ extension_trait! { # use async_std::prelude::*; use std::collections::VecDeque; + use std::cmp::Ordering; let s1 = VecDeque::from(vec![1]); @@ -1267,6 +1270,43 @@ extension_trait! { PartialCmpFuture::new(self, other) } + + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than or equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + 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); + assert_eq!(multi_gt.clone().ge(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn ge( + self, + other: S + ) -> impl Future [GeFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GeFuture::new(self, other) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than those of another. From 2bd82ac2497ba5a710c28476c2797c4af82c3957 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 01:48:40 +0200 Subject: [PATCH 094/407] updates Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6979d514..f7e8731f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,24 +56,29 @@ while let Some(_) = interval.next().await { ## Added - Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::ge` +- Added `Stream::le` +- Added `Stream::lt` - Added `Stream::merge` as "unstable", replacing `stream::join!` - Added `Stream::partial_cmp` - Added `Stream::take_while` - Added `future::IntoFuture` as "unstable" - Added `io::BufRead::split` - Added `io::Write::write_fmt` -- Added `stream::from_fn` - Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable" - Added `process` as "unstable", re-exporting std types only for now +- Added `std::net` re-exports to the `net` submodule - Added `std::path::PathBuf` with all associated methods - Added `std::path::Path` with all associated methods - Added `stream::ExactSizeStream` as "unstable" - Added `stream::FusedStream` as "unstable" +- Added `stream::Product` +- Added `stream::Sum` +- Added `stream::from_fn` - Added `stream::interval` as "unstable" - Added `task::spawn_blocking` as "unstable", replacing `task::blocking` - Added `task::yield_now` - Added `write!` and `writeln!` macros as "unstable" -- Added `std::net` re-exports to the `net` submodule - Stabilized `future::join!` and `future::try_join!` - Stabilized `future::timeout` - Stabilized `path` From 9f8fa45dc7f6c2c0bb744aba752cd7d33fa7cd42 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 02:03:26 +0200 Subject: [PATCH 095/407] io docs Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 812c97ab..7f9a40f7 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,24 +1,74 @@ -//! Basic input and output. +//! Traits, helpers, and type definitions for core I/O functionality. +//! +//! The `async_std::io` module contains a number of common things you'll need +//! when doing input and output. The most core part of this module is +//! the [`Read`] and [`Write`] traits, which provide the +//! most general interface for reading and writing input and output. //! //! This module is an async version of [`std::io`]. //! //! [`std::io`]: https://doc.rust-lang.org/std/io/index.html //! -//! # Examples +//! # Read and Write +//! +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number +//! of other types, and you can implement them for your types too. As such, +//! you'll see a few different types of I/O throughout the documentation in +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For +//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on +//! [`File`]s: +//! +//! ```no_run +//! use async_std::prelude::*; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut f = File::open("foo.txt").await?; +//! let mut buffer = [0; 10]; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer).await?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a +//! nickname: readers and writers. So you'll sometimes see 'a reader' instead +//! of 'a type that implements the [`Read`] trait'. Much easier! +//! +//! ## Seek and BufRead //! -//! Read a line from the standard input: +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is +//! coming from: //! //! ```no_run +//! use async_std::prelude::*; +//! use async_std::io::SeekFrom; +//! use async_std::fs::File; +//! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # -//! use async_std::io; +//! let mut f = File::open("foo.txt").await?; +//! let mut buffer = [0; 10]; +//! +//! // skip to the last 10 bytes of the file +//! f.seek(SeekFrom::End(-10)).await?; //! -//! let stdin = io::stdin(); -//! let mut line = String::new(); -//! stdin.read_line(&mut line).await?; +//! // read up to 10 bytes +//! let n = f.read(&mut buffer).await?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); //! # //! # Ok(()) }) } //! ``` +//! +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but +//! to show it off, we'll need to talk about buffers in general. Keep reading! #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; From d250eee55688b2d084fcd0a1bd433eb62629d87f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 02:15:20 +0200 Subject: [PATCH 096/407] port the std::io docs to async_std::io Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 7f9a40f7..9a125b20 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -47,7 +47,7 @@ //! coming from: //! //! ```no_run -//! use async_std::prelude::*; +//! use async_std::io::prelude::*; //! use async_std::io::SeekFrom; //! use async_std::fs::File; //! @@ -69,6 +69,205 @@ //! //! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but //! to show it off, we'll need to talk about buffers in general. Keep reading! +//! +//! ## BufReader and BufWriter +//! +//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be +//! making near-constant calls to the operating system. To help with this, +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap +//! readers and writers. The wrapper uses a buffer, reducing the number of +//! calls and providing nicer methods for accessing exactly what you want. +//! +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra +//! methods to any reader: +//! +//! ```no_run +//! use async_std::io::prelude::*; +//! use async_std::io::BufReader; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let f = File::open("foo.txt").await?; +//! let mut reader = BufReader::new(f); +//! let mut buffer = String::new(); +//! +//! // read a line into buffer +//! reader.read_line(&mut buffer).await?; +//! +//! println!("{}", buffer); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write`][`Write::write`]: +//! +//! ```no_run +//! use async_std::io::prelude::*; +//! use async_std::io::BufWriter; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let f = File::create("foo.txt").await?; +//! { +//! let mut writer = BufWriter::new(f); +//! +//! // write a byte to the buffer +//! writer.write(&[42]).await?; +//! +//! } // the buffer is flushed once writer goes out of scope +//! # +//! # Ok(()) }) } +//! ``` +//! +//! ## Standard input and output +//! +//! A very common source of input is standard input: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; +//! +//! println!("You typed: {}", input.trim()); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Note that you cannot use the [`?` operator] in functions that do not return +//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] +//! or `match` on the return value to catch any possible errors: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await.unwrap(); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! And a very common source of output is standard output: +//! +//! ```no_run +//! use async_std::io; +//! use async_std::io::prelude::*; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! io::stdout().write(&[42]).await?; +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Of course, using [`io::stdout`] directly is less common than something like +//! [`println!`]. +//! +//! ## Iterator types +//! +//! A large number of the structures provided by `std::io` are for various +//! ways of iterating over I/O. For example, [`Lines`] is used to split over +//! lines: +//! +//! ```no_run +//! use async_std::prelude::*; +//! use async_std::io::BufReader; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let f = File::open("foo.txt").await?; +//! let reader = BufReader::new(f); +//! +//! let mut lines = reader.lines(); +//! while let Some(line) = lines.next().await { +//! println!("{}", line?); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! ## Functions +//! +//! There are a number of [functions][functions-list] that offer access to various +//! features. For example, we can use three of these functions to copy everything +//! from standard input to standard output: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! io::copy(&mut io::stdin(), &mut io::stdout()).await?; +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [functions-list]: #functions-1 +//! +//! ## io::Result +//! +//! Last, but certainly not least, is [`io::Result`]. This type is used +//! as the return type of many `std::io` functions that can cause an error, and +//! can be returned from your own functions as well. Many of the examples in this +//! module use the [`?` operator]: +//! +//! ``` +//! #![allow(dead_code)] +//! use async_std::io; +//! +//! async fn read_input() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; +//! +//! println!("You typed: {}", input.trim()); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The return type of `read_input`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is +//! to read the line and print it, so we use `()`. +//! +//! ## Platform-specific behavior +//! +//! Many I/O functions throughout the standard library are documented to indicate +//! what various library or syscalls they are delegated to. This is done to help +//! applications both understand what's happening under the hood as well as investigate +//! any possibly unclear semantics. Note, however, that this is informative, not a binding +//! contract. The implementation of many of these functions are subject to change over +//! time and may call fewer or more syscalls/library functions. +//! +//! [`Read`]: trait.Read.html +//! [`Write`]: trait.Write.html +//! [`Seek`]: trait.Seek.html +//! [`BufRead`]: trait.BufRead.html +//! [`File`]: ../fs/struct.File.html +//! [`TcpStream`]: ../net/struct.TcpStream.html +//! [`Vec`]: ../vec/struct.Vec.html +//! [`BufReader`]: struct.BufReader.html +//! [`BufWriter`]: struct.BufWriter.html +//! [`Write::write`]: trait.Write.html#tymethod.write +//! [`io::stdout`]: fn.stdout.html +//! [`println!`]: ../macro.println.html +//! [`Lines`]: struct.Lines.html +//! [`io::Result`]: type.Result.html +//! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html +//! [`Read::read`]: trait.Read.html#tymethod.read +//! [`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}; From 4b96ea127366bca42f6143e7cdf681884e9d6857 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 20:23:41 -0400 Subject: [PATCH 097/407] Adds Stream::cmp (#273) * Adds cmp * Fixes formatting * cleans up examples * attempts to fix rustdoc issue * formats with cargo fmt * Adds proper trait bounds for cmp --- src/stream/stream/cmp.rs | 91 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 39 +++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/stream/stream/cmp.rs diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs new file mode 100644 index 00000000..fc7161ad --- /dev/null +++ b/src/stream/stream/cmp.rs @@ -0,0 +1,91 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::fuse::Fuse; +use crate::future::Future; +use crate::prelude::*; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +// Lexicographically compares the elements of this `Stream` with those +// of another using `Ord`. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct CmpFuture { + l: Fuse, + r: Fuse, + l_cache: Option, + r_cache: Option, +} + +impl CmpFuture { + pin_utils::unsafe_pinned!(l: Fuse); + pin_utils::unsafe_pinned!(r: Fuse); + pin_utils::unsafe_unpinned!(l_cache: Option); + pin_utils::unsafe_unpinned!(r_cache: Option); + + pub(super) fn new(l: L, r: R) -> Self { + CmpFuture { + l: l.fuse(), + r: r.fuse(), + l_cache: None, + r_cache: None, + } + } +} + +impl Future for CmpFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: Ord, +{ + type Output = Ordering; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + // Stream that completes earliest can be considered Less, etc + let l_complete = self.l.done && self.as_mut().l_cache.is_none(); + let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + + if l_complete && r_complete { + return Poll::Ready(Ordering::Equal); + } else if l_complete { + return Poll::Ready(Ordering::Less); + } else if r_complete { + return Poll::Ready(Ordering::Greater); + } + + // Get next value if possible and necesary + if !self.l.done && self.as_mut().l_cache.is_none() { + let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + if let Some(item) = l_next { + *self.as_mut().l_cache() = Some(item); + } + } + + if !self.r.done && self.as_mut().r_cache.is_none() { + let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if let Some(item) = r_next { + *self.as_mut().r_cache() = Some(item); + } + } + + // Compare if both values are available. + if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { + let l_value = self.as_mut().l_cache().take().unwrap(); + let r_value = self.as_mut().r_cache().take().unwrap(); + let result = l_value.cmp(&r_value); + + if let Ordering::Equal = result { + // Reset cache to prepare for next comparison + *self.as_mut().l_cache() = None; + *self.as_mut().r_cache() = None; + } else { + // Return non equal value + return Poll::Ready(result); + } + } + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index fbbd8f66..f2b9830a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod chain; +mod cmp; mod enumerate; mod filter; mod filter_map; @@ -53,6 +54,7 @@ mod zip; use all::AllFuture; use any::AnyFuture; +use cmp::CmpFuture; use enumerate::Enumerate; use filter_map::FilterMap; use find::FindFuture; @@ -1270,6 +1272,43 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another using 'Ord'. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + 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]); + assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); + assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); + assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); + assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); + assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); + # + # }) } + ``` + "#] + fn cmp( + self, + other: S + ) -> impl Future [CmpFuture] + where + Self: Sized + Stream, + S: Stream, + ::Item: Ord + { + CmpFuture::new(self, other) + } #[doc = r#" Determines if the elements of this `Stream` are lexicographically From 6b00e5e66cae4bda1017dd4aab494744409c4a20 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 16 Oct 2019 02:32:27 +0200 Subject: [PATCH 098/407] Implemented StreamExt::try_fold (#344) --- src/stream/stream/mod.rs | 42 +++++++++++++++++++++++++ src/stream/stream/try_fold.rs | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/stream/stream/try_fold.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f2b9830a..d582d700 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -49,6 +49,7 @@ mod skip_while; mod step_by; mod take; mod take_while; +mod try_fold; mod try_for_each; mod zip; @@ -69,6 +70,7 @@ use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use try_fold::TryFoldFuture; use try_for_each::TryForEeachFuture; pub use chain::Chain; @@ -1042,6 +1044,46 @@ extension_trait! { Skip::new(self, n) } + #[doc = r#" + A combinator that applies a function as long as it returns successfully, producing a single, final value. + Immediately returns the error when the function returns unsuccessfully. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + let sum = s.try_fold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") + } + }).await; + + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_fold( + self, + init: T, + f: F, + ) -> impl Future> [TryFoldFuture] + where + Self: Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryFoldFuture::new(self, init, f) + } + #[doc = r#" Applies a falliable function to each element in a stream, stopping at first error and returning it. diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs new file mode 100644 index 00000000..212b0589 --- /dev/null +++ b/src/stream/stream/try_fold.rs @@ -0,0 +1,59 @@ +use std::marker::PhantomData; +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 TryFoldFuture { + stream: S, + f: F, + acc: Option, + __t: PhantomData, +} + +impl TryFoldFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_unpinned!(acc: Option); + + pub(super) fn new(stream: S, init: T, f: F) -> Self { + TryFoldFuture { + stream, + f, + acc: Some(init), + __t: PhantomData, + } + } +} + +impl Future for TryFoldFuture +where + S: Stream + Sized, + F: FnMut(T, S::Item) -> Result, +{ + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => { + let old = self.as_mut().acc().take().unwrap(); + let new = (self.as_mut().f())(old, v); + + match new { + Ok(o) => { + *self.as_mut().acc() = Some(o); + } + Err(e) => return Poll::Ready(Err(e)), + } + } + None => return Poll::Ready(Ok(self.as_mut().acc().take().unwrap())), + } + } + } +} From f00d32ee7d5c6afd1abc9db5a0e60081af7296ea Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 15:30:52 +0900 Subject: [PATCH 099/407] 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 100/407] 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 101/407] 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 faff1f7370a9bc31e7cedd7dc0cf7a6bd6966c09 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 10:28:14 +0200 Subject: [PATCH 102/407] task docs (#346) Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 2 +- src/task/mod.rs | 110 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index c41ceb68..7e8939b7 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -1,4 +1,4 @@ -//! Asynchronous iteration. +//! Composable asynchronous iteration. //! //! This module is an async version of [`std::iter`]. //! diff --git a/src/task/mod.rs b/src/task/mod.rs index fc702dfc..069aa3ae 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -1,26 +1,122 @@ -//! Asynchronous tasks. +//! Types and Traits for working with asynchronous tasks.. //! //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of //! threads. //! -//! [`std::thread`]: https://doc.rust-lang.org/std/thread/index.html +//! [`std::thread`]: https://doc.rust-lang.org/std/thread //! -//! # Examples +//! ## The task model //! -//! Spawn a task and await its result: +//! An executing asynchronous Rust program consists of a collection of native OS threads, on top of +//! which multiple stackless coroutines are multiplexed. We refer to these as "tasks". Tasks can +//! be named, and provide some built-in support for synchronization. //! +//! Communication between tasks can be done through channels, Rust's message-passing types, along +//! with [other forms of tasks synchronization](../sync/index.html) and shared-memory data +//! structures. In particular, types that are guaranteed to be threadsafe are easily shared between +//! tasks using the atomically-reference-counted container, [`Arc`]. +//! +//! Fatal logic errors in Rust cause *thread panic*, during which a thread will unwind the stack, +//! running destructors and freeing owned resources. If a panic occurs inside a task, there is no +//! meaningful way of recovering, so the panic will propagate through any thread boundaries all the +//! way to the root task. This is also known as a "panic = abort" model. +//! +//! ## Spawning a task +//! +//! A new task can be spawned using the [`task::spawn`][`spawn`] function: +//! +//! ```no_run +//! use async_std::task; +//! +//! task::spawn(async { +//! // some work here +//! }); //! ``` +//! +//! In this example, the spawned task is "detached" from the current task. This means that it can +//! outlive its parent (the task that spawned it), unless this parent is the root task. +//! +//! The root task can also wait on the completion of the child task; a call to [`spawn`] produces a +//! [`JoinHandle`], which provides implements `Future` and can be `await`ed: +//! +//! ``` +//! use async_std::task; +//! //! # async_std::task::block_on(async { //! # +//! let child = task::spawn(async { +//! // some work here +//! }); +//! // some work here +//! let res = child.await; +//! # +//! # }) +//! ``` +//! +//! The `await` operator returns the final value produced by the child task. +//! +//! ## Configuring tasks +//! +//! A new task can be configured before it is spawned via the [`Builder`] type, +//! which currently allows you to set the name and stack size for the child task: +//! +//! ``` +//! # #![allow(unused_must_use)] //! use async_std::task; //! -//! let handle = task::spawn(async { -//! 1 + 2 +//! # async_std::task::block_on(async { +//! # +//! task::Builder::new().name("child1".to_string()).spawn(async { +//! println!("Hello, world!"); //! }); -//! assert_eq!(handle.await, 3); //! # //! # }) //! ``` +//! +//! ## The `Task` type +//! +//! Tasks are represented via the [`Task`] type, which you can get in one of +//! two ways: +//! +//! * By spawning a new task, e.g., using the [`task::spawn`][`spawn`] +//! function, and calling [`task`][`JoinHandle::task`] on the [`JoinHandle`]. +//! * By requesting the current task, using the [`task::current`] function. +//! +//! ## Task-local storage +//! +//! This module also provides an implementation of task-local storage for Rust +//! programs. Task-local storage is a method of storing data into a global +//! variable that each task in the program will have its own copy of. +//! Tasks do not share this data, so accesses do not need to be synchronized. +//! +//! A task-local key owns the value it contains and will destroy the value when the +//! task exits. It is created with the [`task_local!`] macro and can contain any +//! value that is `'static` (no borrowed pointers). It provides an accessor function, +//! [`with`], that yields a shared reference to the value to the specified +//! closure. Task-local keys allow only shared access to values, as there would be no +//! way to guarantee uniqueness if mutable borrows were allowed. +//! +//! ## Naming tasks +//! +//! Tasks are able to have associated names for identification purposes. By default, spawned +//! tasks are unnamed. To specify a name for a task, build the task with [`Builder`] and pass +//! the desired task name to [`Builder::name`]. To retrieve the task name from within the +//! task, use [`Task::name`]. +//! +//! [`Arc`]: ../gsync/struct.Arc.html +//! [`spawn`]: fn.spawn.html +//! [`JoinHandle`]: struct.JoinHandle.html +//! [`JoinHandle::task`]: struct.JoinHandle.html#method.task +//! [`join`]: struct.JoinHandle.html#method.join +//! [`panic!`]: https://doc.rust-lang.org/std/macro.panic.html +//! [`Builder`]: struct.Builder.html +//! [`Builder::stack_size`]: struct.Builder.html#method.stack_size +//! [`Builder::name`]: struct.Builder.html#method.name +//! [`task::current`]: fn.current.html +//! [`Task`]: struct.Thread.html +//! [`Task::name`]: struct.Task.html#method.name +//! [`task_local!`]: ../macro.task_local.html +//! [`with`]: struct.LocalKey.html#method.with #[doc(inline)] pub use std::task::{Context, Poll, Waker}; From b251fc999a2faaeac6107af60f6dcf7ad077a5c3 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:18:05 +0900 Subject: [PATCH 103/407] 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 add6863185a12dbc415536720ad43cb31d2b7601 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 16 Oct 2019 12:24:18 +0200 Subject: [PATCH 104/407] Fix typos --- src/task/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index 069aa3ae..e2c89aea 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -1,4 +1,4 @@ -//! Types and Traits for working with asynchronous tasks.. +//! Types and traits for working with asynchronous tasks. //! //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of //! threads. From 358d2bc038f8894794ad71b6003938452859093b Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:20:07 +0900 Subject: [PATCH 105/407] 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 aaa1b6ca39fc1f81cc6b56193480d113116b44f2 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Wed, 16 Oct 2019 13:14:54 +0200 Subject: [PATCH 106/407] add Stream::last (#347) * add stream::LastFuture (not compiling) Struggling with the associated type, pinning and how to move/copy LastFuture.last. * fix type signature -> still cannot assign still problems assigning the new value to self.last * remove unused bound * add doctest * unpin LastFuture.last * RustFmt * add static lifetime * remove redundant lifetime --- src/stream/stream/last.rs | 42 ++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/stream/stream/last.rs diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs new file mode 100644 index 00000000..c58dd66a --- /dev/null +++ b/src/stream/stream/last.rs @@ -0,0 +1,42 @@ +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 LastFuture { + stream: S, + last: Option, +} + +impl LastFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(last: Option); + + pub(crate) fn new(stream: S) -> Self { + LastFuture { stream, last: None } + } +} + +impl Future for LastFuture +where + S: Stream + Unpin + Sized, + S::Item: 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) => { + cx.waker().wake_by_ref(); + *self.as_mut().last() = Some(new); + Poll::Pending + } + None => Poll::Ready(self.last), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d582d700..764dc978 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -36,6 +36,7 @@ mod fuse; mod ge; mod gt; mod inspect; +mod last; mod le; mod lt; mod map; @@ -64,6 +65,7 @@ use fold::FoldFuture; use for_each::ForEachFuture; use ge::GeFuture; use gt::GtFuture; +use last::LastFuture; use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; @@ -456,6 +458,54 @@ extension_trait! { Inspect::new(self, f) } + #[doc = r#" + Returns the last element of the stream. + + # Examples + + Basic usage: + + ``` + # 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 last = s.last().await; + assert_eq!(last, Some(3)); + # + # }) } + ``` + + An empty stream will return `None: + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![].into_iter().collect(); + + let last = s.last().await; + assert_eq!(last, None); + # + # }) } + ``` + + "#] + fn last( + self, + ) -> impl Future> [LastFuture] + where + Self: Sized, + { + LastFuture::new(self) + } + #[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 From 10f32ca817551565d6602911a79ad6a9736fde95 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 19:49:07 +0900 Subject: [PATCH 107/407] 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 6e0905d3caf10848cf3bab90485c5df9579907f2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 14:13:35 +0200 Subject: [PATCH 108/407] correctly mark stream::Merge as unstable (#352) Signed-off-by: Yoshua Wuyts --- src/stream/interval.rs | 1 + src/stream/stream/merge.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 271cd81d..21ac0329 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -43,6 +43,7 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub fn interval(dur: Duration) -> Interval { diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 5e7b226e..ab97d2cb 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -8,6 +8,8 @@ use futures_core::Stream; /// This stream is returned by [`Stream::merge`]. /// /// [`Stream::merge`]: trait.Stream.html#method.merge +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { left: L, From 4d34a153633ad5b4de5e68becc5bfee9b5f858a6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 14:34:11 +0200 Subject: [PATCH 109/407] fix macros, take II Signed-off-by: Yoshua Wuyts --- src/macros.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index d70b779f..12ca7ed2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -86,7 +86,10 @@ macro_rules! print { #[macro_export] macro_rules! println { () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) + ($($arg:tt)*) => (async { + $crate::io::_print(format_args!($($arg)*)).await; + $crate::io::_print(format_args!("\n")).await; + }) } /// Prints to the standard error. @@ -158,6 +161,7 @@ macro_rules! eprintln { ($($arg:tt)*) => ( async { $crate::io::_eprint(format_args!($($arg)*)).await; + $crate::io::_eprint(format_args!("\n")).await; } ); } From f1ed034600f17eb6ab2756e22e2fdc0f2944dd87 Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 16 Oct 2019 22:21:32 +0900 Subject: [PATCH 110/407] 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 802d4dfc3ba83f13ce1074f1eea60aec996d39f1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 15:24:35 +0200 Subject: [PATCH 111/407] finalize changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e8731f..f0e735a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,12 +56,15 @@ while let Some(_) = interval.next().await { ## Added - Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::cmp` - Added `Stream::ge` +- Added `Stream::last` - Added `Stream::le` - Added `Stream::lt` - Added `Stream::merge` as "unstable", replacing `stream::join!` - Added `Stream::partial_cmp` - Added `Stream::take_while` +- Added `Stream::try_fold` - Added `future::IntoFuture` as "unstable" - Added `io::BufRead::split` - Added `io::Write::write_fmt` @@ -76,6 +79,7 @@ while let Some(_) = interval.next().await { - Added `stream::Sum` - Added `stream::from_fn` - Added `stream::interval` as "unstable" +- Added `stream::repeat_with` - Added `task::spawn_blocking` as "unstable", replacing `task::blocking` - Added `task::yield_now` - Added `write!` and `writeln!` macros as "unstable" @@ -93,6 +97,8 @@ while let Some(_) = interval.next().await { - Rewrote the README.md - Stabilized `io::Cursor` - Switched bors over to use GitHub actions +- Updated the `io` documentation to match std's `io` docs +- Updated the `task` documentation to match std's `thread` docs ## Removed From 9d55fff81da0d4f76a9266df399fd8084406d4d2 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 16 Oct 2019 22:38:28 +0900 Subject: [PATCH 112/407] 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 113/407] 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 114/407] 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 115/407] 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 a2393501c5edd4c0ef682dfdfe09f05e02bd5e5c Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 16 Oct 2019 18:43:34 +0200 Subject: [PATCH 116/407] Implemented StreamExt::throttle --- src/stream/stream/mod.rs | 10 +++++++ src/stream/stream/throttle.rs | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/stream/stream/throttle.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 764dc978..8035769a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -50,6 +50,7 @@ mod skip_while; mod step_by; mod take; mod take_while; +mod throttle; mod try_fold; mod try_for_each; mod zip; @@ -86,10 +87,12 @@ pub use skip_while::SkipWhile; pub use step_by::StepBy; pub use take::Take; pub use take_while::TakeWhile; +pub use throttle::Throttle; pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; +use std::time::Duration; use cfg_if::cfg_if; @@ -288,6 +291,13 @@ extension_trait! { TakeWhile::new(self, predicate) } + fn throttle(self, d: Duration) -> Throttle + where + Self: Sized, + { + Throttle::new(self, d) + } + #[doc = r#" Creates a stream that yields each `step`th element. diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs new file mode 100644 index 00000000..c37c972c --- /dev/null +++ b/src/stream/stream/throttle.rs @@ -0,0 +1,56 @@ +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that only yields one element once every `duration`, and drops all others. +/// #[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Throttle { + stream: S, + duration: Duration, + delay: Option, +} + +impl Unpin for Throttle {} + +impl Throttle { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(duration: Duration); + pin_utils::unsafe_pinned!(delay: Option); + + pub(super) fn new(stream: S, duration: Duration) -> Self { + Throttle { + stream, + duration, + delay: None, + } + } +} + +impl Stream for Throttle { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.as_mut().stream().poll_next(cx) { + Poll::Ready(v) => match self.as_mut().delay().as_pin_mut() { + None => { + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(v) + } + Some(d) => match d.poll(cx) { + Poll::Ready(_) => { + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + }, + }, + Poll::Pending => Poll::Pending, + } + } +} From e405544ea0b6cac9fd599ab6f86383db261ff601 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 17 Oct 2019 07:06:29 +0900 Subject: [PATCH 117/407] Enable tests on CI (#357) * Enable tests on CI * Fix failed test --- .github/workflows/ci.yml | 2 +- tests/buf_writer.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b92e50b4..c622a59b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --doc --features unstable + args: --all --features unstable check_fmt_and_docs: name: Checking fmt and docs diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index fa0e1ed3..cb2368aa 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -48,13 +48,13 @@ fn test_buffered_writer() { } #[test] -fn test_buffered_writer_inner_into_inner_does_not_flush() { +fn test_buffered_writer_inner_into_inner_flushes() { task::block_on(async { let mut w = BufWriter::with_capacity(3, Vec::new()); w.write(&[0, 1]).await.unwrap(); assert_eq!(*w.get_ref(), []); let w = w.into_inner().await.unwrap(); - assert_eq!(w, []); + assert_eq!(w, [0, 1]); }) } From a5a00d7b1465b728592ae0cff55fbefba853d528 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 16:29:23 +0900 Subject: [PATCH 118/407] 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 119/407] 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 120/407] $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 46f0fb1c6407a5417eeedfedfed5fdf5a5e9cef6 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 11:52:42 +0200 Subject: [PATCH 121/407] Make sure each invocation of block_on uses its own Parker (#358) --- Cargo.toml | 1 + src/task/block_on.rs | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1c10a62..e8e2ccc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ async-task = "1.0.0" cfg-if = "0.1.9" 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" diff --git a/src/task/block_on.rs b/src/task/block_on.rs index db46f025..032bf02d 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,10 +1,12 @@ -use std::cell::UnsafeCell; +use std::cell::{Cell, UnsafeCell}; 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::{self, Thread}; +use std::thread; + +use crossbeam_utils::sync::Parker; use super::task; use super::task_local; @@ -119,13 +121,21 @@ where F: Future, { thread_local! { - static ARC_THREAD: Arc = Arc::new(thread::current()); + // May hold a pre-allocated parker that can be reused for efficiency. + // + // Note that each invocation of `block` needs its own parker. In particular, if `block` + // recursively calls itself, we must make sure that each recursive call uses a distinct + // parker instance. + static CACHE: Cell>> = Cell::new(None); } pin_utils::pin_mut!(f); - ARC_THREAD.with(|arc_thread: &Arc| { - let ptr = (&**arc_thread as *const Thread) as *const (); + 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))) }; @@ -133,32 +143,34 @@ where loop { if let Poll::Ready(t) = f.as_mut().poll(cx) { + // Save the parker for the next invocation of `block`. + cache.set(Some(arc_parker)); return t; } - thread::park(); + arc_parker.park(); } }) } fn vtable() -> &'static RawWakerVTable { unsafe fn clone_raw(ptr: *const ()) -> RawWaker { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); + 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 Thread); - arc.unpark(); + 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 Thread)); - arc.unpark(); + 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 Thread)) + drop(Arc::from_raw(ptr as *const Parker)) } &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) From ec98b41c85bb8c2891755fc6b86366fb85413a5e Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 17 Oct 2019 23:56:01 +0900 Subject: [PATCH 122/407] 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 ec23632f3e7d2d8f9ed49995b85741eeb0a5be69 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 19:17:49 +0200 Subject: [PATCH 123/407] Cleanup: replace cfg-if with our macros (#361) * Cleanup: replace cfg-if with our macros * Prefix macros with cfg_ * Remove #[macro_export] from internal macros --- Cargo.toml | 3 +- src/fs/dir_builder.rs | 23 +- src/fs/dir_entry.rs | 21 +- src/fs/file.rs | 85 +++----- src/fs/file_type.rs | 148 +++++++------ src/fs/metadata.rs | 350 +++++++++++++++--------------- src/fs/open_options.rs | 31 +-- src/fs/permissions.rs | 98 ++++----- src/future/future.rs | 14 +- src/future/into_future.rs | 2 +- src/future/mod.rs | 16 +- src/io/buf_read/mod.rs | 11 +- src/io/read/mod.rs | 66 +++--- src/io/{seek.rs => seek/mod.rs} | 37 +--- src/io/seek/seek.rs | 21 ++ src/io/stderr.rs | 37 +--- src/io/stdin.rs | 37 +--- src/io/stdout.rs | 37 +--- src/io/write/mod.rs | 18 +- src/lib.rs | 34 ++- src/macros.rs | 8 +- src/net/addr.rs | 24 +- src/net/tcp/listener.rs | 84 +++---- src/net/tcp/stream.rs | 81 +++---- src/net/udp/mod.rs | 83 +++---- src/os/mod.rs | 12 +- src/os/unix/fs.rs | 72 +++--- src/os/unix/io.rs | 90 ++++---- src/os/unix/net/mod.rs | 142 ++++++------ src/os/windows/io.rs | 80 ++++--- src/{pin.rs => pin/mod.rs} | 0 src/prelude.rs | 15 +- src/stream/double_ended_stream.rs | 2 +- src/stream/exact_size_stream.rs | 2 +- src/stream/extend.rs | 1 + src/stream/from_stream.rs | 2 +- src/stream/fused_stream.rs | 2 +- src/stream/interval.rs | 7 +- src/stream/into_stream.rs | 2 +- src/stream/mod.rs | 45 ++-- src/stream/product.rs | 2 +- src/stream/stream/merge.rs | 2 +- src/stream/stream/mod.rs | 44 ++-- src/stream/sum.rs | 2 +- src/sync/barrier.rs | 2 + src/sync/mod.rs | 11 +- src/task/mod.rs | 10 +- src/task/yield_now.rs | 2 +- src/utils.rs | 69 +++++- 49 files changed, 917 insertions(+), 1070 deletions(-) rename src/io/{seek.rs => seek/mod.rs} (80%) create mode 100644 src/io/seek/seek.rs rename src/{pin.rs => pin/mod.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index e8e2ccc9..2741a19a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,13 +21,12 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -docs = ["broadcaster"] +docs = ["unstable"] unstable = ["broadcaster"] [dependencies] async-macros = "1.0.0" async-task = "1.0.0" -cfg-if = "0.1.9" crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" crossbeam-utils = "0.6.6" diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 6dfddcb3..a55a9a92 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,7 +1,5 @@ use std::future::Future; -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -113,22 +111,13 @@ impl DirBuilder { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::DirBuilderExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::DirBuilderExt; - } -} +cfg_unix! { + use crate::os::unix::fs::DirBuilderExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl DirBuilderExt for DirBuilder { - fn mode(&mut self, mode: u32) -> &mut Self { - self.mode = Some(mode); - self - } + impl DirBuilderExt for DirBuilder { + fn mode(&mut self, mode: u32) -> &mut Self { + self.mode = Some(mode); + self } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 80d4bbdf..959e2ada 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -2,8 +2,6 @@ use std::ffi::OsString; use std::fmt; use std::sync::Arc; -use cfg_if::cfg_if; - use crate::fs::{FileType, Metadata}; use crate::io; use crate::path::PathBuf; @@ -160,21 +158,12 @@ impl fmt::Debug for DirEntry { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::DirEntryExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::DirEntryExt; - } -} +cfg_unix! { + use crate::os::unix::fs::DirEntryExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl DirEntryExt for DirEntry { - fn ino(&self) -> u64 { - self.0.ino() - } + impl DirEntryExt for DirEntry { + fn ino(&self) -> u64 { + self.0.ino() } } } diff --git a/src/fs/file.rs b/src/fs/file.rs index 39162345..3129e96a 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -7,8 +7,6 @@ use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use cfg_if::cfg_if; - use crate::fs::{Metadata, Permissions}; use crate::future; use crate::io::{self, Read, Seek, SeekFrom, Write}; @@ -401,67 +399,54 @@ impl From for File { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for File { - fn as_raw_fd(&self) -> RawFd { - self.file.as_raw_fd() - } + impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() } + } - impl FromRawFd for File { - unsafe fn from_raw_fd(fd: RawFd) -> File { - std::fs::File::from_raw_fd(fd).into() - } + impl FromRawFd for File { + unsafe fn from_raw_fd(fd: RawFd) -> File { + std::fs::File::from_raw_fd(fd).into() } + } - impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") - .into_raw_fd() - } + impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect("cannot acquire ownership of the file handle after drop") + .into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for File { - fn as_raw_handle(&self) -> RawHandle { - self.file.as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + + impl AsRawHandle for File { + fn as_raw_handle(&self) -> RawHandle { + self.file.as_raw_handle() } + } - impl FromRawHandle for File { - unsafe fn from_raw_handle(handle: RawHandle) -> File { - std::fs::File::from_raw_handle(handle).into() - } + impl FromRawHandle for File { + unsafe fn from_raw_handle(handle: RawHandle) -> File { + std::fs::File::from_raw_handle(handle).into() } + } - impl IntoRawHandle for File { - fn into_raw_handle(self) -> RawHandle { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") - .into_raw_handle() - } + impl IntoRawHandle for File { + fn into_raw_handle(self) -> RawHandle { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect("cannot acquire ownership of the file handle after drop") + .into_raw_handle() } } } diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs index a1627984..11f47d1c 100644 --- a/src/fs/file_type.rs +++ b/src/fs/file_type.rs @@ -1,86 +1,84 @@ -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::fs::FileType; +} + +cfg_docs! { + /// The type of a file or directory. + /// + /// A file type is returned by [`Metadata::file_type`]. + /// + /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], + /// [`is_file`], and [`is_symlink`] can return `true`. + /// + /// This type is a re-export of [`std::fs::FileType`]. + /// + /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`is_symlink`]: #method.is_symlink + /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct FileType { + _private: (), + } -cfg_if! { - if #[cfg(feature = "docs")] { - /// The type of a file or directory. + impl FileType { + /// Returns `true` if this file type represents a regular directory. /// - /// A file type is returned by [`Metadata::file_type`]. + /// If this file type represents a symbolic link, this method returns `false`. /// - /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], - /// [`is_file`], and [`is_symlink`] can return `true`. + /// # Examples /// - /// This type is a re-export of [`std::fs::FileType`]. + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; /// - /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type - /// [`is_dir`]: #method.is_dir - /// [`is_file`]: #method.is_file - /// [`is_symlink`]: #method.is_symlink - /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub struct FileType { - _private: (), + /// let file_type = fs::metadata(".").await?.file_type(); + /// println!("{:?}", file_type.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() } - impl FileType { - /// Returns `true` if this file type represents a regular directory. - /// - /// If this file type represents a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata(".").await?.file_type(); - /// println!("{:?}", file_type.is_dir()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_dir(&self) -> bool { - unimplemented!() - } - - /// Returns `true` if this file type represents a regular file. - /// - /// If this file type represents a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata("a.txt").await?.file_type(); - /// println!("{:?}", file_type.is_file()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_file(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this file type represents a regular file. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } - /// Returns `true` if this file type represents a symbolic link. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata("a.txt").await?.file_type(); - /// println!("{:?}", file_type.is_symlink()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_symlink(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this file type represents a symbolic link. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_symlink()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_symlink(&self) -> bool { + unimplemented!() } - } else { - pub use std::fs::FileType; } } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 65f494b5..4afc5595 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,5 +1,3 @@ -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -39,193 +37,193 @@ pub async fn metadata>(path: P) -> io::Result { blocking::spawn(move || std::fs::metadata(path)).await } -cfg_if! { - if #[cfg(feature = "docs")] { - use std::time::SystemTime; +cfg_not_docs! { + pub use std::fs::Metadata; +} + +cfg_docs! { + use std::time::SystemTime; - use crate::fs::{FileType, Permissions}; + use crate::fs::{FileType, Permissions}; - /// Metadata for a file or directory. + /// Metadata for a file or directory. + /// + /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// + /// This type is a re-export of [`std::fs::Metadata`]. + /// + /// [`metadata`]: fn.metadata.html + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html + #[derive(Clone, Debug)] + pub struct Metadata { + _private: (), + } + + impl Metadata { + /// Returns the file type from this metadata. /// - /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// # Examples /// - /// This type is a re-export of [`std::fs::Metadata`]. + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; /// - /// [`metadata`]: fn.metadata.html - /// [`symlink_metadata`]: fn.symlink_metadata.html - /// [`is_dir`]: #method.is_dir - /// [`is_file`]: #method.is_file - /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html - #[derive(Clone, Debug)] - pub struct Metadata { - _private: (), + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn file_type(&self) -> FileType { + unimplemented!() } - impl Metadata { - /// Returns the file type from this metadata. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.file_type()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn file_type(&self) -> FileType { - unimplemented!() - } - - /// Returns `true` if this metadata is for a regular directory. - /// - /// If this metadata is for a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata(".").await?; - /// println!("{:?}", metadata.is_dir()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_dir(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this metadata is for a regular directory. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata(".").await?; + /// println!("{:?}", metadata.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } - /// Returns `true` if this metadata is for a regular file. - /// - /// If this metadata is for a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.is_file()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_file(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this metadata is for a regular file. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } - /// Returns the file size in bytes. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{}", metadata.len()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn len(&self) -> u64 { - unimplemented!() - } + /// Returns the file size in bytes. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{}", metadata.len()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn len(&self) -> u64 { + unimplemented!() + } - /// Returns the permissions from this metadata. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.permissions()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn permissions(&self) -> Permissions { - unimplemented!() - } + /// Returns the permissions from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.permissions()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn permissions(&self) -> Permissions { + unimplemented!() + } - /// Returns the last modification time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.modified()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn modified(&self) -> io::Result { - unimplemented!() - } + /// Returns the last modification time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.modified()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn modified(&self) -> io::Result { + unimplemented!() + } - /// Returns the last access time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.accessed()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn accessed(&self) -> io::Result { - unimplemented!() - } + /// Returns the last access time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.accessed()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn accessed(&self) -> io::Result { + unimplemented!() + } - /// Returns the creation time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.created()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn created(&self) -> io::Result { - unimplemented!() - } + /// Returns the creation time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.created()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn created(&self) -> io::Result { + unimplemented!() } - } else { - pub use std::fs::Metadata; } } diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index fe65e2f4..a2eb9e76 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,7 +1,5 @@ use std::future::Future; -use cfg_if::cfg_if; - use crate::fs::File; use crate::io; use crate::path::Path; @@ -296,27 +294,18 @@ impl Default for OpenOptions { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::OpenOptionsExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::OpenOptionsExt; - } -} +cfg_unix! { + use crate::os::unix::fs::OpenOptionsExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut Self { - self.0.mode(mode); - self - } + impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut Self { + self.0.mode(mode); + self + } - fn custom_flags(&mut self, flags: i32) -> &mut Self { - self.0.custom_flags(flags); - self - } + fn custom_flags(&mut self, flags: i32) -> &mut Self { + self.0.custom_flags(flags); + self } } } diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs index 628dd392..1339a7c7 100644 --- a/src/fs/permissions.rs +++ b/src/fs/permissions.rs @@ -1,58 +1,56 @@ -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::fs::Permissions; +} + +cfg_docs! { + /// A set of permissions on a file or directory. + /// + /// This type is a re-export of [`std::fs::Permissions`]. + /// + /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Permissions { + _private: (), + } -cfg_if! { - if #[cfg(feature = "docs")] { - /// A set of permissions on a file or directory. + impl Permissions { + /// Returns the read-only flag. /// - /// This type is a re-export of [`std::fs::Permissions`]. + /// # Examples /// - /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html - #[derive(Clone, PartialEq, Eq, Debug)] - pub struct Permissions { - _private: (), + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let perm = fs::metadata("a.txt").await?.permissions(); + /// println!("{:?}", perm.readonly()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn readonly(&self) -> bool { + unimplemented!() } - impl Permissions { - /// Returns the read-only flag. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let perm = fs::metadata("a.txt").await?.permissions(); - /// println!("{:?}", perm.readonly()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn readonly(&self) -> bool { - unimplemented!() - } - - /// Configures the read-only flag. - /// - /// [`fs::set_permissions`]: fn.set_permissions.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let mut perm = fs::metadata("a.txt").await?.permissions(); - /// perm.set_readonly(true); - /// fs::set_permissions("a.txt", perm).await?; - /// # - /// # Ok(()) }) } - /// ``` - pub fn set_readonly(&mut self, readonly: bool) { - unimplemented!() - } + /// Configures the read-only flag. + /// + /// [`fs::set_permissions`]: fn.set_permissions.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let mut perm = fs::metadata("a.txt").await?.permissions(); + /// perm.set_readonly(true); + /// fs::set_permissions("a.txt", perm).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn set_readonly(&mut self, readonly: bool) { + unimplemented!() } - } else { - pub use std::fs::Permissions; } } diff --git a/src/future/future.rs b/src/future/future.rs index 556dc1ac..8c9c12a2 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -1,15 +1,9 @@ -use crate::utils::extension_trait; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; -cfg_if::cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; + use crate::task::{Context, Poll}; - use crate::task::{Context, Poll}; - } -} - -extension_trait! { #[doc = r#" A future represents an asynchronous computation. diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 58b67661..42839a20 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -30,7 +30,7 @@ use crate::future::Future; /// } /// } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait IntoFuture { /// The type of value produced on completion. diff --git a/src/future/mod.rs b/src/future/mod.rs index cc3b7a5d..a45bf96c 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -44,12 +44,6 @@ #[doc(inline)] pub use async_macros::{join, try_join}; -#[doc(inline)] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub use async_macros::{select, try_select}; - -use cfg_if::cfg_if; - pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; @@ -62,10 +56,10 @@ mod poll_fn; mod ready; mod timeout; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod into_future; +cfg_unstable! { + #[doc(inline)] + pub use async_macros::{select, try_select}; - pub use into_future::IntoFuture; - } + pub use into_future::IntoFuture; + mod into_future; } diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index dff10fab..b82971fe 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -12,19 +12,12 @@ use read_until::ReadUntilFuture; use std::mem; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::io; use crate::task::{Context, Poll}; -use crate::utils::extension_trait; - -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; - } -} extension_trait! { + use std::ops::{Deref, DerefMut}; + #[doc = r#" Allows reading from a buffered byte stream. diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 40cb3ca0..ed61590b 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -13,23 +13,17 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture}; use read_to_string::ReadToStringFuture; use read_vectored::ReadVectoredFuture; -use cfg_if::cfg_if; use std::mem; use crate::io::IoSliceMut; -use crate::utils::extension_trait; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; - use crate::io; - use crate::task::{Context, Poll}; - } -} + use crate::io; + use crate::task::{Context, Poll}; -extension_trait! { #[doc = r#" Allows reading from a byte stream. @@ -309,34 +303,34 @@ extension_trait! { #[doc = r#" Creates a "by reference" adaptor for this instance of `Read`. - + The returned adaptor also implements `Read` and will simply borrow this current reader. - + # Examples - + [`File`][file]s implement `Read`: - + [file]: ../fs/struct.File.html - + ```no_run # fn main() -> std::io::Result<()> { async_std::task::block_on(async { # use async_std::prelude::*; use async_std::fs::File; - + let mut f = File::open("foo.txt").await?; let mut buffer = Vec::new(); let mut other_buffer = Vec::new(); - + { let reference = f.by_ref(); - + // read at most 5 bytes reference.take(5).read_to_end(&mut buffer).await?; - + } // drop our &mut reference so we can use f again - + // original file still usable, read the rest f.read_to_end(&mut other_buffer).await?; # @@ -348,27 +342,27 @@ extension_trait! { #[doc = r#" Transforms this `Read` instance to a `Stream` over its bytes. - + The returned type implements `Stream` where the `Item` is `Result`. The yielded item is `Ok` if a byte was successfully read and `Err` otherwise. EOF is mapped to returning `None` from this iterator. - + # Examples - + [`File`][file]s implement `Read`: - + [file]: ../fs/struct.File.html - + ```no_run # fn main() -> std::io::Result<()> { async_std::task::block_on(async { # use async_std::prelude::*; use async_std::fs::File; - + let f = File::open("foo.txt").await?; let mut s = f.bytes(); - + while let Some(byte) = s.next().await { println!("{}", byte.unwrap()); } @@ -382,29 +376,29 @@ extension_trait! { #[doc = r#" Creates an adaptor which will chain this stream with another. - + The returned `Read` instance will first read all bytes from this object until EOF is encountered. Afterwards the output is equivalent to the output of `next`. - + # Examples - + [`File`][file]s implement `Read`: - + [file]: ../fs/struct.File.html - + ```no_run # fn main() -> std::io::Result<()> { async_std::task::block_on(async { # use async_std::prelude::*; use async_std::fs::File; - + let f1 = File::open("foo.txt").await?; let f2 = File::open("bar.txt").await?; - + let mut handle = f1.chain(f2); let mut buffer = String::new(); - + // read the value into a String. We could use any Read method here, // this is just one example. handle.read_to_string(&mut buffer).await?; diff --git a/src/io/seek.rs b/src/io/seek/mod.rs similarity index 80% rename from src/io/seek.rs rename to src/io/seek/mod.rs index 2d1c4d37..ec2dd8f9 100644 --- a/src/io/seek.rs +++ b/src/io/seek/mod.rs @@ -1,19 +1,16 @@ -use std::pin::Pin; +mod seek; -use cfg_if::cfg_if; +use seek::SeekFuture; -use crate::future::Future; -use crate::io::{self, SeekFrom}; -use crate::task::{Context, Poll}; -use crate::utils::extension_trait; - -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; - } -} +use crate::io::SeekFrom; extension_trait! { + use std::ops::{Deref, DerefMut}; + use std::pin::Pin; + + use crate::io; + use crate::task::{Context, Poll}; + #[doc = r#" Allows seeking through a byte stream. @@ -114,19 +111,3 @@ extension_trait! { } } } - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct SeekFuture<'a, T: Unpin + ?Sized> { - seeker: &'a mut T, - pos: SeekFrom, -} - -impl Future for SeekFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let pos = self.pos; - Pin::new(&mut *self.seeker).poll_seek(cx, pos) - } -} diff --git a/src/io/seek/seek.rs b/src/io/seek/seek.rs new file mode 100644 index 00000000..65743be2 --- /dev/null +++ b/src/io/seek/seek.rs @@ -0,0 +1,21 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::io::{self, Seek, SeekFrom}; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct SeekFuture<'a, T: Unpin + ?Sized> { + pub(crate) seeker: &'a mut T, + pub(crate) pos: SeekFrom, +} + +impl Future for SeekFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let pos = self.pos; + Pin::new(&mut *self.seeker).poll_seek(cx, pos) + } +} diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 34d4d1dc..1ec28b29 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io::{self, Write}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -162,35 +160,22 @@ impl Write for Stderr { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stderr { - fn as_raw_fd(&self) -> RawFd { - std::io::stderr().as_raw_fd() - } + impl AsRawFd for Stderr { + fn as_raw_fd(&self) -> RawFd { + std::io::stderr().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stderr { - fn as_raw_handle(&self) -> RawHandle { - std::io::stderr().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stderr { + fn as_raw_handle(&self) -> RawHandle { + std::io::stderr().as_raw_handle() } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 178e819d..dd3991fd 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::{self, Future}; use crate::io::{self, Read}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -186,35 +184,22 @@ impl Read for Stdin { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stdin { - fn as_raw_fd(&self) -> RawFd { - std::io::stdin().as_raw_fd() - } + impl AsRawFd for Stdin { + fn as_raw_fd(&self) -> RawFd { + std::io::stdin().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stdin { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdin().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdin { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdin().as_raw_handle() } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 4128aae0..7945bfdd 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io::{self, Write}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -162,35 +160,22 @@ impl Write for Stdout { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stdout { - fn as_raw_fd(&self) -> RawFd { - std::io::stdout().as_raw_fd() - } + impl AsRawFd for Stdout { + fn as_raw_fd(&self) -> RawFd { + std::io::stdout().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stdout { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdout().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdout { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdout().as_raw_handle() } } } diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index be69b7e2..a07c8968 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -10,22 +10,14 @@ use write_all::WriteAllFuture; use write_fmt::WriteFmtFuture; use write_vectored::WriteVectoredFuture; -use cfg_if::cfg_if; +use crate::io::{self, IoSlice}; -use crate::io::IoSlice; -use crate::utils::extension_trait; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; -use crate::io; + use crate::task::{Context, Poll}; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; - use crate::task::{Context, Poll}; - } -} - -extension_trait! { #[doc = r#" Allows writing to a byte stream. diff --git a/src/lib.rs b/src/lib.rs index e138c87d..7f888a14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,8 @@ #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "1024"] -use cfg_if::cfg_if; +#[macro_use] +mod utils; pub mod fs; pub mod future; @@ -61,26 +62,19 @@ pub mod stream; pub mod sync; pub mod task; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub mod pin; - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - 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; -pub(crate) mod utils; - -#[cfg(any(feature = "unstable", feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] -pub use std::{write, writeln}; diff --git a/src/macros.rs b/src/macros.rs index 12ca7ed2..f932e47e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -43,7 +43,7 @@ /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! print { @@ -81,7 +81,7 @@ macro_rules! print { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! println { @@ -119,7 +119,7 @@ macro_rules! println { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprint { @@ -153,7 +153,7 @@ macro_rules! eprint { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprintln { diff --git a/src/net/addr.rs b/src/net/addr.rs index adc24083..1dada64c 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -3,24 +3,22 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io; use crate::task::{blocking, Context, JoinHandle, Poll}; -cfg_if! { - if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct ImplFuture(std::marker::PhantomData); +cfg_not_docs! { + macro_rules! ret { + (impl Future, $fut:ty) => ($fut); + } +} + +cfg_docs! { + #[doc(hidden)] + pub struct ImplFuture(std::marker::PhantomData); - macro_rules! ret { - (impl Future, $fut:ty) => (ImplFuture<$out>); - } - } else { - macro_rules! ret { - (impl Future, $fut:ty) => ($fut); - } + macro_rules! ret { + (impl Future, $fut:ty) => (ImplFuture<$out>); } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 26e19d79..6fd27f0f 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,13 +1,10 @@ use std::net::SocketAddr; use std::pin::Pin; -use cfg_if::cfg_if; - -use super::TcpStream; use crate::future::{self, Future}; use crate::io; use crate::net::driver::Watcher; -use crate::net::ToSocketAddrs; +use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -213,59 +210,46 @@ impl From for TcpListener { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for TcpListener { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { - std::net::TcpListener::from_raw_fd(fd).into() - } + impl FromRawFd for TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { + std::net::TcpListener::from_raw_fd(fd).into() } + } - impl IntoRawFd for TcpListener { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for TcpListener { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // impl AsRawSocket for TcpListener { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpListener { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { - // net::TcpListener::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } - } +cfg_windows! { + // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + // + // impl AsRawSocket for TcpListener { + // fn as_raw_socket(&self) -> RawSocket { + // self.raw_socket + // } + // } + // + // impl FromRawSocket for TcpListener { + // unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { + // net::TcpListener::from_raw_socket(handle).try_into().unwrap() + // } + // } + // + // impl IntoRawSocket for TcpListener { + // fn into_raw_socket(self) -> RawSocket { + // self.raw_socket + // } + // } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1ffd6363..5988194f 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -2,8 +2,6 @@ use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::net::SocketAddr; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::future; use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; @@ -367,59 +365,46 @@ impl From for TcpStream { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for TcpStream { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { - std::net::TcpStream::from_raw_fd(fd).into() - } + impl FromRawFd for TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { + std::net::TcpStream::from_raw_fd(fd).into() } + } - impl IntoRawFd for TcpStream { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for TcpStream { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // impl AsRawSocket for TcpStream { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpStream { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { - // net::TcpStream::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } - } +cfg_windows! { + // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + // + // impl AsRawSocket for TcpStream { + // fn as_raw_socket(&self) -> RawSocket { + // self.raw_socket + // } + // } + // + // impl FromRawSocket for TcpStream { + // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { + // net::TcpStream::from_raw_socket(handle).try_into().unwrap() + // } + // } + // + // impl IntoRawSocket for TcpListener { + // fn into_raw_socket(self) -> RawSocket { + // self.raw_socket + // } + // } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 4588be12..37c9d50c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -1,7 +1,5 @@ use std::io; use std::net::SocketAddr; - -use cfg_if::cfg_if; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; @@ -463,61 +461,46 @@ impl From for UdpSocket { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for UdpSocket { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { - std::net::UdpSocket::from_raw_fd(fd).into() - } + impl FromRawFd for UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { + std::net::UdpSocket::from_raw_fd(fd).into() } + } - impl IntoRawFd for UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; - // - // impl AsRawSocket for UdpSocket { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for UdpSocket { - // unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { - // net::UdpSocket::from_raw_socket(handle).into() - // } - // } - // - // impl IntoRawSocket for UdpSocket { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } - } +cfg_windows! { + // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + // + // impl AsRawSocket for UdpSocket { + // fn as_raw_socket(&self) -> RawSocket { + // self.raw_socket + // } + // } + // + // impl FromRawSocket for UdpSocket { + // unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { + // net::UdpSocket::from_raw_socket(handle).into() + // } + // } + // + // impl IntoRawSocket for UdpSocket { + // fn into_raw_socket(self) -> RawSocket { + // self.raw_socket + // } + // } } diff --git a/src/os/mod.rs b/src/os/mod.rs index fb9b9412..5b836aec 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,9 +1,9 @@ //! OS-specific extensions. -#[cfg(any(unix, feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -pub mod unix; +cfg_unix! { + pub mod unix; +} -#[cfg(any(windows, feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -pub mod windows; +cfg_windows! { + pub mod windows; +} diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index 4a155106..d3e85234 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -1,7 +1,5 @@ //! Unix-specific filesystem extensions. -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -31,43 +29,43 @@ pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Resu blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await } -cfg_if! { - if #[cfg(feature = "docs")] { - /// Unix-specific extensions to `DirBuilder`. - pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// `0o777`. - fn mode(&mut self, mode: u32) -> &mut Self; - } +cfg_not_docs! { + pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt}; +} - /// Unix-specific extension methods for `DirEntry`. - pub trait DirEntryExt { - /// Returns the underlying `d_ino` field in the contained `dirent` - /// structure. - fn ino(&self) -> u64; - } +cfg_docs! { + /// Unix-specific extensions to `DirBuilder`. + pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// `0o777`. + fn mode(&mut self, mode: u32) -> &mut Self; + } + + /// Unix-specific extension methods for `DirEntry`. + pub trait DirEntryExt { + /// Returns the underlying `d_ino` field in the contained `dirent` + /// structure. + fn ino(&self) -> u64; + } - /// Unix-specific extensions to `OpenOptions`. - pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of a `File::open_opts` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the systems `umask`, to produce - /// the final permissions. - fn mode(&mut self, mode: u32) -> &mut Self; + /// Unix-specific extensions to `OpenOptions`. + pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the systems `umask`, to produce + /// the final permissions. + fn mode(&mut self, mode: u32) -> &mut Self; - /// Pass custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - fn custom_flags(&mut self, flags: i32) -> &mut Self; - } - } else { - pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt}; + /// Pass custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rusts options. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + fn custom_flags(&mut self, flags: i32) -> &mut Self; } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 820d509c..0b984607 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -1,56 +1,54 @@ //! Unix-specific I/O extensions. -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +} -cfg_if! { - if #[cfg(feature = "docs")] { - /// Raw file descriptors. - pub type RawFd = std::os::raw::c_int; +cfg_docs! { + /// Raw file descriptors. + pub type RawFd = std::os::raw::c_int; - /// A trait to extract the raw unix file descriptor from an underlying - /// object. + /// A trait to extract the raw unix file descriptor from an underlying + /// object. + /// + /// This is only available on unix platforms and must be imported in order + /// to call the method. Windows platforms have a corresponding `AsRawHandle` + /// and `AsRawSocket` set of traits. + pub trait AsRawFd { + /// Extracts the raw file descriptor. /// - /// This is only available on unix platforms and must be imported in order - /// to call the method. Windows platforms have a corresponding `AsRawHandle` - /// and `AsRawSocket` set of traits. - pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - fn as_raw_fd(&self) -> RawFd; - } + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + fn as_raw_fd(&self) -> RawFd; + } - /// A trait to express the ability to construct an object from a raw file + /// A trait to express the ability to construct an object from a raw file + /// descriptor. + pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file /// descriptor. - pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; - } + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; + } - /// A trait to express the ability to consume an object and acquire ownership of - /// its raw file descriptor. - pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. - fn into_raw_fd(self) -> RawFd; - } - } else { - pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw file descriptor. + pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + fn into_raw_fd(self) -> RawFd; } } diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs index 2c79e8c3..e0a3b9b6 100644 --- a/src/os/unix/net/mod.rs +++ b/src/os/unix/net/mod.rs @@ -1,7 +1,5 @@ //! Unix-specific networking extensions. -use cfg_if::cfg_if; - pub use datagram::UnixDatagram; pub use listener::{Incoming, UnixListener}; pub use stream::UnixStream; @@ -10,90 +8,90 @@ mod datagram; mod listener; mod stream; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::fmt; +cfg_not_docs! { + pub use std::os::unix::net::SocketAddr; +} + +cfg_docs! { + use std::fmt; + + use crate::path::Path; - use crate::path::Path; + /// An address associated with a Unix socket. + /// + /// # Examples + /// + /// ``` + /// use async_std::os::unix::net::UnixListener; + /// + /// let socket = UnixListener::bind("/tmp/socket").await?; + /// let addr = socket.local_addr()?; + /// ``` + #[derive(Clone)] + pub struct SocketAddr { + _private: (), + } - /// An address associated with a Unix socket. + impl SocketAddr { + /// Returns `true` if the address is unnamed. /// /// # Examples /// - /// ``` + /// A named address: + /// + /// ```no_run /// use async_std::os::unix::net::UnixListener; /// /// let socket = UnixListener::bind("/tmp/socket").await?; /// let addr = socket.local_addr()?; + /// assert_eq!(addr.is_unnamed(), false); /// ``` - #[derive(Clone)] - pub struct SocketAddr { - _private: (), + /// + /// An unnamed address: + /// + /// ```no_run + /// use async_std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().await?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.is_unnamed(), true); + /// ``` + pub fn is_unnamed(&self) -> bool { + unreachable!("this impl only appears in the rendered docs") } - impl SocketAddr { - /// Returns `true` if the address is unnamed. - /// - /// # Examples - /// - /// A named address: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixListener; - /// - /// let socket = UnixListener::bind("/tmp/socket").await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.is_unnamed(), false); - /// ``` - /// - /// An unnamed address: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound().await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.is_unnamed(), true); - /// ``` - pub fn is_unnamed(&self) -> bool { - unreachable!("this impl only appears in the rendered docs") - } - - /// Returns the contents of this address if it is a `pathname` address. - /// - /// # Examples - /// - /// With a pathname: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixListener; - /// use async_std::path::Path; - /// - /// let socket = UnixListener::bind("/tmp/socket").await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket"))); - /// ``` - /// - /// Without a pathname: - /// - /// ``` - /// use async_std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.as_pathname(), None); - /// ``` - pub fn as_pathname(&self) -> Option<&Path> { - unreachable!("this impl only appears in the rendered docs") - } + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use async_std::os::unix::net::UnixListener; + /// use async_std::path::Path; + /// + /// let socket = UnixListener::bind("/tmp/socket").await?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket"))); + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use async_std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.as_pathname(), None); + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + unreachable!("this impl only appears in the rendered docs") } + } - impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - unreachable!("this impl only appears in the rendered docs") - } + impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + unreachable!("this impl only appears in the rendered docs") } - } else { - pub use std::os::unix::net::SocketAddr; } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 20f87d29..e83d5571 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -1,50 +1,48 @@ //! Windows-specific I/O extensions. -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::os::windows::io::{ + AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, + }; +} -cfg_if! { - if #[cfg(feature = "docs")] { - /// Raw HANDLEs. - pub type RawHandle = *mut std::os::raw::c_void; +cfg_docs! { + /// Raw HANDLEs. + pub type RawHandle = *mut std::os::raw::c_void; - /// Raw SOCKETs. - pub type RawSocket = u64; + /// Raw SOCKETs. + pub type RawSocket = u64; - /// Extracts raw handles. - pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. - fn as_raw_handle(&self) -> RawHandle; - } + /// Extracts raw handles. + pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + fn as_raw_handle(&self) -> RawHandle; + } - /// Construct I/O objects from raw handles. - pub trait FromRawHandle { - /// Constructs a new I/O object from the specified raw handle. - /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_handle(handle: RawHandle) -> Self; - } + /// Construct I/O objects from raw handles. + pub trait FromRawHandle { + /// Constructs a new I/O object from the specified raw handle. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_handle(handle: RawHandle) -> Self; + } - /// A trait to express the ability to consume an object and acquire ownership of - /// its raw `HANDLE`. - pub trait IntoRawHandle { - /// Consumes this object, returning the raw underlying handle. - /// - /// This function **transfers ownership** of the underlying handle to the - /// caller. Callers are then the unique owners of the handle and must close - /// it once it's no longer needed. - fn into_raw_handle(self) -> RawHandle; - } - } else { - pub use std::os::windows::io::{ - AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, - }; + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw `HANDLE`. + pub trait IntoRawHandle { + /// Consumes this object, returning the raw underlying handle. + /// + /// This function **transfers ownership** of the underlying handle to the + /// caller. Callers are then the unique owners of the handle and must close + /// it once it's no longer needed. + fn into_raw_handle(self) -> RawHandle; } } diff --git a/src/pin.rs b/src/pin/mod.rs similarity index 100% rename from src/pin.rs rename to src/pin/mod.rs diff --git a/src/prelude.rs b/src/prelude.rs index 6c670cc7..91432e0b 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,8 +11,6 @@ //! use async_std::prelude::*; //! ``` -use cfg_if::cfg_if; - #[doc(no_inline)] pub use crate::future::Future; #[doc(no_inline)] @@ -41,12 +39,9 @@ pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - #[doc(no_inline)] - pub use crate::stream::DoubleEndedStream; - - #[doc(no_inline)] - pub use crate::stream::ExactSizeStream; - } +cfg_unstable! { + #[doc(no_inline)] + pub use crate::stream::DoubleEndedStream; + #[doc(no_inline)] + pub use crate::stream::ExactSizeStream; } diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 6fab77c2..129bb1cd 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -10,8 +10,8 @@ use std::task::{Context, Poll}; /// `Item`s from the back, as well as the front. /// /// [`Stream`]: trait.Stream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait DoubleEndedStream: Stream { /// Removes and returns an element from the end of the stream. /// diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index ef236910..7d2e7cb9 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -76,8 +76,8 @@ pub use crate::stream::Stream; /// # }); /// # } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait ExactSizeStream: Stream { /// Returns the exact number of times the stream will iterate. /// diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 27efd8b7..d9e14816 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -27,6 +27,7 @@ use crate::stream::IntoStream; /// # /// # }) } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 047dab8f..54a22291 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -106,8 +106,8 @@ use std::pin::Pin; ///``` /// /// [`IntoStream`]: trait.IntoStream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait FromStream { /// Creates a value from a stream. /// diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs index 42e7e6f3..e14ab5b1 100644 --- a/src/stream/fused_stream.rs +++ b/src/stream/fused_stream.rs @@ -14,7 +14,7 @@ use crate::stream::Stream; /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`Stream::fuse`]: trait.Stream.html#method.fuse /// [`Fuse`]: struct.Fuse.html -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait FusedStream: Stream {} diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 21ac0329..043d3074 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -43,9 +43,8 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] pub fn interval(dur: Duration) -> Interval { Interval { delay: Delay::new(dur), @@ -55,9 +54,9 @@ pub fn interval(dur: Duration) -> Interval { /// A stream representing notifications at fixed interval /// -#[derive(Debug)] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] +#[derive(Debug)] pub struct Interval { delay: Delay, interval: Duration, diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 92331814..f96dc59b 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -13,8 +13,8 @@ use crate::stream::Stream; /// See also: [`FromStream`]. /// /// [`FromStream`]: trait.FromStream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait IntoStream { /// The type of the elements being iterated over. type Item; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 3b5f4610..e796510d 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -21,8 +21,6 @@ //! # }) //! ``` -use cfg_if::cfg_if; - pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use once::{once, Once}; @@ -40,28 +38,25 @@ mod once; mod repeat; mod repeat_with; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod double_ended_stream; - mod exact_size_stream; - mod extend; - mod from_stream; - mod fused_stream; - mod interval; - mod into_stream; - mod product; - mod sum; - - pub use double_ended_stream::DoubleEndedStream; - pub use exact_size_stream::ExactSizeStream; - pub use extend::Extend; - pub use from_stream::FromStream; - pub use fused_stream::FusedStream; - pub use interval::{interval, Interval}; - pub use into_stream::IntoStream; - pub use product::Product; - pub use sum::Sum; +cfg_unstable! { + mod double_ended_stream; + mod exact_size_stream; + mod extend; + mod from_stream; + mod fused_stream; + mod interval; + mod into_stream; + mod product; + mod sum; - pub use stream::Merge; - } + pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; + pub use extend::Extend; + pub use from_stream::FromStream; + pub use fused_stream::FusedStream; + pub use interval::{interval, Interval}; + pub use into_stream::IntoStream; + pub use product::Product; + pub use stream::Merge; + pub use sum::Sum; } diff --git a/src/stream/product.rs b/src/stream/product.rs index b3227761..5799990d 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -11,8 +11,8 @@ use crate::stream::Stream; /// [`product`]: trait.Product.html#tymethod.product /// [`FromStream`]: trait.FromStream.html /// [`Stream::product`]: trait.Stream.html#method.product +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index ab97d2cb..9889dc70 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -8,7 +8,7 @@ use futures_core::Stream; /// This stream is returned by [`Stream::merge`]. /// /// [`Stream::merge`]: trait.Stream.html#method.merge -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 764dc978..501ece1b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -91,32 +91,22 @@ pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use cfg_if::cfg_if; +cfg_unstable! { + use std::pin::Pin; -use crate::utils::extension_trait; + use crate::future::Future; + use crate::stream::FromStream; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; + pub use merge::Merge; - use crate::task::{Context, Poll}; - } + mod merge; } -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod merge; - - use std::pin::Pin; - - use crate::future::Future; - use crate::stream::FromStream; +extension_trait! { + use std::ops::{Deref, DerefMut}; - pub use merge::Merge; - } -} + use crate::task::{Context, Poll}; -extension_trait! { #[doc = r#" An asynchronous stream of values. @@ -495,7 +485,7 @@ extension_trait! { # # }) } ``` - + "#] fn last( self, @@ -1276,7 +1266,7 @@ extension_trait! { [`stream`]: trait.Stream.html#tymethod.next "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[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>( @@ -1315,7 +1305,7 @@ extension_trait! { # }); ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn merge(self, other: U) -> Merge where @@ -1345,9 +1335,9 @@ extension_trait! { let s4 = VecDeque::from(vec![1, 2, 4]); assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); - assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); + assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); - assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); + assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); # # }) } ``` @@ -1366,7 +1356,7 @@ extension_trait! { #[doc = r#" Lexicographically compares the elements of this `Stream` with those - of another using 'Ord'. + of another using 'Ord'. # Examples @@ -1383,9 +1373,9 @@ extension_trait! { let s4 = VecDeque::from(vec![1, 2, 4]); assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); - assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); + assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); - assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); + assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); # # }) } ``` diff --git a/src/stream/sum.rs b/src/stream/sum.rs index fd5d7d5e..a87ade1a 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -11,8 +11,8 @@ use crate::stream::Stream; /// [`sum`]: trait.Sum.html#tymethod.sum /// [`FromStream`]: trait.FromStream.html /// [`Stream::sum`]: trait.Stream.html#method.sum +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 43488ee4..080eff8c 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -32,6 +32,7 @@ use crate::sync::Mutex; /// # }); /// # } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Barrier { @@ -61,6 +62,7 @@ struct BarrierState { /// let barrier = Barrier::new(1); /// let barrier_wait_result = barrier.wait(); /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug, Clone)] pub struct BarrierWaitResult(bool); diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 3d3b7b80..be74d8f7 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -32,14 +32,13 @@ #[doc(inline)] pub use std::sync::{Arc, Weak}; -#[cfg(any(feature = "unstable", feature = "docs"))] -pub use barrier::{Barrier, BarrierWaitResult}; - pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -#[cfg(any(feature = "unstable", feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -mod barrier; mod mutex; mod rwlock; + +cfg_unstable! { + pub use barrier::{Barrier, BarrierWaitResult}; + mod barrier; +} diff --git a/src/task/mod.rs b/src/task/mod.rs index e2c89aea..24eae081 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -143,11 +143,9 @@ mod worker; pub(crate) mod blocking; -cfg_if::cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod yield_now; - pub use yield_now::yield_now; - } +cfg_unstable! { + mod yield_now; + pub use yield_now::yield_now; } /// Spawns a blocking task. @@ -178,7 +176,7 @@ cfg_if::cfg_if! { /// ``` // Once this function stabilizes we should merge `blocking::spawn` into this so // all code in our crate uses `task::blocking` too. -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub fn spawn_blocking(f: F) -> task::JoinHandle diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 6f596388..2a4788d7 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -26,7 +26,7 @@ use std::pin::Pin; /// # /// # }) } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub async fn yield_now() { diff --git a/src/utils.rs b/src/utils.rs index 76db50b8..60516d25 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,6 +20,64 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } +/// Declares unstable items. +#[doc(hidden)] +macro_rules! cfg_unstable { + ($($item:item)*) => { + $( + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + $item + )* + } +} + +/// Declares Unix-specific items. +#[doc(hidden)] +macro_rules! cfg_unix { + ($($item:item)*) => { + $( + #[cfg(any(unix, feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unix)))] + $item + )* + } +} + +/// Declares Windows-specific items. +#[doc(hidden)] +macro_rules! cfg_windows { + ($($item:item)*) => { + $( + #[cfg(any(windows, feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(windows)))] + $item + )* + } +} + +/// Declares items when the "docs" feature is enabled. +#[doc(hidden)] +macro_rules! cfg_docs { + ($($item:item)*) => { + $( + #[cfg(feature = "docs")] + $item + )* + } +} + +/// Declares items when the "docs" feature is disabled. +#[doc(hidden)] +macro_rules! cfg_not_docs { + ($($item:item)*) => { + $( + #[cfg(not(feature = "docs"))] + $item + )* + } +} + /// Defines an extension trait for a base trait. /// /// In generated docs, the base trait will contain methods from the extension trait. In actual @@ -29,7 +87,6 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { /// Inside invocations of this macro, we write a definitions that looks similar to the final /// rendered docs, and the macro then generates all the boilerplate for us. #[doc(hidden)] -#[macro_export] macro_rules! extension_trait { ( // Interesting patterns: @@ -113,6 +170,12 @@ macro_rules! extension_trait { // Handle the end of the token list. (@doc ($($head:tt)*)) => { $($head)* }; (@ext ($($head:tt)*)) => { $($head)* }; -} -pub use crate::extension_trait; + // Parse imports at the beginning of the macro. + ($import:item $($tail:tt)*) => { + #[cfg(feature = "docs")] + $import + + extension_trait!($($tail)*); + }; +} From 8bef2e9e95e8a7c4a90bb334afdbc7d2f67122b0 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 21:28:38 +0200 Subject: [PATCH 124/407] 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 125/407] 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 126/407] 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 127/407] 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 128/407] 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 129/407] 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 130/407] 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 131/407] 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 a9a7bdc29039951c39290741f1e512afa4f70831 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Fri, 18 Oct 2019 07:23:52 +0200 Subject: [PATCH 132/407] add stream::count --- src/stream/stream/count.rs | 41 ++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 29 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/stream/stream/count.rs diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs new file mode 100644 index 00000000..fcd75f6a --- /dev/null +++ b/src/stream/stream/count.rs @@ -0,0 +1,41 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct CountFuture { + stream: S, + count: usize, +} + +impl CountFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(count: usize); + + pub(crate) fn new(stream: S) -> Self { + CountFuture { stream, count: 0 } + } +} + +impl Future for CountFuture +where + S: Sized + Stream, +{ + type Output = usize; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(_) => { + cx.waker().wake_by_ref(); + *self.as_mut().count() += 1; + Poll::Pending + } + None => Poll::Ready(self.count), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b..b9d4bc86 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -25,6 +25,7 @@ mod all; mod any; mod chain; mod cmp; +mod count; mod enumerate; mod filter; mod filter_map; @@ -57,6 +58,7 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; +use count::CountFuture; use enumerate::Enumerate; use filter_map::FilterMap; use find::FindFuture; @@ -1392,6 +1394,33 @@ extension_trait! { CmpFuture::new(self, other) } + #[doc = r#" + Counts the number of elements in the stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use std::collections::VecDeque; + + let s1 = VecDeque::from(vec![0]); + let s2 = VecDeque::from(vec![1, 2, 3]); + + assert_eq!(s1.count().await, 1); + assert_eq!(s2.count().await, 3); + # + # }) } + ``` + "#] + fn count(self) -> impl Future [CountFuture] + where + Self: Sized + Stream, + { + CountFuture::new(self) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than or equal to those of another. From 97094b2a1c78e20ed01bbc1a27776506907c49a9 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Sun, 20 Oct 2019 22:03:51 +0200 Subject: [PATCH 133/407] remove Sized constraint --- src/stream/stream/count.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index fcd75f6a..b6d53ca8 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -22,7 +22,7 @@ impl CountFuture { impl Future for CountFuture where - S: Sized + Stream, + S: Stream, { type Output = usize; From 0d4a907335beec5472f03c28c204e039fcc5e6fb Mon Sep 17 00:00:00 2001 From: Sunjay Varma Date: Sun, 20 Oct 2019 19:18:37 -0400 Subject: [PATCH 134/407] 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 135/407] 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 136/407] 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 137/407] 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 138/407] 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 944e43d4bf1e228b9e9243c79a3a600d97855308 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 18:35:02 +0900 Subject: [PATCH 139/407] =?UTF-8?q?Remove=20Pin=20API=20related=20unsafe?= =?UTF-8?q?=20code=20by=20using=20pin-project-lite=20cra=E2=80=A6=20(#381)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/future/timeout.rs | 25 ++-- src/io/buf_read/lines.rs | 63 +++++----- src/io/buf_read/split.rs | 59 ++++----- src/io/buf_reader.rs | 141 +++++++++++---------- src/io/buf_writer.rs | 195 ++++++++++++++++-------------- src/io/copy.rs | 43 +++---- src/io/read/chain.rs | 89 +++++++------- src/io/read/take.rs | 54 +++++---- src/io/timeout.rs | 37 +++--- src/stream/from_fn.rs | 52 ++++---- src/stream/interval.rs | 8 +- src/stream/once.rs | 24 ++-- src/stream/repeat_with.rs | 51 ++++---- src/stream/stream/chain.rs | 32 ++--- src/stream/stream/cmp.rs | 58 ++++----- src/stream/stream/enumerate.rs | 29 +++-- src/stream/stream/filter.rs | 27 +++-- src/stream/stream/filter_map.rs | 29 +++-- src/stream/stream/fold.rs | 36 +++--- src/stream/stream/for_each.rs | 27 +++-- src/stream/stream/fuse.rs | 33 +++-- src/stream/stream/ge.rs | 27 +++-- src/stream/stream/gt.rs | 23 ++-- src/stream/stream/inspect.rs | 27 +++-- src/stream/stream/last.rs | 27 +++-- src/stream/stream/le.rs | 23 ++-- src/stream/stream/lt.rs | 23 ++-- src/stream/stream/map.rs | 29 +++-- src/stream/stream/merge.rs | 40 +++--- src/stream/stream/min_by.rs | 38 +++--- src/stream/stream/partial_cmp.rs | 58 ++++----- src/stream/stream/scan.rs | 27 +++-- src/stream/stream/skip.rs | 27 +++-- src/stream/stream/skip_while.rs | 29 +++-- src/stream/stream/step_by.rs | 32 ++--- src/stream/stream/take.rs | 33 +++-- src/stream/stream/take_while.rs | 27 +++-- src/stream/stream/try_fold.rs | 38 +++--- src/stream/stream/try_for_each.rs | 29 +++-- src/stream/stream/zip.rs | 35 +++--- src/task/block_on.rs | 14 +-- 42 files changed, 894 insertions(+), 825 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2741a19a..e353386d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ pin-utils = "0.1.0-alpha.4" slab = "0.4.2" kv-log-macro = "1.0.4" broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } +pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" diff --git a/src/future/timeout.rs b/src/future/timeout.rs index a8338fba..c745d732 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::future::Future; use crate::task::{Context, Poll}; @@ -39,24 +40,24 @@ where f.await } -/// A future that times out after a duration of time. -struct TimeoutFuture { - future: F, - delay: Delay, -} - -impl TimeoutFuture { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(delay: Delay); +pin_project! { + /// A future that times out after a duration of time. + struct TimeoutFuture { + #[pin] + future: F, + #[pin] + delay: Delay, + } } impl Future for TimeoutFuture { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Ready(v) => Poll::Ready(Ok(v)), - Poll::Pending => match self.delay().poll(cx) { + Poll::Pending => match this.delay.poll(cx) { Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Pending => Poll::Pending, }, diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 6cb4a076..c60529cd 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -2,50 +2,55 @@ use std::mem; use std::pin::Pin; use std::str; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream of lines in a byte stream. -/// -/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Lines`]. -/// -/// [`lines`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html -#[derive(Debug)] -pub struct Lines { - pub(crate) reader: R, - pub(crate) buf: String, - pub(crate) bytes: Vec, - pub(crate) read: usize, +pin_project! { + /// A stream of lines in a byte stream. + /// + /// This stream is created by the [`lines`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Lines`]. + /// + /// [`lines`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html + #[derive(Debug)] + pub struct Lines { + #[pin] + pub(crate) reader: R, + pub(crate) buf: String, + pub(crate) bytes: Vec, + pub(crate) read: usize, + } } impl Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - bytes, - read, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::ready!(read_line_internal( + this.reader, + cx, + this.buf, + this.bytes, + this.read + ))?; + if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); + if this.buf.ends_with('\n') { + this.buf.pop(); + if this.buf.ends_with('\r') { + this.buf.pop(); } } - Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) + Poll::Ready(Some(Ok(mem::replace(this.buf, String::new())))) } } diff --git a/src/io/buf_read/split.rs b/src/io/buf_read/split.rs index aa3b6fb6..229a99b3 100644 --- a/src/io/buf_read/split.rs +++ b/src/io/buf_read/split.rs @@ -1,46 +1,51 @@ use std::mem; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. -/// -/// This stream is created by the [`split`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Split`]. -/// -/// [`split`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html -#[derive(Debug)] -pub struct Split { - pub(crate) reader: R, - pub(crate) buf: Vec, - pub(crate) read: usize, - pub(crate) delim: u8, +pin_project! { + /// A stream over the contents of an instance of [`BufRead`] split on a particular byte. + /// + /// This stream is created by the [`split`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Split`]. + /// + /// [`split`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html + #[derive(Debug)] + pub struct Split { + #[pin] + pub(crate) reader: R, + pub(crate) buf: Vec, + pub(crate) read: usize, + pub(crate) delim: u8, + } } impl Stream for Split { type Item = io::Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - read, - delim, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::ready!(read_until_internal( + this.reader, + cx, + *this.delim, + this.buf, + this.read + ))?; + if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } - if buf[buf.len() - 1] == *delim { - buf.pop(); + if this.buf[this.buf.len() - 1] == *this.delim { + this.buf.pop(); } - Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) + Poll::Ready(Some(Ok(mem::replace(this.buf, vec![])))) } } diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 13cb4cc5..1d00b526 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,51 +2,56 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// Adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` -/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer -/// of the incoming byte stream. -/// -/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to -/// the same file or network socket. It does not help when reading very large amounts at once, or -/// reading just one or a few times. It also provides no advantage when reading from a source that -/// is already in memory, like a `Vec`. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating -/// multiple instances of a `BufReader` on the same stream can cause data loss. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`Read`]: trait.Read.html -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// -/// # Examples -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// # -/// use async_std::fs::File; -/// use async_std::io::BufReader; -/// use async_std::prelude::*; -/// -/// let mut file = BufReader::new(File::open("a.txt").await?); -/// -/// let mut line = String::new(); -/// file.read_line(&mut line).await?; -/// # -/// # Ok(()) }) } -/// ``` -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, +pin_project! { + /// Adds buffering to any reader. + /// + /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` + /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer + /// of the incoming byte stream. + /// + /// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to + /// the same file or network socket. It does not help when reading very large amounts at once, or + /// reading just one or a few times. It also provides no advantage when reading from a source that + /// is already in memory, like a `Vec`. + /// + /// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating + /// multiple instances of a `BufReader` on the same stream can cause data loss. + /// + /// This type is an async version of [`std::io::BufReader`]. + /// + /// [`Read`]: trait.Read.html + /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// + /// let mut line = String::new(); + /// file.read_line(&mut line).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub struct BufReader { + #[pin] + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, + } } impl BufReader { @@ -95,10 +100,6 @@ impl BufReader { } impl BufReader { - pin_utils::unsafe_pinned!(inner: R); - pin_utils::unsafe_unpinned!(pos: usize); - pin_utils::unsafe_unpinned!(cap: usize); - /// Gets a reference to the underlying reader. /// /// It is inadvisable to directly read from the underlying reader. @@ -141,6 +142,13 @@ impl BufReader { &mut self.inner } + /// Gets a pinned mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { + self.project().inner + } + /// Returns a reference to the internal buffer. /// /// This function will not attempt to fill the buffer if it is empty. @@ -185,9 +193,10 @@ impl BufReader { /// Invalidates all data in the internal buffer. #[inline] - fn discard_buffer(mut self: Pin<&mut Self>) { - *self.as_mut().pos() = 0; - *self.cap() = 0; + fn discard_buffer(self: Pin<&mut Self>) { + let this = self.project(); + *this.pos = 0; + *this.cap = 0; } } @@ -201,7 +210,7 @@ impl Read for BufReader { // (larger than our internal buffer), bypass our internal buffer // entirely. if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); + let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -218,7 +227,8 @@ impl Read for BufReader { ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.pos == self.cap && total_len >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + let res = + futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -234,28 +244,23 @@ impl BufRead for BufReader { self: Pin<&'a mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let Self { - inner, - buf, - cap, - pos, - } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + let mut this = self.project(); // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. - if *pos >= *cap { - debug_assert!(*pos == *cap); - *cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; - *pos = 0; + if *this.pos >= *this.cap { + debug_assert!(*this.pos == *this.cap); + *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?; + *this.pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&this.buf[*this.pos..*this.cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - *self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + *this.pos = cmp::min(*this.pos + amt, *this.cap); } } @@ -305,24 +310,26 @@ impl Seek for BufReader { if let Some(offset) = n.checked_sub(remainder) { result = futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(offset)) )?; } else { // seek backwards by our remainder, and then by the offset futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(-remainder)) )?; self.as_mut().discard_buffer(); result = futures_core::ready!( - self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) + self.as_mut() + .get_pin_mut() + .poll_seek(cx, SeekFrom::Current(n)) )?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index f12aacbc..6327ca71 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -2,6 +2,7 @@ use std::fmt; use std::pin::Pin; use futures_core::ready; +use pin_project_lite::pin_project; use crate::io::write::WriteExt; use crate::io::{self, Seek, SeekFrom, Write}; @@ -9,88 +10,88 @@ use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A -/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying -/// writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a `Vec`. -/// -/// When the `BufWriter` is dropped, the contents of its buffer will be written -/// out. However, any errors that happen in the process of flushing the buffer -/// when the writer is dropped will be ignored. Code that wishes to handle such -/// errors must manually call [`flush`] before the writer is dropped. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// -/// # Examples -/// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::net::TcpStream; -/// use async_std::prelude::*; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; -/// -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; -/// } -/// # -/// # Ok(()) }) } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::io::BufWriter; -/// use async_std::net::TcpStream; -/// use async_std::prelude::*; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; -/// }; -/// # -/// # Ok(()) }) } -/// ``` -/// -/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped -/// together by the buffer, and will all be written out in one system call when -/// the `stream` is dropped. -/// -/// [`Write`]: trait.Write.html -/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write -/// [`TcpStream`]: ../net/struct.TcpStream.html -/// [`flush`]: trait.Write.html#tymethod.flush -pub struct BufWriter { - inner: W, - buf: Vec, - written: usize, +pin_project! { + /// Wraps a writer and buffers its output. + /// + /// It can be excessively inefficient to work directly with something that + /// implements [`Write`]. For example, every call to + /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A + /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying + /// writer in large, infrequent batches. + /// + /// `BufWriter` can improve the speed of programs that make *small* and + /// *repeated* write calls to the same file or network socket. It does not + /// help when writing very large amounts at once, or writing just one or a few + /// times. It also provides no advantage when writing to a destination that is + /// in memory, like a `Vec`. + /// + /// When the `BufWriter` is dropped, the contents of its buffer will be written + /// out. However, any errors that happen in the process of flushing the buffer + /// when the writer is dropped will be ignored. Code that wishes to handle such + /// errors must manually call [`flush`] before the writer is dropped. + /// + /// This type is an async version of [`std::io::BufReader`]. + /// + /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// + /// # Examples + /// + /// Let's write the numbers one through ten to a [`TcpStream`]: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; + /// + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// } + /// # + /// # Ok(()) }) } + /// ``` + /// + /// Because we're not buffering, we write each one in turn, incurring the + /// overhead of a system call per byte written. We can fix this with a + /// `BufWriter`: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::io::BufWriter; + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// }; + /// # + /// # Ok(()) }) } + /// ``` + /// + /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped + /// together by the buffer, and will all be written out in one system call when + /// the `stream` is dropped. + /// + /// [`Write`]: trait.Write.html + /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write + /// [`TcpStream`]: ../net/struct.TcpStream.html + /// [`flush`]: trait.Write.html#tymethod.flush + pub struct BufWriter { + #[pin] + inner: W, + buf: Vec, + written: usize, + } } #[derive(Debug)] pub struct IntoInnerError(W, std::io::Error); impl BufWriter { - pin_utils::unsafe_pinned!(inner: W); - pin_utils::unsafe_unpinned!(buf: Vec); - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. /// @@ -178,6 +179,13 @@ impl BufWriter { &mut self.inner } + /// Gets a pinned mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { + self.project().inner + } + /// Consumes BufWriter, returning the underlying writer /// /// This method will not write leftover data, it will be lost. @@ -234,16 +242,15 @@ impl BufWriter { /// /// [`LineWriter`]: struct.LineWriter.html fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - inner, - buf, - written, - } = unsafe { Pin::get_unchecked_mut(self) }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; - let len = buf.len(); + let mut this = self.project(); + let len = this.buf.len(); let mut ret = Ok(()); - while *written < len { - match inner.as_mut().poll_write(cx, &buf[*written..]) { + while *this.written < len { + match this + .inner + .as_mut() + .poll_write(cx, &this.buf[*this.written..]) + { Poll::Ready(Ok(0)) => { ret = Err(io::Error::new( io::ErrorKind::WriteZero, @@ -251,7 +258,7 @@ impl BufWriter { )); break; } - Poll::Ready(Ok(n)) => *written += n, + Poll::Ready(Ok(n)) => *this.written += n, Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {} Poll::Ready(Err(e)) => { ret = Err(e); @@ -260,10 +267,10 @@ impl BufWriter { Poll::Pending => return Poll::Pending, } } - if *written > 0 { - buf.drain(..*written); + if *this.written > 0 { + this.buf.drain(..*this.written); } - *written = 0; + *this.written = 0; Poll::Ready(ret) } } @@ -278,20 +285,20 @@ impl Write for BufWriter { ready!(self.as_mut().poll_flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.get_pin_mut().poll_write(cx, buf) } else { - Pin::new(&mut *self.buf()).poll_write(cx, buf) + Pin::new(&mut *self.project().buf).poll_write(cx, buf) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_flush(cx) + self.get_pin_mut().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_close(cx) + self.get_pin_mut().poll_close(cx) } } @@ -314,6 +321,6 @@ impl Seek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.get_pin_mut().poll_seek(cx, pos) } } diff --git a/src/io/copy.rs b/src/io/copy.rs index 3840d2af..098df8d7 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; @@ -46,47 +48,38 @@ where R: Read + Unpin + ?Sized, W: Write + Unpin + ?Sized, { - pub struct CopyFuture<'a, R, W: ?Sized> { - reader: R, - writer: &'a mut W, - amt: u64, - } - - impl CopyFuture<'_, R, W> { - fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { - unsafe { - let this = self.get_unchecked_mut(); - ( - Pin::new_unchecked(&mut this.reader), - Pin::new(&mut *this.writer), - &mut this.amt, - ) - } + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, } } - impl Future for CopyFuture<'_, R, W> + impl Future for CopyFuture where R: BufRead, - W: Write + Unpin + ?Sized, + W: Write + Unpin, { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut reader, mut writer, amt) = self.project(); + let mut this = self.project(); loop { - let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; if buffer.is_empty() { - futures_core::ready!(writer.as_mut().poll_flush(cx))?; - return Poll::Ready(Ok(*amt)); + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); } - let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } - *amt += i as u64; - reader.as_mut().consume(i); + *this.amt += i as u64; + this.reader.as_mut().consume(i); } } } diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 09517cca..335cac25 100644 --- a/src/io/read/chain.rs +++ b/src/io/read/chain.rs @@ -1,20 +1,25 @@ -use crate::io::IoSliceMut; use std::fmt; use std::pin::Pin; -use crate::io::{self, BufRead, Read}; +use pin_project_lite::pin_project; + +use crate::io::{self, BufRead, IoSliceMut, Read}; use crate::task::{Context, Poll}; -/// Adaptor to chain together two readers. -/// -/// This struct is generally created by calling [`chain`] on a reader. -/// Please see the documentation of [`chain`] for more details. -/// -/// [`chain`]: trait.Read.html#method.chain -pub struct Chain { - pub(crate) first: T, - pub(crate) second: U, - pub(crate) done_first: bool, +pin_project! { + /// Adaptor to chain together two readers. + /// + /// This struct is generally created by calling [`chain`] on a reader. + /// Please see the documentation of [`chain`] for more details. + /// + /// [`chain`]: trait.Read.html#method.chain + pub struct Chain { + #[pin] + pub(crate) first: T, + #[pin] + pub(crate) second: U, + pub(crate) done_first: bool, + } } impl Chain { @@ -98,76 +103,64 @@ impl fmt::Debug for Chain { } } -impl Read for Chain { +impl Read for Chain { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - let rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read(cx, buf)) { - Ok(0) if !buf.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read(cx, buf)) { + Ok(0) if !buf.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.poll_read(cx, buf) + this.second.poll_read(cx, buf) } fn poll_read_vectored( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - if !self.done_first { - let rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) { - Ok(0) if !bufs.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) { + Ok(0) if !bufs.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.poll_read_vectored(cx, bufs) + this.second.poll_read_vectored(cx, bufs) } } -impl BufRead for Chain { +impl BufRead for Chain { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - - if !*done_first { - let first = unsafe { Pin::new_unchecked(first) }; - match futures_core::ready!(first.poll_fill_buf(cx)) { + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_fill_buf(cx)) { Ok(buf) if buf.is_empty() => { - *done_first = true; + *this.done_first = true; } Ok(buf) => return Poll::Ready(Ok(buf)), Err(err) => return Poll::Ready(Err(err)), } } - let second = unsafe { Pin::new_unchecked(second) }; - second.poll_fill_buf(cx) + this.second.poll_fill_buf(cx) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - let rd = Pin::new(&mut self.first); - rd.consume(amt) + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + if !*this.done_first { + this.first.consume(amt) } else { - let rd = Pin::new(&mut self.second); - rd.consume(amt) + this.second.consume(amt) } } } diff --git a/src/io/read/take.rs b/src/io/read/take.rs index def4e240..09b02c2f 100644 --- a/src/io/read/take.rs +++ b/src/io/read/take.rs @@ -1,19 +1,24 @@ use std::cmp; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read}; use crate::task::{Context, Poll}; -/// Reader adaptor which limits the bytes read from an underlying reader. -/// -/// This struct is generally created by calling [`take`] on a reader. -/// Please see the documentation of [`take`] for more details. -/// -/// [`take`]: trait.Read.html#method.take -#[derive(Debug)] -pub struct Take { - pub(crate) inner: T, - pub(crate) limit: u64, +pin_project! { + /// Reader adaptor which limits the bytes read from an underlying reader. + /// + /// This struct is generally created by calling [`take`] on a reader. + /// Please see the documentation of [`take`] for more details. + /// + /// [`take`]: trait.Read.html#method.take + #[derive(Debug)] + pub struct Take { + #[pin] + pub(crate) inner: T, + pub(crate) limit: u64, + } } impl Take { @@ -152,15 +157,15 @@ impl Take { } } -impl Read for Take { +impl Read for Take { /// Attempt to read from the `AsyncRead` into `buf`. fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let Self { inner, limit } = &mut *self; - take_read_internal(Pin::new(inner), cx, buf, limit) + let this = self.project(); + take_read_internal(this.inner, cx, buf, this.limit) } } @@ -186,31 +191,30 @@ pub fn take_read_internal( } } -impl BufRead for Take { +impl BufRead for Take { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, limit } = unsafe { self.get_unchecked_mut() }; - let inner = unsafe { Pin::new_unchecked(inner) }; + let this = self.project(); - if *limit == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(&[])); } - match futures_core::ready!(inner.poll_fill_buf(cx)) { + match futures_core::ready!(this.inner.poll_fill_buf(cx)) { Ok(buf) => { - let cap = cmp::min(buf.len() as u64, *limit) as usize; + let cap = cmp::min(buf.len() as u64, *this.limit) as usize; Poll::Ready(Ok(&buf[..cap])) } Err(e) => Poll::Ready(Err(e)), } } - fn consume(mut self: Pin<&mut Self>, amt: usize) { + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; + let amt = cmp::min(amt as u64, *this.limit) as usize; + *this.limit -= amt as u64; - let rd = Pin::new(&mut self.inner); - rd.consume(amt); + this.inner.consume(amt); } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 9fcc15ef..8ef844d9 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -3,7 +3,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use futures_timer::Delay; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; use crate::future::Future; use crate::io; @@ -43,22 +43,18 @@ where .await } -/// Future returned by the `FutureExt::timeout` method. -#[derive(Debug)] -pub struct Timeout -where - F: Future>, -{ - future: F, - timeout: Delay, -} - -impl Timeout -where - F: Future>, -{ - unsafe_pinned!(future: F); - unsafe_pinned!(timeout: Delay); +pin_project! { + /// Future returned by the `FutureExt::timeout` method. + #[derive(Debug)] + pub struct Timeout + where + F: Future>, + { + #[pin] + future: F, + #[pin] + timeout: Delay, + } } impl Future for Timeout @@ -67,13 +63,14 @@ where { type Output = io::Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Pending => {} other => return other, } - if self.timeout().poll(cx).is_ready() { + if this.timeout.poll(cx).is_ready() { let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); Poll::Ready(err) } else { diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index c1cb97af..f53f3e5b 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields elements by calling a closure. -/// -/// This stream is constructed by [`from_fn`] function. -/// -/// [`from_fn`]: fn.from_fn.html -#[derive(Debug)] -pub struct FromFn { - f: F, - future: Option, - __t: PhantomData, +pin_project! { + /// A stream that yields elements by calling a closure. + /// + /// This stream is constructed by [`from_fn`] function. + /// + /// [`from_fn`]: fn.from_fn.html + #[derive(Debug)] + pub struct FromFn { + f: F, + #[pin] + future: Option, + __t: PhantomData, + } } /// Creates a new stream where to produce each new element a provided closure is called. @@ -68,11 +73,6 @@ where } } -impl FromFn { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for FromFn where F: FnMut() -> Fut, @@ -80,20 +80,18 @@ where { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - match &self.future { - Some(_) => { - let next = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); + if this.future.is_some() { + let next = + futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); + this.future.set(None); - return Poll::Ready(next); - } - None => { - let fut = (self.as_mut().f())(); - self.as_mut().future().set(Some(fut)); - } + return Poll::Ready(next); + } else { + let fut = (this.f)(); + this.future.set(Some(fut)); } } } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 043d3074..2f7fe9e3 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -4,8 +4,6 @@ use std::time::{Duration, Instant}; use futures_core::future::Future; use futures_core::stream::Stream; -use pin_utils::unsafe_pinned; - use futures_timer::Delay; /// Creates a new stream that yields at a set interval. @@ -62,15 +60,11 @@ pub struct Interval { interval: Duration, } -impl Interval { - unsafe_pinned!(delay: Delay); -} - impl Stream for Interval { type Item = (); fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if Pin::new(&mut *self).delay().poll(cx).is_pending() { + if Pin::new(&mut self.delay).poll(cx).is_pending() { return Poll::Pending; } let when = Instant::now(); diff --git a/src/stream/once.rs b/src/stream/once.rs index be875e41..ae90d639 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -24,20 +26,22 @@ pub fn once(t: T) -> Once { Once { value: Some(t) } } -/// A stream that yields a single item. -/// -/// This stream is constructed by the [`once`] function. -/// -/// [`once`]: fn.once.html -#[derive(Debug)] -pub struct Once { - value: Option, +pin_project! { + /// A stream that yields a single item. + /// + /// This stream is constructed by the [`once`] function. + /// + /// [`once`]: fn.once.html + #[derive(Debug)] + pub struct Once { + value: Option, + } } impl Stream for Once { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(self.value.take()) + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.project().value.take()) } } diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index f38b323d..fda30fed 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that repeats elements of type `T` endlessly by applying a provided closure. -/// -/// This stream is constructed by the [`repeat_with`] function. -/// -/// [`repeat_with`]: fn.repeat_with.html -#[derive(Debug)] -pub struct RepeatWith { - f: F, - future: Option, - __a: PhantomData, +pin_project! { + /// A stream that repeats elements of type `T` endlessly by applying a provided closure. + /// + /// This stream is constructed by the [`repeat_with`] function. + /// + /// [`repeat_with`]: fn.repeat_with.html + #[derive(Debug)] + pub struct RepeatWith { + f: F, + #[pin] + future: Option, + __a: PhantomData, + } } /// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. @@ -69,11 +74,6 @@ where } } -impl RepeatWith { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for RepeatWith where F: FnMut() -> Fut, @@ -81,22 +81,19 @@ where { type Item = A; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - match &self.future { - Some(_) => { - let res = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + if this.future.is_some() { + let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); + this.future.set(None); - return Poll::Ready(Some(res)); - } - None => { - let fut = (self.as_mut().f())(); + return Poll::Ready(Some(res)); + } else { + let fut = (this.f)(); - self.as_mut().future().set(Some(fut)); - } + this.future.set(Some(fut)); } } } diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 2693382e..df316150 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,20 +1,23 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::prelude::*; use crate::task::{Context, Poll}; -/// Chains two streams one after another. -#[derive(Debug)] -pub struct Chain { - first: Fuse, - second: Fuse, +pin_project! { + /// Chains two streams one after another. + #[derive(Debug)] + pub struct Chain { + #[pin] + first: Fuse, + #[pin] + second: Fuse, + } } impl Chain { - pin_utils::unsafe_pinned!(first: Fuse); - pin_utils::unsafe_pinned!(second: Fuse); - pub(super) fn new(first: S, second: U) -> Self { Chain { first: first.fuse(), @@ -26,22 +29,23 @@ impl Chain { impl> Stream for Chain { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if !self.first.done { - let next = futures_core::ready!(self.as_mut().first().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if !this.first.done { + let next = futures_core::ready!(this.first.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if !self.second.done { - let next = futures_core::ready!(self.as_mut().second().poll_next(cx)); + if !this.second.done { + let next = futures_core::ready!(this.second.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if self.first.done && self.second.done { + if this.first.done && this.second.done { return Poll::Ready(None); } diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index fc7161ad..df08e9db 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another using `Ord`. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct CmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another using `Ord`. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct CmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl CmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { CmpFuture { l: l.fuse(), @@ -42,11 +43,12 @@ where { type Output = Ordering; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Ordering::Equal); @@ -57,30 +59,30 @@ where } // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + if !this.l.done && this.l_cache.is_none() { + let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.take().unwrap(); + let r_value = this.r_cache.take().unwrap(); let result = l_value.cmp(&r_value); if let Ordering::Equal = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index 7d5a3d68..2a3afa87 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,19 +1,21 @@ -use crate::task::{Context, Poll}; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; +use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Enumerate { - stream: S, - i: usize, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Enumerate { + #[pin] + stream: S, + i: usize, + } } impl Enumerate { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(i: usize); - pub(super) fn new(stream: S) -> Self { Enumerate { stream, i: 0 } } @@ -25,13 +27,14 @@ where { type Item = (usize, S::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(v) => { - let ret = (self.i, v); - *self.as_mut().i() += 1; + let ret = (*this.i, v); + *this.i += 1; Poll::Ready(Some(ret)) } None => Poll::Ready(None), diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 8ed282ce..eb4153f7 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to filter elements of another stream with a predicate. -#[derive(Debug)] -pub struct Filter { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream to filter elements of another stream with a predicate. + #[derive(Debug)] + pub struct Filter { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl Filter { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { Filter { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), Some(_) => { cx.waker().wake_by_ref(); Poll::Pending diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 756efff1..6a4593f9 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -2,21 +2,23 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; +use pin_project_lite::pin_project; + use crate::stream::Stream; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FilterMap { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FilterMap { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl FilterMap { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { FilterMap { stream, @@ -34,10 +36,11 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) => match (self.as_mut().f())(v) { + Some(v) => match (this.f)(v) { Some(b) => Poll::Ready(Some(b)), None => { cx.waker().wake_by_ref(); diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 18ddcd81..5b0eb124 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + __t: PhantomData, + } } impl FoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - pub(super) fn new(stream: S, init: B, f: F) -> Self { FoldFuture { stream, @@ -36,17 +37,18 @@ where { type Output = B; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); - *self.as_mut().acc() = Some(new); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + *this.acc = Some(new); } - None => return Poll::Ready(self.as_mut().acc().take().unwrap()), + None => return Poll::Ready(this.acc.take().unwrap()), } } } diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 0406a507..4696529b 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ForEachFuture { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct ForEachFuture { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl ForEachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { ForEachFuture { stream, @@ -33,12 +35,13 @@ where { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => (self.as_mut().f())(v), + Some(v) => (this.f)(v), None => return Poll::Ready(()), } } diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index ff5bdab1..11629700 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,33 +1,32 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A `Stream` that is permanently closed once a single call to `poll` results in -/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. -#[derive(Clone, Debug)] -pub struct Fuse { - pub(crate) stream: S, - pub(crate) done: bool, -} - -impl Unpin for Fuse {} - -impl Fuse { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(done: bool); +pin_project! { + /// A `Stream` that is permanently closed once a single call to `poll` results in + /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. + #[derive(Clone, Debug)] + pub struct Fuse { + #[pin] + pub(crate) stream: S, + pub(crate) done: bool, + } } impl Stream for Fuse { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.done { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if *this.done { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); if next.is_none() { - *self.as_mut().done() = true; + *this.done = true; } Poll::Ready(next) } diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index eb9786b5..3dc6031c 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { GeFuture { partial_cmp: l.partial_cmp(r), @@ -30,14 +33,14 @@ where impl Future for GeFuture where - L: Stream + Sized, - R: Stream + Sized, + L: Stream, + R: Stream, L::Item: PartialOrd, { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 6c480a25..513ca764 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { GtFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) => Poll::Ready(true), diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index e63b5849..5de60fb3 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that does something with each element of another stream. -#[derive(Debug)] -pub struct Inspect { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + /// A stream that does something with each element of another stream. + #[derive(Debug)] + pub struct Inspect { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl Inspect { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { Inspect { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); Poll::Ready(next.and_then(|x| { - (self.as_mut().f())(&x); + (this.f)(&x); Some(x) })) } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index c58dd66a..eba01e5c 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,20 +1,22 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LastFuture { - stream: S, - last: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LastFuture { + #[pin] + stream: S, + last: Option, + } } impl LastFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(last: Option); - pub(crate) fn new(stream: S) -> Self { LastFuture { stream, last: None } } @@ -27,16 +29,17 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - *self.as_mut().last() = Some(new); + *this.last = Some(new); Poll::Pending } - None => Poll::Ready(self.last), + None => Poll::Ready(*this.last), } } } diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 37b62d83..af727005 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// Determines if the elements of this `Stream` are lexicographically -/// less or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + /// Determines if the elements of this `Stream` are lexicographically + /// less or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { LeFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index b774d7b4..524f2689 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// less than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // less than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { LtFuture { partial_cmp: l.partial_cmp(r), @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) => Poll::Ready(true), diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 4bc2e366..a1fafc30 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Map { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Map { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl Map { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { Map { stream, @@ -34,8 +36,9 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - Poll::Ready(next.map(self.as_mut().f())) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.map(this.f)) } } diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 9889dc70..3ccc223d 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -2,22 +2,25 @@ use std::pin::Pin; use std::task::{Context, Poll}; use futures_core::Stream; +use pin_project_lite::pin_project; -/// A stream that merges two other streams into a single stream. -/// -/// This stream is returned by [`Stream::merge`]. -/// -/// [`Stream::merge`]: trait.Stream.html#method.merge -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct Merge { - left: L, - right: R, +pin_project! { + /// A stream that merges two other streams into a single stream. + /// + /// This stream is returned by [`Stream::merge`]. + /// + /// [`Stream::merge`]: trait.Stream.html#method.merge + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[derive(Debug)] + pub struct Merge { + #[pin] + left: L, + #[pin] + right: R, + } } -impl Unpin for Merge {} - impl Merge { pub(crate) fn new(left: L, right: R) -> Self { Self { left, right } @@ -26,19 +29,20 @@ impl Merge { impl Stream for Merge where - L: Stream + Unpin, - R: Stream + Unpin, + L: Stream, + R: Stream, { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Poll::Ready(Some(item)) = Pin::new(&mut self.left).poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if let Poll::Ready(Some(item)) = this.left.poll_next(cx) { // The first stream made progress. The Merge needs to be polled // again to check the progress of the second stream. cx.waker().wake_by_ref(); Poll::Ready(Some(item)) } else { - Pin::new(&mut self.right).poll_next(cx) + this.right.poll_next(cx) } } } diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index a68cf313..ab12aa05 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,23 +1,24 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct MinByFuture { - stream: S, - compare: F, - min: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinByFuture { + #[pin] + stream: S, + compare: F, + min: Option, + } } impl MinByFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(compare: F); - pin_utils::unsafe_unpinned!(min: Option); - pub(super) fn new(stream: S, compare: F) -> Self { MinByFuture { stream, @@ -35,22 +36,23 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - match self.as_mut().min().take() { - None => *self.as_mut().min() = Some(new), - Some(old) => match (&mut self.as_mut().compare())(&new, &old) { - Ordering::Less => *self.as_mut().min() = Some(new), - _ => *self.as_mut().min() = Some(old), + match this.min.take() { + None => *this.min = Some(new), + Some(old) => match (this.compare)(&new, &old) { + Ordering::Less => *this.min = Some(new), + _ => *this.min = Some(old), }, } Poll::Pending } - None => Poll::Ready(self.min), + None => Poll::Ready(*this.min), } } } diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index fac2705d..e30c6ea8 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct PartialCmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct PartialCmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl PartialCmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { PartialCmpFuture { l: l.fuse(), @@ -42,12 +43,13 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { // Short circuit logic // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Some(Ordering::Equal)); @@ -58,30 +60,30 @@ where } // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + if !this.l.done && this.l_cache.is_none() { + let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.as_mut().take().unwrap(); + let r_value = this.r_cache.as_mut().take().unwrap(); let result = l_value.partial_cmp(&r_value); if let Some(Ordering::Equal) = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 78975161..c4771d85 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,13 +1,18 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to maintain state while polling another stream. -#[derive(Debug)] -pub struct Scan { - stream: S, - state_f: (St, F), +pin_project! { + /// A stream to maintain state while polling another stream. + #[derive(Debug)] + pub struct Scan { + #[pin] + stream: S, + state_f: (St, F), + } } impl Scan { @@ -17,13 +22,8 @@ impl Scan { state_f: (initial_state, f), } } - - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(state_f: (St, F)); } -impl Unpin for Scan {} - impl Stream for Scan where S: Stream, @@ -31,11 +31,12 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let poll_result = self.as_mut().stream().poll_next(cx); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let poll_result = this.stream.as_mut().poll_next(cx); poll_result.map(|item| { item.and_then(|item| { - let (state, f) = self.as_mut().state_f(); + let (state, f) = this.state_f; f(state, item) }) }) diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index 8a2d966d..6562b99f 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -1,19 +1,21 @@ use std::pin::Pin; use std::task::{Context, Poll}; +use pin_project_lite::pin_project; + use crate::stream::Stream; -/// A stream to skip first n elements of another stream. -#[derive(Debug)] -pub struct Skip { - stream: S, - n: usize, +pin_project! { + /// A stream to skip first n elements of another stream. + #[derive(Debug)] + pub struct Skip { + #[pin] + stream: S, + n: usize, + } } impl Skip { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(n: usize); - pub(crate) fn new(stream: S, n: usize) -> Self { Skip { stream, n } } @@ -25,14 +27,15 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.n { + Some(v) => match *this.n { 0 => return Poll::Ready(Some(v)), - _ => *self.as_mut().n() -= 1, + _ => *this.n -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index b1a8d9ea..0499df23 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to skip elements of another stream based on a predicate. -#[derive(Debug)] -pub struct SkipWhile { - stream: S, - predicate: Option

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

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

); - pub(crate) fn new(stream: S, predicate: P) -> Self { SkipWhile { stream, @@ -32,15 +34,16 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.as_mut().predicate() { + Some(v) => match this.predicate { Some(p) => { if !p(&v) { - *self.as_mut().predicate() = None; + *this.predicate = None; return Poll::Ready(Some(v)); } } diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index f84feecd..ab9e45b6 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -1,21 +1,22 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that steps a given amount of elements of another stream. -#[derive(Debug)] -pub struct StepBy { - stream: S, - step: usize, - i: usize, +pin_project! { + /// A stream that steps a given amount of elements of another stream. + #[derive(Debug)] + pub struct StepBy { + #[pin] + stream: S, + step: usize, + i: usize, + } } impl StepBy { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(step: usize); - pin_utils::unsafe_unpinned!(i: usize); - pub(crate) fn new(stream: S, step: usize) -> Self { StepBy { stream, @@ -31,17 +32,18 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.i { + Some(v) => match this.i { 0 => { - *self.as_mut().i() = self.step; + *this.i = *this.step; return Poll::Ready(Some(v)); } - _ => *self.as_mut().i() -= 1, + _ => *this.i -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 81d48d23..835bc447 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,33 +1,32 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields the first `n` items of another stream. -#[derive(Clone, Debug)] -pub struct Take { - pub(crate) stream: S, - pub(crate) remaining: usize, -} - -impl Unpin for Take {} - -impl Take { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(remaining: usize); +pin_project! { + /// A stream that yields the first `n` items of another stream. + #[derive(Clone, Debug)] + pub struct Take { + #[pin] + pub(crate) stream: S, + pub(crate) remaining: usize, + } } impl Stream for Take { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.remaining == 0 { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if *this.remaining == 0 { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + Some(_) => *this.remaining -= 1, + None => *this.remaining = 0, } Poll::Ready(next) } diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 6f3cc8f2..bf89458b 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields elements based on a predicate. -#[derive(Debug)] -pub struct TakeWhile { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream that yields elements based on a predicate. + #[derive(Debug)] + pub struct TakeWhile { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl TakeWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { TakeWhile { stream, @@ -32,11 +34,12 @@ where { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), Some(_) => { cx.waker().wake_by_ref(); Poll::Pending diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index 212b0589..80392e10 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct TryFoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + __t: PhantomData, + } } impl TryFoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - pub(super) fn new(stream: S, init: T, f: F) -> Self { TryFoldFuture { stream, @@ -36,23 +37,22 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); match new { - Ok(o) => { - *self.as_mut().acc() = Some(o); - } + Ok(o) => *this.acc = Some(o), Err(e) => return Poll::Ready(Err(e)), } } - None => return Poll::Ready(Ok(self.as_mut().acc().take().unwrap())), + None => return Poll::Ready(Ok(this.acc.take().unwrap())), } } } diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index ae3d5ea5..578b854c 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,23 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct TryForEeachFuture { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryForEeachFuture { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl TryForEeachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { TryForEeachFuture { stream, @@ -36,14 +38,15 @@ where { type Output = Result<(), E>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match item { None => return Poll::Ready(Ok(())), Some(v) => { - let res = (self.as_mut().f())(v); + let res = (this.f)(v); if let Err(e) = res { return Poll::Ready(Err(e)); } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 4c66aefd..9b7c299b 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -1,14 +1,20 @@ use std::fmt; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -/// An iterator that iterates two other iterators simultaneously. -pub struct Zip { - item_slot: Option, - first: A, - second: B, +pin_project! { + /// An iterator that iterates two other iterators simultaneously. + pub struct Zip { + item_slot: Option, + #[pin] + first: A, + #[pin] + second: B, + } } impl fmt::Debug for Zip { @@ -20,8 +26,6 @@ impl fmt::Debug for Zip { } } -impl Unpin for Zip {} - impl Zip { pub(crate) fn new(first: A, second: B) -> Self { Zip { @@ -30,25 +34,22 @@ impl Zip { second, } } - - pin_utils::unsafe_unpinned!(item_slot: Option); - pin_utils::unsafe_pinned!(first: A); - pin_utils::unsafe_pinned!(second: B); } impl Stream for Zip { type Item = (A::Item, B::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.as_mut().item_slot().is_none() { - match self.as_mut().first().poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if this.item_slot.is_none() { + match this.first.poll_next(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(None) => return Poll::Ready(None), - Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item), + Poll::Ready(Some(item)) => *this.item_slot = Some(item), } } - let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx)); - let first_item = self.as_mut().item_slot().take().unwrap(); + let second_item = futures_core::ready!(this.second.poll_next(cx)); + let first_item = this.item_slot.take().unwrap(); Poll::Ready(second_item.map(|second_item| (first_item, second_item))) } } diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 032bf02d..b0adc387 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -7,6 +7,7 @@ use std::task::{RawWaker, RawWakerVTable}; use std::thread; use crossbeam_utils::sync::Parker; +use pin_project_lite::pin_project; use super::task; use super::task_local; @@ -100,19 +101,18 @@ where } } -struct CatchUnwindFuture { - future: F, -} - -impl CatchUnwindFuture { - pin_utils::unsafe_pinned!(future: F); +pin_project! { + struct CatchUnwindFuture { + #[pin] + future: F, + } } impl Future for CatchUnwindFuture { type Output = thread::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + panic::catch_unwind(AssertUnwindSafe(|| self.project().future.poll(cx)))?.map(Ok) } } From 020eb85093d714b25015980ed0aeeb148ae25ec3 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 23 Oct 2019 18:59:06 +0800 Subject: [PATCH 140/407] 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 141/407] 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 142/407] 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 2abf5ca891b72bcab0afa3adcdc2b99ba2f59f85 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 20:20:59 +0900 Subject: [PATCH 143/407] Deny warnings on CI (#378) * Deny warnings on CI * Fix some clippy warnings --- .github/workflows/ci.yml | 7 +++++++ src/io/timeout.rs | 2 +- src/io/write/write_fmt.rs | 4 ++-- src/net/addr.rs | 2 +- src/path/path.rs | 2 +- src/path/pathbuf.rs | 4 ++-- src/stream/exact_size_stream.rs | 3 +-- src/stream/extend.rs | 4 ++-- src/stream/from_fn.rs | 5 ++--- src/stream/repeat_with.rs | 8 ++++---- src/sync/barrier.rs | 9 +++------ src/task/yield_now.rs | 4 ++-- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c622a59b..565134dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ jobs: build_and_test: name: Build and test runs-on: ${{ matrix.os }} + env: + RUSTFLAGS: -Dwarnings strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] @@ -46,6 +48,8 @@ jobs: check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest + env: + RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@master @@ -77,6 +81,9 @@ jobs: clippy_check: name: Clippy check runs-on: ubuntu-latest + # TODO: There is a lot of warnings + # env: + # RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@v1 - id: component diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 8ef844d9..ec3668ea 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -71,7 +71,7 @@ where } if this.timeout.poll(cx).is_ready() { - let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); + let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out")); Poll::Ready(err) } else { Poll::Pending diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index a1149cde..ad3e94ad 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -32,7 +32,7 @@ impl Future for WriteFmtFuture<'_, T> { buffer, .. } = &mut *self; - let mut buffer = buffer.as_mut().unwrap(); + let buffer = buffer.as_mut().unwrap(); // Copy the data from the buffer into the writer until it's done. loop { @@ -40,7 +40,7 @@ impl Future for WriteFmtFuture<'_, T> { futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; return Poll::Ready(Ok(())); } - let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &mut buffer))?; + let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } diff --git a/src/net/addr.rs b/src/net/addr.rs index 1dada64c..519b1846 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -210,7 +210,7 @@ impl ToSocketAddrs for str { impl Future, ToSocketAddrsFuture ) { - if let Some(addr) = self.parse().ok() { + if let Ok(addr) = self.parse() { return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); } diff --git a/src/path/path.rs b/src/path/path.rs index aa15ab22..22dab769 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -799,7 +799,7 @@ impl AsRef for String { impl AsRef for std::path::PathBuf { fn as_ref(&self) -> &Path { - Path::new(self.into()) + Path::new(self) } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 64744e14..38018c9d 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -5,7 +5,7 @@ use crate::path::Path; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -206,7 +206,7 @@ impl From for PathBuf { impl Into for PathBuf { fn into(self) -> std::path::PathBuf { - self.inner.into() + self.inner } } diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 7d2e7cb9..32a1eb31 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -59,7 +59,7 @@ pub use crate::stream::Stream; /// # } /// # } /// # } -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// impl ExactSizeStream for Counter { /// // We can easily calculate the remaining number of iterations. @@ -74,7 +74,6 @@ pub use crate::stream::Stream; /// /// assert_eq!(5, counter.len()); /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/extend.rs b/src/stream/extend.rs index d9e14816..350418d8 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -14,7 +14,7 @@ use crate::stream::IntoStream; /// ## Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream::{self, Extend}; @@ -25,7 +25,7 @@ use crate::stream::IntoStream; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index f53f3e5b..7fee8926 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ pin_project! { /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::sync::Mutex; @@ -58,8 +58,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(3)); /// assert_eq!(s.next().await, None); /// # -/// # }) } -/// +/// # }) /// ``` pub fn from_fn(f: F) -> FromFn where diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index fda30fed..15d4aa12 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -29,7 +29,7 @@ pin_project! { /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -42,13 +42,13 @@ pin_project! { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); -/// # }) } +/// # }) /// ``` /// /// Going finite: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -60,7 +60,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, None); -/// # }) } +/// # }) /// ``` pub fn repeat_with(repeater: F) -> RepeatWith where diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 080eff8c..0163e3fc 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -8,7 +8,7 @@ use crate::sync::Mutex; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -30,7 +30,6 @@ use crate::sync::Mutex; /// handle.await; /// } /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -120,7 +119,7 @@ impl Barrier { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -142,7 +141,6 @@ impl Barrier { /// handle.await; /// } /// # }); - /// # } /// ``` pub async fn wait(&self) -> BarrierWaitResult { let mut lock = self.state.lock().await; @@ -190,7 +188,7 @@ impl BarrierWaitResult { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::Barrier; /// @@ -198,7 +196,6 @@ impl BarrierWaitResult { /// let barrier_wait_result = barrier.wait().await; /// println!("{:?}", barrier_wait_result.is_leader()); /// # }); - /// # } /// ``` pub fn is_leader(&self) -> bool { self.0 diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 2a4788d7..c11408ab 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -18,13 +18,13 @@ use std::pin::Pin; /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::task; /// /// task::yield_now().await; /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] From b2fe91385b08cbcd3046294decce9136df904d8c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 23 Oct 2019 17:02:03 +0100 Subject: [PATCH 144/407] Add channel behind unstable feature flag (#380) * Add channel behind unstable feature flag * Don't check tests without unstable feature flag * Fix typos * Remove useless attribute --- .github/workflows/ci.yml | 2 +- Cargo.toml | 1 + src/sync/channel.rs | 1132 ++++++++++++++++++++++++++++++++++++++ src/sync/mod.rs | 3 + tests/channel.rs | 350 ++++++++++++ 5 files changed, 1487 insertions(+), 1 deletion(-) create mode 100644 src/sync/channel.rs create mode 100644 tests/channel.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 565134dc..d47eabce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --benches --bins --examples --tests + args: --all --bins --examples - name: check unstable uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index e353386d..7aaba687 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" +rand = "0.7.2" # surf = "1.0.2" tempdir = "0.3.7" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } diff --git a/src/sync/channel.rs b/src/sync/channel.rs new file mode 100644 index 00000000..6751417a --- /dev/null +++ b/src/sync/channel.rs @@ -0,0 +1,1132 @@ +use std::cell::UnsafeCell; +use std::fmt; +use std::future::Future; +use std::isize; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::pin::Pin; +use std::process; +use std::ptr; +use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll, Waker}; + +use crossbeam_utils::{Backoff, CachePadded}; +use futures_core::stream::Stream; +use slab::Slab; + +/// Creates a bounded multi-producer multi-consumer channel. +/// +/// This channel has a buffer that can hold at most `cap` messages at a time. +/// +/// Senders and receivers can be cloned. When all senders associated with a channel get dropped, it +/// becomes closed. Receive operations on a closed and empty channel return `None` instead of +/// blocking. +/// +/// # Panics +/// +/// If `cap` is zero, this function will panic. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// let (s, r) = channel(1); +/// +/// // This call returns immediately because there is enough space in the channel. +/// s.send(1).await; +/// +/// task::spawn(async move { +/// // This call blocks the current task because the channel is full. +/// // It will be able to complete only after the first message is received. +/// s.send(2).await; +/// }); +/// +/// task::sleep(Duration::from_secs(1)).await; +/// assert_eq!(r.recv().await, Some(1)); +/// assert_eq!(r.recv().await, Some(2)); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub fn channel(cap: usize) -> (Sender, Receiver) { + let channel = Arc::new(Channel::with_capacity(cap)); + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + channel, + opt_key: None, + }; + (s, r) +} + +/// The sending side of a channel. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// let (s1, r) = channel(100); +/// let s2 = s1.clone(); +/// +/// task::spawn(async move { s1.send(1).await }); +/// task::spawn(async move { s2.send(2).await }); +/// +/// let msg1 = r.recv().await.unwrap(); +/// let msg2 = r.recv().await.unwrap(); +/// +/// assert_eq!(msg1 + msg2, 3); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub struct Sender { + /// The inner channel. + channel: Arc>, +} + +impl Sender { + /// Sends a message into the channel. + /// + /// If the channel is full, this method will block the current task until the send operation + /// can proceed. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// use async_std::task; + /// + /// let (s, r) = channel(1); + /// + /// task::spawn(async move { + /// s.send(1).await; + /// s.send(2).await; + /// }); + /// + /// assert_eq!(r.recv().await, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn send(&self, msg: T) { + struct SendFuture<'a, T> { + sender: &'a Sender, + msg: Option, + opt_key: Option, + } + + impl Unpin for SendFuture<'_, T> {} + + impl Future for SendFuture<'_, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let msg = self.msg.take().unwrap(); + + // Try sending the message. + let poll = match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + Err(PushError::Full(msg)) => { + // Register the current task. + match self.opt_key { + None => self.opt_key = Some(self.sender.channel.sends.register(cx)), + Some(key) => self.sender.channel.sends.reregister(key, cx), + } + + // Try sending the message again. + match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) | Err(PushError::Full(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key.take() { + // `true` means the send operation is completed. + self.sender.channel.sends.unregister(key, true); + } + } + + poll + } + } + + impl Drop for SendFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the send operation is cancelled. + self.sender.channel.sends.unregister(key, false); + } + } + } + + SendFuture { + sender: self, + msg: Some(msg), + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::channel; + /// + /// let (s, _) = channel::(5); + /// assert_eq!(s.capacity(), 5); + /// ``` + pub fn capacity(&self) -> usize { + self.channel.cap + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(s.is_empty()); + /// s.send(0).await; + /// assert!(!s.is_empty()); + /// # + /// # }) + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(!s.is_full()); + /// s.send(0).await; + /// assert!(s.is_full()); + /// # + /// # }) + /// ``` + pub fn is_full(&self) -> bool { + self.channel.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(2); + /// assert_eq!(s.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(s.len(), 2); + /// # + /// # }) + /// ``` + pub fn len(&self) -> usize { + self.channel.len() + } +} + +impl Drop for Sender { + fn drop(&mut self) { + // Decrement the sender count and disconnect the channel if it drops down to zero. + if self.channel.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.disconnect(); + } + } +} + +impl Clone for Sender { + fn clone(&self) -> Sender { + let count = self.channel.sender_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of sender clones are leaked. + if count > isize::MAX as usize { + process::abort(); + } + + Sender { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Sender { .. }") + } +} + +/// The receiving side of a channel. +/// +/// This type implements the [`Stream`] trait, which means it can act as an asynchronous iterator. +/// +/// [`Stream`]: ../stream/trait.Stream.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// let (s, r) = channel(100); +/// +/// task::spawn(async move { +/// s.send(1).await; +/// task::sleep(Duration::from_secs(1)).await; +/// s.send(2).await; +/// }); +/// +/// assert_eq!(r.recv().await, Some(1)); // Received immediately. +/// assert_eq!(r.recv().await, Some(2)); // Received after 1 second. +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub struct Receiver { + /// The inner channel. + channel: Arc>, + + /// The registration key for this receiver in the `channel.streams` registry. + opt_key: Option, +} + +impl Receiver { + /// Receives a message from the channel. + /// + /// If the channel is empty and it still has senders, this method will block the current task + /// until the receive operation can proceed. If the channel is empty and there are no more + /// senders, this method returns `None`. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// use async_std::task; + /// + /// let (s, r) = channel(1); + /// + /// task::spawn(async move { + /// s.send(1).await; + /// s.send(2).await; + /// }); + /// + /// assert_eq!(r.recv().await, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn recv(&self) -> Option { + struct RecvFuture<'a, T> { + channel: &'a Channel, + opt_key: Option, + } + + impl Future for RecvFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + poll_recv(&self.channel, &self.channel.recvs, &mut self.opt_key, cx) + } + } + + impl Drop for RecvFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the receive operation is cancelled. + self.channel.recvs.unregister(key, false); + } + } + } + + RecvFuture { + channel: &self.channel, + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::channel; + /// + /// let (_, r) = channel::(5); + /// assert_eq!(r.capacity(), 5); + /// ``` + pub fn capacity(&self) -> usize { + self.channel.cap + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(r.is_empty()); + /// s.send(0).await; + /// assert!(!r.is_empty()); + /// # + /// # }) + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(!r.is_full()); + /// s.send(0).await; + /// assert!(r.is_full()); + /// # + /// # }) + /// ``` + pub fn is_full(&self) -> bool { + self.channel.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(2); + /// assert_eq!(r.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(r.len(), 2); + /// # + /// # }) + /// ``` + pub fn len(&self) -> usize { + self.channel.len() + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + // If the current task was registered as blocked on this stream, unregister now. + if let Some(key) = self.opt_key { + // `false` means the last request for a stream item is cancelled. + self.channel.streams.unregister(key, false); + } + + // Decrement the receiver count and disconnect the channel if it drops down to zero. + if self.channel.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.disconnect(); + } + } +} + +impl Clone for Receiver { + fn clone(&self) -> Receiver { + let count = self.channel.receiver_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of receiver clones are leaked. + if count > isize::MAX as usize { + process::abort(); + } + + Receiver { + channel: self.channel.clone(), + opt_key: None, + } + } +} + +impl Stream for Receiver { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + poll_recv(&this.channel, &this.channel.streams, &mut this.opt_key, cx) + } +} + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Receiver { .. }") + } +} + +/// Polls a receive operation on a channel. +/// +/// If the receive operation is blocked, the current task will be registered in `registry` and its +/// registration key will then be stored in `opt_key`. +fn poll_recv( + channel: &Channel, + registry: &Registry, + opt_key: &mut Option, + cx: &mut Context<'_>, +) -> Poll> { + // Try receiving a message. + let poll = match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => { + // Register the current task. + match *opt_key { + None => *opt_key = Some(registry.register(cx)), + Some(key) => registry.reregister(key, cx), + } + + // Try receiving a message again. + match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => Poll::Pending, + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = opt_key.take() { + // `true` means the receive operation is completed. + registry.unregister(key, true); + } + } + + poll +} + +/// A slot in a channel. +struct Slot { + /// The current stamp. + stamp: AtomicUsize, + + /// The message in this slot. + msg: UnsafeCell, +} + +/// Bounded channel based on a preallocated array. +struct Channel { + /// The head of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit in the head is always zero. + /// + /// Messages are popped from the head of the channel. + head: CachePadded, + + /// The tail of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit indicates that the channel is disconnected. + /// + /// Messages are pushed into the tail of the channel. + tail: CachePadded, + + /// The buffer holding slots. + buffer: *mut Slot, + + /// The channel capacity. + cap: usize, + + /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. + one_lap: usize, + + /// If this bit is set in the tail, that means either all senders were dropped or all receivers + /// were dropped. + mark_bit: usize, + + /// Send operations waiting while the channel is full. + sends: Registry, + + /// Receive operations waiting while the channel is empty and not disconnected. + recvs: Registry, + + /// Streams waiting while the channel is empty and not disconnected. + streams: Registry, + + /// The number of currently active `Sender`s. + sender_count: AtomicUsize, + + /// The number of currently active `Receivers`s. + receiver_count: AtomicUsize, + + /// Indicates that dropping a `Channel` may drop values of type `T`. + _marker: PhantomData, +} + +unsafe impl Send for Channel {} +unsafe impl Sync for Channel {} +impl Unpin for Channel {} + +impl Channel { + /// Creates a bounded channel of capacity `cap`. + fn with_capacity(cap: usize) -> Self { + assert!(cap > 0, "capacity must be positive"); + + // Compute constants `mark_bit` and `one_lap`. + let mark_bit = (cap + 1).next_power_of_two(); + let one_lap = mark_bit * 2; + + // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let head = 0; + // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let tail = 0; + + // Allocate a buffer of `cap` slots. + let buffer = { + let mut v = Vec::>::with_capacity(cap); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + + // Initialize stamps in the slots. + for i in 0..cap { + unsafe { + // Set the stamp to `{ lap: 0, mark: 0, index: i }`. + let slot = buffer.add(i); + ptr::write(&mut (*slot).stamp, AtomicUsize::new(i)); + } + } + + Channel { + buffer, + cap, + one_lap, + mark_bit, + head: CachePadded::new(AtomicUsize::new(head)), + tail: CachePadded::new(AtomicUsize::new(tail)), + sends: Registry::new(), + recvs: Registry::new(), + streams: Registry::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + _marker: PhantomData, + } + } + + /// Attempts to push a message. + fn push(&self, msg: T) -> Result<(), PushError> { + let backoff = Backoff::new(); + let mut tail = self.tail.load(Ordering::Relaxed); + + loop { + // Deconstruct the tail. + let index = tail & (self.mark_bit - 1); + let lap = tail & !(self.one_lap - 1); + + // Inspect the corresponding slot. + let slot = unsafe { &*self.buffer.add(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the tail and the stamp match, we may attempt to push. + if tail == stamp { + let new_tail = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + tail + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the tail. + match self.tail.compare_exchange_weak( + tail, + new_tail, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Write the message into the slot and update the stamp. + unsafe { slot.msg.get().write(msg) }; + let stamp = tail + 1; + slot.stamp.store(stamp, Ordering::Release); + + // Wake a blocked receive operation. + self.recvs.notify_one(); + + // Wake all blocked streams. + self.streams.notify_all(); + + return Ok(()); + } + Err(t) => { + tail = t; + backoff.spin(); + } + } + } else if stamp.wrapping_add(self.one_lap) == tail + 1 { + atomic::fence(Ordering::SeqCst); + let head = self.head.load(Ordering::Relaxed); + + // If the head lags one lap behind the tail as well... + if head.wrapping_add(self.one_lap) == tail { + // ...then the channel is full. + + // Check if the channel is disconnected. + if tail & self.mark_bit != 0 { + return Err(PushError::Disconnected(msg)); + } else { + return Err(PushError::Full(msg)); + } + } + + backoff.spin(); + tail = self.tail.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + tail = self.tail.load(Ordering::Relaxed); + } + } + } + + /// Attempts to pop a message. + fn pop(&self) -> Result { + let backoff = Backoff::new(); + let mut head = self.head.load(Ordering::Relaxed); + + loop { + // Deconstruct the head. + let index = head & (self.mark_bit - 1); + let lap = head & !(self.one_lap - 1); + + // Inspect the corresponding slot. + let slot = unsafe { &*self.buffer.add(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the the stamp is ahead of the head by 1, we may attempt to pop. + if head + 1 == stamp { + let new = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + head + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the head. + match self.head.compare_exchange_weak( + head, + new, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Read the message from the slot and update the stamp. + let msg = unsafe { slot.msg.get().read() }; + let stamp = head.wrapping_add(self.one_lap); + slot.stamp.store(stamp, Ordering::Release); + + // Wake a blocked send operation. + self.sends.notify_one(); + + return Ok(msg); + } + Err(h) => { + head = h; + backoff.spin(); + } + } + } else if stamp == head { + atomic::fence(Ordering::SeqCst); + let tail = self.tail.load(Ordering::Relaxed); + + // If the tail equals the head, that means the channel is empty. + if (tail & !self.mark_bit) == head { + // If the channel is disconnected... + if tail & self.mark_bit != 0 { + return Err(PopError::Disconnected); + } else { + // Otherwise, the receive operation is not ready. + return Err(PopError::Empty); + } + } + + backoff.spin(); + head = self.head.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + head = self.head.load(Ordering::Relaxed); + } + } + } + + /// Returns the current number of messages inside the channel. + fn len(&self) -> usize { + loop { + // Load the tail, then load the head. + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // If the tail didn't change, we've got consistent values to work with. + if self.tail.load(Ordering::SeqCst) == tail { + let hix = head & (self.mark_bit - 1); + let tix = tail & (self.mark_bit - 1); + + return if hix < tix { + tix - hix + } else if hix > tix { + self.cap - hix + tix + } else if (tail & !self.mark_bit) == head { + 0 + } else { + self.cap + }; + } + } + } + + /// Returns `true` if the channel is empty. + fn is_empty(&self) -> bool { + let head = self.head.load(Ordering::SeqCst); + let tail = self.tail.load(Ordering::SeqCst); + + // Is the tail equal to the head? + // + // Note: If the head changes just before we load the tail, that means there was a moment + // when the channel was not empty, so it is safe to just return `false`. + (tail & !self.mark_bit) == head + } + + /// Returns `true` if the channel is full. + fn is_full(&self) -> bool { + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // Is the head lagging one lap behind tail? + // + // Note: If the tail changes just before we load the head, that means there was a moment + // when the channel was not full, so it is safe to just return `false`. + head.wrapping_add(self.one_lap) == tail & !self.mark_bit + } + + /// Disconnects the channel and wakes up all blocked operations. + fn disconnect(&self) { + let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); + + if tail & self.mark_bit == 0 { + // Notify everyone blocked on this channel. + self.sends.notify_all(); + self.recvs.notify_all(); + self.streams.notify_all(); + } + } +} + +impl Drop for Channel { + fn drop(&mut self) { + // Get the index of the head. + let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1); + + // Loop over all slots that hold a message and drop them. + for i in 0..self.len() { + // Compute the index of the next slot holding a message. + let index = if hix + i < self.cap { + hix + i + } else { + hix + i - self.cap + }; + + unsafe { + self.buffer.add(index).drop_in_place(); + } + } + + // Finally, deallocate the buffer, but don't run any destructors. + unsafe { + Vec::from_raw_parts(self.buffer, 0, self.cap); + } + } +} + +/// An error returned from the `push()` method. +enum PushError { + /// The channel is full but not disconnected. + Full(T), + + /// The channel is full and disconnected. + Disconnected(T), +} + +/// An error returned from the `pop()` method. +enum PopError { + /// The channel is empty but not disconnected. + Empty, + + /// The channel is empty and disconnected. + Disconnected, +} + +/// A list of blocked channel operations. +struct Blocked { + /// A list of registered channel operations. + /// + /// Each entry has a waker associated with the task that is executing the operation. If the + /// waker is set to `None`, that means the task has been woken up but hasn't removed itself + /// from the registry yet. + entries: Slab>, + + /// The number of wakers in the entry list. + waker_count: usize, +} + +/// A registry of blocked channel operations. +/// +/// Blocked operations register themselves in a registry. Successful operations on the opposite +/// side of the channel wake blocked operations in the registry. +struct Registry { + /// A list of blocked channel operations. + blocked: Spinlock, + + /// Set to `true` if there are no wakers in the registry. + /// + /// Note that this either means there are no entries in the registry, or that all entries have + /// been notified. + is_empty: AtomicBool, +} + +impl Registry { + /// Creates a new registry. + fn new() -> Registry { + Registry { + blocked: Spinlock::new(Blocked { + entries: Slab::new(), + waker_count: 0, + }), + is_empty: AtomicBool::new(true), + } + } + + /// Registers a blocked channel operation and returns a key associated with it. + fn register(&self, cx: &Context<'_>) -> usize { + let mut blocked = self.blocked.lock(); + + // Insert a new entry into the list of blocked tasks. + let w = cx.waker().clone(); + let key = blocked.entries.insert(Some(w)); + + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + + key + } + + /// Re-registers a blocked channel operation by filling in its waker. + fn reregister(&self, key: usize, cx: &Context<'_>) { + let mut blocked = self.blocked.lock(); + + let was_none = blocked.entries[key].is_none(); + let w = cx.waker().clone(); + blocked.entries[key] = Some(w); + + if was_none { + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + } + } + + /// Unregisters a channel operation. + /// + /// If `completed` is `true`, the operation will be removed from the registry. If `completed` + /// is `false`, that means the operation was cancelled so another one will be notified. + fn unregister(&self, key: usize, completed: bool) { + let mut blocked = self.blocked.lock(); + let mut removed = false; + + match blocked.entries.remove(key) { + Some(_) => removed = true, + None => { + if !completed { + // This operation was cancelled. Notify another one. + if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { + if let Some(w) = opt_waker.take() { + w.wake(); + removed = true; + } + } + } + } + } + + if removed { + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + + /// Notifies one blocked channel operation. + #[inline] + fn notify_one(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + } + } + + /// Notifies all blocked channel operations. + #[inline] + fn notify_all(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + for (_, opt_waker) in blocked.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + } + } + + blocked.waker_count = 0; + self.is_empty.store(true, Ordering::SeqCst); + } + } +} + +/// A simple spinlock. +struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, +} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } +} + +/// A guard holding a spinlock locked. +struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.flag.store(false, Ordering::Release); + } +} + +impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } +} + +impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index be74d8f7..3ad2776c 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -40,5 +40,8 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; + pub use channel::{channel, Sender, Receiver}; + mod barrier; + mod channel; } diff --git a/tests/channel.rs b/tests/channel.rs new file mode 100644 index 00000000..91622b0d --- /dev/null +++ b/tests/channel.rs @@ -0,0 +1,350 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use async_std::sync::channel; +use async_std::task; +use rand::{thread_rng, Rng}; + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + task::block_on(async { + let (s, r) = channel(1); + + s.send(7).await; + assert_eq!(r.recv().await, Some(7)); + + s.send(8).await; + assert_eq!(r.recv().await, Some(8)); + + drop(s); + assert_eq!(r.recv().await, None); + }) +} + +#[test] +fn capacity() { + for i in 1..10 { + let (s, r) = channel::<()>(i); + assert_eq!(s.capacity(), i); + assert_eq!(r.capacity(), i); + } +} + +#[test] +fn len_empty_full() { + task::block_on(async { + let (s, r) = channel(2); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + s.send(()).await; + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + s.send(()).await; + + assert_eq!(s.len(), 2); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), true); + assert_eq!(r.len(), 2); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), true); + + r.recv().await; + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + }) +} + +#[test] +fn recv() { + task::block_on(async { + let (s, r) = channel(100); + + task::spawn(async move { + assert_eq!(r.recv().await, Some(7)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(8)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(9)); + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1500)).await; + s.send(7).await; + s.send(8).await; + s.send(9).await; + }) +} + +#[test] +fn send() { + task::block_on(async { + let (s, r) = channel(1); + + task::spawn(async move { + s.send(7).await; + task::sleep(ms(1000)).await; + s.send(8).await; + task::sleep(ms(1000)).await; + s.send(9).await; + task::sleep(ms(1000)).await; + s.send(10).await; + }); + + task::sleep(ms(1500)).await; + assert_eq!(r.recv().await, Some(7)); + assert_eq!(r.recv().await, Some(8)); + assert_eq!(r.recv().await, Some(9)); + }) +} + +#[test] +fn recv_after_disconnect() { + task::block_on(async { + let (s, r) = channel(100); + + s.send(1).await; + s.send(2).await; + s.send(3).await; + + drop(s); + + assert_eq!(r.recv().await, Some(1)); + assert_eq!(r.recv().await, Some(2)); + assert_eq!(r.recv().await, Some(3)); + assert_eq!(r.recv().await, None); + }) +} + +#[test] +fn len() { + const COUNT: usize = 25_000; + const CAP: usize = 1000; + + task::block_on(async { + let (s, r) = channel(CAP); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for _ in 0..CAP / 10 { + for i in 0..50 { + s.send(i).await; + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + r.recv().await; + assert_eq!(r.len(), 50 - i - 1); + } + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..CAP { + s.send(i).await; + assert_eq!(s.len(), i + 1); + } + + for _ in 0..CAP { + r.recv().await.unwrap(); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + let child = task::spawn({ + let r = r.clone(); + async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + let len = r.len(); + assert!(len <= CAP); + } + } + }); + + for i in 0..COUNT { + s.send(i).await; + let len = s.len(); + assert!(len <= CAP); + } + + child.await; + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + }) +} + +#[test] +fn disconnect_wakes_receiver() { + task::block_on(async { + let (s, r) = channel::<()>(1); + + let child = task::spawn(async move { + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1000)).await; + drop(s); + + child.await; + }) +} + +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + task::block_on(async { + let (s, r) = channel(3); + + let child = task::spawn(async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + } + assert_eq!(r.recv().await, None); + }); + + for i in 0..COUNT { + s.send(i).await; + } + drop(s); + + child.await; + }) +} + +#[test] +fn mpmc() { + const COUNT: usize = 25_000; + const TASKS: usize = 4; + + task::block_on(async { + let (s, r) = channel::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = Arc::new(v); + + let mut tasks = Vec::new(); + + for _ in 0..TASKS { + let r = r.clone(); + let v = v.clone(); + tasks.push(task::spawn(async move { + for _ in 0..COUNT { + let n = r.recv().await.unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + })); + } + + for _ in 0..TASKS { + let s = s.clone(); + tasks.push(task::spawn(async move { + for i in 0..COUNT { + s.send(i).await; + } + })); + } + + for t in tasks { + t.await; + } + + for c in v.iter() { + assert_eq!(c.load(Ordering::SeqCst), TASKS); + } + }); +} + +#[test] +fn oneshot() { + const COUNT: usize = 10_000; + + task::block_on(async { + for _ in 0..COUNT { + let (s, r) = channel(1); + + let c1 = task::spawn(async move { r.recv().await.unwrap() }); + let c2 = task::spawn(async move { s.send(0).await }); + + c1.await; + c2.await; + } + }) +} + +#[test] +fn drops() { + const RUNS: usize = 100; + + static DROPS: AtomicUsize = AtomicUsize::new(0); + + #[derive(Debug, PartialEq)] + struct DropCounter; + + impl Drop for DropCounter { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut rng = thread_rng(); + + for _ in 0..RUNS { + task::block_on(async { + let steps = rng.gen_range(0, 10_000); + let additional = rng.gen_range(0, 50); + + DROPS.store(0, Ordering::SeqCst); + let (s, r) = channel::(50); + + let child = task::spawn({ + let r = r.clone(); + async move { + for _ in 0..steps { + r.recv().await.unwrap(); + } + } + }); + + for _ in 0..steps { + s.send(DropCounter).await; + } + + child.await; + + for _ in 0..additional { + s.send(DropCounter).await; + } + + assert_eq!(DROPS.load(Ordering::SeqCst), steps); + drop(s); + drop(r); + assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); + }) + } +} From 1c843a8124d599c19930da286363fc29b135d644 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 23 Oct 2019 22:27:51 +0200 Subject: [PATCH 145/407] Re-implemented Throttle to keep last value in memory --- src/stream/stream/mod.rs | 2 +- src/stream/stream/throttle.rs | 43 ++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 95b04449..3584be3d 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -281,7 +281,7 @@ extension_trait! { TakeWhile::new(self, predicate) } - fn throttle(self, d: Duration) -> Throttle + fn throttle(self, d: Duration) -> Throttle where Self: Sized, { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index c37c972c..8e96fea9 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -10,47 +10,58 @@ use crate::task::{Context, Poll}; /// A stream that only yields one element once every `duration`, and drops all others. /// #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct Throttle { +pub struct Throttle { stream: S, duration: Duration, delay: Option, + last: Option, } -impl Unpin for Throttle {} +impl Unpin for Throttle {} -impl Throttle { +impl Throttle { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(duration: Duration); pin_utils::unsafe_pinned!(delay: Option); + pin_utils::unsafe_unpinned!(last: Option); pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, duration, delay: None, + last: None, } } } -impl Stream for Throttle { +impl Stream for Throttle { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(v) => match self.as_mut().delay().as_pin_mut() { - None => { + if let Some(d) = self.as_mut().delay().as_pin_mut() { + if d.poll(cx).is_ready() { + if let Some(v) = self.as_mut().last().take() { + // Sets last to None. *self.as_mut().delay() = Some(Delay::new(self.duration)); - Poll::Ready(v) + return Poll::Ready(Some(v)); } - Some(d) => match d.poll(cx) { - Poll::Ready(_) => { - *self.as_mut().delay() = Some(Delay::new(self.duration)); - Poll::Ready(v) - } - Poll::Pending => Poll::Pending, - }, - }, + } + } + + match self.as_mut().stream().poll_next(cx) { Poll::Pending => Poll::Pending, + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some(v)) => { + if self.as_mut().delay().is_some() { + *self.as_mut().last() = Some(v); + cx.waker().wake_by_ref(); // Continue driving even though emitting Pending + return Poll::Pending; + } + + *self.as_mut().delay() = Some(Delay::new(self.duration)); + Poll::Ready(Some(v)) + } } } } From 1fd05a157f70c99157079672584d3f1aa1626bd8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 23 Oct 2019 22:34:39 +0200 Subject: [PATCH 146/407] Reset delay to prevent poll after ready --- src/stream/stream/throttle.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 8e96fea9..2a0cc563 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -45,6 +45,8 @@ impl Stream for Throttle { // Sets last to None. *self.as_mut().delay() = Some(Delay::new(self.duration)); return Poll::Ready(Some(v)); + } else { + *self.as_mut().delay() = None; } } } From d97b3dfdf3a159608023aea7a197b522514d817a Mon Sep 17 00:00:00 2001 From: k-nasa Date: Thu, 24 Oct 2019 08:29:05 +0900 Subject: [PATCH 147/407] 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 148/407] 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 149/407] 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 150/407] 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 151/407] $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 152/407] 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 153/407] 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 154/407] 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 155/407] 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 156/407] 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 157/407] 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 158/407] 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 159/407] 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 81e3cab00db42c2084fba9734a53e7074048bfd9 Mon Sep 17 00:00:00 2001 From: nasa Date: Sat, 26 Oct 2019 06:14:57 +0900 Subject: [PATCH 160/407] Change homepage link (#389) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7aaba687..ad887303 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = [ edition = "2018" license = "Apache-2.0/MIT" repository = "https://github.com/async-rs/async-std" -homepage = "https://github.com/async-rs/async-std" +homepage = "https://async.rs" documentation = "https://docs.rs/async-std" description = "Async version of the Rust standard library" keywords = ["async", "await", "future", "std", "task"] From 37a7eadf17c55d0cb524080d34c2f30d848ecd7e Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sat, 26 Oct 2019 11:52:41 +0800 Subject: [PATCH 161/407] 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 162/407] 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 163/407] 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 164/407] 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 165/407] 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 166/407] 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 167/407] 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 168/407] 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 169/407] 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 6608d39c59f508ae09a32410df14c2e623b9cf44 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Sat, 26 Oct 2019 21:58:34 +0200 Subject: [PATCH 170/407] remove Stream trait bound --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b9d4bc86..9e0c1ef1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1416,7 +1416,7 @@ extension_trait! { "#] fn count(self) -> impl Future [CountFuture] where - Self: Sized + Stream, + Self: Sized, { CountFuture::new(self) } From 13a08b0d54f5c983883ce3efca60278d38a8b6d7 Mon Sep 17 00:00:00 2001 From: nasa Date: Sun, 27 Oct 2019 12:35:14 +0900 Subject: [PATCH 171/407] 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 172/407] 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 173/407] 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 174/407] $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 175/407] 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 176/407] 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 177/407] 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 178/407] 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 179/407] 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 180/407] 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 181/407] 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 182/407] 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 183/407] 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 184/407] 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 185/407] 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 186/407] 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 187/407] 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 188/407] 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 189/407] 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 190/407] $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 191/407] 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 192/407] 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 193/407] 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 194/407] 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 195/407] 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 196/407] 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 197/407] 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 198/407] 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 199/407] 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 200/407] 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 201/407] 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 202/407] 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 203/407] 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 204/407] 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 205/407] 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 206/407] 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 207/407] 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 208/407] 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 209/407] 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 210/407] 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 211/407] 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 212/407] 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 213/407] 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 214/407] 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 215/407] 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 216/407] 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 217/407] 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 218/407] 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 219/407] 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 220/407] 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 221/407] 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 222/407] 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 223/407] =?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 224/407] 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 225/407] 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 226/407] 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 227/407] 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 228/407] 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 229/407] 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 230/407] 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 231/407] 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 232/407] 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 233/407] 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 234/407] 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 235/407] 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 236/407] 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 237/407] 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 238/407] 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 239/407] 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 240/407] 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 241/407] 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 242/407] 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 243/407] 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 244/407] 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 245/407] 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 246/407] 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 247/407] 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 248/407] 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 249/407] 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 250/407] 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 251/407] 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 252/407] 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 253/407] 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 254/407] 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 255/407] 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 256/407] 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 257/407] 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 258/407] 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 259/407] 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 260/407] 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 261/407] 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 262/407] 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 263/407] 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 264/407] 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 265/407] 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 266/407] 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 267/407] 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 268/407] 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 269/407] 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 270/407] 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 271/407] 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 272/407] 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 273/407] 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 274/407] 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 275/407] 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 276/407] 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 277/407] 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 278/407] 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 279/407] 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 280/407] 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 281/407] 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 282/407] 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 283/407] 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 284/407] 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 285/407] 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 286/407] 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 287/407] 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 288/407] 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 289/407] 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 290/407] 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 291/407] 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 292/407] 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 293/407] 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 294/407] 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 295/407] 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 296/407] 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 297/407] 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 298/407] 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 299/407] 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 79bbf4938deeef223f405e1756945fea45965986 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 10:44:12 +0100 Subject: [PATCH 300/407] Randomize Stream::merge to improve the throughput. Implements #490. --- src/stream/stream/merge.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index fe3579e9..ababcf42 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -27,7 +27,10 @@ pin_project! { impl Merge { pub(crate) fn new(left: L, right: R) -> Self { - Self { left: left.fuse(), right: right.fuse() } + Self { + left: left.fuse(), + right: right.fuse(), + } } } @@ -40,14 +43,19 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - match this.left.poll_next(cx) { + let (first, second) = if (utils::random(1) == 1) { + (this.left, this.right) + } else { + (this.right, this.left) + }; + match first.poll_next(cx) { Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.right.poll_next(cx), - Poll::Pending => match this.right.poll_next(cx) { + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { Poll::Ready(Some(item)) => Poll::Ready(Some(item)), Poll::Ready(None) => Poll::Pending, Poll::Pending => Poll::Pending, - } + }, } } } From 352f18bc2a404b755dd7cd021ca50b3a79a7cc14 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 11:10:36 +0100 Subject: [PATCH 301/407] 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 0c37d4af106487f575d5fbd6f9a764ed25418c5f Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:25:50 +0100 Subject: [PATCH 302/407] Anonymous function to avoid type issues --- src/stream/stream/merge.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index ababcf42..6c8c20b7 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -5,6 +5,7 @@ use pin_project_lite::pin_project; use crate::prelude::*; use crate::stream::Fuse; +use crate::utils; pin_project! { /// A stream that merges two other streams into a single stream. @@ -43,19 +44,27 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - let (first, second) = if (utils::random(1) == 1) { - (this.left, this.right) + if utils::random(1) == 1 { + poll_next_in_order(cx, this.left, this.right) } else { - (this.right, this.left) - }; - match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => second.poll_next(cx), - Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, + poll_next_in_order(cx, this.right, this.left) } } } + +/// Pools the next item, trying in order, first the first item, then the second one. +fn poll_next_in_order(cx: &mut Context<'_>, first: F, second: S) -> Poll> +where + F: Stream, + S: Stream, +{ + match first.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } +} From e48e4637361c37cc18d447c5ec3fc2eb135c6fd5 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:26:32 +0100 Subject: [PATCH 303/407] Duplicating code due to strange Rust error. --- src/stream/stream/merge.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 6c8c20b7..b08b586e 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -45,26 +45,25 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if utils::random(1) == 1 { - poll_next_in_order(cx, this.left, this.right) + match this.left.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => this.right.poll_next(cx), + Poll::Pending => match this.right.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } } else { - poll_next_in_order(cx, this.right, this.left) + match this.right.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => this.left.poll_next(cx), + Poll::Pending => match this.left.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } } } } - -/// Pools the next item, trying in order, first the first item, then the second one. -fn poll_next_in_order(cx: &mut Context<'_>, first: F, second: S) -> Poll> -where - F: Stream, - S: Stream, -{ - match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => second.poll_next(cx), - Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } -} From 5d558ca213327f465feb60ac96ad9ae8421002e2 Mon Sep 17 00:00:00 2001 From: razican Date: Mon, 11 Nov 2019 11:39:30 +0100 Subject: [PATCH 304/407] Fixed test, order is no longer guaranteed --- src/stream/stream/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ea1459f..7c4bceb0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1670,11 +1670,14 @@ extension_trait! { let c = stream::once(3u8); let mut s = a.merge(b).merge(c); + let mut lst = Vec::new(); - assert_eq!(s.next().await, Some(1u8)); - assert_eq!(s.next().await, Some(2u8)); - assert_eq!(s.next().await, Some(3u8)); - assert_eq!(s.next().await, None); + while let Some(n) = s.next().await { + lst.push(n) + } + + lst.sort_unstable(); + assert_eq!(&lst, &[1u8, 2u8, 3u8]); # }); ``` "#] From b591fc68bdee365cfc1f6228c2d2f264b1b2f6e8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 12:17:00 +0100 Subject: [PATCH 305/407] Changed semantics of throttle to non-dropping variant with backpressure --- examples/throttle.rs | 27 ++++++++++++++++++++++++++ src/stream/stream/mod.rs | 29 ++++++++++++++++++++++++++-- src/stream/stream/throttle.rs | 36 +++++++++++++---------------------- 3 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 examples/throttle.rs diff --git a/examples/throttle.rs b/examples/throttle.rs new file mode 100644 index 00000000..1b9a6f2e --- /dev/null +++ b/examples/throttle.rs @@ -0,0 +1,27 @@ +//! Spawns a timed task which gets throttled. + +fn main() { + #[cfg(feature = "unstable")] + { + use async_std::prelude::*; + use async_std::task; + + task::block_on(async { + use async_std::stream; + use std::time::Duration; + + // emit value every 1 second + let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + + // throttle for 2 seconds + let s = s.throttle(Duration::from_secs(2)); + + s.for_each(|(n, _)| { + dbg!(n); + }) + .await; + // => 0 .. 1 .. 2 .. 3 + // with a pause of 2 seconds between each print + }) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8c39eb96..8b30c5e9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -117,7 +117,6 @@ use std::time::Duration; cfg_unstable! { use std::future::Future; use std::pin::Pin; - use std::time::Duration; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; @@ -316,7 +315,33 @@ extension_trait! { TakeWhile::new(self, predicate) } - fn throttle(self, d: Duration) -> Throttle + #[doc = r#" + Limit the amount of items yielded per timeslice in a stream. + + # Examples + ```ignore + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use std::time::Duration; + + // emit value every 1 second + let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + + // throttle for 2 seconds + let s = s.throttle(Duration::from_secs(2)); + + s.for_each(|(n, _)| { + dbg!(n); + }) + .await; + // => 0 .. 1 .. 2 .. 3 + // with a pause of 2 seconds between each print + # + # }) } + ``` + "#] + fn throttle(self, d: Duration) -> Throttle where Self: Sized, { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 2a0cc563..010839cc 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -7,60 +7,50 @@ use futures_timer::Delay; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that only yields one element once every `duration`, and drops all others. +/// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. /// #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct Throttle { +pub struct Throttle { stream: S, duration: Duration, delay: Option, - last: Option, } -impl Unpin for Throttle {} +impl Unpin for Throttle {} -impl Throttle { +impl Throttle { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(duration: Duration); pin_utils::unsafe_pinned!(delay: Option); - pin_utils::unsafe_unpinned!(last: Option); pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, duration, delay: None, - last: None, } } } -impl Stream for Throttle { +impl Stream for Throttle { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if let Some(d) = self.as_mut().delay().as_pin_mut() { if d.poll(cx).is_ready() { - if let Some(v) = self.as_mut().last().take() { - // Sets last to None. - *self.as_mut().delay() = Some(Delay::new(self.duration)); - return Poll::Ready(Some(v)); - } else { - *self.as_mut().delay() = None; - } + *self.as_mut().delay() = None; + } else { + return Poll::Pending; } } match self.as_mut().stream().poll_next(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => return Poll::Ready(None), + Poll::Pending => { + cx.waker().wake_by_ref(); // Continue driving even though emitting Pending + Poll::Pending + } + Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - if self.as_mut().delay().is_some() { - *self.as_mut().last() = Some(v); - cx.waker().wake_by_ref(); // Continue driving even though emitting Pending - return Poll::Pending; - } - *self.as_mut().delay() = Some(Delay::new(self.duration)); Poll::Ready(Some(v)) } From 139a34b6852b5bf74bb97ead4a26241f4a88edde Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 12:26:32 +0100 Subject: [PATCH 306/407] Make throttle an unstable feature --- src/stream/stream/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8b30c5e9..756e8e96 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -60,7 +60,6 @@ mod skip_while; mod step_by; mod take; mod take_while; -mod throttle; mod try_fold; mod try_for_each; mod zip; @@ -107,16 +106,15 @@ pub use skip_while::SkipWhile; pub use step_by::StepBy; pub use take::Take; pub use take_while::TakeWhile; -pub use throttle::Throttle; pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use std::time::Duration; cfg_unstable! { use std::future::Future; use std::pin::Pin; + use std::time::Duration; use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; @@ -125,11 +123,13 @@ cfg_unstable! { pub use flatten::Flatten; pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; + pub use throttle::Throttle; mod merge; mod flatten; mod flat_map; mod timeout; + mod throttle; } extension_trait! { @@ -315,6 +315,7 @@ extension_trait! { TakeWhile::new(self, predicate) } + #[cfg(all(feature = "default", feature = "unstable"))] #[doc = r#" Limit the amount of items yielded per timeslice in a stream. From c2f750d2882b4215ae6962d419056ba00b000f6d Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 10:21:42 +0100 Subject: [PATCH 307/407] 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 ef958f0408c713de6b93cb995d00244212472de8 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Mon, 11 Nov 2019 13:09:35 +0100 Subject: [PATCH 308/407] Use pin_project_lite instead for throttle --- src/stream/stream/mod.rs | 3 ++- src/stream/stream/throttle.rs | 36 +++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 756e8e96..a91517d8 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -315,7 +315,6 @@ extension_trait! { TakeWhile::new(self, predicate) } - #[cfg(all(feature = "default", feature = "unstable"))] #[doc = r#" Limit the amount of items yielded per timeslice in a stream. @@ -342,6 +341,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn throttle(self, d: Duration) -> Throttle where Self: Sized, diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 010839cc..88136078 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -3,26 +3,25 @@ use std::pin::Pin; use std::time::Duration; use futures_timer::Delay; +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. -/// #[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Throttle { - stream: S, - duration: Duration, - delay: Option, +pin_project! { + /// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Throttle { + #[pin] + stream: S, + duration: Duration, + #[pin] + delay: Option, + } } -impl Unpin for Throttle {} - impl Throttle { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(duration: Duration); - pin_utils::unsafe_pinned!(delay: Option); - pub(super) fn new(stream: S, duration: Duration) -> Self { Throttle { stream, @@ -35,23 +34,24 @@ impl Throttle { impl Stream for Throttle { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(d) = self.as_mut().delay().as_pin_mut() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if let Some(d) = this.delay.as_mut().as_pin_mut() { if d.poll(cx).is_ready() { - *self.as_mut().delay() = None; + this.delay.set(None); } else { return Poll::Pending; } } - match self.as_mut().stream().poll_next(cx) { + match this.stream.poll_next(cx) { Poll::Pending => { cx.waker().wake_by_ref(); // Continue driving even though emitting Pending Poll::Pending } Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - *self.as_mut().delay() = Some(Delay::new(self.duration)); + this.delay.set(Some(Delay::new(*this.duration))); Poll::Ready(Some(v)) } } From 5438258ceec7932fa407c5c7e0697c7ccec7d6f2 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 11 Nov 2019 13:19:59 +0100 Subject: [PATCH 309/407] 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 310/407] 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 311/407] 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 312/407] 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" ``` From 7d2282dbd284c80a5ae32f1a4d1c24234755f147 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Mon, 11 Nov 2019 22:11:06 +0100 Subject: [PATCH 313/407] fix merge conflict --- src/stream/stream/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 653261fb..2f21c384 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -68,11 +68,8 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; -<<<<<<< HEAD use count::CountFuture; -======= use cycle::Cycle; ->>>>>>> master use enumerate::Enumerate; use eq::EqFuture; use filter_map::FilterMap; From 37922408e56560eec2049ba8b57ca4217a203211 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Mon, 11 Nov 2019 22:17:29 +0100 Subject: [PATCH 314/407] use pin_project --- src/stream/stream/count.rs | 29 ++++++++++++++++------------- src/stream/stream/mod.rs | 6 +++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index b6d53ca8..221b0f0c 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -1,20 +1,22 @@ +use std::future::Future; use std::pin::Pin; -use crate::future::Future; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct CountFuture { - stream: S, - count: usize, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct CountFuture { + #[pin] + stream: S, + count: usize, + } } impl CountFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(count: usize); - pub(crate) fn new(stream: S) -> Self { CountFuture { stream, count: 0 } } @@ -26,16 +28,17 @@ where { type Output = usize; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(_) => { cx.waker().wake_by_ref(); - *self.as_mut().count() += 1; + *this.count += 1; Poll::Pending } - None => Poll::Ready(self.count), + None => Poll::Ready(*this.count), } } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2f21c384..d6592271 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1813,10 +1813,10 @@ extension_trait! { # fn main() { async_std::task::block_on(async { # use async_std::prelude::*; - use std::collections::VecDeque; + use async_std::stream; - let s1 = VecDeque::from(vec![0]); - let s2 = VecDeque::from(vec![1, 2, 3]); + let s1 = stream::from_iter(vec![0]); + let s2 = stream::from_iter(vec![1, 2, 3]); assert_eq!(s1.count().await, 1); assert_eq!(s2.count().await, 3); From 6677d52c2df87eab7f06400f9fc1099d725c96f8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:35:29 +0100 Subject: [PATCH 315/407] Improve thread creating algorithm in spawn_blocking --- src/task/spawn_blocking.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 6076d1bc..a93a68bf 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -68,16 +68,13 @@ static POOL: Lazy = Lazy::new(|| { fn start_thread() { SLEEPING.fetch_add(1, Ordering::SeqCst); - - // Generate a random duration of time between 1 second and 10 seconds. If the thread doesn't - // receive the next task in this duration of time, it will stop running. - let timeout = Duration::from_millis(1000 + u64::from(random(9_000))); + let timeout = Duration::from_secs(10); thread::Builder::new() .name("async-std/blocking".to_string()) .spawn(move || { loop { - let task = match POOL.receiver.recv_timeout(timeout) { + let mut task = match POOL.receiver.recv_timeout(timeout) { Ok(task) => task, Err(_) => { // Check whether this is the last sleeping thread. @@ -100,8 +97,22 @@ fn start_thread() { start_thread(); } - // Run the task. - abort_on_panic(|| task.run()); + loop { + // Run the task. + abort_on_panic(|| task.run()); + + // Try taking another task if there are any available. + task = match POOL.receiver.try_recv() { + Ok(task) => task, + Err(_) => break, + }; + } + + // If there is at least one sleeping thread, stop this thread instead of putting it + // to sleep. + if SLEEPING.load(Ordering::SeqCst) > 0 { + return; + } SLEEPING.fetch_add(1, Ordering::SeqCst); } From 21c5c48cb6cbf2cd6ab687f4e7938bcb5e35cea3 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:37:54 +0100 Subject: [PATCH 316/407] Lower the timeout to 1 second --- src/task/spawn_blocking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index a93a68bf..6cce082d 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -68,7 +68,7 @@ static POOL: Lazy = Lazy::new(|| { fn start_thread() { SLEEPING.fetch_add(1, Ordering::SeqCst); - let timeout = Duration::from_secs(10); + let timeout = Duration::from_secs(1); thread::Builder::new() .name("async-std/blocking".to_string()) From 1a50ffd144684199325cbd8a38fc0953373dc5ee Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 12 Nov 2019 00:38:22 +0100 Subject: [PATCH 317/407] Delete unused import --- src/task/spawn_blocking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs index 6cce082d..578afa4e 100644 --- a/src/task/spawn_blocking.rs +++ b/src/task/spawn_blocking.rs @@ -6,7 +6,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use once_cell::sync::Lazy; use crate::task::{JoinHandle, Task}; -use crate::utils::{abort_on_panic, random}; +use crate::utils::abort_on_panic; /// Spawns a blocking task. /// From b5b2b5a0a34c6a4ebaf078aa8b8f8eb02a6593ac Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 00:48:26 +0100 Subject: [PATCH 318/407] 1.0.1 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 14 +++++++++++++- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c890f7a4..37c14c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.0.1] - 2019-11-12 + +We were seeing a regression in our fs performance, caused by too many +long-running tasks. This patch fixes that regression by being more proactive +about closing down idle threads. + +## Changes + +- Improved thread startup/shutdown algorithm in spawn_blocking. +- Fixed a typo in the tutorial. + # [1.0.0] - 2019-11-11 [API Documentation](https://docs.rs/async-std/1.0.0/async-std) @@ -429,7 +440,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD +[1.0.0]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 [0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 diff --git a/Cargo.toml b/Cargo.toml index c1c68655..4d19a934 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.0.0" +version = "1.0.1" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 9d7b2d6696ad369a5710700a7b4c3321fcd05cd8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 01:34:07 +0100 Subject: [PATCH 319/407] fix changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c14c73..9d19fddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview # [1.0.1] - 2019-11-12 +[API Documentation](https://docs.rs/async-std/1.0.1/async-std) + We were seeing a regression in our fs performance, caused by too many long-running tasks. This patch fixes that regression by being more proactive about closing down idle threads. ## Changes -- Improved thread startup/shutdown algorithm in spawn_blocking. +- Improved thread startup/shutdown algorithm in `task::spawn_blocking`. - Fixed a typo in the tutorial. # [1.0.0] - 2019-11-11 @@ -441,7 +443,7 @@ task::blocking(async { - Initial beta release [Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD -[1.0.0]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 +[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 [0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 From 0d5c7a217fac0ff611c4e0ad59d8480a4314c454 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 02:09:38 +0100 Subject: [PATCH 320/407] stabilize task::yield_now Signed-off-by: Yoshua Wuyts --- src/task/mod.rs | 8 +++----- src/task/yield_now.rs | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index bcdea72c..198e5787 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -124,6 +124,9 @@ cfg_std! { #[doc(inline)] pub use async_macros::ready; + + pub use yield_now::yield_now; + mod yield_now; } cfg_default! { @@ -157,8 +160,3 @@ cfg_default! { #[cfg(not(any(feature = "unstable", test)))] pub(crate) use spawn_blocking::spawn_blocking; } - -cfg_unstable! { - pub use yield_now::yield_now; - mod yield_now; -} diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 03f83e2e..40306966 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -26,8 +26,6 @@ use crate::task::{Context, Poll}; /// # /// # }) /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub async fn yield_now() { YieldNow(false).await From 1b7d5bea6b4fdda52deeeb1c4e0f76533bb6ecb3 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 12 Nov 2019 12:19:11 +0000 Subject: [PATCH 321/407] Enable surf example 1.0.3 has been released with the required change --- Cargo.toml | 2 +- examples/surf-web.rs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d19a934..a005404b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ slab = { version = "0.4.2", optional = true } [dev-dependencies] femme = "1.2.0" rand = "0.7.2" -# surf = "1.0.2" +surf = "1.0.3" tempdir = "0.3.7" futures = "0.3.0" diff --git a/examples/surf-web.rs b/examples/surf-web.rs index b3101d15..df139e5b 100644 --- a/examples/surf-web.rs +++ b/examples/surf-web.rs @@ -1,6 +1,3 @@ -/* TODO: Once the next version of surf released, re-enable this example. -//! Sends an HTTP request to the Rust website. - use async_std::task; fn main() -> Result<(), surf::Exception> { @@ -18,6 +15,3 @@ fn main() -> Result<(), surf::Exception> { Ok(()) }) } -*/ - -fn main() {} From 7c7386735ef013f29a72cea801689a71e3db74ac Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:34:31 +0100 Subject: [PATCH 322/407] Wrap around throttle comment Co-Authored-By: Yoshua Wuyts --- src/stream/stream/throttle.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 88136078..44ce7e16 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -9,7 +9,13 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { - /// A stream that only yields one element once every `duration`, and applies backpressure. Does not drop any elements. + /// A stream that only yields one element once every `duration`. + /// + /// This `struct` is created by the [`throttle`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`throttle`]: trait.Stream.html#method.throttle + /// [`Stream`]: trait.Stream.html #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct Throttle { From 6f6d5e9d205765a6676a08c74e00c3f6001da93e Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:35:03 +0100 Subject: [PATCH 323/407] Updated throttle fn comments. Co-Authored-By: Yoshua Wuyts --- src/stream/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index a91517d8..6a6d43d0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -318,6 +318,7 @@ extension_trait! { #[doc = r#" Limit the amount of items yielded per timeslice in a stream. +This stream does not drop any items, but will only limit the rate at which items pass through. # Examples ```ignore # fn main() { async_std::task::block_on(async { From 88cbf2c119371830714e6e26417a00439e968659 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 14:30:28 +0100 Subject: [PATCH 324/407] Change throttle test to run in milliseconds --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index a91517d8..6a7f89fd 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -319,23 +319,23 @@ extension_trait! { Limit the amount of items yielded per timeslice in a stream. # Examples - ```ignore + ``` # fn main() { async_std::task::block_on(async { # + use async_std::prelude::*; use async_std::stream; use std::time::Duration; // emit value every 1 second - let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + let s = stream::interval(Duration::from_millis(5)).enumerate().take(3); // throttle for 2 seconds - let s = s.throttle(Duration::from_secs(2)); + let mut s = s.throttle(Duration::from_millis(10)); - s.for_each(|(n, _)| { - dbg!(n); - }) - .await; - // => 0 .. 1 .. 2 .. 3 + assert_eq!(s.next().await, Some((0, ()))); + assert_eq!(s.next().await, Some((1, ()))); + assert_eq!(s.next().await, Some((2, ()))); + assert_eq!(s.next().await, None); // with a pause of 2 seconds between each print # # }) } From 6990c1403f85f598f5bd3536a9a01ef7c5464a31 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 15:07:20 +0100 Subject: [PATCH 325/407] Reimplemented throttle to never drop Delay, added boolean flag --- src/stream/stream/mod.rs | 2 +- src/stream/stream/throttle.rs | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cc1646cd..48a1a201 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -318,7 +318,7 @@ extension_trait! { #[doc = r#" Limit the amount of items yielded per timeslice in a stream. -This stream does not drop any items, but will only limit the rate at which items pass through. + This stream does not drop any items, but will only limit the rate at which items pass through. # Examples ``` # fn main() { async_std::task::block_on(async { diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 44ce7e16..8896899e 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -1,6 +1,6 @@ use std::future::Future; use std::pin::Pin; -use std::time::Duration; +use std::time::{Duration, Instant}; use futures_timer::Delay; use pin_project_lite::pin_project; @@ -23,7 +23,9 @@ pin_project! { stream: S, duration: Duration, #[pin] - delay: Option, + blocked: bool, + #[pin] + delay: Delay, } } @@ -32,7 +34,8 @@ impl Throttle { Throttle { stream, duration, - delay: None, + blocked: false, + delay: Delay::new(Duration::default()), } } } @@ -42,9 +45,10 @@ impl Stream for Throttle { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); - if let Some(d) = this.delay.as_mut().as_pin_mut() { + if *this.blocked { + let d = this.delay.as_mut(); if d.poll(cx).is_ready() { - this.delay.set(None); + *this.blocked = false; } else { return Poll::Pending; } @@ -57,7 +61,8 @@ impl Stream for Throttle { } Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(v)) => { - this.delay.set(Some(Delay::new(*this.duration))); + *this.blocked = true; + this.delay.reset(Instant::now() + *this.duration); Poll::Ready(Some(v)) } } From 4ab7b213de8d8f8307f0a1877cb4a4d4c802e792 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 15:38:02 +0100 Subject: [PATCH 326/407] Updated example to be consistent; added timing measurements to throttle --- examples/throttle.rs | 2 +- src/stream/stream/mod.rs | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/throttle.rs b/examples/throttle.rs index 1b9a6f2e..74c1fd30 100644 --- a/examples/throttle.rs +++ b/examples/throttle.rs @@ -11,7 +11,7 @@ fn main() { use std::time::Duration; // emit value every 1 second - let s = stream::interval(Duration::from_nanos(1000000)).enumerate(); + let s = stream::interval(Duration::from_secs(1)).enumerate(); // throttle for 2 seconds let s = s.throttle(Duration::from_secs(2)); diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 48a1a201..cec874fc 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -325,19 +325,32 @@ extension_trait! { # use async_std::prelude::*; use async_std::stream; - use std::time::Duration; + use std::time::{Duration, Instant}; - // emit value every 1 second - let s = stream::interval(Duration::from_millis(5)).enumerate().take(3); + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)) + .enumerate() + .take(3); - // throttle for 2 seconds + // throttle for 10 milliseconds let mut s = s.throttle(Duration::from_millis(10)); + let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 5 && duration_ms < 15); + assert_eq!(s.next().await, Some((1, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 15 && duration_ms < 25); + assert_eq!(s.next().await, Some((2, ()))); + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 25 && duration_ms < 35); + assert_eq!(s.next().await, None); - // with a pause of 2 seconds between each print + let duration_ms = start.elapsed().as_millis(); + assert!(duration_ms >= 35 && duration_ms < 45); # # }) } ``` From c5b3a98e5b4f12f80f5e9b5ddb135c22da60502f Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Tue, 12 Nov 2019 16:22:25 +0100 Subject: [PATCH 327/407] Increased throttle test to 10x time --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index cec874fc..86645fce 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,30 +327,30 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; - // emit value every 5 milliseconds - let s = stream::interval(Duration::from_millis(5)) + // emit value every 50 milliseconds + let s = stream::interval(Duration::from_millis(50)) .enumerate() .take(3); - // throttle for 10 milliseconds - let mut s = s.throttle(Duration::from_millis(10)); + // throttle for 100 milliseconds + let mut s = s.throttle(Duration::from_millis(100)); let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 5 && duration_ms < 15); + assert!(duration_ms >= 50 && duration_ms < 150); assert_eq!(s.next().await, Some((1, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 15 && duration_ms < 25); + assert!(duration_ms >= 150 && duration_ms < 250); assert_eq!(s.next().await, Some((2, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 25 && duration_ms < 35); + assert!(duration_ms >= 250 && duration_ms < 350); assert_eq!(s.next().await, None); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 35 && duration_ms < 45); + assert!(duration_ms >= 350 && duration_ms < 450); # # }) } ``` From 74a7d93611119ac9affea692d77745bcc3abaad8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 17:34:13 +0100 Subject: [PATCH 328/407] upgrade async-macros to 2.0.0 (#519) Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a005404b..e9207395 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ std = [ [dependencies] async-attributes = { version = "1.1.0", optional = true } -async-macros = { version = "1.0.0", optional = true } +async-macros = { version = "2.0.0", optional = true } async-task = { version = "1.0.0", optional = true } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } crossbeam-channel = { version = "0.3.9", optional = true } From f611ceccc87aaf6b6cfb5bdee8bfffaffee5498f Mon Sep 17 00:00:00 2001 From: Devashish Dixit Date: Wed, 13 Nov 2019 00:47:03 +0800 Subject: [PATCH 329/407] Run cargo fmt for doc comments (#515) --- rustfmt.toml | 1 + src/macros.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 1082fd88..c6d404e2 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,2 @@ version = "Two" +format_code_in_doc_comments = true diff --git a/src/macros.rs b/src/macros.rs index b7811d2e..638a2348 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,8 +23,8 @@ /// ``` /// # async_std::task::block_on(async { /// # -/// use async_std::prelude::*; /// use async_std::io; +/// use async_std::prelude::*; /// use async_std::print; /// /// print!("this ").await; @@ -181,8 +181,8 @@ macro_rules! eprintln { /// # /// use std::cell::Cell; /// -/// use async_std::task; /// use async_std::prelude::*; +/// use async_std::task; /// /// task_local! { /// static VAL: Cell = Cell::new(5); From f0875d2dca8428f140c709bfc45f651748692e7b Mon Sep 17 00:00:00 2001 From: Grzegorz Gierlach Date: Tue, 12 Nov 2019 19:34:08 +0100 Subject: [PATCH 330/407] Cleaning up stream pinning. --- src/collections/binary_heap/from_stream.rs | 2 -- src/collections/btree_map/from_stream.rs | 2 -- src/collections/btree_set/from_stream.rs | 2 -- src/collections/hash_map/from_stream.rs | 2 -- src/collections/hash_set/from_stream.rs | 2 -- src/collections/linked_list/from_stream.rs | 2 -- src/collections/vec_deque/from_stream.rs | 2 -- src/option/from_stream.rs | 2 -- src/option/product.rs | 2 -- src/option/sum.rs | 2 -- src/path/pathbuf.rs | 7 ++----- src/result/from_stream.rs | 2 -- src/result/product.rs | 2 -- src/result/sum.rs | 2 -- src/stream/from_fn.rs | 3 +-- src/stream/repeat_with.rs | 4 +--- src/string/extend.rs | 10 ---------- src/string/from_stream.rs | 10 ---------- src/unit/extend.rs | 2 +- src/vec/from_stream.rs | 10 ---------- 20 files changed, 5 insertions(+), 67 deletions(-) diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index 148a57f4..6851948e 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for BinaryHeap { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BinaryHeap::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index e0653ab5..85312236 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream<(K, V)> for BTreeMap { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeMap::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index c4197df4..318af9e6 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for BTreeSet { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeSet::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index bf47d8e7..d74a7ccf 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashMap::with_hasher(Default::default()); stream::extend(&mut out, stream).await; out diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index 69b38538..dc5e61e3 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashSet::with_hasher(Default::default()); stream::extend(&mut out, stream).await; out diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index 12262471..d93bbb7b 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for LinkedList { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = LinkedList::new(); stream::extend(&mut out, stream).await; out diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 767ec068..241bd74e 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -12,8 +12,6 @@ impl FromStream for VecDeque { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = VecDeque::new(); stream::extend(&mut out, stream).await; out diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index d2d53b60..86791143 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -17,8 +17,6 @@ where let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = false; diff --git a/src/option/product.rs b/src/option/product.rs index 9b7274ff..abaab73e 100644 --- a/src/option/product.rs +++ b/src/option/product.rs @@ -39,8 +39,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; diff --git a/src/option/sum.rs b/src/option/sum.rs index 5c154f42..d2e44830 100644 --- a/src/option/sum.rs +++ b/src/option/sum.rs @@ -34,8 +34,6 @@ where where S: Stream> + 'a { Box::pin(async move { - pin_utils::pin_mut!(stream); - // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_none = false; diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 56a63a47..808acb2e 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -327,8 +327,6 @@ impl> stream::Extend

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

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

for PathBuf { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(item.as_ref()); } diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index 3ace6583..8067176e 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ impl Unpin for FromFn {} /// use async_std::stream; /// /// let mut count = 0u8; -/// let mut s = stream::from_fn(|| { +/// let s = stream::from_fn(|| { /// count += 1; /// if count > 3 { /// None @@ -39,6 +39,8 @@ impl Unpin for FromFn {} /// } /// }); /// +/// pin_utils::pin_mut!(s); +/// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(2)); /// assert_eq!(s.next().await, Some(3)); diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index 954693d8..e183a77c 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -28,7 +28,9 @@ impl Unpin for RepeatWith {} /// use async_std::prelude::*; /// use async_std::stream; /// -/// let mut s = stream::repeat_with(|| 1); +/// let s = stream::repeat_with(|| 1); +/// +/// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); diff --git a/src/string/extend.rs b/src/string/extend.rs index 43bd46d6..55bec0c5 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -13,6 +13,8 @@ impl stream::Extend for String { self.reserve(stream.size_hint().0); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(item); } @@ -28,6 +30,8 @@ impl<'b> stream::Extend<&'b char> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push(*item); } @@ -43,6 +47,8 @@ impl<'b> stream::Extend<&'b str> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(item); } @@ -58,6 +64,8 @@ impl stream::Extend for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(&item); } @@ -73,6 +81,8 @@ impl<'b> stream::Extend> for String { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { self.push_str(&item); } diff --git a/src/unit/extend.rs b/src/unit/extend.rs index 5b0bc1d1..55c8e0d0 100644 --- a/src/unit/extend.rs +++ b/src/unit/extend.rs @@ -11,6 +11,8 @@ impl stream::Extend<()> for () { let stream = stream.into_stream(); Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(_) = stream.next().await {} }) } From 2dfdc1c4821c097066f93f64d49abe035616c9dc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:07:39 +0100 Subject: [PATCH 332/407] polish lib.rs examples Signed-off-by: Yoshua Wuyts --- src/lib.rs | 50 ++++++++++++++++++++++++++++++++++++++------ src/task/block_on.rs | 8 ++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ddc6462c..5442909f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,17 +131,55 @@ //! //! # Examples //! -//! Spawn a task and block the current thread on its result: +//! All examples require the [`"attributes"` feature](#features) to be enabled. +//! This feature is not enabled by default because it significantly impacts +//! compile times. See [`task::block_on`] for an alternative way to start +//! executing tasks. //! +//! Call an async function from the main function: +//! +//! ``` +//! async fn say_hello() { +//! println!("Hello, world!"); +//! } +//! +//! #[async_std::main] +//! async fn main() { +//! say_hello().await; +//! } +//! ``` +//! +//! Await two futures concurrently, and return a tuple of their output: +//! +//! ``` +//! #[async_std::main] +//! async fn main() { +//! let a = || async move { 1u8 }; +//! let b = || async move { 2u8 }; +//! assert_eq!(a.join(b).await, (1u8, 2u8)) +//! } //! ``` -//! use async_std::task; //! -//! fn main() { -//! task::block_on(async { -//! println!("Hello, world!"); -//! }) +//! Create a UDP server that echoes back each received message to the sender: +//! +//! ```no_run +//! use async_std::net::UdpSocket; +//! +//! #[async_std::main] +//! async fn main() -> std::io::Result<()> { +//! let mut socket = UdpSocket::bind("127.0.0.1:8080")?; +//! println!("Listening on {}", socket.local_addr()?); +//! +//! let mut buf = vec![0u8; 1024]; +//! +//! loop { +//! let (recv, peer) = socket.recv_from(&mut buf).await?; +//! let sent = socket.send_to(&buf[..recv], &peer).await?; +//! println!("Sent {} out of {} bytes to {}", sent, recv, peer); +//! } //! } //! ``` +//! [`task::block_on`]: task/fn.block_on.html //! //! # Features //! diff --git a/src/task/block_on.rs b/src/task/block_on.rs index f61a22b6..80259c57 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -28,9 +28,11 @@ use crate::task::{Context, Poll, Task, Waker}; /// ```no_run /// use async_std::task; /// -/// task::block_on(async { -/// println!("Hello, world!"); -/// }) +/// fn main() { +/// task::block_on(async { +/// println!("Hello, world!"); +/// }) +/// } /// ``` pub fn block_on(future: F) -> T where From 1431ee04220bd3292f38a913523e4b432997a41b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:25:52 +0100 Subject: [PATCH 333/407] polish README.md examples Signed-off-by: Yoshua Wuyts --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9af20a39..3c074fee 100644 --- a/README.md +++ b/README.md @@ -75,19 +75,21 @@ syntax. ## Examples ```rust -use async_std::task; +async fn say_hello() { + println!("Hello, world!"); +} -fn main() { - task::block_on(async { - println!("Hello, world!"); - }) +#[async_std::main] +async fn main() { + say_hello().await; } ``` More examples, including networking and file access, can be found in our -[`examples`] directory. +[`examples`] directory and in our [documentation]. [`examples`]: https://github.com/async-rs/async-std/tree/master/examples +[documentation]: https://docs.rs/async-std#examples ## Philosophy From 79962e20a5a9fe346a02e2eefd258569259732d8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 12 Nov 2019 23:37:43 +0100 Subject: [PATCH 334/407] enable attributes feature Signed-off-by: Yoshua Wuyts --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99436b72..1d14e21d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --features unstable + args: --all --features unstable attributes check_fmt_and_docs: name: Checking fmt and docs From 879af6dc857d37f51dc48c638b06ee1a922dade9 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 10:50:09 +0800 Subject: [PATCH 335/407] Add Stream max --- src/stream/stream/max.rs | 60 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 35 +++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/stream/stream/max.rs diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs new file mode 100644 index 00000000..d8ff119d --- /dev/null +++ b/src/stream/stream/max.rs @@ -0,0 +1,60 @@ +use std::cmp::{Ord, Ordering}; +use std::marker::PhantomData; +use std::pin::Pin; +use std::future::Future; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxFuture { + #[pin] + stream: S, + _compare: PhantomData, + max: Option, + } +} + +impl MaxFuture { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + _compare: PhantomData, + max: None, + } + } +} + +impl Future for MaxFuture +where + S: Stream, + S::Item: Ord, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.max.take() { + None => *this.max = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take()), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5756a21e..a3cf0552 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -44,6 +44,7 @@ mod last; mod le; mod lt; mod map; +mod max; mod max_by; mod max_by_key; mod min; @@ -80,6 +81,7 @@ use gt::GtFuture; use last::LastFuture; use le::LeFuture; use lt::LtFuture; +use max::MaxFuture; use max_by::MaxByFuture; use max_by_key::MaxByKeyFuture; use min::MinFuture; @@ -913,6 +915,39 @@ extension_trait! { } #[doc = r#" + Returns the element that gives the maximum value. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ```ignore + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1usize, 2, 3]); + + let max = s.clone().max().await; + assert_eq!(max, Some(3)); + + let max = stream::empty::().max().await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max( + self, + ) -> impl Future> [MaxFuture] + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MaxFuture::new(self) + } + + #[doc = r#" Returns the element that gives the minimum value. If several elements are equally minimum, the first element is returned. If the stream is empty, `None` is returned. From 9d634cb2a7cc045d3e3b8da0ad1e40ffc3cb525a Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 12:42:59 +0800 Subject: [PATCH 336/407] refactor io dir to be same with std --- src/io/buf_reader.rs | 6 ++---- src/io/buf_writer.rs | 6 ++---- src/io/mod.rs | 2 ++ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 1d00b526..e6d8e669 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -4,11 +4,9 @@ use std::{cmp, fmt}; use pin_project_lite::pin_project; -use crate::io::{self, BufRead, Read, Seek, SeekFrom}; +use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; use crate::task::{Context, Poll}; -const DEFAULT_CAPACITY: usize = 8 * 1024; - pin_project! { /// Adds buffering to any reader. /// @@ -72,7 +70,7 @@ impl BufReader { /// # Ok(()) }) } /// ``` pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_CAPACITY, inner) + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new buffered reader with the specified capacity. diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 8fa9eba4..35b511f8 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -4,11 +4,9 @@ use std::pin::Pin; use pin_project_lite::pin_project; use crate::io::write::WriteExt; -use crate::io::{self, Seek, SeekFrom, Write}; +use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE}; use crate::task::{Context, Poll, ready}; -const DEFAULT_CAPACITY: usize = 8 * 1024; - pin_project! { /// Wraps a writer and buffers its output. /// @@ -107,7 +105,7 @@ impl BufWriter { /// # Ok(()) }) } /// ``` pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_CAPACITY, inner) + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new `BufWriter` with the specified buffer capacity. diff --git a/src/io/mod.rs b/src/io/mod.rs index 4e832305..065aaab5 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -269,6 +269,8 @@ //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap +const DEFAULT_BUF_SIZE: usize = 8 * 1024; + cfg_std! { #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; From 5adb112a00b7854f16025a6f343796723c444fe1 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Wed, 13 Nov 2019 13:52:16 +0800 Subject: [PATCH 337/407] export IntoInnerError for io --- src/io/buf_writer.rs | 26 +++++++++++++++++++++++++- src/io/mod.rs | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index 35b511f8..ce6a97b3 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -85,8 +85,32 @@ pin_project! { } } +/// An error returned by `into_inner` which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// use async_std::io::BufWriter; +/// use async_std::net::TcpStream; +/// +/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?); +/// +/// // unwrap the TcpStream and flush the buffer +/// let stream = match buf_writer.into_inner().await { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// # +/// # Ok(()) }) } +///``` #[derive(Debug)] -pub struct IntoInnerError(W, std::io::Error); +pub struct IntoInnerError(W, crate::io::Error); impl BufWriter { /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, diff --git a/src/io/mod.rs b/src/io/mod.rs index 065aaab5..0c8144b5 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -277,7 +277,7 @@ cfg_std! { pub use buf_read::{BufRead, Lines}; pub use buf_reader::BufReader; - pub use buf_writer::BufWriter; + pub use buf_writer::{BufWriter, IntoInnerError}; pub use copy::copy; pub use cursor::Cursor; pub use empty::{empty, Empty}; From 6f4bea07a11a07b2b1d8a0c9a35a65903bc56e36 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 13 Nov 2019 15:27:29 +0100 Subject: [PATCH 338/407] Update version requirements in the tutorial --- docs/src/tutorial/specification.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index 307c79e9..7b1a0167 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -38,18 +38,10 @@ $ cargo new a-chat $ cd a-chat ``` -At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience: - -```bash -$ rustup override add nightly -$ rustc --version -rustc 1.38.0-nightly (c4715198b 2019-08-05) -``` - Add the following lines to `Cargo.toml`: ```toml [dependencies] futures = "0.3.0" -async-std = "1.0.0" +async-std = "1" ``` From 0c2282ffdc63fa1c9d1aab8d836675279805207c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 13 Nov 2019 20:32:37 +0100 Subject: [PATCH 339/407] Optimization: a slot for the next task to run (#529) * Optimization: a slot for the next task to run * Only notify workers when a task is pushed into a queue --- benches/mutex.rs | 4 +- src/sync/mutex.rs | 1 + src/sync/waker_set.rs | 5 +- src/task/executor/pool.rs | 131 +++++++++++++++++++++++++------------- 4 files changed, 91 insertions(+), 50 deletions(-) diff --git a/benches/mutex.rs b/benches/mutex.rs index b159ba12..4f1910a6 100644 --- a/benches/mutex.rs +++ b/benches/mutex.rs @@ -2,9 +2,7 @@ extern crate test; -use std::sync::Arc; - -use async_std::sync::Mutex; +use async_std::sync::{Arc, Mutex}; use async_std::task; use test::Bencher; diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index 2c0ac0cc..4d2cf251 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -170,6 +170,7 @@ impl Mutex { /// # /// # }) /// ``` + #[inline] pub fn try_lock(&self) -> Option> { if !self.locked.swap(true, Ordering::SeqCst) { Some(MutexGuard(self)) diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs index 5ba4cfbd..7e897af1 100644 --- a/src/sync/waker_set.rs +++ b/src/sync/waker_set.rs @@ -60,6 +60,7 @@ impl WakerSet { } /// Inserts a waker for a blocked operation and returns a key associated with it. + #[cold] pub fn insert(&self, cx: &Context<'_>) -> usize { let w = cx.waker().clone(); let mut inner = self.lock(); @@ -70,6 +71,7 @@ impl WakerSet { } /// Removes the waker of an operation. + #[cold] pub fn remove(&self, key: usize) { let mut inner = self.lock(); @@ -81,6 +83,7 @@ impl WakerSet { /// Removes the waker of a cancelled operation. /// /// Returns `true` if another blocked operation from the set was notified. + #[cold] pub fn cancel(&self, key: usize) -> bool { let mut inner = self.lock(); @@ -147,6 +150,7 @@ impl WakerSet { /// Notifies blocked operations, either one or all of them. /// /// Returns `true` if at least one operation was notified. + #[cold] fn notify(&self, n: Notify) -> bool { let mut inner = &mut *self.lock(); let mut notified = false; @@ -172,7 +176,6 @@ impl WakerSet { } /// Locks the list of entries. - #[cold] fn lock(&self) -> Lock<'_> { let backoff = Backoff::new(); while self.flag.fetch_or(LOCKED, Ordering::Acquire) & LOCKED != 0 { diff --git a/src/task/executor/pool.rs b/src/task/executor/pool.rs index 1e743844..08694dd4 100644 --- a/src/task/executor/pool.rs +++ b/src/task/executor/pool.rs @@ -1,10 +1,11 @@ -use std::cell::UnsafeCell; +use std::cell::Cell; use std::iter; use std::thread; use std::time::Duration; use crossbeam_deque::{Injector, Stealer, Worker}; use once_cell::sync::Lazy; +use once_cell::unsync::OnceCell; use crate::task::executor::Sleepers; use crate::task::Runnable; @@ -32,9 +33,18 @@ static POOL: Lazy = Lazy::new(|| { let worker = Worker::new_fifo(); stealers.push(worker.stealer()); + let proc = Processor { + worker, + slot: Cell::new(None), + slot_runs: Cell::new(0), + }; + thread::Builder::new() .name("async-std/executor".to_string()) - .spawn(|| abort_on_panic(|| main_loop(worker))) + .spawn(|| { + let _ = PROCESSOR.with(|p| p.set(proc)); + abort_on_panic(|| main_loop()); + }) .expect("cannot start a thread driving tasks"); } @@ -45,59 +55,75 @@ static POOL: Lazy = Lazy::new(|| { } }); +/// The state of a worker thread. +struct Processor { + /// The local task queue. + worker: Worker, + + /// Contains the next task to run as an optimization that skips queues. + slot: Cell>, + + /// How many times in a row tasks have been taked from the slot rather than the queue. + slot_runs: Cell, +} + thread_local! { - /// Local task queue associated with the current worker thread. - static QUEUE: UnsafeCell>> = UnsafeCell::new(None); + /// Worker thread state. + static PROCESSOR: OnceCell = OnceCell::new(); } /// Schedules a new runnable task for execution. pub(crate) fn schedule(task: Runnable) { - QUEUE.with(|queue| { - let local = unsafe { (*queue.get()).as_ref() }; - - // If the current thread is a worker thread, push the task into its local task queue. - // Otherwise, push it into the global task queue. - match local { - None => POOL.injector.push(task), - Some(q) => q.push(task), + PROCESSOR.with(|proc| { + // If the current thread is a worker thread, store it into its task slot or push it into + // its local task queue. Otherwise, push it into the global task queue. + match proc.get() { + Some(proc) => { + // Replace the task in the slot. + if let Some(task) = proc.slot.replace(Some(task)) { + // If the slot already contained a task, push it into the local task queue. + proc.worker.push(task); + POOL.sleepers.notify_one(); + } + } + None => { + POOL.injector.push(task); + POOL.sleepers.notify_one(); + } } - }); - - // Notify a sleeping worker that new work just came in. - POOL.sleepers.notify_one(); + }) } /// Main loop running a worker thread. -fn main_loop(local: Worker) { - // Initialize the local task queue. - QUEUE.with(|queue| unsafe { *queue.get() = Some(local) }); +fn main_loop() { + /// Number of yields when no runnable task is found. + const YIELDS: u32 = 3; + /// Number of short sleeps when no runnable task in found. + const SLEEPS: u32 = 1; // The number of times the thread didn't find work in a row. - let mut step = 0; + let mut fails = 0; loop { // Try to find a runnable task. match find_runnable() { Some(task) => { - // Found. Now run the task. + fails = 0; + + // Run the found task. task.run(); - step = 0; } None => { + fails += 1; + // Yield the current thread or put it to sleep. - match step { - 0..=2 => { - thread::yield_now(); - step += 1; - } - 3 => { - thread::sleep(Duration::from_micros(10)); - step += 1; - } - _ => { - POOL.sleepers.wait(); - step = 0; - } + if fails <= YIELDS { + thread::yield_now(); + } else if fails <= YIELDS + SLEEPS { + thread::sleep(Duration::from_micros(10)); + } else { + POOL.sleepers.wait(); + fails = 0; } } } @@ -106,29 +132,42 @@ fn main_loop(local: Worker) { /// Find the next runnable task. fn find_runnable() -> Option { - let pool = &*POOL; - - QUEUE.with(|queue| { - let local = unsafe { (*queue.get()).as_ref().unwrap() }; + /// Maximum number of times the slot can be used in a row. + const SLOT_LIMIT: u32 = 16; + + PROCESSOR.with(|proc| { + let proc = proc.get().unwrap(); + + // Try taking a task from the slot. + let runs = proc.slot_runs.get(); + if runs < SLOT_LIMIT { + if let Some(task) = proc.slot.take() { + proc.slot_runs.set(runs + 1); + return Some(task); + } + } + proc.slot_runs.set(0); // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { + proc.worker.pop().or_else(|| { // Otherwise, we need to look for a task elsewhere. iter::repeat_with(|| { // Try stealing a batch of tasks from the global queue. - pool.injector - .steal_batch_and_pop(&local) + POOL.injector + .steal_batch_and_pop(&proc.worker) // Or try stealing a batch of tasks from one of the other threads. .or_else(|| { // First, pick a random starting point in the list of local queues. - let len = pool.stealers.len(); + let len = POOL.stealers.len(); let start = random(len as u32) as usize; // Try stealing a batch of tasks from each local queue starting from the // chosen point. - let (l, r) = pool.stealers.split_at(start); - let rotated = r.iter().chain(l.iter()); - rotated.map(|s| s.steal_batch_and_pop(&local)).collect() + let (l, r) = POOL.stealers.split_at(start); + let stealers = r.iter().chain(l.iter()); + stealers + .map(|s| s.steal_batch_and_pop(&proc.worker)) + .collect() }) }) // Loop while no task was stolen and any steal operation needs to be retried. From 8473b738d05bfe47d477dbc49bf5bf14550e68e4 Mon Sep 17 00:00:00 2001 From: sclaire-1 <54961957+sclaire-1@users.noreply.github.com> Date: Wed, 13 Nov 2019 16:33:44 -0800 Subject: [PATCH 340/407] Edit tutorial index.md Edited the structure of sentences to make it easier to read --- docs/src/tutorial/index.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md index 99ddf8eb..aee0b3f4 100644 --- a/docs/src/tutorial/index.md +++ b/docs/src/tutorial/index.md @@ -1,11 +1,14 @@ # Tutorial: Writing a chat -Nothing is as simple as a chat server, right? Not quite, chat servers -already expose you to all the fun of asynchronous programming: how -do you handle clients connecting concurrently. How do you handle them disconnecting? +Nothing is simpler than creating a chat server, right? +Not quite, chat servers expose you to all the fun of asynchronous programming: -How do you distribute the messages? +How will the server handle clients connecting concurrently? -In this tutorial, we will show you how to write one in `async-std`. +How will it handle them disconnecting? + +How will it distribute the messages? + +This tutorial explains how to write a chat server in `async-std`. You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat). From 90c67c223a383feaf7f898f6bbfd8b7ac4feee89 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 10:26:56 +0100 Subject: [PATCH 341/407] Decreased throttle test time to original values; only test lower bound --- src/stream/stream/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 86645fce..99a11203 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,30 +327,30 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; - // emit value every 50 milliseconds - let s = stream::interval(Duration::from_millis(50)) + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)) .enumerate() .take(3); - // throttle for 100 milliseconds - let mut s = s.throttle(Duration::from_millis(100)); + // throttle for 10 milliseconds + let mut s = s.throttle(Duration::from_millis(10)); let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 50 && duration_ms < 150); + assert!(duration_ms >= 5); assert_eq!(s.next().await, Some((1, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 150 && duration_ms < 250); + assert!(duration_ms >= 15); assert_eq!(s.next().await, Some((2, ()))); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 250 && duration_ms < 350); + assert!(duration_ms >= 25); assert_eq!(s.next().await, None); let duration_ms = start.elapsed().as_millis(); - assert!(duration_ms >= 350 && duration_ms < 450); + assert!(duration_ms >= 35); # # }) } ``` From 9ebe41f2d62f41aef481b2b4d780e6309080ade0 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Thu, 14 Nov 2019 10:34:09 +0100 Subject: [PATCH 342/407] Update src/stream/stream/mod.rs Co-Authored-By: nasa --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d6592271..98c63ff9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1824,7 +1824,7 @@ extension_trait! { # }) } ``` "#] - fn count(self) -> impl Future [CountFuture] + fn count(self) -> impl Future [CountFuture] where Self: Sized, { From dda65cbff0c9a68b0c6efda2a61754065fdee4dc Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Thu, 14 Nov 2019 11:29:49 +0100 Subject: [PATCH 343/407] Start throttle measurement before initialisation --- src/stream/stream/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 99a11203..de4a8fb7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -327,6 +327,8 @@ extension_trait! { use async_std::stream; use std::time::{Duration, Instant}; + let start = Instant::now(); + // emit value every 5 milliseconds let s = stream::interval(Duration::from_millis(5)) .enumerate() @@ -335,7 +337,6 @@ extension_trait! { // throttle for 10 milliseconds let mut s = s.throttle(Duration::from_millis(10)); - let start = Instant::now(); assert_eq!(s.next().await, Some((0, ()))); let duration_ms = start.elapsed().as_millis(); assert!(duration_ms >= 5); From 154644880021950268e523c17f46dc6de50ef6a1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 21:27:14 +0100 Subject: [PATCH 344/407] remove throttle example Signed-off-by: Yoshua Wuyts --- examples/throttle.rs | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 examples/throttle.rs diff --git a/examples/throttle.rs b/examples/throttle.rs deleted file mode 100644 index 74c1fd30..00000000 --- a/examples/throttle.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Spawns a timed task which gets throttled. - -fn main() { - #[cfg(feature = "unstable")] - { - use async_std::prelude::*; - use async_std::task; - - task::block_on(async { - use async_std::stream; - use std::time::Duration; - - // emit value every 1 second - let s = stream::interval(Duration::from_secs(1)).enumerate(); - - // throttle for 2 seconds - let s = s.throttle(Duration::from_secs(2)); - - s.for_each(|(n, _)| { - dbg!(n); - }) - .await; - // => 0 .. 1 .. 2 .. 3 - // with a pause of 2 seconds between each print - }) - } -} From fe3c9ef626801455028325f3a5bfeefa97406470 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 17 Oct 2019 14:23:14 +0200 Subject: [PATCH 345/407] First attempt at successor --- src/stream/stream/mod.rs | 1 + src/stream/stream/successor.rs | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/stream/stream/successor.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d6292c32..b5583e10 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -57,6 +57,7 @@ mod partial_cmp; mod position; mod scan; mod skip; +mod successor; mod skip_while; mod step_by; mod take; diff --git a/src/stream/stream/successor.rs b/src/stream/stream/successor.rs new file mode 100644 index 00000000..519729f9 --- /dev/null +++ b/src/stream/stream/successor.rs @@ -0,0 +1,59 @@ +use std::pin::Pin; +use std::marker::PhantomData; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[derive(Debug)] +pub struct Successor +where Fut: Future +{ + successor: F, + next: T, + _marker: PhantomData +} + +pub fn successor(func: F, start: T) -> Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + { + Successor { + successor: func, + next: start, + _marker: PhantomData, + } + } + +impl Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + +{ + pin_utils::unsafe_unpinned!(successor: F); + pin_utils::unsafe_unpinned!(next: T); +} + +impl Stream for Successor +where + Fut: Future, + F: FnMut(T) -> Fut, + T: Copy, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + + match self.as_mut().successor()(self.next).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(val) => { + self.next = val; + Poll::Ready(Some(val)) + } + } + } +} From 02b261de10d09c699fb949d3ac5347282756e1d8 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 17 Oct 2019 23:27:41 +0200 Subject: [PATCH 346/407] It compiles! Store the future and poll it instead of creating multiple new ones --- src/stream/mod.rs | 2 + src/stream/stream/mod.rs | 1 - src/stream/stream/successor.rs | 59 ------------------- src/stream/successor.rs | 102 +++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 60 deletions(-) delete mode 100644 src/stream/stream/successor.rs create mode 100644 src/stream/successor.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f7828822..f410e080 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -303,6 +303,7 @@ pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; +pub use successor::{successor, Successor}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; @@ -316,6 +317,7 @@ mod from_iter; mod once; mod repeat; mod repeat_with; +mod successor; cfg_unstable! { mod double_ended_stream; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b5583e10..d6292c32 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -57,7 +57,6 @@ mod partial_cmp; mod position; mod scan; mod skip; -mod successor; mod skip_while; mod step_by; mod take; diff --git a/src/stream/stream/successor.rs b/src/stream/stream/successor.rs deleted file mode 100644 index 519729f9..00000000 --- a/src/stream/stream/successor.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::pin::Pin; -use std::marker::PhantomData; - -use crate::future::Future; -use crate::stream::Stream; -use crate::task::{Context, Poll}; - -#[derive(Debug)] -pub struct Successor -where Fut: Future -{ - successor: F, - next: T, - _marker: PhantomData -} - -pub fn successor(func: F, start: T) -> Successor -where - F: FnMut(T) -> Fut, - Fut: Future, - T: Copy, - { - Successor { - successor: func, - next: start, - _marker: PhantomData, - } - } - -impl Successor -where - F: FnMut(T) -> Fut, - Fut: Future, - T: Copy, - -{ - pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: T); -} - -impl Stream for Successor -where - Fut: Future, - F: FnMut(T) -> Fut, - T: Copy, -{ - type Item = T; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - - match self.as_mut().successor()(self.next).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(val) => { - self.next = val; - Poll::Ready(Some(val)) - } - } - } -} diff --git a/src/stream/successor.rs b/src/stream/successor.rs new file mode 100644 index 00000000..434ef979 --- /dev/null +++ b/src/stream/successor.rs @@ -0,0 +1,102 @@ +use std::pin::Pin; +use std::marker::PhantomData; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that yields elements by calling an async closure with the previous value as an +/// argument +/// +/// This stream is constructed by [`successor`] function +/// +/// [`successor`]: fn.successor.html +#[derive(Debug)] +pub struct Successor +where Fut: Future +{ + successor: F, + future: Option, + next: T, + _marker: PhantomData +} + +/// Creates a new stream where to produce each new element a clousre is called with the previous +/// value. +/// +/// #Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::successor(22, |val| { +/// async move { +/// val + 1 +/// } +/// }); +/// +/// pin_utils::pin_mut!(s); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(3)); +/// # +/// # }) } +/// +/// ``` +pub fn successor(start: T, func: F) -> Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + { + Successor { + successor: func, + future: None, + next: start, + _marker: PhantomData, + } + } + +impl Successor +where + F: FnMut(T) -> Fut, + Fut: Future, + T: Copy, + +{ + pin_utils::unsafe_unpinned!(successor: F); + pin_utils::unsafe_unpinned!(next: T); + pin_utils::unsafe_pinned!(future: Option); + +} + +impl Stream for Successor +where + Fut: Future, + F: FnMut(T) -> Fut, + T: Copy, +{ + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match &self.future { + Some(_) => { + let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + self.as_mut().future().set(None); + + Poll::Ready(Some(next)) + }, + None => { + let x = self.next; + let fut = (self.as_mut().successor())(x); + self.as_mut().future().set(Some(fut)); + // Probably can poll the value here? + Poll::Pending + } + } + } +} + From 95a3e53fcdcab2d0610e54aa9520c152a11a1ceb Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 18 Oct 2019 09:00:38 +0200 Subject: [PATCH 347/407] Only use the Option of the future to decide to construct a new one --- src/stream/successor.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 434ef979..3ddeef47 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -39,9 +39,9 @@ where Fut: Future /// }); /// /// pin_utils::pin_mut!(s); -/// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(2)); -/// assert_eq!(s.next().await, Some(3)); +/// assert_eq!(s.next().await, Some(23)); +/// assert_eq!(s.next().await, Some(24)); +/// assert_eq!(s.next().await, Some(25)); /// # /// # }) } /// @@ -83,20 +83,18 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &self.future { - Some(_) => { - let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - Poll::Ready(Some(next)) - }, None => { let x = self.next; let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); - // Probably can poll the value here? - Poll::Pending } + _ => {}, } + + let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + *self.as_mut().next() = next; + self.as_mut().future().set(None); + Poll::Ready(Some(next)) } } From 8b662b659df21c09bc5f00c9e57f353f99457fd4 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 18 Oct 2019 09:10:15 +0200 Subject: [PATCH 348/407] Run rustfmt --- src/stream/mod.rs | 1 + src/stream/successor.rs | 32 +++++++++++++++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f410e080..bab9dc79 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -308,6 +308,7 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; +pub use successor::{successor, Successor}; pub(crate) mod stream; diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 3ddeef47..8e93956f 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::marker::PhantomData; +use std::pin::Pin; use crate::future::Future; use crate::stream::Stream; @@ -12,13 +12,14 @@ use crate::task::{Context, Poll}; /// /// [`successor`]: fn.successor.html #[derive(Debug)] -pub struct Successor -where Fut: Future +pub struct Successor +where + Fut: Future, { successor: F, future: Option, next: T, - _marker: PhantomData + _marker: PhantomData, } /// Creates a new stream where to produce each new element a clousre is called with the previous @@ -51,29 +52,27 @@ where F: FnMut(T) -> Fut, Fut: Future, T: Copy, - { - Successor { - successor: func, - future: None, - next: start, - _marker: PhantomData, - } +{ + Successor { + successor: func, + future: None, + next: start, + _marker: PhantomData, } +} -impl Successor +impl Successor where F: FnMut(T) -> Fut, Fut: Future, T: Copy, - { pin_utils::unsafe_unpinned!(successor: F); pin_utils::unsafe_unpinned!(next: T); pin_utils::unsafe_pinned!(future: Option); - } -impl Stream for Successor +impl Stream for Successor where Fut: Future, F: FnMut(T) -> Fut, @@ -88,7 +87,7 @@ where let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); } - _ => {}, + _ => {} } let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); @@ -97,4 +96,3 @@ where Poll::Ready(Some(next)) } } - From 554d5cfbc1ec93c4240c9cdbfac90ea62bd596ea Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 13:37:26 +0200 Subject: [PATCH 349/407] Slight renamings --- src/stream/successor.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/stream/successor.rs b/src/stream/successor.rs index 8e93956f..32353d0a 100644 --- a/src/stream/successor.rs +++ b/src/stream/successor.rs @@ -12,7 +12,7 @@ use crate::task::{Context, Poll}; /// /// [`successor`]: fn.successor.html #[derive(Debug)] -pub struct Successor +pub struct Successors where Fut: Future, { @@ -22,7 +22,7 @@ where _marker: PhantomData, } -/// Creates a new stream where to produce each new element a clousre is called with the previous +/// Creates a new stream where to produce each new element a closure is called with the previous /// value. /// /// #Examples @@ -33,7 +33,7 @@ where /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successor(22, |val| { +/// let s = stream::successors(22, |val| { /// async move { /// val + 1 /// } @@ -47,13 +47,13 @@ where /// # }) } /// /// ``` -pub fn successor(start: T, func: F) -> Successor +pub fn successors(start: T, func: F) -> Successors where F: FnMut(T) -> Fut, Fut: Future, T: Copy, { - Successor { + Successors { successor: func, future: None, next: start, @@ -61,7 +61,7 @@ where } } -impl Successor +impl Successors where F: FnMut(T) -> Fut, Fut: Future, @@ -72,7 +72,7 @@ where pin_utils::unsafe_pinned!(future: Option); } -impl Stream for Successor +impl Stream for Successors where Fut: Future, F: FnMut(T) -> Fut, From 266754897ec4574959ef35202af4d15bc83860e3 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 13:38:32 +0200 Subject: [PATCH 350/407] Rename the module to 'successors' --- src/stream/mod.rs | 2 +- src/stream/{successor.rs => successors.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/stream/{successor.rs => successors.rs} (100%) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index bab9dc79..d5cc5ac1 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -318,7 +318,7 @@ mod from_iter; mod once; mod repeat; mod repeat_with; -mod successor; +mod successors; cfg_unstable! { mod double_ended_stream; diff --git a/src/stream/successor.rs b/src/stream/successors.rs similarity index 100% rename from src/stream/successor.rs rename to src/stream/successors.rs From 8d97e0f974175040840a15e9ab568a2560f1658d Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 20 Oct 2019 17:56:35 +0200 Subject: [PATCH 351/407] Only produes empty value if next is ever a 'None' --- src/stream/successors.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 32353d0a..e7084165 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -14,11 +14,11 @@ use crate::task::{Context, Poll}; #[derive(Debug)] pub struct Successors where - Fut: Future, + Fut: Future>, { successor: F, future: Option, - next: T, + next: Option, _marker: PhantomData, } @@ -33,9 +33,9 @@ where /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(22, |val| { +/// let s = stream::successors(Some(22), |val| { /// async move { -/// val + 1 +/// Some(val + 1) /// } /// }); /// @@ -43,14 +43,25 @@ where /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); +/// +/// +///let never = stream::successors(None, |val: usize| { +/// async move { +/// Some(val + 1) +/// } +/// }); +/// +/// pin_utils::pin_mut!(never); +/// assert_eq!(never.next().await, None); +/// assert_eq!(never.next().await, None); /// # /// # }) } /// /// ``` -pub fn successors(start: T, func: F) -> Successors +pub fn successors(start: Option, func: F) -> Successors where F: FnMut(T) -> Fut, - Fut: Future, + Fut: Future>, T: Copy, { Successors { @@ -64,26 +75,30 @@ where impl Successors where F: FnMut(T) -> Fut, - Fut: Future, + Fut: Future>, T: Copy, { pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: T); + pin_utils::unsafe_unpinned!(next: Option); pin_utils::unsafe_pinned!(future: Option); } impl Stream for Successors where - Fut: Future, + Fut: Future>, F: FnMut(T) -> Fut, T: Copy, { type Item = T; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.next.is_none() { + return Poll::Ready(None); + } + match &self.future { None => { - let x = self.next; + let x = self.next.unwrap(); let fut = (self.as_mut().successor())(x); self.as_mut().future().set(Some(fut)); } @@ -93,6 +108,6 @@ where let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); *self.as_mut().next() = next; self.as_mut().future().set(None); - Poll::Ready(Some(next)) + Poll::Ready(next) } } From af928163e44731b27b23f1a01e8d4e7f3432a6cc Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 13:57:41 +0100 Subject: [PATCH 352/407] Got further! Thx Josh! --- src/stream/successors.rs | 80 +++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index e7084165..4186c667 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,25 +1,31 @@ use std::marker::PhantomData; use std::pin::Pin; +use std::mem; use crate::future::Future; use crate::stream::Stream; -use crate::task::{Context, Poll}; +use crate::task::{Context, Poll, ready}; -/// A stream that yields elements by calling an async closure with the previous value as an -/// argument -/// -/// This stream is constructed by [`successor`] function -/// -/// [`successor`]: fn.successor.html -#[derive(Debug)] -pub struct Successors -where - Fut: Future>, -{ - successor: F, - future: Option, - next: Option, - _marker: PhantomData, + + +pin_project_lite::pin_project! { + /// A stream that yields elements by calling an async closure with the previous value as an + /// argument + /// + /// This stream is constructed by [`successor`] function + /// + /// [`successor`]: fn.successor.html + #[derive(Debug)] + pub struct Successors + where + Fut: Future>, + { + successor: F, + #[pin] + future: Option, + slot: Option, + _marker: PhantomData, + } } /// Creates a new stream where to produce each new element a closure is called with the previous @@ -40,6 +46,7 @@ where /// }); /// /// pin_utils::pin_mut!(s); +/// assert_eq!(s.next().await, Some(22)); /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); @@ -58,31 +65,20 @@ where /// # }) } /// /// ``` -pub fn successors(start: Option, func: F) -> Successors +pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(T) -> Fut, Fut: Future>, T: Copy, { Successors { - successor: func, + successor: succ, future: None, - next: start, + slot: first, _marker: PhantomData, } } -impl Successors -where - F: FnMut(T) -> Fut, - Fut: Future>, - T: Copy, -{ - pin_utils::unsafe_unpinned!(successor: F); - pin_utils::unsafe_unpinned!(next: Option); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for Successors where Fut: Future>, @@ -91,23 +87,23 @@ where { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.next.is_none() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if this.slot.is_none() { return Poll::Ready(None); } - match &self.future { - None => { - let x = self.next.unwrap(); - let fut = (self.as_mut().successor())(x); - self.as_mut().future().set(Some(fut)); - } - _ => {} + if this.future.is_none() { + let x = this.slot.unwrap(); + let fut = (this.successor)(x); + this.future.set(Some(fut)); } - let next = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - *self.as_mut().next() = next; - self.as_mut().future().set(None); + let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); + + this.future.set(None); + mem::swap(this.slot, &mut next); Poll::Ready(next) } } From a257b7018c83748ff98f07a9592aa7b29f6f62ef Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 14:29:43 +0100 Subject: [PATCH 353/407] Rename some variables to match iter --- src/stream/successors.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 4186c667..7f598f51 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::pin::Pin; use std::mem; @@ -12,19 +11,18 @@ pin_project_lite::pin_project! { /// A stream that yields elements by calling an async closure with the previous value as an /// argument /// - /// This stream is constructed by [`successor`] function + /// This stream is constructed by [`successors`] function /// - /// [`successor`]: fn.successor.html + /// [`succcessors`]: fn.succssors.html #[derive(Debug)] pub struct Successors where Fut: Future>, { - successor: F, + succ: F, #[pin] future: Option, slot: Option, - _marker: PhantomData, } } @@ -72,10 +70,9 @@ where T: Copy, { Successors { - successor: succ, + succ: succ, future: None, slot: first, - _marker: PhantomData, } } @@ -95,14 +92,15 @@ where } if this.future.is_none() { - let x = this.slot.unwrap(); - let fut = (this.successor)(x); + let fut = (this.succ)(this.slot.unwrap()); this.future.set(Some(fut)); } let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); this.future.set(None); + + // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration mem::swap(this.slot, &mut next); Poll::Ready(next) } From 243cdd7ff1fe9ff2262ac0f7c7a729988dfa9482 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 17:56:31 +0100 Subject: [PATCH 354/407] Slight miss-merge --- src/stream/mod.rs | 1 - src/stream/successors.rs | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d5cc5ac1..d980f6cc 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -303,7 +303,6 @@ pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use from_iter::{from_iter, FromIter}; -pub use successor::{successor, Successor}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 7f598f51..fb4e1c60 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -5,9 +5,11 @@ use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll, ready}; +use pin_project_lite::pin_project; -pin_project_lite::pin_project! { + +pin_project! { /// A stream that yields elements by calling an async closure with the previous value as an /// argument /// From 4c09cdbeace41a6d44ac7017275330dbf8c0ba55 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sun, 10 Nov 2019 18:03:07 +0100 Subject: [PATCH 355/407] Mark successors as unstable --- src/stream/mod.rs | 3 ++- src/stream/successors.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index d980f6cc..47635eed 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -317,7 +317,6 @@ mod from_iter; mod once; mod repeat; mod repeat_with; -mod successors; cfg_unstable! { mod double_ended_stream; @@ -328,6 +327,7 @@ cfg_unstable! { mod interval; mod into_stream; mod product; + mod successors; mod sum; pub use double_ended_stream::DoubleEndedStream; @@ -339,5 +339,6 @@ cfg_unstable! { pub use into_stream::IntoStream; pub use product::Product; pub use stream::Merge; + pub use successors::{successors, Successors}; pub use sum::Sum; } diff --git a/src/stream/successors.rs b/src/stream/successors.rs index fb4e1c60..0295b33e 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -16,6 +16,8 @@ pin_project! { /// This stream is constructed by [`successors`] function /// /// [`succcessors`]: fn.succssors.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Successors where @@ -65,6 +67,8 @@ pin_project! { /// # }) } /// /// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(T) -> Fut, From bfb42b432ee636ab4005df3d24a70f5729363af9 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:07:48 +0100 Subject: [PATCH 356/407] Rearrange docs to match 'repeat' --- src/stream/successors.rs | 46 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 0295b33e..f7d5bd8e 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -7,33 +7,10 @@ use crate::task::{Context, Poll, ready}; use pin_project_lite::pin_project; - - -pin_project! { - /// A stream that yields elements by calling an async closure with the previous value as an - /// argument - /// - /// This stream is constructed by [`successors`] function - /// - /// [`succcessors`]: fn.succssors.html - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[derive(Debug)] - pub struct Successors - where - Fut: Future>, - { - succ: F, - #[pin] - future: Option, - slot: Option, - } -} - /// Creates a new stream where to produce each new element a closure is called with the previous /// value. /// -/// #Examples +/// # Examples /// /// ``` /// # fn main() { async_std::task::block_on(async { @@ -82,6 +59,27 @@ where } } +pin_project! { + /// A stream that yields elements by calling an async closure with the previous value as an + /// argument + /// + /// This stream is constructed by [`successors`] function + /// + /// [`successors`]: fn.succssors.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[derive(Debug)] + pub struct Successors + where + Fut: Future>, + { + succ: F, + #[pin] + future: Option, + slot: Option, + } +} + impl Stream for Successors where Fut: Future>, From 7677e9a3dfdada2bf8f97eadb286258c194c1e4b Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:13:57 +0100 Subject: [PATCH 357/407] Make the closure take a borrow to the value --- src/stream/successors.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index f7d5bd8e..446ffe56 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -18,7 +18,7 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |val| { +/// let s = stream::successors(Some(22), |&val| { /// async move { /// Some(val + 1) /// } @@ -31,9 +31,9 @@ use pin_project_lite::pin_project; /// assert_eq!(s.next().await, Some(25)); /// /// -///let never = stream::successors(None, |val: usize| { +///let never = stream::successors(None, |_| { /// async move { -/// Some(val + 1) +/// Some(1) /// } /// }); /// @@ -48,7 +48,7 @@ use pin_project_lite::pin_project; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn successors(first: Option, succ: F) -> Successors where - F: FnMut(T) -> Fut, + F: FnMut(&T) -> Fut, Fut: Future>, T: Copy, { @@ -83,7 +83,7 @@ pin_project! { impl Stream for Successors where Fut: Future>, - F: FnMut(T) -> Fut, + F: FnMut(&T) -> Fut, T: Copy, { type Item = T; @@ -96,7 +96,7 @@ where } if this.future.is_none() { - let fut = (this.succ)(this.slot.unwrap()); + let fut = (this.succ)(&this.slot.unwrap()); this.future.set(Some(fut)); } From f14b37ff17618a72dbb441cb1a33cb122c70339d Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Mon, 11 Nov 2019 09:15:38 +0100 Subject: [PATCH 358/407] Remoe the T: Copy bound on the item --- src/stream/successors.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index 446ffe56..e86512bf 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -50,7 +50,6 @@ pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Fut, Fut: Future>, - T: Copy, { Successors { succ: succ, @@ -84,7 +83,6 @@ impl Stream for Successors where Fut: Future>, F: FnMut(&T) -> Fut, - T: Copy, { type Item = T; @@ -96,7 +94,7 @@ where } if this.future.is_none() { - let fut = (this.succ)(&this.slot.unwrap()); + let fut = (this.succ)(this.slot.as_ref().unwrap()); this.future.set(Some(fut)); } From 786a52a09d40bb9303f237c6bac756132e1651c9 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 14 Nov 2019 21:37:51 +0100 Subject: [PATCH 359/407] Slight miss-merge --- src/stream/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 47635eed..d8b96ec2 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -307,7 +307,6 @@ pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; pub use stream::*; -pub use successor::{successor, Successor}; pub(crate) mod stream; From 64216b8e6bf24ccb95a296650e08cc56cc59ad74 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 14 Nov 2019 21:49:24 +0100 Subject: [PATCH 360/407] Take a normal closure, not an async one --- src/stream/successors.rs | 51 ++++++++++------------------------------ 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index e86512bf..d5840eec 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,9 +1,8 @@ use std::pin::Pin; use std::mem; -use crate::future::Future; use crate::stream::Stream; -use crate::task::{Context, Poll, ready}; +use crate::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -18,11 +17,7 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |&val| { -/// async move { -/// Some(val + 1) -/// } -/// }); +/// let s = stream::successors(Some(22), |&val| Some(val + 1) ); /// /// pin_utils::pin_mut!(s); /// assert_eq!(s.next().await, Some(22)); @@ -30,30 +25,18 @@ use pin_project_lite::pin_project; /// assert_eq!(s.next().await, Some(24)); /// assert_eq!(s.next().await, Some(25)); /// -/// -///let never = stream::successors(None, |_| { -/// async move { -/// Some(1) -/// } -/// }); -/// -/// pin_utils::pin_mut!(never); -/// assert_eq!(never.next().await, None); -/// assert_eq!(never.next().await, None); /// # /// # }) } /// /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub fn successors(first: Option, succ: F) -> Successors +pub fn successors(first: Option, succ: F) -> Successors where - F: FnMut(&T) -> Fut, - Fut: Future>, + F: FnMut(&T) -> Option, { Successors { - succ: succ, - future: None, + succ, slot: first, } } @@ -68,39 +51,29 @@ pin_project! { #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] - pub struct Successors + pub struct Successors where - Fut: Future>, + F: FnMut(&T) -> Option { succ: F, - #[pin] - future: Option, slot: Option, } } -impl Stream for Successors +impl Stream for Successors where - Fut: Future>, - F: FnMut(&T) -> Fut, + F: FnMut(&T) -> Option, { type Item = T; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut this = self.project(); + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let this = self.project(); if this.slot.is_none() { return Poll::Ready(None); } - if this.future.is_none() { - let fut = (this.succ)(this.slot.as_ref().unwrap()); - this.future.set(Some(fut)); - } - - let mut next = ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - - this.future.set(None); + let mut next = (this.succ)(&this.slot.as_ref().unwrap()); // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration mem::swap(this.slot, &mut next); From 31f129ebe7a8f3c43b596f848934c586c7a131e8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 22:37:04 +0100 Subject: [PATCH 361/407] backlink channel types Signed-off-by: Yoshua Wuyts --- src/sync/channel.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sync/channel.rs b/src/sync/channel.rs index 392c8511..2647f650 100644 --- a/src/sync/channel.rs +++ b/src/sync/channel.rs @@ -71,6 +71,11 @@ pub fn channel(cap: usize) -> (Sender, Receiver) { /// The sending side of a channel. /// +/// This struct is created by the [`channel`] function. See its +/// documentation for more. +/// +/// [`channel`]: fn.channel.html +/// /// # Examples /// /// ``` @@ -298,8 +303,11 @@ impl fmt::Debug for Sender { /// The receiving side of a channel. /// -/// This type implements the [`Stream`] trait, which means it can act as an asynchronous iterator. +/// This type receives messages by calling `recv`. But it also implements the [`Stream`] trait, +/// which means it can act as an asynchronous iterator. This struct is created by the [`channel`] +/// function. See its documentation for more. /// +/// [`channel`]: fn.channel.html /// [`Stream`]: ../stream/trait.Stream.html /// /// # Examples From 30ff7b09b64a52e727f498f53f34f62b02de26ca Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 14 Nov 2019 22:45:46 +0100 Subject: [PATCH 362/407] mark Stream::count as unstable Signed-off-by: Yoshua Wuyts --- src/stream/stream/count.rs | 2 ++ src/stream/stream/mod.rs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 221b0f0c..09657cff 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -9,6 +9,8 @@ use crate::task::{Context, Poll}; pin_project! { #[doc(hidden)] #[allow(missing_debug_implementations)] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct CountFuture { #[pin] stream: S, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 51ac857a..281e4d88 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -26,7 +26,6 @@ mod any; mod chain; mod cloned; mod cmp; -mod count; mod copied; mod cycle; mod enumerate; @@ -69,7 +68,6 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; -use count::CountFuture; use cycle::Cycle; use enumerate::Enumerate; use eq::EqFuture; @@ -123,12 +121,14 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; + use count::CountFuture; pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; + mod count; mod merge; mod flatten; mod flat_map; @@ -1911,6 +1911,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn count(self) -> impl Future [CountFuture] where Self: Sized, From 4ef55d4d7bf51cc5d0a97a33b1773cc398da5167 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 15 Nov 2019 09:01:41 +0900 Subject: [PATCH 363/407] Enable CI on master branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99436b72..dac0ff44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: pull_request: push: branches: + - master - staging - trying From de67bf0fd4449e3eec9058238c3e682101f8a18f Mon Sep 17 00:00:00 2001 From: k-nasa Date: Fri, 15 Nov 2019 11:17:39 +0900 Subject: [PATCH 364/407] feat: Add stream by_ref --- src/stream/stream/mod.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d88..2bef88ff 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1404,6 +1404,52 @@ extension_trait! { } } + #[doc = r#" + Borrows an stream, rather than consuming it. + + This is useful to allow applying stream adaptors while still retaining ownership of the original stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let a = vec![1isize, 2, 3]; + + let stream = stream::from_iter(a); + + let sum: isize = stream.take(5).sum().await; + + assert_eq!(sum, 6); + + // if we try to use stream again, it won't work. The following line + // gives "error: use of moved value: `stream` + // assert_eq!(stream.next(), None); + + // let's try that again + let a = vec![1isize, 2, 3]; + + let mut stream = stream::from_iter(a); + + // instead, we add in a .by_ref() + let sum: isize = stream.by_ref().take(2).sum().await; + + assert_eq!(sum, 3); + + // now this is just fine: + assert_eq!(stream.next().await, Some(3)); + assert_eq!(stream.next().await, None); + # + # }) } + ``` + "#] + fn by_ref(&mut self) -> &mut Self { + self + } + #[doc = r#" A stream adaptor similar to [`fold`] that holds internal state and produces a new stream. From 11268a80fbc1fe833bee5d022eb99379a6aa937c Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 12:28:03 +0800 Subject: [PATCH 365/407] add stream-partition --- src/stream/stream/mod.rs | 38 +++++++++++++++++++++++ src/stream/stream/partition.rs | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/stream/stream/partition.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d88..1d9ae6e1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -54,6 +54,7 @@ mod ne; mod next; mod nth; mod partial_cmp; +mod partition; mod position; mod scan; mod skip; @@ -91,6 +92,7 @@ use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use partition::PartitionFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; @@ -1308,6 +1310,42 @@ extension_trait! { FoldFuture::new(self, init, f) } + #[doc = r#" + A combinator that applies a function to every element in a stream + creating two collections from it. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let (even, odd): (Vec, Vec) = stream::from_iter(vec![1, 2, 3]) + .partition(|&n| n % 2 == 0).await; + + assert_eq!(even, vec![2]); + assert_eq!(odd, vec![1, 3]); + + # + # }) } + ``` + "#] + fn partition( + self, + f: F, + ) -> impl Future [PartitionFuture] + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + B: Default, + { + PartitionFuture::new(self, f) + } + #[doc = r#" Call a closure on each element of the stream. diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs new file mode 100644 index 00000000..46e957cb --- /dev/null +++ b/src/stream/stream/partition.rs @@ -0,0 +1,57 @@ +use std::future::Future; +use std::pin::Pin; +use std::default::Default; +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[derive(Debug)] + pub struct PartitionFuture { + #[pin] + stream: S, + f: F, + res: Option<(B, B)>, + } +} + +impl PartitionFuture { + pub(super) fn new(stream: S, f: F) -> Self { + Self { + stream, + f, + res: Some((B::default(), B::default())), + } + } +} + +impl Future for PartitionFuture +where + S: Stream + Sized, + F: FnMut(&S::Item) -> bool, + B: Default + Extend, +{ + type Output = (B, B); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some(v) => { + let mut res = this.res.take().unwrap(); + match (this.f)(&v) { + true => res.0.extend(Some(v)), + false => res.1.extend(Some(v)), + }; + + *this.res = Some(res); + } + None => return Poll::Ready(this.res.take().unwrap()), + } + } + } +} From d76b32e6d45e2bd6b5693e0705490332af616fa2 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 14:23:34 +0800 Subject: [PATCH 366/407] make it unstable and fix trait bound --- src/stream/stream/mod.rs | 10 +++++++--- src/stream/stream/partition.rs | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 1d9ae6e1..672a0855 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -54,7 +54,6 @@ mod ne; mod next; mod nth; mod partial_cmp; -mod partition; mod position; mod scan; mod skip; @@ -92,7 +91,6 @@ use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; -use partition::PartitionFuture; use position::PositionFuture; use try_fold::TryFoldFuture; use try_for_each::TryForEachFuture; @@ -122,8 +120,11 @@ cfg_unstable! { use crate::stream::into_stream::IntoStream; use crate::stream::{FromStream, Product, Sum}; + use crate::stream::Extend; use count::CountFuture; + use partition::PartitionFuture; + pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; @@ -134,6 +135,7 @@ cfg_unstable! { mod merge; mod flatten; mod flat_map; + mod partition; mod timeout; mod throttle; } @@ -1334,6 +1336,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn partition( self, f: F, @@ -1341,7 +1345,7 @@ extension_trait! { where Self: Sized, F: FnMut(&Self::Item) -> bool, - B: Default, + B: Default + Extend, { PartitionFuture::new(self, f) } diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index 46e957cb..ba4938cf 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -8,6 +8,9 @@ use crate::task::{Context, Poll}; pin_project! { #[derive(Debug)] + #[allow(missing_debug_implementations)] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct PartitionFuture { #[pin] stream: S, From 76ec9c45638231127fd63e7279bd3c1be1430cfd Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Fri, 15 Nov 2019 14:33:34 +0800 Subject: [PATCH 367/407] update doc url --- docs/src/overview/std-and-library-futures.md | 6 +++--- src/io/buf_read/mod.rs | 2 +- src/io/read/mod.rs | 2 +- src/io/seek/mod.rs | 2 +- src/io/write/mod.rs | 2 +- src/net/tcp/stream.rs | 6 +++--- src/stream/stream/mod.rs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md index 5c5f96f5..9b4801ed 100644 --- a/docs/src/overview/std-and-library-futures.md +++ b/docs/src/overview/std-and-library-futures.md @@ -4,11 +4,11 @@ Rust has two kinds of types commonly referred to as `Future`: - the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html). -- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`. +- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html). -The future defined in the [futures-rs](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`. +The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`. -It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. +It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`. diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 45c5f28c..d919a782 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -34,7 +34,7 @@ extension_trait! { [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html [`futures::io::AsyncBufRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html + https://docs.rs/futures/0.3/futures/io/trait.AsyncBufRead.html [provided methods]: #provided-methods [`BufReadExt`]: ../io/prelude/trait.BufReadExt.html [prelude]: ../prelude/index.html diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 56f63235..0d7f4dcc 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -40,7 +40,7 @@ extension_trait! { [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html [`futures::io::AsyncRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html + https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html [`poll_read`]: #tymethod.poll_read [`poll_read_vectored`]: #method.poll_read_vectored [`ReadExt`]: ../io/prelude/trait.ReadExt.html diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index 7dc30aee..e97cabe7 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -27,7 +27,7 @@ extension_trait! { [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html [`futures::io::AsyncSeek`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html + https://docs.rs/futures/0.3/futures/stream/trait.Stream.html [provided methods]: #provided-methods [`SeekExt`]: ../io/prelude/trait.SeekExt.html [prelude]: ../prelude/index.html diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index eb114344..0ed91dda 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -35,7 +35,7 @@ extension_trait! { [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html [`futures::io::AsyncWrite`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html + https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html [`poll_write`]: #tymethod.poll_write [`poll_write_vectored`]: #method.poll_write_vectored [`poll_flush`]: #tymethod.poll_flush diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 13a1752f..1da9c7c2 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -22,9 +22,9 @@ use crate::task::{spawn_blocking, Context, Poll}; /// [`connect`]: struct.TcpStream.html#method.connect /// [accepting]: struct.TcpListener.html#method.accept /// [listener]: struct.TcpListener.html -/// [`AsyncRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html -/// [`AsyncWrite`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html -/// [`futures::io`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/index.html +/// [`AsyncRead`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html +/// [`futures::io`]: https://docs.rs/futures/0.3/futures/io/index.html /// [`shutdown`]: struct.TcpStream.html#method.shutdown /// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html /// diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 281e4d88..de5eb38e 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -157,7 +157,7 @@ extension_trait! { [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html [`futures::stream::Stream`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html + https://docs.rs/futures/0.3/futures/stream/trait.Stream.html [provided methods]: #provided-methods [`StreamExt`]: ../prelude/trait.StreamExt.html [prelude]: ../prelude/index.html From 74caed2d4bcf6097c798d96d424c8d217fd520ce Mon Sep 17 00:00:00 2001 From: yjh Date: Fri, 15 Nov 2019 18:22:06 +0800 Subject: [PATCH 368/407] Update src/io/seek/mod.rs Co-Authored-By: Taiki Endo --- src/io/seek/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index e97cabe7..f565ca46 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -27,7 +27,7 @@ extension_trait! { [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html [`futures::io::AsyncSeek`]: - https://docs.rs/futures/0.3/futures/stream/trait.Stream.html + https://docs.rs/futures/0.3/futures/io/trait.AsyncSeek.html [provided methods]: #provided-methods [`SeekExt`]: ../io/prelude/trait.SeekExt.html [prelude]: ../prelude/index.html From 31cf932d808bdfb3cbdf024968b333f23d510547 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 00:24:59 +0900 Subject: [PATCH 369/407] wip: Add stream unzip --- src/stream/stream/mod.rs | 13 ++++++++++ src/stream/stream/unzip.rs | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/stream/stream/unzip.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 89385837..900bde3a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -130,6 +130,7 @@ cfg_unstable! { pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; + pub use unzip::UnzipFuture; mod count; mod merge; @@ -138,6 +139,7 @@ cfg_unstable! { mod partition; mod timeout; mod throttle; + mod unzip; } extension_trait! { @@ -1717,6 +1719,17 @@ extension_trait! { Zip::new(self, other) } + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn unzip(self) -> impl Future [UnzipFuture] + where + FromA: Default + Extend, + FromB: Default + Extend, + Self: Stream + Sized, + { + UnzipFuture::new(self) + } + #[doc = r#" Transforms a stream into a collection. diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs new file mode 100644 index 00000000..ef1ff3a0 --- /dev/null +++ b/src/stream/stream/unzip.rs @@ -0,0 +1,53 @@ +use std::future::Future; +use std::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub struct UnzipFuture { + #[pin] + stream: S, + res: (FromA, FromB), + } +} + +impl UnzipFuture +where + FromA: Default, + FromB: Default, +{ + pub(super) fn new(stream: S) -> Self { + UnzipFuture { + stream, + res: (FromA::default(), FromB::default()), + } + } +} + +impl Future for UnzipFuture +where + S: Stream, + FromA: Default + Extend + Copy, + FromB: Default + Extend + Copy, +{ + type Output = (FromA, FromB); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some((a, b)) => { + this.res.0.extend(Some(a)); + this.res.1.extend(Some(b)); + Poll::Pending + } + None => Poll::Ready(*this.res), + } + } +} From df92c633375f43d319d01792476e073df21b2c21 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 00:29:54 +0900 Subject: [PATCH 370/407] fix: Add unstable features --- src/stream/stream/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2bef88ff..bc2482d3 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1446,6 +1446,8 @@ extension_trait! { # }) } ``` "#] + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn by_ref(&mut self) -> &mut Self { self } From 3564be9c0ce12ba3cebcd77bd9f31e51af6807d8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 16:58:33 +0100 Subject: [PATCH 371/407] update futures-timer dep Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e9207395..b8d24dfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ crossbeam-deque = { version = "0.7.1", optional = true } crossbeam-utils = { version = "0.6.6", optional = true } futures-core = { version = "0.3.0", optional = true } futures-io = { version = "0.3.0", optional = true } -futures-timer = { version = "1.0.2", optional = true } +futures-timer = { version = "2.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.2.1", optional = true } From 8779c04dc7d0dd1ea8ede5b03fe531931219e2a1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 16:59:58 +0100 Subject: [PATCH 372/407] upgrade all deps Signed-off-by: Yoshua Wuyts --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8d24dfa..7ffaae3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,33 +50,33 @@ std = [ ] [dependencies] -async-attributes = { version = "1.1.0", optional = true } +async-attributes = { version = "1.1.1", optional = true } async-macros = { version = "2.0.0", optional = true } async-task = { version = "1.0.0", optional = true } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } -crossbeam-channel = { version = "0.3.9", optional = true } -crossbeam-deque = { version = "0.7.1", optional = true } -crossbeam-utils = { version = "0.6.6", optional = true } -futures-core = { version = "0.3.0", optional = true } -futures-io = { version = "0.3.0", optional = true } +crossbeam-channel = { version = "0.4.0", optional = true } +crossbeam-deque = { version = "0.7.2", optional = true } +crossbeam-utils = { version = "0.7.0", optional = true } +futures-core = { version = "0.3.1", optional = true } +futures-io = { version = "0.3.1", optional = true } futures-timer = { version = "2.0.2", optional = true } kv-log-macro = { version = "1.0.4", optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true } memchr = { version = "2.2.1", optional = true } mio = { version = "0.6.19", optional = true } mio-uds = { version = "0.6.7", optional = true } -num_cpus = { version = "1.10.1", optional = true } +num_cpus = { version = "1.11.1", optional = true } once_cell = { version = "1.2.0", optional = true } -pin-project-lite = { version = "0.1", optional = true } +pin-project-lite = { version = "0.1.1", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true } slab = { version = "0.4.2", optional = true } [dev-dependencies] -femme = "1.2.0" +femme = "1.3.0" rand = "0.7.2" surf = "1.0.3" tempdir = "0.3.7" -futures = "0.3.0" +futures = "0.3.1" [[test]] name = "stream" From 603b3c508559129bf2f866291511e35db0a93c4d Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:16:35 +0900 Subject: [PATCH 373/407] add: Add stream unzip --- src/stream/stream/mod.rs | 2 +- src/stream/stream/unzip.rs | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 900bde3a..04aa4d68 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -124,13 +124,13 @@ cfg_unstable! { use count::CountFuture; use partition::PartitionFuture; + use unzip::UnzipFuture; pub use merge::Merge; pub use flatten::Flatten; pub use flat_map::FlatMap; pub use timeout::{TimeoutError, Timeout}; pub use throttle::Throttle; - pub use unzip::UnzipFuture; mod count; mod merge; diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index ef1ff3a0..4f5dfa19 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -7,12 +7,13 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { + #[derive(Clone, Debug)] #[cfg(all(feature = "default", feature = "unstable"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub struct UnzipFuture { + pub struct UnzipFuture { #[pin] stream: S, - res: (FromA, FromB), + res: Option<(FromA, FromB)>, } } @@ -24,7 +25,7 @@ where pub(super) fn new(stream: S) -> Self { UnzipFuture { stream, - res: (FromA::default(), FromB::default()), + res: Some((FromA::default(), FromB::default())), } } } @@ -32,22 +33,27 @@ where impl Future for UnzipFuture where S: Stream, - FromA: Default + Extend + Copy, - FromB: Default + Extend + Copy, + FromA: Default + Extend, + FromB: Default + Extend, { type Output = (FromA, FromB); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); - let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - match next { - Some((a, b)) => { - this.res.0.extend(Some(a)); - this.res.1.extend(Some(b)); - Poll::Pending + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some((a, b)) => { + let mut res = this.res.take().unwrap(); + res.0.extend(Some(a)); + res.1.extend(Some(b)); + + *this.res = Some(res); + } + None => return Poll::Ready(this.res.take().unwrap()), } - None => Poll::Ready(*this.res), } } } From 91ee4c7b9fddcf0c245d9527b75fec2642846702 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:16:48 +0900 Subject: [PATCH 374/407] doc: Add stream unzip doc --- src/stream/stream/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 04aa4d68..e0e9b7e4 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1719,6 +1719,33 @@ extension_trait! { Zip::new(self, other) } + #[doc = r#" + Converts an stream of pairs into a pair of containers. + + unzip() consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. + + This function is, in some sense, the opposite of [`zip`]. + + [`zip`]: trait.Stream.html#method.zip + + # Example + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![(1,2), (3,4)]); + + let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; + + assert_eq!(left, [1, 3]); + assert_eq!(right, [2, 4]); + # + # }) } + ``` + "#] #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn unzip(self) -> impl Future [UnzipFuture] From 6cbf48f12d1ffa58b97256686a95005f57737fec Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:29:16 +0900 Subject: [PATCH 375/407] fix clippy warn --- src/future/future/join.rs | 14 ++++++-------- src/stream/stream/last.rs | 2 +- src/stream/stream/partition.rs | 15 ++++++++------- src/task/executor/pool.rs | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/future/future/join.rs b/src/future/future/join.rs index 90ea3237..5cfbd99a 100644 --- a/src/future/future/join.rs +++ b/src/future/future/join.rs @@ -45,16 +45,14 @@ where let mut left = this.left; let mut right = this.right; - if Future::poll(Pin::new(&mut left), cx).is_ready() { - if right.as_ref().output().is_some() { - return Poll::Ready((left.take().unwrap(), right.take().unwrap())); - } + let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready(); + if is_left_ready && right.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); } - if Future::poll(Pin::new(&mut right), cx).is_ready() { - if left.as_ref().output().is_some() { - return Poll::Ready((left.take().unwrap(), right.take().unwrap())); - } + let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready(); + if is_right_ready && left.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); } Poll::Pending diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index 188da3c8..60f88068 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index ba4938cf..077d4ec2 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -1,14 +1,13 @@ +use pin_project_lite::pin_project; +use std::default::Default; use std::future::Future; use std::pin::Pin; -use std::default::Default; -use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; pin_project! { #[derive(Debug)] - #[allow(missing_debug_implementations)] #[cfg(all(feature = "default", feature = "unstable"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub struct PartitionFuture { @@ -46,10 +45,12 @@ where match next { Some(v) => { let mut res = this.res.take().unwrap(); - match (this.f)(&v) { - true => res.0.extend(Some(v)), - false => res.1.extend(Some(v)), - }; + + if (this.f)(&v) { + res.0.extend(Some(v)) + } else { + res.1.extend(Some(v)) + } *this.res = Some(res); } diff --git a/src/task/executor/pool.rs b/src/task/executor/pool.rs index 08694dd4..5249b3d9 100644 --- a/src/task/executor/pool.rs +++ b/src/task/executor/pool.rs @@ -43,7 +43,7 @@ static POOL: Lazy = Lazy::new(|| { .name("async-std/executor".to_string()) .spawn(|| { let _ = PROCESSOR.with(|p| p.set(proc)); - abort_on_panic(|| main_loop()); + abort_on_panic(main_loop); }) .expect("cannot start a thread driving tasks"); } From a05b6a38104c26a1ffd2eb7ed6e4e32912c856fb Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:36:53 +0900 Subject: [PATCH 376/407] fix: mutable ref --- src/stream/stream/unzip.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs index 4f5dfa19..e0832ff7 100644 --- a/src/stream/stream/unzip.rs +++ b/src/stream/stream/unzip.rs @@ -46,11 +46,9 @@ where match next { Some((a, b)) => { - let mut res = this.res.take().unwrap(); + let res = this.res.as_mut().unwrap(); res.0.extend(Some(a)); res.1.extend(Some(b)); - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From a69b3a8a9e215c689bfde3e07d5b50fe2ecc08e7 Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sat, 16 Nov 2019 00:54:50 +0800 Subject: [PATCH 377/407] use `as_mut` for stream-partition --- src/stream/stream/partition.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index ba4938cf..73744563 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -45,13 +45,11 @@ where match next { Some(v) => { - let mut res = this.res.take().unwrap(); + let res = this.res.as_mut().unwrap(); match (this.f)(&v) { true => res.0.extend(Some(v)), false => res.1.extend(Some(v)), }; - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From 7d616c695d627aa9465a8c8c17b7cadc3f3cb886 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Sat, 16 Nov 2019 01:54:09 +0900 Subject: [PATCH 378/407] refactor: change to as_mut --- src/stream/stream/partition.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs index 077d4ec2..74231ab7 100644 --- a/src/stream/stream/partition.rs +++ b/src/stream/stream/partition.rs @@ -44,15 +44,12 @@ where match next { Some(v) => { - let mut res = this.res.take().unwrap(); - + let res = this.res.as_mut().unwrap(); if (this.f)(&v) { res.0.extend(Some(v)) } else { res.1.extend(Some(v)) } - - *this.res = Some(res); } None => return Poll::Ready(this.res.take().unwrap()), } From d68dc659b254569bae4bf4694d84ff5afaa46b1b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 15 Nov 2019 18:08:00 +0100 Subject: [PATCH 379/407] remove pin_mut from successors test Signed-off-by: Yoshua Wuyts --- src/stream/successors.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/stream/successors.rs b/src/stream/successors.rs index d5840eec..4421564e 100644 --- a/src/stream/successors.rs +++ b/src/stream/successors.rs @@ -1,5 +1,5 @@ -use std::pin::Pin; use std::mem; +use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -17,9 +17,8 @@ use pin_project_lite::pin_project; /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::successors(Some(22), |&val| Some(val + 1) ); +/// let mut s = stream::successors(Some(22), |&val| Some(val + 1)); /// -/// pin_utils::pin_mut!(s); /// assert_eq!(s.next().await, Some(22)); /// assert_eq!(s.next().await, Some(23)); /// assert_eq!(s.next().await, Some(24)); @@ -27,7 +26,6 @@ use pin_project_lite::pin_project; /// /// # /// # }) } -/// /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -35,10 +33,7 @@ pub fn successors(first: Option, succ: F) -> Successors where F: FnMut(&T) -> Option, { - Successors { - succ, - slot: first, - } + Successors { succ, slot: first } } pin_project! { From 223fcc30eece3944dbff8ce0f61c7335b490451c Mon Sep 17 00:00:00 2001 From: yjhmelody <465402634@qq.com> Date: Sun, 17 Nov 2019 00:35:56 +0800 Subject: [PATCH 380/407] fix code style for stream --- src/stream/stream/all.rs | 11 +++++++++++ src/stream/stream/any.rs | 11 +++++++++++ src/stream/stream/chain.rs | 2 +- src/stream/stream/cmp.rs | 2 +- src/stream/stream/copied.rs | 2 +- src/stream/stream/count.rs | 2 +- src/stream/stream/cycle.rs | 4 ++-- src/stream/stream/enumerate.rs | 2 +- src/stream/stream/eq.rs | 2 +- src/stream/stream/filter.rs | 2 +- src/stream/stream/filter_map.rs | 2 +- src/stream/stream/find.rs | 2 +- src/stream/stream/find_map.rs | 2 +- src/stream/stream/flat_map.rs | 4 ++-- src/stream/stream/flatten.rs | 4 ++-- src/stream/stream/fold.rs | 2 +- src/stream/stream/for_each.rs | 2 +- src/stream/stream/fuse.rs | 9 +++++++++ src/stream/stream/ge.rs | 2 +- src/stream/stream/gt.rs | 2 +- src/stream/stream/inspect.rs | 2 +- src/stream/stream/last.rs | 2 +- src/stream/stream/le.rs | 2 +- src/stream/stream/lt.rs | 2 +- src/stream/stream/map.rs | 2 +- src/stream/stream/max_by.rs | 2 +- src/stream/stream/min_by.rs | 2 +- src/stream/stream/min_by_key.rs | 2 +- src/stream/stream/mod.rs | 27 +++++---------------------- src/stream/stream/nth.rs | 2 +- src/stream/stream/partial_cmp.rs | 2 +- src/stream/stream/position.rs | 2 +- src/stream/stream/skip.rs | 2 +- src/stream/stream/skip_while.rs | 2 +- src/stream/stream/step_by.rs | 2 +- src/stream/stream/take.rs | 9 +++++++++ src/stream/stream/take_while.rs | 2 +- src/stream/stream/throttle.rs | 2 +- src/stream/stream/timeout.rs | 4 ++-- src/stream/stream/try_fold.rs | 2 +- src/stream/stream/try_for_each.rs | 2 +- src/stream/stream/zip.rs | 2 +- 42 files changed, 86 insertions(+), 63 deletions(-) diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 7b84abe3..5adb68f3 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -14,6 +14,17 @@ pub struct AllFuture<'a, S, F, T> { pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AllFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + result: true, // the default if the empty stream + _marker: PhantomData, + } + } +} + impl Unpin for AllFuture<'_, S, F, T> {} impl Future for AllFuture<'_, S, F, S::Item> diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index c7fc7665..d6853a1c 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -14,6 +14,17 @@ pub struct AnyFuture<'a, S, F, T> { pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AnyFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + result: false, // the default if the empty stream + _marker: PhantomData, + } + } +} + impl Unpin for AnyFuture<'_, S, F, T> {} impl Future for AnyFuture<'_, S, F, S::Item> diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index f6d9cf64..909fc19b 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -25,7 +25,7 @@ pin_project! { impl Chain { pub(super) fn new(first: S, second: U) -> Self { - Chain { + Self { first: first.fuse(), second: second.fuse(), } diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index 19437e70..2be0c1a3 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -26,7 +26,7 @@ pin_project! { impl CmpFuture { pub(super) fn new(l: L, r: R) -> Self { - CmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs index e3c8367b..651c31b6 100644 --- a/src/stream/stream/copied.rs +++ b/src/stream/stream/copied.rs @@ -14,7 +14,7 @@ pin_project! { impl Copied { pub(super) fn new(stream: S) -> Self { - Copied { stream } + Self { stream } } } diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs index 09657cff..ebf2a2f1 100644 --- a/src/stream/stream/count.rs +++ b/src/stream/stream/count.rs @@ -20,7 +20,7 @@ pin_project! { impl CountFuture { pub(crate) fn new(stream: S) -> Self { - CountFuture { stream, count: 0 } + Self { stream, count: 0 } } } diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs index 7f01a61d..5f8eaa20 100644 --- a/src/stream/stream/cycle.rs +++ b/src/stream/stream/cycle.rs @@ -15,8 +15,8 @@ impl Cycle where S: Stream + Clone, { - pub fn new(source: S) -> Cycle { - Cycle { + pub(crate) fn new(source: S) -> Self { + Self { orig: source.clone(), source: ManuallyDrop::new(source), } diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index a758010e..c4a37d6e 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -16,7 +16,7 @@ pin_project! { impl Enumerate { pub(super) fn new(stream: S) -> Self { - Enumerate { stream, i: 0 } + Self { stream, i: 0 } } } diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs index addcfa2e..58ccc90e 100644 --- a/src/stream/stream/eq.rs +++ b/src/stream/stream/eq.rs @@ -26,7 +26,7 @@ where L::Item: PartialEq, { pub(super) fn new(l: L, r: R) -> Self { - EqFuture { + Self { l: l.fuse(), r: r.fuse(), } diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 594b0949..00344b0e 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -23,7 +23,7 @@ pin_project! { impl Filter { pub(super) fn new(stream: S, predicate: P) -> Self { - Filter { + Self { stream, predicate, } diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index e110f514..3cd1e47a 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -16,7 +16,7 @@ pin_project! { impl FilterMap { pub(crate) fn new(stream: S, f: F) -> Self { - FilterMap { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index 0c5ad62f..4a0749b1 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -13,7 +13,7 @@ pub struct FindFuture<'a, S, P> { impl<'a, S, P> FindFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, p: P) -> Self { - FindFuture { stream, p } + Self { stream, p } } } diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index b10bd9ca..c7949439 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -13,7 +13,7 @@ pub struct FindMapFuture<'a, S, F> { impl<'a, S, F> FindMapFuture<'a, S, F> { pub(super) fn new(stream: &'a mut S, f: F) -> Self { - FindMapFuture { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs index ab45c9c7..6c828c92 100644 --- a/src/stream/stream/flat_map.rs +++ b/src/stream/stream/flat_map.rs @@ -30,8 +30,8 @@ where U: IntoStream, F: FnMut(S::Item) -> U, { - pub(super) fn new(stream: S, f: F) -> FlatMap { - FlatMap { + pub(super) fn new(stream: S, f: F) -> Self { + Self { stream: stream.map(f), inner_stream: None, } diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs index edaffd04..1d6fcae6 100644 --- a/src/stream/stream/flatten.rs +++ b/src/stream/stream/flatten.rs @@ -32,8 +32,8 @@ where S: Stream, S::Item: IntoStream, { - pub(super) fn new(stream: S) -> Flatten { - Flatten { + pub(super) fn new(stream: S) -> Self { + Self { stream, inner_stream: None, } diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index c4da5915..a346eb67 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -18,7 +18,7 @@ pin_project! { impl FoldFuture { pub(super) fn new(stream: S, init: B, f: F) -> Self { - FoldFuture { + Self { stream, f, acc: Some(init), diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 01833fd9..dce5cdae 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -18,7 +18,7 @@ pin_project! { impl ForEachFuture { pub(super) fn new(stream: S, f: F) -> Self { - ForEachFuture { + Self { stream, f, } diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 6297bef7..c7449c27 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -21,6 +21,15 @@ pin_project! { } } +impl Fuse { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + done: false, + } + } +} + impl Stream for Fuse { type Item = S::Item; diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index f9012697..67b20bed 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - GeFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 81e95a1a..1c121891 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - GtFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index acf22465..bb39662b 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -23,7 +23,7 @@ pin_project! { impl Inspect { pub(super) fn new(stream: S, f: F) -> Self { - Inspect { + Self { stream, f, } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index 188da3c8..3e0a0b38 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -18,7 +18,7 @@ pin_project! { impl LastFuture { pub(crate) fn new(stream: S) -> Self { - LastFuture { stream, last: None } + Self { stream, last: None } } } diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 35b04bfb..7b86161c 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - LeFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index 86c31295..100a0034 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -25,7 +25,7 @@ where L::Item: PartialOrd, { pub(super) fn new(l: L, r: R) -> Self { - LtFuture { + Self { partial_cmp: l.partial_cmp(r), } } diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 7accb6fc..8e074a75 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -17,7 +17,7 @@ pin_project! { impl Map { pub(crate) fn new(stream: S, f: F) -> Self { - Map { + Self { stream, f, } diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs index cfba9b93..36b876bb 100644 --- a/src/stream/stream/max_by.rs +++ b/src/stream/stream/max_by.rs @@ -20,7 +20,7 @@ pin_project! { impl MaxByFuture { pub(super) fn new(stream: S, compare: F) -> Self { - MaxByFuture { + Self { stream, compare, max: None, diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index fc332c26..e35719e6 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -20,7 +20,7 @@ pin_project! { impl MinByFuture { pub(super) fn new(stream: S, compare: F) -> Self { - MinByFuture { + Self { stream, compare, min: None, diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 8179fb31..c515dad7 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -20,7 +20,7 @@ pin_project! { impl MinByKeyFuture { pub(super) fn new(stream: S, key_by: K) -> Self { - MinByKeyFuture { + Self { stream, min: None, key_by, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f8765762..220e791a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -111,7 +111,6 @@ pub use take_while::TakeWhile; pub use zip::Zip; use std::cmp::Ordering; -use std::marker::PhantomData; cfg_unstable! { use std::future::Future; @@ -288,10 +287,7 @@ extension_trait! { where Self: Sized, { - Take { - stream: self, - remaining: n, - } + Take::new(self, n) } #[doc = r#" @@ -714,10 +710,7 @@ extension_trait! { where Self: Sized, { - Fuse { - stream: self, - done: false, - } + Fuse::new(self) } #[doc = r#" @@ -1193,12 +1186,7 @@ extension_trait! { Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { - AllFuture { - stream: self, - result: true, // the default if the empty stream - _marker: PhantomData, - f, - } + AllFuture::new(self, f) } #[doc = r#" @@ -1438,12 +1426,7 @@ extension_trait! { Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { - AnyFuture { - stream: self, - result: false, // the default if the empty stream - _marker: PhantomData, - f, - } + AnyFuture::new(self, f) } #[doc = r#" @@ -1468,7 +1451,7 @@ extension_trait! { assert_eq!(sum, 6); // if we try to use stream again, it won't work. The following line - // gives "error: use of moved value: `stream` + // gives error: use of moved value: `stream` // assert_eq!(stream.next(), None); // let's try that again diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index 711287a3..267bd40a 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -15,7 +15,7 @@ impl Unpin for NthFuture<'_, S> {} impl<'a, S> NthFuture<'a, S> { pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { - NthFuture { stream, n } + Self { stream, n } } } diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index 6bc28f78..85587c99 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -26,7 +26,7 @@ pin_project! { impl PartialCmpFuture { pub(super) fn new(l: L, r: R) -> Self { - PartialCmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs index 5a51d7a7..df60eaae 100644 --- a/src/stream/stream/position.rs +++ b/src/stream/stream/position.rs @@ -16,7 +16,7 @@ impl<'a, S, P> Unpin for PositionFuture<'a, S, P> {} impl<'a, S, P> PositionFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, predicate: P) -> Self { - PositionFuture { + Self { stream, predicate, index: 0, diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index cc2ba905..bcff50d6 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -23,7 +23,7 @@ pin_project! { impl Skip { pub(crate) fn new(stream: S, n: usize) -> Self { - Skip { stream, n } + Self { stream, n } } } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 5cb273ee..23347132 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -23,7 +23,7 @@ pin_project! { impl SkipWhile { pub(crate) fn new(stream: S, predicate: P) -> Self { - SkipWhile { + Self { stream, predicate: Some(predicate), } diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index 13020982..2149cdad 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -24,7 +24,7 @@ pin_project! { impl StepBy { pub(crate) fn new(stream: S, step: usize) -> Self { - StepBy { + Self { stream, step: step.checked_sub(1).unwrap(), i: 0, diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index e680b42b..8c852276 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -21,6 +21,15 @@ pin_project! { } } +impl Take { + pub(super) fn new(stream: S, remaining: usize) -> Self { + Self { + stream, + remaining, + } + } +} + impl Stream for Take { type Item = S::Item; diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 08b5a86c..2ba8490e 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -23,7 +23,7 @@ pin_project! { impl TakeWhile { pub(super) fn new(stream: S, predicate: P) -> Self { - TakeWhile { + Self { stream, predicate, } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs index 8896899e..b2480bbd 100644 --- a/src/stream/stream/throttle.rs +++ b/src/stream/stream/throttle.rs @@ -31,7 +31,7 @@ pin_project! { impl Throttle { pub(super) fn new(stream: S, duration: Duration) -> Self { - Throttle { + Self { stream, duration, blocked: false, diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs index 560a0e41..f580360d 100644 --- a/src/stream/stream/timeout.rs +++ b/src/stream/stream/timeout.rs @@ -22,10 +22,10 @@ pin_project! { } impl Timeout { - pub(crate) fn new(stream: S, dur: Duration) -> Timeout { + pub(crate) fn new(stream: S, dur: Duration) -> Self { let delay = Delay::new(dur); - Timeout { stream, delay } + Self { stream, delay } } } diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index efb9e339..3b92d95a 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -16,7 +16,7 @@ impl<'a, S, F, T> Unpin for TryFoldFuture<'a, S, F, T> {} impl<'a, S, F, T> TryFoldFuture<'a, S, F, T> { pub(super) fn new(stream: &'a mut S, init: T, f: F) -> Self { - TryFoldFuture { + Self { stream, f, acc: Some(init), diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index 30e31850..86f1674a 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -15,7 +15,7 @@ impl<'a, S, F> Unpin for TryForEachFuture<'a, S, F> {} impl<'a, S, F> TryForEachFuture<'a, S, F> { pub(crate) fn new(stream: &'a mut S, f: F) -> Self { - TryForEachFuture { stream, f } + Self { stream, f } } } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index f57d7359..597691b4 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -34,7 +34,7 @@ impl fmt::Debug for Zip { impl Zip { pub(crate) fn new(first: A, second: B) -> Self { - Zip { + Self { item_slot: None, first, second, From 8ce3e78952bd8f1fcbf6098951fa4e3937018c8b Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sun, 17 Nov 2019 21:54:44 +0100 Subject: [PATCH 381/407] verbose errors feature This adds a new "verbose-errors" feature flag to async-std that enables wrapping certain errors in structures with more context. As an example, we use it in `fs::File::{open,create}` to add the given path to the error message (something that is lacking in std to annoyance of many). --- .github/workflows/ci.yml | 6 +++++ Cargo.toml | 1 + src/fs/file.rs | 13 ++++++++-- src/io/mod.rs | 1 + src/io/utils.rs | 51 ++++++++++++++++++++++++++++++++++++++++ src/utils.rs | 8 +++++++ tests/verbose_errors.rs | 20 ++++++++++++++++ 7 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/io/utils.rs create mode 100644 tests/verbose_errors.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99436b72..5d5639c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,12 @@ jobs: command: test args: --all --features unstable + - name: tests with verbose errors + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --features 'unstable verbose-errors' + check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 7ffaae3a..7886bcfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ std = [ "pin-utils", "slab", ] +verbose-errors = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 8bc6c2ce..5186e96f 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; +use crate::utils::VerboseErrorExt; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; @@ -112,7 +113,11 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = spawn_blocking(move || std::fs::File::open(&path)).await?; + let file = spawn_blocking(move || { + std::fs::File::open(&path) + .verbose_context(|| format!("Could not open {}", path.display())) + }) + .await?; Ok(File::new(file, true)) } @@ -147,7 +152,11 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = spawn_blocking(move || std::fs::File::create(&path)).await?; + let file = spawn_blocking(move || { + std::fs::File::create(&path) + .verbose_context(|| format!("Could not create {}", path.display())) + }) + .await?; Ok(File::new(file, true)) } diff --git a/src/io/mod.rs b/src/io/mod.rs index 4e832305..d9660a72 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -291,6 +291,7 @@ cfg_std! { pub(crate) mod read; pub(crate) mod seek; pub(crate) mod write; + pub(crate) mod utils; mod buf_reader; mod buf_writer; diff --git a/src/io/utils.rs b/src/io/utils.rs new file mode 100644 index 00000000..ba6285f6 --- /dev/null +++ b/src/io/utils.rs @@ -0,0 +1,51 @@ +use std::{error::Error, fmt, io}; +use crate::utils::VerboseErrorExt; + +/// Wrap `std::io::Error` with additional message +/// +/// *Note* Only active when `verbose-errors` feature is enabled for this crate! +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl VerboseErrorExt for Result { + fn verbose_context(self, message: impl Fn() -> String) -> Self { + if cfg!(feature = "verbose-errors") { + self.map_err(|e| VerboseError::wrap(e, message())) + } else { + self + } + } +} + +#[derive(Debug)] +struct VerboseError { + source: io::Error, + message: String, +} + +impl VerboseError { + fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + VerboseError { + source, + message: message.into(), + }, + ) + } +} + +impl fmt::Display for VerboseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for VerboseError { + fn description(&self) -> &str { + self.source.description() + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) + } +} diff --git a/src/utils.rs b/src/utils.rs index 13dbe37d..00dc7931 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -52,6 +52,14 @@ pub fn random(n: u32) -> u32 { }) } +/// Add additional context to errors +/// +/// *Note for implementors:* The given closure must only be executed when +/// `verbose-errors` feature is enabled for this crate! +pub(crate) trait VerboseErrorExt { + fn verbose_context(self, message: impl Fn() -> String) -> Self; +} + /// Defers evaluation of a block of code until the end of the scope. #[cfg(feature = "default")] #[doc(hidden)] diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs new file mode 100644 index 00000000..3d99ede7 --- /dev/null +++ b/tests/verbose_errors.rs @@ -0,0 +1,20 @@ +#[cfg(feature = "verbose-errors")] +mod verbose_tests { + use async_std::{fs, task}; + + #[test] + fn open_file() { + task::block_on(async { + let non_existing_file = + "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; + let res = fs::File::open(non_existing_file).await; + match res { + Ok(_) => panic!("Found file with random name: We live in a simulation"), + Err(e) => assert_eq!( + "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + &format!("{}", e) + ), + } + }) + } +} From 99ddfb3f9393273e401b94afee84004ae3e284cb Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sun, 17 Nov 2019 23:11:11 +0100 Subject: [PATCH 382/407] Wrap code more clearly in cfg blocks --- src/io/utils.rs | 63 +++++++++++++++++++++++++------------------------ src/utils.rs | 8 ++++++- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/io/utils.rs b/src/io/utils.rs index ba6285f6..ebd22149 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -1,4 +1,3 @@ -use std::{error::Error, fmt, io}; use crate::utils::VerboseErrorExt; /// Wrap `std::io::Error` with additional message @@ -6,46 +5,48 @@ use crate::utils::VerboseErrorExt; /// *Note* Only active when `verbose-errors` feature is enabled for this crate! /// /// Keeps the original error kind and stores the original I/O error as `source`. -impl VerboseErrorExt for Result { +impl VerboseErrorExt for Result { + #[cfg(feature = "verbose-errors")] fn verbose_context(self, message: impl Fn() -> String) -> Self { - if cfg!(feature = "verbose-errors") { - self.map_err(|e| VerboseError::wrap(e, message())) - } else { - self - } + self.map_err(|e| verbose::Error::wrap(e, message())) } } -#[derive(Debug)] -struct VerboseError { - source: io::Error, - message: String, -} +#[cfg(feature = "verbose-errors")] +mod verbose { + use std::{error::Error as StdError, fmt, io}; -impl VerboseError { - fn wrap(source: io::Error, message: impl Into) -> io::Error { - io::Error::new( - source.kind(), - VerboseError { - source, - message: message.into(), - }, - ) + #[derive(Debug)] + pub(crate) struct Error { + source: io::Error, + message: String, } -} -impl fmt::Display for VerboseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) + impl Error { + pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + Error { + source, + message: message.into(), + }, + ) + } } -} -impl Error for VerboseError { - fn description(&self) -> &str { - self.source.description() + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } } - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.source) + impl StdError for Error { + fn description(&self) -> &str { + self.source.description() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } } } diff --git a/src/utils.rs b/src/utils.rs index 00dc7931..ce4a85cc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -56,8 +56,14 @@ pub fn random(n: u32) -> u32 { /// /// *Note for implementors:* The given closure must only be executed when /// `verbose-errors` feature is enabled for this crate! -pub(crate) trait VerboseErrorExt { +pub(crate) trait VerboseErrorExt: Sized { + #[cfg(feature = "verbose-errors")] fn verbose_context(self, message: impl Fn() -> String) -> Self; + + #[cfg(not(feature = "verbose-errors"))] + fn verbose_context(self, _: impl Fn() -> String) -> Self { + self + } } /// Defers evaluation of a block of code until the end of the scope. From 2c9b558d14d9125534953a596e08ed4f6d33bc5d Mon Sep 17 00:00:00 2001 From: hhggit Date: Mon, 18 Nov 2019 10:07:47 +0800 Subject: [PATCH 383/407] add os::windows::symlink_{dir,file} --- src/fs/mod.rs | 2 ++ src/os/windows/fs.rs | 55 +++++++++++++++++++++++++++++++++++++++++++ src/os/windows/mod.rs | 1 + 3 files changed, 58 insertions(+) create mode 100644 src/os/windows/fs.rs diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 4598ec84..5cf086de 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -3,11 +3,13 @@ //! This module is an async version of [`std::fs`]. //! //! [`os::unix::fs`]: ../os/unix/fs/index.html +//! [`os::windows::fs`]: ../os/windows/fs/index.html //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html //! //! # Platform-specific extensions //! //! * Unix: use the [`os::unix::fs`] module. +//! * Windows: use the [`os::windows::fs`] module. //! //! # Examples //! diff --git a/src/os/windows/fs.rs b/src/os/windows/fs.rs new file mode 100644 index 00000000..243f3819 --- /dev/null +++ b/src/os/windows/fs.rs @@ -0,0 +1,55 @@ +//! Windows-specific filesystem extensions. + +use crate::io; +use crate::path::Path; +use crate::task::spawn_blocking; + +/// Creates a new directory symbolic link on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_dir`]. +/// +/// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_dir; +/// +/// symlink_dir("a", "b").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_dir(&src, &dst)).await +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_file`]. +/// +/// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_file; +/// +/// symlink_file("a.txt", "b.txt").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await +} diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index f3350007..5f0bc0e4 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -2,4 +2,5 @@ cfg_std! { pub mod io; + pub mod fs; } From f6829859fef56585498938f99ca2cec731abd1c1 Mon Sep 17 00:00:00 2001 From: Razican Date: Mon, 18 Nov 2019 16:39:21 +0100 Subject: [PATCH 384/407] Fixed deduplication of code --- src/stream/stream/merge.rs | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index b08b586e..bdf7a29f 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -45,25 +45,29 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if utils::random(1) == 1 { - match this.left.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.right.poll_next(cx), - Poll::Pending => match this.right.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } + poll_next_in_order(this.left, this.right, cx) } else { - match this.right.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => this.left.poll_next(cx), - Poll::Pending => match this.left.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, - }, - } + poll_next_in_order(this.right, this.left, cx) } } } + +fn poll_next_in_order( + first: Pin<&mut F>, + second: Pin<&mut S>, + cx: &mut Context<'_>, +) -> Poll> +where + F: Stream, + S: Stream, +{ + match first.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => second.poll_next(cx), + Poll::Pending => match second.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) => Poll::Pending, + Poll::Pending => Poll::Pending, + }, + } +} From d3e7f32a30dee9202f242f35b43e5f3d40429175 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 18 Nov 2019 15:47:45 +0000 Subject: [PATCH 385/407] Macro optimization to reduce compilation times --- src/utils.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 13dbe37d..49e3d993 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -221,6 +221,11 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; + // Optimization: expand `$head` eagerly before starting a new method definition. + (@ext ($($head:tt)*) #[doc = $d:literal] $($tail:tt)*) => { + $($head)* extension_trait!(@ext (#[doc = $d]) $($tail)*); + }; + // Parse the return type in an extension method. (@doc ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); From 65afd41a33c32059fdd42575f8d4199fd98333ee Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 19 Nov 2019 05:04:18 +0100 Subject: [PATCH 386/407] Once doesn't need Unpin bound (#554) --- src/stream/once.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/once.rs b/src/stream/once.rs index a33bd6ac..e4ac682c 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -39,7 +39,7 @@ pin_project! { } } -impl Stream for Once { +impl Stream for Once { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { From c7046432965b374ed7bc03dc35b6e465a706b215 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 18 Nov 2019 23:55:48 +0100 Subject: [PATCH 387/407] Remove verbose-errors cargo feature --- .github/workflows/ci.yml | 6 ---- Cargo.toml | 1 - src/fs/file.rs | 6 ++-- src/io/utils.rs | 66 ++++++++++++++++++---------------------- src/utils.rs | 13 ++------ tests/verbose_errors.rs | 32 +++++++++---------- 6 files changed, 49 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d5639c6..99436b72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,12 +64,6 @@ jobs: command: test args: --all --features unstable - - name: tests with verbose errors - uses: actions-rs/cargo@v1 - with: - command: test - args: --all --features 'unstable verbose-errors' - check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 7886bcfd..7ffaae3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ std = [ "pin-utils", "slab", ] -verbose-errors = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 5186e96f..f8242811 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::utils::VerboseErrorExt; +use crate::utils::Context as _; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; @@ -115,7 +115,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::open(&path) - .verbose_context(|| format!("Could not open {}", path.display())) + .context(|| format!("Could not open {}", path.display())) }) .await?; Ok(File::new(file, true)) @@ -154,7 +154,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::create(&path) - .verbose_context(|| format!("Could not create {}", path.display())) + .context(|| format!("Could not create {}", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/io/utils.rs b/src/io/utils.rs index ebd22149..1b730645 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -1,52 +1,46 @@ -use crate::utils::VerboseErrorExt; +use crate::utils::Context; /// Wrap `std::io::Error` with additional message /// -/// *Note* Only active when `verbose-errors` feature is enabled for this crate! -/// /// Keeps the original error kind and stores the original I/O error as `source`. -impl VerboseErrorExt for Result { - #[cfg(feature = "verbose-errors")] - fn verbose_context(self, message: impl Fn() -> String) -> Self { - self.map_err(|e| verbose::Error::wrap(e, message())) +impl Context for Result { + fn context(self, message: impl Fn() -> String) -> Self { + self.map_err(|e| VerboseError::wrap(e, message())) } } -#[cfg(feature = "verbose-errors")] -mod verbose { - use std::{error::Error as StdError, fmt, io}; +use std::{error::Error as StdError, fmt, io}; - #[derive(Debug)] - pub(crate) struct Error { - source: io::Error, - message: String, - } +#[derive(Debug)] +pub(crate) struct VerboseError { + source: io::Error, + message: String, +} - impl Error { - pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { - io::Error::new( - source.kind(), - Error { - source, - message: message.into(), - }, - ) - } +impl VerboseError { + pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + VerboseError { + source, + message: message.into(), + }, + ) } +} - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) - } +impl fmt::Display for VerboseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) } +} - impl StdError for Error { - fn description(&self) -> &str { - self.source.description() - } +impl StdError for VerboseError { + fn description(&self) -> &str { + self.source.description() + } - fn source(&self) -> Option<&(dyn StdError + 'static)> { - Some(&self.source) - } + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) } } diff --git a/src/utils.rs b/src/utils.rs index ce4a85cc..645dc849 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -53,17 +53,8 @@ pub fn random(n: u32) -> u32 { } /// Add additional context to errors -/// -/// *Note for implementors:* The given closure must only be executed when -/// `verbose-errors` feature is enabled for this crate! -pub(crate) trait VerboseErrorExt: Sized { - #[cfg(feature = "verbose-errors")] - fn verbose_context(self, message: impl Fn() -> String) -> Self; - - #[cfg(not(feature = "verbose-errors"))] - fn verbose_context(self, _: impl Fn() -> String) -> Self { - self - } +pub(crate) trait Context { + fn context(self, message: impl Fn() -> String) -> Self; } /// Defers evaluation of a block of code until the end of the scope. diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 3d99ede7..54d04f8d 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,20 +1,16 @@ -#[cfg(feature = "verbose-errors")] -mod verbose_tests { - use async_std::{fs, task}; +use async_std::{fs, task}; - #[test] - fn open_file() { - task::block_on(async { - let non_existing_file = - "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; - let res = fs::File::open(non_existing_file).await; - match res { - Ok(_) => panic!("Found file with random name: We live in a simulation"), - Err(e) => assert_eq!( - "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", - &format!("{}", e) - ), - } - }) - } +#[test] +fn open_file() { + task::block_on(async { + let non_existing_file = "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; + let res = fs::File::open(non_existing_file).await; + match res { + Ok(_) => panic!("Found file with random name: We live in a simulation"), + Err(e) => assert_eq!( + "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + &format!("{}", e) + ), + } + }) } From 314a75da288367f92481f20424e728fe177e8fed Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:00:54 +0900 Subject: [PATCH 388/407] fix typo --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f8765762..c61da554 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -916,7 +916,7 @@ extension_trait! { let max = s.clone().max_by_key(|x| x.abs()).await; assert_eq!(max, Some(3)); - let max = stream::empty::().min_by_key(|x| x.abs()).await; + let max = stream::empty::().max_by_key(|x| x.abs()).await; assert_eq!(max, None); # # }) } From 64b2e10b930caf1f5f047b81060f415b570b3768 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:17:29 +0900 Subject: [PATCH 389/407] fix max_by_key mistake --- src/stream/stream/max_by_key.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index b5bc7e0c..5ebc25d7 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; @@ -13,7 +13,7 @@ pin_project! { pub struct MaxByKeyFuture { #[pin] stream: S, - max: Option, + max: Option<(T, T)>, key_by: K, } } @@ -37,24 +37,32 @@ where type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + let this = self.project(); let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { - let new = (this.key_by)(&new); + let (key, value) = key(this.key_by)(new); cx.waker().wake_by_ref(); + match this.max.take() { - None => *this.max = Some(new), + None => *this.max = Some((key, value)), - Some(old) => match new.cmp(&old) { - Ordering::Greater => *this.max = Some(new), + Some(old) => match key.cmp(&old.0) { + Ordering::Greater => *this.max = Some((key, value)), _ => *this.max = Some(old), }, } Poll::Pending } - None => Poll::Ready(this.max.take()), + None => Poll::Ready(match this.max.take() { + None => None, + Some(max) => Some(max.1), + }), } } } From 667bbc1019783d0af0d8424a31c59b178c503eab Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:18:11 +0900 Subject: [PATCH 390/407] doc: update doc test --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index c61da554..ca386e0f 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -911,10 +911,10 @@ extension_trait! { use async_std::prelude::*; use async_std::stream; - let s = stream::from_iter(vec![-1isize, -2, -3]); + let s = stream::from_iter(vec![-3_i32, 0, 1, 5, -10]); let max = s.clone().max_by_key(|x| x.abs()).await; - assert_eq!(max, Some(3)); + assert_eq!(max, Some(-10)); let max = stream::empty::().max_by_key(|x| x.abs()).await; assert_eq!(max, None); From ca71ad073bba886f84aaf0fa14e3801f54b203f9 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:25:35 +0900 Subject: [PATCH 391/407] fix stream min_by_key mistake --- src/stream/stream/min_by_key.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 8179fb31..3cd00143 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; @@ -13,7 +13,7 @@ pin_project! { pub struct MinByKeyFuture { #[pin] stream: S, - min: Option, + min: Option<(T, T)>, key_by: K, } } @@ -37,24 +37,32 @@ where type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + let this = self.project(); let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { - let new = (this.key_by)(&new); + let (key, value) = key(this.key_by)(new); cx.waker().wake_by_ref(); + match this.min.take() { - None => *this.min = Some(new), + None => *this.min = Some((key, value)), - Some(old) => match new.cmp(&old) { - Ordering::Less => *this.min = Some(new), + Some(old) => match key.cmp(&old.0) { + Ordering::Less => *this.min = Some((key, value)), _ => *this.min = Some(old), }, } Poll::Pending } - None => Poll::Ready(this.min.take()), + None => Poll::Ready(match this.min.take() { + None => None, + Some(max) => Some(max.1), + }), } } } From 080875edc9effe5387b37b99d56b92302599c9ba Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:25:48 +0900 Subject: [PATCH 392/407] update min_by_key doc --- src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index ca386e0f..48d865e0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -875,10 +875,10 @@ extension_trait! { use async_std::prelude::*; use async_std::stream; - let s = stream::from_iter(vec![1isize, 2, -3]); + let s = stream::from_iter(vec![-1isize, 2, -3]); let min = s.clone().min_by_key(|x| x.abs()).await; - assert_eq!(min, Some(1)); + assert_eq!(min, Some(-1)); let min = stream::empty::().min_by_key(|x| x.abs()).await; assert_eq!(min, None); From b5e66c4f93d699e986ab2681e94b6b79170cec4c Mon Sep 17 00:00:00 2001 From: k-nasa Date: Wed, 20 Nov 2019 00:38:51 +0900 Subject: [PATCH 393/407] refactor: Refactoring option type handling --- src/stream/stream/max_by_key.rs | 5 +---- src/stream/stream/min_by_key.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs index 5ebc25d7..e421f94a 100644 --- a/src/stream/stream/max_by_key.rs +++ b/src/stream/stream/max_by_key.rs @@ -59,10 +59,7 @@ where } Poll::Pending } - None => Poll::Ready(match this.max.take() { - None => None, - Some(max) => Some(max.1), - }), + None => Poll::Ready(this.max.take().map(|max| max.1)), } } } diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs index 3cd00143..142dfe19 100644 --- a/src/stream/stream/min_by_key.rs +++ b/src/stream/stream/min_by_key.rs @@ -59,10 +59,7 @@ where } Poll::Pending } - None => Poll::Ready(match this.min.take() { - None => None, - Some(max) => Some(max.1), - }), + None => Poll::Ready(this.min.take().map(|min| min.1)), } } } From 72ca2c1a24ea535752401fbfb9628f30ec209efe Mon Sep 17 00:00:00 2001 From: razican Date: Tue, 19 Nov 2019 21:14:56 +0100 Subject: [PATCH 394/407] Improved the code with some minor changes --- src/stream/stream/merge.rs | 7 +++---- src/stream/stream/mod.rs | 10 +++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index bdf7a29f..d9d2b0a0 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -62,12 +62,11 @@ where S: Stream, { match first.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), Poll::Ready(None) => second.poll_next(cx), + Poll::Ready(item) => Poll::Ready(item), Poll::Pending => match second.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) => Poll::Pending, - Poll::Pending => Poll::Pending, + Poll::Ready(None) | Poll::Pending => Poll::Pending, + Poll::Ready(item) => Poll::Ready(item), }, } } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 7c4bceb0..223aea5a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1663,18 +1663,14 @@ extension_trait! { ``` # async_std::task::block_on(async { use async_std::prelude::*; - use async_std::stream; + use async_std::stream::{self, FromStream}; let a = stream::once(1u8); let b = stream::once(2u8); let c = stream::once(3u8); - let mut s = a.merge(b).merge(c); - let mut lst = Vec::new(); - - while let Some(n) = s.next().await { - lst.push(n) - } + let s = a.merge(b).merge(c); + let mut lst = Vec::from_stream(s).await; lst.sort_unstable(); assert_eq!(&lst, &[1u8, 2u8, 3u8]); From 72ed4eb4fde43ec89d7135f0d16b4df3b0d88fda Mon Sep 17 00:00:00 2001 From: hhggit Date: Wed, 20 Nov 2019 19:51:01 +0800 Subject: [PATCH 395/407] Update mod.rs --- src/os/windows/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 5f0bc0e4..cd8deb60 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -2,5 +2,8 @@ cfg_std! { pub mod io; +} + +cfg_default! { pub mod fs; } From 5fba3a09289ffd043e37b38622b1e3cc25a5125e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 20 Nov 2019 13:22:46 +0100 Subject: [PATCH 396/407] Fix rng use in Stream::merge --- src/stream/stream/merge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index d9d2b0a0..84ac4322 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -44,7 +44,7 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - if utils::random(1) == 1 { + if utils::random(2) == 0 { poll_next_in_order(this.left, this.right, cx) } else { poll_next_in_order(this.right, this.left, cx) From d146d95a3934ab214c8ec22c2cb029c562b60ef0 Mon Sep 17 00:00:00 2001 From: nasa Date: Wed, 20 Nov 2019 21:38:42 +0900 Subject: [PATCH 397/407] Update src/stream/stream/mod.rs Co-Authored-By: Taiki Endo --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index e0e9b7e4..51c41390 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -1722,7 +1722,7 @@ extension_trait! { #[doc = r#" Converts an stream of pairs into a pair of containers. - unzip() consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. + `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. This function is, in some sense, the opposite of [`zip`]. From b3d30de4a11d785929a086e458ec1da4aec34bf3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 20 Nov 2019 14:58:49 +0100 Subject: [PATCH 398/407] mark windows fs APIs as "unstable" (#567) Signed-off-by: Yoshua Wuyts --- src/os/windows/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index cd8deb60..6dac11b6 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -4,6 +4,6 @@ cfg_std! { pub mod io; } -cfg_default! { +cfg_unstable! { pub mod fs; } From e01f07d72a224ff0427dac70db544a12c2c0bf8c Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 21 Nov 2019 00:22:06 +0100 Subject: [PATCH 399/407] Add context to more errors cc #569 --- src/fs/canonicalize.rs | 8 ++++- src/fs/copy.rs | 7 ++++- src/fs/create_dir.rs | 7 ++++- src/fs/create_dir_all.rs | 7 ++++- src/fs/file.rs | 7 ++--- src/fs/hard_link.rs | 12 +++++++- src/fs/read.rs | 6 +++- src/fs/read_dir.rs | 12 +++++--- src/fs/read_link.rs | 8 ++++- src/fs/read_to_string.rs | 7 ++++- src/fs/remove_dir.rs | 7 ++++- src/fs/remove_dir_all.rs | 7 ++++- src/fs/remove_file.rs | 7 ++++- src/fs/rename.rs | 12 +++++++- src/fs/write.rs | 7 ++++- src/io/copy.rs | 7 +++-- src/io/stdin.rs | 4 ++- src/net/tcp/listener.rs | 9 ++++-- src/net/tcp/stream.rs | 13 ++++++-- src/net/udp/mod.rs | 64 +++++++++++++++++++++++++++++++++++----- tests/verbose_errors.rs | 2 +- 21 files changed, 181 insertions(+), 39 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 6eb6977d..38a5a6b9 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Returns the canonical form of a path. /// @@ -32,5 +33,10 @@ use crate::task::spawn_blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::canonicalize(&path).map(Into::into)).await + spawn_blocking(move || { + std::fs::canonicalize(&path) + .map(Into::into) + .context(|| format!("could not canonicalize `{}`", path.display())) + }) + .await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 170b66ec..8fb447bb 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Copies the contents and permissions of a file to a new location. /// @@ -41,5 +42,9 @@ use crate::task::spawn_blocking; pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::copy(&from, &to)).await + spawn_blocking(move || { + std::fs::copy(&from, &to) + .context(|| format!("could not copy `{}` to `{}`", from.display(), to.display())) + }) + .await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 03c24918..37923c05 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory. /// @@ -34,5 +35,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::create_dir(path)).await + spawn_blocking(move || { + std::fs::create_dir(&path) + .context(|| format!("could not create directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 15241943..753dfd49 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory and all of its parents if they are missing. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::create_dir_all(path)).await + spawn_blocking(move || { + std::fs::create_dir_all(&path) + .context(|| format!("could not create directory path `{}`", path.display())) + }) + .await } diff --git a/src/fs/file.rs b/src/fs/file.rs index f8242811..24c72c6d 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,11 +9,11 @@ use std::sync::{Arc, Mutex}; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::utils::Context as _; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; use crate::task::{self, spawn_blocking, Context, Poll, Waker}; +use crate::utils::Context as _; /// An open file on the filesystem. /// @@ -114,8 +114,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { - std::fs::File::open(&path) - .context(|| format!("Could not open {}", path.display())) + std::fs::File::open(&path).context(|| format!("Could not open `{}`", path.display())) }) .await?; Ok(File::new(file, true)) @@ -154,7 +153,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::create(&path) - .context(|| format!("Could not create {}", path.display())) + .context(|| format!("Could not create `{}`", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index e6e56cd5..a6a40698 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a hard link on the filesystem. /// @@ -32,5 +33,14 @@ use crate::task::spawn_blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::hard_link(&from, &to)).await + spawn_blocking(move || { + std::fs::hard_link(&from, &to).context(|| { + format!( + "could not create a hard link from `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/read.rs b/src/fs/read.rs index ab7d1756..3b568f7a 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as raw bytes. /// @@ -36,5 +37,8 @@ use crate::task::spawn_blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read(path)).await + spawn_blocking(move || { + std::fs::read(&path).context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 5e51065b..d8261a94 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,11 +1,12 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use crate::fs::DirEntry; use crate::io; use crate::path::Path; use crate::stream::Stream; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; /// Returns a stream of entries in a directory. /// @@ -45,9 +46,12 @@ use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_dir(path)) - .await - .map(ReadDir::new) + spawn_blocking(move || { + std::fs::read_dir(&path) + .context(|| format!("could not read directory `{}`", path.display())) + }) + .await + .map(ReadDir::new) } /// A stream of entries in a directory. diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 7ec18a45..d8cabb72 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads a symbolic link and returns the path it points to. /// @@ -28,5 +29,10 @@ use crate::task::spawn_blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_link(path).map(Into::into)).await + spawn_blocking(move || { + std::fs::read_link(&path) + .map(Into::into) + .context(|| format!("could not read link `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index d06aa614..2378aaed 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as a string. /// @@ -37,5 +38,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::read_to_string(path)).await + spawn_blocking(move || { + std::fs::read_to_string(&path) + .context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index 1a62db2e..8fdba18c 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes an empty directory. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_dir(path)).await + spawn_blocking(move || { + std::fs::remove_dir(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 33667406..d4bad3a2 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a directory and all of its contents. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_dir_all(path)).await + spawn_blocking(move || { + std::fs::remove_dir_all(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 9a74ec11..b881f8be 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a file. /// @@ -29,5 +30,9 @@ use crate::task::spawn_blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - spawn_blocking(move || std::fs::remove_file(path)).await + spawn_blocking(move || { + std::fs::remove_file(&path) + .context(|| format!("could not remove file `{}`", path.display())) + }) + .await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index ed7f39c9..25fc55fa 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Renames a file or directory to a new location. /// @@ -34,5 +35,14 @@ use crate::task::spawn_blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - spawn_blocking(move || std::fs::rename(&from, &to)).await + spawn_blocking(move || { + std::fs::rename(&from, &to).context(|| { + format!( + "could not rename `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 4e5d20bb..7c14098b 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Writes a slice of bytes as the new contents of a file. /// @@ -33,5 +34,9 @@ use crate::task::spawn_blocking; pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - spawn_blocking(move || std::fs::write(path, contents)).await + spawn_blocking(move || { + std::fs::write(&path, contents) + .context(|| format!("could not write to file `{}`", path.display())) + }) + .await } diff --git a/src/io/copy.rs b/src/io/copy.rs index 8ec3c1af..f05ed0e1 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,10 +1,11 @@ -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; use pin_project_lite::pin_project; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; +use crate::utils::Context as _; /// Copies the entire contents of a reader into a writer. /// @@ -90,7 +91,7 @@ where writer, amt: 0, }; - future.await + future.await.context(|| String::from("io::copy failed")) } /// Copies the entire contents of a reader into a writer. @@ -177,5 +178,5 @@ where writer, amt: 0, }; - future.await + future.await.context(|| String::from("io::copy failed")) } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 167ea2dd..618393af 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,10 +1,11 @@ +use std::future::Future; use std::pin::Pin; use std::sync::Mutex; -use std::future::Future; use crate::future; use crate::io::{self, Read}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; cfg_unstable! { use once_cell::sync::Lazy; @@ -162,6 +163,7 @@ impl Stdin { } }) .await + .context(|| String::from("Could not read line on stdin")) } /// Locks this handle to the standard input stream, returning a readable guard. diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index f98bbdc7..9d944f4b 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,5 +1,5 @@ -use std::net::SocketAddr; use std::future::Future; +use std::net::SocketAddr; use std::pin::Pin; use crate::future; @@ -8,6 +8,7 @@ use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; +use crate::utils::Context as _; /// A TCP socket server, listening for connections. /// @@ -75,8 +76,12 @@ impl TcpListener { /// [`local_addr`]: #method.local_addr pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { match mio::net::TcpListener::bind(&addr) { Ok(mio_listener) => { return Ok(TcpListener { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1da9c7c2..9d3c2ecd 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -7,6 +7,7 @@ use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; use crate::task::{spawn_blocking, Context, Poll}; +use crate::utils::Context as _; /// A TCP stream between a local and a remote socket. /// @@ -71,11 +72,17 @@ impl TcpStream { /// ``` pub async fn connect(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { let res = spawn_blocking(move || { - let std_stream = std::net::TcpStream::connect(addr)?; - let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; + let std_stream = std::net::TcpStream::connect(addr) + .context(|| format!("could not connect to {}", addr))?; + let mio_stream = mio::net::TcpStream::from_stream(std_stream) + .context(|| format!("could not open async connection to {}", addr))?; Ok(TcpStream { watcher: Watcher::new(mio_stream), }) diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 37c9d50c..e6064136 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -5,6 +5,7 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; +use crate::utils::Context as _; /// A UDP socket. /// @@ -66,10 +67,14 @@ impl UdpSocket { /// # /// # Ok(()) }) } /// ``` - pub async fn bind(addr: A) -> io::Result { + pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addr.to_socket_addrs().await? { + for addr in addrs { match mio::net::UdpSocket::bind(&addr) { Ok(mio_socket) => { return Ok(UdpSocket { @@ -106,7 +111,10 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.watcher.get_ref().local_addr() + self.watcher + .get_ref() + .local_addr() + .context(|| String::from("could not get local address")) } /// Sends data on the socket to the given address. @@ -151,6 +159,7 @@ impl UdpSocket { .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) }) .await + .context(|| format!("Could not send packet to {}", addr)) } /// Receives data from the socket. @@ -178,6 +187,17 @@ impl UdpSocket { .poll_read_with(cx, |inner| inner.recv_from(buf)) }) .await + .context(|| { + use std::fmt::Write; + + let mut error = String::from("Could not receive data on "); + if let Ok(addr) = self.local_addr() { + let _ = write!(&mut error, "{}", addr); + } else { + error.push_str("socket"); + } + error + }) } /// Connects the UDP socket to a remote address. @@ -195,7 +215,7 @@ impl UdpSocket { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::net::UdpSocket; + /// use async_std::net::UdpSocket; /// /// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// socket.connect("127.0.0.1:8080").await?; @@ -204,8 +224,12 @@ impl UdpSocket { /// ``` pub async fn connect(&self, addrs: A) -> io::Result<()> { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { // TODO(stjepang): connect on the blocking pool match self.watcher.get_ref().connect(addr) { Ok(()) => return Ok(()), @@ -248,7 +272,19 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await + future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))) + .await + .context(|| { + use std::fmt::Write; + + let mut error = String::from("Could not send data on "); + if let Ok(addr) = self.local_addr() { + let _ = write!(&mut error, "{}", addr); + } else { + error.push_str("socket"); + } + error + }) } /// Receives data from the socket. @@ -271,7 +307,19 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await + future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))) + .await + .context(|| { + use std::fmt::Write; + + let mut error = String::from("Could not receive data on "); + if let Ok(addr) = self.local_addr() { + let _ = write!(&mut error, "{}", addr); + } else { + error.push_str("socket"); + } + error + }) } /// Gets the value of the `SO_BROADCAST` option for this socket. @@ -415,7 +463,7 @@ impl UdpSocket { /// use async_std::net::UdpSocket; /// /// let socket_addr = SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(), 0); - /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123) ; + /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123); /// let socket = UdpSocket::bind(&socket_addr).await?; /// /// socket.join_multicast_v6(&mdns_addr, 0)?; diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 54d04f8d..207d0b27 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -8,7 +8,7 @@ fn open_file() { match res { Ok(_) => panic!("Found file with random name: We live in a simulation"), Err(e) => assert_eq!( - "Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas", + "Could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`", &format!("{}", e) ), } From 16edec346498f3a6e32869b78b57c63ca826cc17 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 21 Nov 2019 17:50:30 +0100 Subject: [PATCH 400/407] Ignore seek errors in poll_unread --- src/fs/file.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index f8242811..94e2989b 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -742,7 +742,10 @@ impl LockGuard { if n > 0 { // Seek `n` bytes backwards. This call should not block because it only changes // the internal offset into the file and doesn't touch the actual file on disk. - (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; + // + // We ignore errors here because special files like `/dev/random` are not + // seekable. + let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64))); } // Switch to idle mode. From ba1ee2d204f1e27f26d0cb98c004ce7676b6aae4 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 18:03:10 +0100 Subject: [PATCH 401/407] Fix a-chat tutorial issues (#573) * tutorial/receiving_messages: fix future output type bound * tutorial/receiving_messages: remove unneeded message trimming Trimming was done twice on messages, so one of the two instances can be removed. I personally think removing the first instance, in which we are splitting names from messages makes the code more readable than removing the second instance, but other examples further in the tutorial show the second instance removed. * tutorial/receiving_messages: declare use of TcpStream and io::BufReader Readers couldn't see the `use` lines corresponding to these two structures. * tutorial/connecting_readers_and_writers: typos and grammar fixes * tutorial/all_together: remove unneeded use async_std::io * tutorial: use SinkExt consistently from futures::sink::SinkExt * tutorial/handling_disconnection: hide mpsc use clause and remove empty lines The empty lines translate to the output making it look weird. * tutorial/handling_disconnection: fix typos * tutorial/handling_disconnection: use ? in broker_handle.await We were happy to return an Err variant from the broker_handle before and nothing has changed in this regard, so bubbling it up to run(). --- docs/src/tutorial/all_together.md | 4 ++-- docs/src/tutorial/clean_shutdown.md | 4 ++-- .../tutorial/connecting_readers_and_writers.md | 12 ++++++------ docs/src/tutorial/handling_disconnection.md | 17 ++++++++--------- docs/src/tutorial/receiving_messages.md | 12 ++++++++---- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 789283e6..8bb01e94 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -6,13 +6,13 @@ At this point, we only need to start the broker to get a fully-functioning (in t # extern crate async_std; # extern crate futures; use async_std::{ - io::{self, BufReader}, + io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; use futures::channel::mpsc; -use futures::SinkExt; +use futures::sink::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 5dcc7f26..bd112c93 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -30,7 +30,7 @@ Let's add waiting to the server: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -163,7 +163,7 @@ And to the broker: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index fcc42b63..921cf90c 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -2,12 +2,12 @@ ## Connecting Readers and Writers So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`? -We should somehow maintain an `peers: HashMap>` map which allows a client to find destination channels. +We should somehow maintain a `peers: HashMap>` map which allows a client to find destination channels. However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message. One trick to make reasoning about state simpler comes from the actor model. -We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels. -By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit. +We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels. +By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit. The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue. ```rust,edition2018 @@ -92,9 +92,9 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { } ``` -1. Broker should handle two types of events: a message or an arrival of a new peer. -2. Internal state of the broker is a `HashMap`. +1. The broker task should handle two types of events: a message or an arrival of a new peer. +2. The internal state of the broker is a `HashMap`. Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers 3. To handle a message, we send it over a channel to each destination -4. To handle new peer, we first register it in the peer's map ... +4. To handle a new peer, we first register it in the peer's map ... 5. ... and then spawn a dedicated task to actually write the messages to the socket. diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index acb744b0..9db9abd2 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -22,7 +22,7 @@ First, let's add a shutdown channel to the `connection_loop`: # extern crate futures; # use async_std::net::TcpStream; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -60,8 +60,8 @@ async fn connection_loop(mut broker: Sender, stream: Arc) -> R } ``` -1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type. -2. We pass the shutdown channel to the writer task +1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type. +2. We pass the shutdown channel to the writer task. 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped. In the `connection_writer_loop`, we now need to choose between shutdown and message channels. @@ -71,14 +71,12 @@ We use the `select` macro for this purpose: # extern crate async_std; # extern crate futures; # use async_std::{net::TcpStream, prelude::*}; -use futures::channel::mpsc; +# use futures::channel::mpsc; use futures::{select, FutureExt}; # use std::sync::Arc; - # type Receiver = mpsc::UnboundedReceiver; # type Result = std::result::Result>; # type Sender = mpsc::UnboundedSender; - # #[derive(Debug)] # enum Void {} // 1 @@ -112,7 +110,7 @@ async fn connection_writer_loop( Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. To not lose these messages completely, we'll return the messages channel back to the broker. -This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable. +This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible. ## Final Code @@ -128,7 +126,8 @@ use async_std::{ task, }; use futures::channel::mpsc; -use futures::{select, FutureExt, SinkExt}; +use futures::sink::SinkExt; +use futures::{select, FutureExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, @@ -158,7 +157,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); - broker_handle.await; + broker_handle.await?; Ok(()) } diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 213589c0..4f705294 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,14 +10,18 @@ We need to: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# io::BufReader, -# net::{TcpListener, TcpStream, ToSocketAddrs}, +# net::{TcpListener, ToSocketAddrs}, # prelude::*, # task, # }; # # type Result = std::result::Result>; # +use async_std::{ + io::BufReader, + net::TcpStream, +}; + async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); @@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> { Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), }; let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); - let msg: String = msg.trim().to_string(); + let msg: String = msg.to_string(); } Ok(()) } @@ -130,7 +134,7 @@ So let's use a helper function for this: # }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { task::spawn(async move { if let Err(e) = fut.await { From ec5415358f929a5ce92f7451503653c7a190893e Mon Sep 17 00:00:00 2001 From: laizy Date: Fri, 22 Nov 2019 01:03:23 +0800 Subject: [PATCH 402/407] simplify AllFuture and AnyFuture (#572) --- src/stream/stream/all.rs | 5 +---- src/stream/stream/any.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 5adb68f3..d2ac5cac 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -10,7 +10,6 @@ use crate::task::{Context, Poll}; pub struct AllFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } @@ -19,7 +18,6 @@ impl<'a, S, F, T> AllFuture<'a, S, F, T> { Self { stream, f, - result: true, // the default if the empty stream _marker: PhantomData, } } @@ -40,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { // don't forget to wake this task again to pull the next item from stream @@ -50,7 +47,7 @@ where Poll::Ready(false) } } - None => Poll::Ready(self.result), + None => Poll::Ready(true), } } } diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index d6853a1c..34168e67 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -10,7 +10,6 @@ use crate::task::{Context, Poll}; pub struct AnyFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } @@ -19,7 +18,6 @@ impl<'a, S, F, T> AnyFuture<'a, S, F, T> { Self { stream, f, - result: false, // the default if the empty stream _marker: PhantomData, } } @@ -40,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { Poll::Ready(true) @@ -50,7 +47,7 @@ where Poll::Pending } } - None => Poll::Ready(self.result), + None => Poll::Ready(false), } } } From cffacf7fa32d448a4b0707bfddd51fc0f06d2850 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 21 Nov 2019 21:21:19 +0100 Subject: [PATCH 403/407] feedback from review Signed-off-by: Yoshua Wuyts --- README.md | 6 ++++++ src/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c074fee..34ebe6df 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ syntax. ## Examples +All examples require the [`"attributes"` feature] to be enabled. This feature +is not enabled by default because it significantly impacts compile times. See +[`task::block_on`] for an alternative way to start executing tasks. + ```rust async fn say_hello() { println!("Hello, world!"); @@ -90,6 +94,8 @@ More examples, including networking and file access, can be found in our [`examples`]: https://github.com/async-rs/async-std/tree/master/examples [documentation]: https://docs.rs/async-std#examples +[`task::block_on`]: task/fn.block_on.html +[`"attributes"` feature]: https://docs.rs/async-std/#features ## Philosophy diff --git a/src/lib.rs b/src/lib.rs index 5442909f..d0c87ff5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,8 +154,8 @@ //! ``` //! #[async_std::main] //! async fn main() { -//! let a = || async move { 1u8 }; -//! let b = || async move { 2u8 }; +//! let a = async { 1u8 }; +//! let b = async { 2u8 }; //! assert_eq!(a.join(b).await, (1u8, 2u8)) //! } //! ``` From 3780ff7b44d438d991a8253ee3cc1916106fea45 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 20 Nov 2019 02:25:00 +0100 Subject: [PATCH 404/407] 1.1.0 Signed-off-by: Yoshua Wuyts changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d19fddb..7977be76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [1.1.0] - 2019-11-21 + +[API Documentation](https://docs.rs/async-std/1.1.0/async-std) + +This patch introduces a faster scheduler algorithm, `Stream::throttle`, and +stabilizes `task::yield_now`. Additionally we're introducing several more stream +APIs, bringing us to almost complete parity with the standard library. + +Furthermore our `path` submodule now returns more context in errors. So if +opening a file fails, async-std will tell you *which* file was failed to open, +making it easier to write and debug programs. + +## Examples + +```rust +let start = Instant::now(); + +let mut s = stream::interval(Duration::from_millis(5)) + .throttle(Duration::from_millis(10)) + .take(2); + +s.next().await; +assert!(start.elapsed().as_millis() >= 5); + +s.next().await; +assert!(start.elapsed().as_millis() >= 15); + +s.next().await; +assert!(start.elapsed().as_millis() >= 25); +``` + +## Added + +- Added `Stream::throttle` as "unstable". +- Added `Stream::count` as "unstable". +- Added `Stream::max` as "unstable". +- Added `Stream::successors` as "unstable". +- Added `Stream::by_ref` as "unstable". +- Added `Stream::partition` as "unstable". +- Added contextual errors to the `path` submodule. +- Added `os::windows::symlink_dir` as "unstable". +- Added `os::windows::symlink_file` as "unstable". +- Stabilized `task::yield_now`. + +## Fixes + +- We now ignore seek errors when rolling back failed `read` calls on `File`. +- Fixed a bug where `Stream::max_by_key` was returning the wrong result. +- Fixed a bug where `Stream::min_by_key` was returning the wrong result. + +## Changed + +- Applied various fixes to the tutorial. +- Fixed an issue with Clippy. +- Optimized an internal code generation macro, improving compilation speeds. +- Removed an `Unpin` bound from `stream::Once`. +- Removed various extra internal uses of `pin_mut!`. +- Simplified `Stream::any` and `Stream::all`'s internals. +- The `surf` example is now enabled again. +- Tweaked some streams internals. +- Updated `futures-timer` to 2.0.0, improving compilation speed. +- Upgraded `async-macros` to 2.0.0. +- `Stream::merge` now uses randomized ordering to reduce overall latency. +- The scheduler is now more efficient by keeping a slot for the next task to + run. This is similar to Go's scheduler, and Tokio's scheduler. +- Fixed the documentation of the `channel` types to link back to the `channel` + function. + # [1.0.1] - 2019-11-12 [API Documentation](https://docs.rs/async-std/1.0.1/async-std) @@ -442,7 +510,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v1.0.1...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 [0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 diff --git a/Cargo.toml b/Cargo.toml index 7ffaae3a..9df05660 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "1.0.1" +version = "1.1.0" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From aa7d1c27a42b262ef47a68e0fb61f5078fb18a33 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 25 Nov 2019 21:18:40 +0100 Subject: [PATCH 405/407] Verbose errors: Apply suggestions Co-Authored-By: Yoshua Wuyts --- src/fs/file.rs | 4 ++-- src/io/stdin.rs | 2 +- src/net/udp/mod.rs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 24c72c6d..c111e013 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -114,7 +114,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { - std::fs::File::open(&path).context(|| format!("Could not open `{}`", path.display())) + std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display())) }) .await?; Ok(File::new(file, true)) @@ -153,7 +153,7 @@ impl File { let path = path.as_ref().to_owned(); let file = spawn_blocking(move || { std::fs::File::create(&path) - .context(|| format!("Could not create `{}`", path.display())) + .context(|| format!("could not create `{}`", path.display())) }) .await?; Ok(File::new(file, true)) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 618393af..369ccae4 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -163,7 +163,7 @@ impl Stdin { } }) .await - .context(|| String::from("Could not read line on stdin")) + .context(|| String::from("could not read line on stdin")) } /// Locks this handle to the standard input stream, returning a readable guard. diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index e6064136..a1ca03f3 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -159,7 +159,7 @@ impl UdpSocket { .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) }) .await - .context(|| format!("Could not send packet to {}", addr)) + .context(|| format!("could not send packet to {}", addr)) } /// Receives data from the socket. @@ -190,7 +190,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not receive data on "); + let mut error = String::from("could not receive data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { @@ -277,7 +277,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not send data on "); + let mut error = String::from("could not send data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { @@ -312,7 +312,7 @@ impl UdpSocket { .context(|| { use std::fmt::Write; - let mut error = String::from("Could not receive data on "); + let mut error = String::from("could not receive data on "); if let Ok(addr) = self.local_addr() { let _ = write!(&mut error, "{}", addr); } else { From 56538ebd9117402d2c8e69a67a08b4ea8b5e660f Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 25 Nov 2019 21:41:54 +0100 Subject: [PATCH 406/407] Improve verbose errors for socket addresses Moves the point of adding error context to the net::addr module so that we have access to the raw address input and can include it in the error message. --- src/net/addr.rs | 32 +++++++++++++++++++++++++++----- src/net/tcp/listener.rs | 4 +--- src/net/tcp/stream.rs | 3 +-- src/net/udp/mod.rs | 3 +-- tests/verbose_errors.rs | 17 ++++++++++++++++- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/net/addr.rs b/src/net/addr.rs index 2769dd5e..ea839500 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -1,11 +1,12 @@ +use std::future::Future; use std::mem; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use std::future::Future; use crate::io; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as ErrorContext; cfg_not_docs! { macro_rules! ret { @@ -67,6 +68,18 @@ pub enum ToSocketAddrsFuture { Done, } +/// Wrap `std::io::Error` with additional message +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl ErrorContext for ToSocketAddrsFuture { + fn context(self, message: impl Fn() -> String) -> Self { + match self { + ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)), + x => x, + } + } +} + impl> Future for ToSocketAddrsFuture { type Output = io::Result; @@ -110,7 +123,9 @@ impl ToSocketAddrs for SocketAddrV4 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V4(*self).to_socket_addrs() + SocketAddr::V4(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -123,7 +138,9 @@ impl ToSocketAddrs for SocketAddrV6 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V6(*self).to_socket_addrs() + SocketAddr::V6(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -195,7 +212,9 @@ impl ToSocketAddrs for (&str, u16) { let host = host.to_string(); let task = spawn_blocking(move || { - std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) + let addr = (host.as_str(), port); + std::net::ToSocketAddrs::to_socket_addrs(&addr) + .context(|| format!("could not resolve address `{:?}`", addr)) }); ToSocketAddrsFuture::Resolving(task) } @@ -215,7 +234,10 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + let task = spawn_blocking(move || { + std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) + .context(|| format!("could not resolve address `{:?}`", addr)) + }); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 9d944f4b..fe06a96d 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -8,7 +8,6 @@ use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Context as _; /// A TCP socket server, listening for connections. /// @@ -78,8 +77,7 @@ impl TcpListener { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::TcpListener::bind(&addr) { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 9d3c2ecd..41317833 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -74,8 +74,7 @@ impl TcpStream { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { let res = spawn_blocking(move || { diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index a1ca03f3..7fef1ed5 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -71,8 +71,7 @@ impl UdpSocket { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::UdpSocket::bind(&addr) { diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 207d0b27..15630928 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,4 +1,4 @@ -use async_std::{fs, task}; +use async_std::{fs, io, net::ToSocketAddrs, task}; #[test] fn open_file() { @@ -14,3 +14,18 @@ fn open_file() { } }) } + +#[test] +fn resolve_address() { + task::block_on(async { + let non_existing_addr = "ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80"; + let res: Result<_, io::Error> = non_existing_addr.to_socket_addrs().await; + match res { + Ok(_) => panic!("Found address with random name: We live in a simulation"), + Err(e) => assert_eq!( + "could not resolve address `\"ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80\"`", + &format!("{}", e) + ), + } + }) +} From 0f30ab8c0a67707e57ac238e7e6b42821b05ba8a Mon Sep 17 00:00:00 2001 From: boats Date: Tue, 26 Nov 2019 14:23:10 +0100 Subject: [PATCH 407/407] Fix the docs and Debug output of BufWriter. (#588) The BufWriter docs inaccurately stated that it flushes on drop, which it does not do. This PR changes the docs, as well as the example, to highlight that the user must explicitly flush a bufwriter. There were also two places where the BufWriter code referred to it as a BufReader: in the link to the std docs, and in the Debug output. Those have also been fixed. --- src/io/buf_writer.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index ce6a97b3..c527d027 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -22,14 +22,14 @@ pin_project! { /// times. It also provides no advantage when writing to a destination that is /// in memory, like a `Vec`. /// - /// When the `BufWriter` is dropped, the contents of its buffer will be written - /// out. However, any errors that happen in the process of flushing the buffer - /// when the writer is dropped will be ignored. Code that wishes to handle such - /// errors must manually call [`flush`] before the writer is dropped. + /// Unlike the `BufWriter` type in `std`, this type does not write out the + /// contents of its buffer when it is dropped. Therefore, it is absolutely + /// critical that users explicitly flush the buffer before dropping a + /// `BufWriter`. /// - /// This type is an async version of [`std::io::BufReader`]. + /// This type is an async version of [`std::io::BufWriter`]. /// - /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html /// /// # Examples /// @@ -61,10 +61,13 @@ pin_project! { /// use async_std::prelude::*; /// /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); + /// /// for i in 0..10 { /// let arr = [i+1]; /// stream.write(&arr).await?; /// }; + /// + /// stream.flush().await?; /// # /// # Ok(()) }) } /// ``` @@ -325,7 +328,7 @@ impl Write for BufWriter { impl fmt::Debug for BufWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BufReader") + f.debug_struct("BufWriter") .field("writer", &self.inner) .field("buf", &self.buf) .finish()