From dde9766fff8371dae4464ead06ed560702d64b70 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Mon, 24 Feb 2020 21:23:58 +0300 Subject: [PATCH 01/83] Add `StreamExt::flat_map` (#2068) --- CHANGELOG.md | 2 +- futures-util/src/stream/mod.rs | 6 +- futures-util/src/stream/stream/flat_map.rs | 169 +++++++++++++++++++++ futures-util/src/stream/stream/mod.rs | 38 +++++ futures/src/lib.rs | 4 +- futures/tests/stream.rs | 18 +++ 6 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 futures-util/src/stream/stream/flat_map.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b93b0ae557..cdf208870f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -239,7 +239,7 @@ * Improvements to `select!` and `join!` macros * Added `try_join!` macro * Added `StreamExt` combinator methods `try_join` and `for_each_concurrent` -* Added `TryStreamExt` combinator methdos `into_stream`, `try_filter_map`, `try_skip_while`, `try_for_each_concurrent` and `try_buffer_unordered` +* Added `TryStreamExt` combinator methods `into_stream`, `try_filter_map`, `try_skip_while`, `try_for_each_concurrent` and `try_buffer_unordered` * Fix stream termination bug in `StreamExt::buffered` and `StreamExt::buffer_unordered` * Added docs for `StreamExt::buffered`, `StreamExt::buffer_unordered` * Added `task::local_waker_ref_from_nonlocal` and `task::local_waker_ref` functions diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index ba3575bf02..4b64e1add4 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -13,9 +13,9 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; #[allow(clippy::module_inception)] mod stream; pub use self::stream::{ - Chain, Collect, Concat, Enumerate, Filter, FilterMap, Flatten, Fold, ForEach, Fuse, Inspect, - Map, Next, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, StreamFuture, Take, - TakeWhile, Then, Zip, + Chain, Collect, Concat, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, Fuse, + Inspect, Map, Next, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, + StreamFuture, Take, TakeWhile, Then, Zip, }; #[cfg(feature = "std")] diff --git a/futures-util/src/stream/stream/flat_map.rs b/futures-util/src/stream/stream/flat_map.rs new file mode 100644 index 0000000000..e92bb853e5 --- /dev/null +++ b/futures-util/src/stream/stream/flat_map.rs @@ -0,0 +1,169 @@ +use super::Map; +use core::fmt; +use core::pin::Pin; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_utils::unsafe_pinned; + +/// Stream for the [`flat_map`](super::StreamExt::flat_map) method. +#[must_use = "streams do nothing unless polled"] +pub struct FlatMap { + stream: Map, + inner_stream: Option, +} + +impl Unpin for FlatMap {} + +impl fmt::Debug for FlatMap +where + St: fmt::Debug, + U: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlatMap") + .field("stream", &self.stream) + .field("inner_stream", &self.inner_stream) + .finish() + } +} + +impl FlatMap +where + St: Stream, + U: Stream, + F: FnMut(St::Item) -> U, +{ + unsafe_pinned!(stream: Map); + unsafe_pinned!(inner_stream: Option); + + pub(super) fn new(stream: St, f: F) -> FlatMap { + FlatMap { + stream: Map::new(stream, f), + inner_stream: None, + } + } + + /// Acquires a reference to the underlying stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &St { + self.stream.get_ref() + } + + /// Acquires a mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut St { + self.stream.get_mut() + } + + /// Acquires a pinned mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { + self.stream().get_pin_mut() + } + + /// Consumes this combinator, returning the underlying stream. + /// + /// Note that this may discard intermediate state of this combinator, so + /// care should be taken to avoid losing resources when this is called. + pub fn into_inner(self) -> St { + self.stream.into_inner() + } +} + +impl FusedStream for FlatMap +where + St: FusedStream, + U: FusedStream, + F: FnMut(St::Item) -> U, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + && self + .inner_stream + .as_ref() + .map(FusedStream::is_terminated) + .unwrap_or(true) + } +} + +impl Stream for FlatMap +where + St: Stream, + U: Stream, + F: FnMut(St::Item) -> U, +{ + type Item = U::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(inner_stream) = self.as_mut().inner_stream().as_pin_mut() { + let next = ready!(inner_stream.poll_next(cx)); + + if next.is_some() { + break Poll::Ready(next); + } else { + self.as_mut().inner_stream().set(None); + } + } + + let next_stream = ready!(self.as_mut().stream().poll_next(cx)); + + if next_stream.is_some() { + self.as_mut().inner_stream().set(next_stream); + } else { + break Poll::Ready(None); + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let stream_size_hint = self.stream.size_hint(); + let no_stream_items_left = stream_size_hint.1 == Some(0); + + if let Some(inner_stream_size_hint) = self.inner_stream.as_ref().map(|st| st.size_hint()) { + ( + stream_size_hint + .0 + .checked_add(inner_stream_size_hint.0) + .unwrap_or(stream_size_hint.0), + if no_stream_items_left { + inner_stream_size_hint.1 + } else { + // Can't know upper bound because next items are `Stream`s + None + }, + ) + } else { + ( + stream_size_hint.0, + if no_stream_items_left { + Some(0) + } else { + // Can't know upper bound because next items are `Stream`s + None + }, + ) + } + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for FlatMap +where + St: Stream + Sink, + U: Stream, + F: FnMut(St::Item) -> U, +{ + type Error = St::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 4f227326f0..670c4a456f 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -78,6 +78,10 @@ mod map; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::map::Map; +mod flat_map; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::flat_map::FlatMap; + mod next; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::next::Next; @@ -544,6 +548,40 @@ pub trait StreamExt: Stream { Flatten::new(self) } + /// Maps a stream like [`StreamExt::map`] but flattens nested `Stream`s. + /// + /// [`StreamExt::map`] is very useful, but if it produces a `Stream` instead, + /// you would have to chain combinators like `.map(f).flatten()` while this + /// combinator provides ability to write `.flat_map(f)` instead of chaining. + /// + /// The provided closure which produce inner streams is executed over all elements + /// of stream as last inner stream is terminated and next stream item is available. + /// + /// Note that this function consumes the stream passed into it and returns a + /// wrapped version of it, similar to the existing `flat_map` methods in the + /// standard library. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..=3); + /// let stream = stream.flat_map(|x| stream::iter(vec![x + 3; x])); + /// + /// assert_eq!(vec![4, 5, 5, 6, 6, 6], stream.collect::>().await); + /// # }); + /// ``` + fn flat_map(self, f: F) -> FlatMap + where + F: FnMut(Self::Item) -> U, + U: Stream, + Self: Sized, + { + FlatMap::new(self, f) + } + /// Combinator similar to [`StreamExt::fold`] that holds internal state /// and produces a new stream. /// diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 3cdb3d34ee..c4acbc0fd1 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -442,8 +442,8 @@ pub mod stream { try_unfold, TryUnfold, StreamExt, - Chain, Collect, Concat, Enumerate, Filter, FilterMap, Flatten, Fold, - Forward, ForEach, Fuse, StreamFuture, Inspect, Map, Next, + Chain, Collect, Concat, Enumerate, Filter, FilterMap, FlatMap, Flatten, + Fold, Forward, ForEach, Fuse, StreamFuture, Inspect, Map, Next, SelectNextSome, Peek, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile, Then, Zip, diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 54f49c668d..f0652d06f1 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -15,6 +15,24 @@ fn select() { select_and_compare(vec![1, 2], vec![4, 5, 6], vec![1, 4, 2, 5, 6]); } +#[test] +fn flat_map() { + futures::executor::block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(0..=2), + ]); + + let values: Vec<_> = st + .flat_map(|s| s.filter(|v| futures::future::ready(v % 2 == 0))) + .collect() + .await; + + assert_eq!(values, vec![0, 2, 4, 6, 8, 10, 0, 2]); + }); +} + #[test] fn scan() { futures::executor::block_on(async { From c858a2b253083abad8d429aa4ed21620548c8872 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Fri, 21 Feb 2020 15:41:35 -0500 Subject: [PATCH 02/83] util: Relax bounds for FuturesUnordered There is no need for the `FuturesUnordered` constructors to require that the provided `Fut` type implements `Future`. It needlessly requires callers to also propagate that bound onto their constructors, all the way up the chain. This patch removes the bounds except where necessary, specifically for the `impl Stream`. --- .../src/stream/futures_unordered/mod.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 5d93a646a0..cdf2a95bf6 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -121,7 +121,13 @@ impl LocalSpawn for FuturesUnordered> { // notifiaction is received, the task will only be inserted into the ready to // run queue if it isn't inserted already. -impl FuturesUnordered { +impl Default for FuturesUnordered { + fn default() -> FuturesUnordered { + FuturesUnordered::new() + } +} + +impl FuturesUnordered { /// Constructs a new, empty [`FuturesUnordered`]. /// /// The returned [`FuturesUnordered`] does not contain any futures. @@ -151,15 +157,7 @@ impl FuturesUnordered { is_terminated: AtomicBool::new(false), } } -} -impl Default for FuturesUnordered { - fn default() -> FuturesUnordered { - FuturesUnordered::new() - } -} - -impl FuturesUnordered { /// Returns the number of futures contained in the set. /// /// This represents the total number of in-flight futures. @@ -607,7 +605,7 @@ impl Drop for FuturesUnordered { } } -impl FromIterator for FuturesUnordered { +impl FromIterator for FuturesUnordered { fn from_iter(iter: I) -> Self where I: IntoIterator, From 56980e76b9eb9827d56cdbbeaf28a2f018a89a16 Mon Sep 17 00:00:00 2001 From: Naja Melan Date: Fri, 14 Feb 2020 14:00:50 +0100 Subject: [PATCH 03/83] Remove the Send bound on the output type of RemoteHandle. Currently Future is only implemented for RemoteHandle if T: Send + 'static I think these bounds don't do anything useful, yet disable valid use cases of the API. The compiler doesn't require them and I can find no good reason for them to be there. In particular if output is not Send, nor will the Remote or the RemoteHandle. Nothing can move to other threads, and if the future panics, the thread will unwind. Seems completely valid to allow that. For 'static, I find it harder to imagine what all the possible consequences might be, but I think it has no reason for being there. I feel less confident to propose a PR to remove it than for Send, so I left it for now. --- futures-util/src/future/future/remote_handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index 06a6b0d1e0..ad7bff4f15 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -52,7 +52,7 @@ impl RemoteHandle { } } -impl Future for RemoteHandle { +impl Future for RemoteHandle { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { From a1e1f50eaf00c67d5aa585714dc8098af8ac07b2 Mon Sep 17 00:00:00 2001 From: Jeff Belgum Date: Wed, 26 Feb 2020 17:15:30 -0800 Subject: [PATCH 04/83] Implement Extend for FuturesOrdered and FuturesUnordered --- futures-util/src/stream/futures_ordered.rs | 11 +++++++++++ futures-util/src/stream/futures_unordered/mod.rs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index a30cbaa40f..7cd7934a68 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -203,3 +203,14 @@ impl FromIterator for FuturesOrdered { iter.into_iter().fold(acc, |mut acc, item| { acc.push(item); acc }) } } + +impl Extend for FuturesOrdered { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter.into_iter() { + self.push(item); + } + } +} diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index cdf2a95bf6..2b7d70455f 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -620,3 +620,14 @@ impl FusedStream for FuturesUnordered { self.is_terminated.load(Relaxed) } } + +impl Extend for FuturesUnordered { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter.into_iter() { + self.push(item); + } + } +} From 6b77d113b2244186c19d19cfaa94d10a181e14ad Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 25 Feb 2020 20:38:01 +0300 Subject: [PATCH 05/83] Specify units for stack_size --- futures-executor/src/thread_pool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 6a87bc5f08..78a225f4d1 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -210,7 +210,7 @@ impl ThreadPoolBuilder { self } - /// Set stack size of threads in the pool. + /// Set stack size of threads in the pool, in bytes. /// /// By default, worker threads use Rust's standard stack size. pub fn stack_size(&mut self, stack_size: usize) -> &mut Self { From 30e50c62422f116511b903d53a59a489dbdbecf3 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 4 Mar 2020 14:34:09 -0800 Subject: [PATCH 06/83] Add a `Cancellation` utility to `oneshot::Sender` (#2092) --- futures-channel/src/oneshot.rs | 26 ++++++++++++++++++++++++++ futures-channel/tests/oneshot.rs | 29 ++++++++--------------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index d9f21d3083..ae7a111d5f 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -358,6 +358,15 @@ impl Sender { self.inner.poll_canceled(cx) } + /// Creates a future that resolves when this `Sender`'s corresponding + /// [`Receiver`](Receiver) half has hung up. + /// + /// This is a utility wrapping [`poll_canceled`](Sender::poll_canceled) + /// to expose a [`Future`](core::future::Future). + pub fn cancellation(&mut self) -> Cancellation<'_, T> { + Cancellation { inner: self } + } + /// Tests to see whether this `Sender`'s corresponding `Receiver` /// has been dropped. /// @@ -375,6 +384,23 @@ impl Drop for Sender { } } +/// A future that resolves when the receiving end of a channel has hung up. +/// +/// This is an `.await`-friendly interface around [`poll_canceled`](Sender::poll_canceled). +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Cancellation<'a, T> { + inner: &'a mut Sender, +} + +impl Future for Cancellation<'_, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + self.inner.poll_canceled(cx) + } +} + /// Error returned from a [`Receiver`](Receiver) when the corresponding /// [`Sender`](Sender) is dropped. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/futures-channel/tests/oneshot.rs b/futures-channel/tests/oneshot.rs index 5194ce468f..a22d039e40 100644 --- a/futures-channel/tests/oneshot.rs +++ b/futures-channel/tests/oneshot.rs @@ -1,9 +1,8 @@ use futures::channel::oneshot::{self, Sender}; use futures::executor::block_on; -use futures::future::{Future, FutureExt, poll_fn}; +use futures::future::{FutureExt, poll_fn}; use futures::task::{Context, Poll}; use futures_test::task::panic_waker_ref; -use std::pin::Pin; use std::sync::mpsc; use std::thread; @@ -25,33 +24,21 @@ fn smoke_poll() { #[test] fn cancel_notifies() { - let (tx, rx) = oneshot::channel::(); + let (mut tx, rx) = oneshot::channel::(); - let t = thread::spawn(|| { - block_on(WaitForCancel { tx }); + let t = thread::spawn(move || { + block_on(tx.cancellation()); }); drop(rx); t.join().unwrap(); } -struct WaitForCancel { - tx: Sender, -} - -impl Future for WaitForCancel { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.tx.poll_canceled(cx) - } -} - #[test] fn cancel_lots() { let (tx, rx) = mpsc::channel::<(Sender<_>, mpsc::Sender<_>)>(); let t = thread::spawn(move || { - for (tx, tx2) in rx { - block_on(WaitForCancel { tx }); + for (mut tx, tx2) in rx { + block_on(tx.cancellation()); tx2.send(()).unwrap(); } }); @@ -93,13 +80,13 @@ fn close() { #[test] fn close_wakes() { - let (tx, mut rx) = oneshot::channel::(); + let (mut tx, mut rx) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); let t = thread::spawn(move || { rx.close(); rx2.recv().unwrap(); }); - block_on(WaitForCancel { tx }); + block_on(tx.cancellation()); tx2.send(()).unwrap(); t.join().unwrap(); } From 18bf8738dfd9f9f7fea6a308a45f763402dff8cb Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Thu, 12 Mar 2020 19:27:18 +0100 Subject: [PATCH 07/83] Stop using impl_trait_in_bindings feature in example (#2094) --- futures-util/src/compat/compat01as03.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 9bb00bf00c..562a7ad170 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -380,15 +380,13 @@ mod io { /// [`AsyncRead`](futures_io::AsyncRead). /// /// ``` - /// #![feature(impl_trait_in_bindings)] - /// # #![allow(incomplete_features)] /// # futures::executor::block_on(async { /// use futures::io::AsyncReadExt; /// use futures_util::compat::AsyncRead01CompatExt; /// /// let input = b"Hello World!"; - /// let reader: impl tokio_io::AsyncRead = std::io::Cursor::new(input); - /// let mut reader: impl futures::io::AsyncRead + Unpin = reader.compat(); + /// let reader /* : impl tokio_io::AsyncRead */ = std::io::Cursor::new(input); + /// let mut reader /* : impl futures::io::AsyncRead + Unpin */ = reader.compat(); /// /// let mut output = Vec::with_capacity(12); /// reader.read_to_end(&mut output).await.unwrap(); From d98e6ecb231988ba0d017870ee95ca51a2c7315c Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Thu, 5 Mar 2020 14:40:53 +0100 Subject: [PATCH 08/83] Use once_cell for sound `&'static Waker` instances --- futures-task/Cargo.toml | 3 ++- futures-task/src/noop_waker.rs | 17 ++++++++++++----- futures-test/Cargo.toml | 5 +++-- futures-test/src/task/panic_waker.rs | 17 ++++++++++++----- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index a387026472..b74d244bfb 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -13,7 +13,7 @@ Tools for working with tasks. [features] default = ["std"] -std = ["alloc"] +std = ["alloc", "once_cell"] alloc = [] # Unstable features @@ -23,6 +23,7 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] +once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } [dev-dependencies] futures = { path = "../futures", version = "0.3.4" } diff --git a/futures-task/src/noop_waker.rs b/futures-task/src/noop_waker.rs index a0924ad925..2a84bcd6f3 100644 --- a/futures-task/src/noop_waker.rs +++ b/futures-task/src/noop_waker.rs @@ -3,7 +3,7 @@ use core::task::{RawWaker, RawWakerVTable, Waker}; use core::ptr::null; #[cfg(feature = "std")] -use core::cell::UnsafeCell; +use once_cell::sync::Lazy; unsafe fn noop_clone(_data: *const ()) -> RawWaker { noop_raw_waker() @@ -47,9 +47,16 @@ pub fn noop_waker() -> Waker { #[inline] #[cfg(feature = "std")] pub fn noop_waker_ref() -> &'static Waker { - thread_local! { - static NOOP_WAKER_INSTANCE: UnsafeCell = - UnsafeCell::new(noop_waker()); + static NOOP_WAKER_INSTANCE: Lazy = Lazy::new(noop_waker); + &*NOOP_WAKER_INSTANCE +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "std")] + fn issue_2091_cross_thread_segfault() { + let waker = std::thread::spawn(super::noop_waker_ref).join().unwrap(); + waker.wake_by_ref(); } - NOOP_WAKER_INSTANCE.with(|l| unsafe { &*l.get() }) } diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0267799c04..0fed77c845 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -18,13 +18,14 @@ futures-io = { version = "0.3.4", path = "../futures-io", default-features = fal futures-util = { version = "0.3.4", path = "../futures-util", default-features = false } futures-executor = { version = "0.3.4", path = "../futures-executor", default-features = false } pin-utils = { version = "0.1.0-alpha.4", default-features = false } +once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } [dev-dependencies] -futures = { version = "0.3.4", path = "../futures", default-features = false, features = ["std"] } +futures = { version = "0.3.4", path = "../futures", default-features = false, features = ["std", "executor"] } [features] default = ["std"] -std = ["futures-core/std", "futures-task/std", "futures-io/std", "futures-util/std", "futures-util/io", "futures-executor/std"] +std = ["futures-core/std", "futures-task/std", "futures-io/std", "futures-util/std", "futures-util/io", "futures-executor/std", "once_cell"] [package.metadata.docs.rs] all-features = true diff --git a/futures-test/src/task/panic_waker.rs b/futures-test/src/task/panic_waker.rs index 74476d823f..e06c9de485 100644 --- a/futures-test/src/task/panic_waker.rs +++ b/futures-test/src/task/panic_waker.rs @@ -1,6 +1,6 @@ use futures_core::task::{Waker, RawWaker, RawWakerVTable}; -use core::cell::UnsafeCell; use core::ptr::null; +use once_cell::sync::Lazy; unsafe fn clone_panic_waker(_data: *const ()) -> RawWaker { raw_panic_waker() @@ -52,9 +52,16 @@ pub fn panic_waker() -> Waker { /// waker.wake_by_ref(); // Will panic /// ``` pub fn panic_waker_ref() -> &'static Waker { - thread_local! { - static PANIC_WAKER_INSTANCE: UnsafeCell = - UnsafeCell::new(panic_waker()); + static PANIC_WAKER_INSTANCE: Lazy = Lazy::new(panic_waker); + &*PANIC_WAKER_INSTANCE +} + +#[cfg(test)] +mod tests { + #[test] + #[should_panic(expected = "should not be woken")] + fn issue_2091_cross_thread_segfault() { + let waker = std::thread::spawn(super::panic_waker_ref).join().unwrap(); + waker.wake_by_ref(); } - PANIC_WAKER_INSTANCE.with(|l| unsafe { &*l.get() }) } From 979f220aaa808f4fccc0fb029b2628d8c89282f4 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 3 Mar 2020 23:07:57 +0100 Subject: [PATCH 09/83] util: Add take_until() in StreamExt The take_until() method stops a stream once a Future passed to take_until() resolves. This is implemented in the TakeUntil stream combinator. Also, the future passed to take_until() can be extracted again out of TakeUntil with the take_future() function. In that case, the stream continues uninterrupted. The stopping Future's eventual result can be extracted with take_result(). Docs and tests included. --- futures-util/src/stream/mod.rs | 2 +- futures-util/src/stream/stream/mod.rs | 48 +++++ futures-util/src/stream/stream/take_until.rs | 211 +++++++++++++++++++ futures/tests/stream.rs | 66 ++++++ 4 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 futures-util/src/stream/stream/take_until.rs diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 4b64e1add4..6a57110009 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -15,7 +15,7 @@ mod stream; pub use self::stream::{ Chain, Collect, Concat, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, Fuse, Inspect, Map, Next, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, - StreamFuture, Take, TakeWhile, Then, Zip, + StreamFuture, Take, TakeWhile, TakeUntil, Then, Zip, }; #[cfg(feature = "std")] diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 670c4a456f..0fb5474e44 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -110,6 +110,10 @@ mod take_while; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::take_while::TakeWhile; +mod take_until; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::take_until::TakeUntil; + mod then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::then::Then; @@ -676,6 +680,50 @@ pub trait StreamExt: Stream { TakeWhile::new(self, f) } + /// Take elements from this stream until the provided future resolves. + /// + /// This function will take elements from the stream until the provided + /// stopping future `fut` resolves. Once the `fut` future becomes ready, + /// this stream combinator will always return that the stream is done. + /// + /// The stopping future may return any type. Once the stream is stopped + /// the result of the stopping future may be aceessed with `TakeUntil::take_result()`. + /// The stream may also be resumed with `TakeUntil::take_future()`. + /// See the documentation of [`TakeUntil`] for more information. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future; + /// use futures::stream::{self, StreamExt}; + /// use futures::task::Poll; + /// + /// let stream = stream::iter(1..=10); + /// + /// let mut i = 0; + /// let stop_fut = future::poll_fn(|_cx| { + /// i += 1; + /// if i <= 5 { + /// Poll::Pending + /// } else { + /// Poll::Ready(()) + /// } + /// }); + /// + /// let stream = stream.take_until(stop_fut); + /// + /// assert_eq!(vec![1, 2, 3, 4, 5], stream.collect::>().await); + /// # }); + /// ``` + fn take_until(self, fut: Fut) -> TakeUntil + where + Fut: Future, + Self: Sized, + { + TakeUntil::new(self, fut) + } + /// Runs this stream to completion, executing the provided asynchronous /// closure for each element on the stream. /// diff --git a/futures-util/src/stream/stream/take_until.rs b/futures-util/src/stream/stream/take_until.rs new file mode 100644 index 0000000000..6fbd605c74 --- /dev/null +++ b/futures-util/src/stream/stream/take_until.rs @@ -0,0 +1,211 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::Future; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_utils::unsafe_pinned; + +// FIXME: docs, tests + +/// Stream for the [`take_until`](super::StreamExt::take_until) method. +#[must_use = "streams do nothing unless polled"] +pub struct TakeUntil { + stream: St, + /// Contains the inner Future on start and None once the inner Future is resolved + /// or taken out by the user. + fut: Option, + /// Contains fut's return value once fut is resolved + fut_result: Option, + /// Whether the future was taken out by the user. + free: bool, +} + +impl Unpin for TakeUntil {} + +impl fmt::Debug for TakeUntil +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, + Fut: Future + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TakeUntil") + .field("stream", &self.stream) + .field("fut", &self.fut) + .finish() + } +} + +impl TakeUntil +where + St: Stream, + Fut: Future, +{ + unsafe_pinned!(stream: St); + unsafe_pinned!(fut: Option); + unsafe_pinned!(fut_result: Option); +} + +impl TakeUntil +where + St: Stream, + Fut: Future, +{ + pub(super) fn new(stream: St, fut: Fut) -> TakeUntil { + TakeUntil { + stream, + fut: Some(fut), + fut_result: None, + free: false, + } + } + + /// Acquires a reference to the underlying stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &St { + &self.stream + } + + /// Acquires a mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut St { + &mut self.stream + } + + /// Acquires a pinned mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { + self.stream() + } + + /// Consumes this combinator, returning the underlying stream and the stopping + /// future, if it isn't resolved yet. + pub fn into_inner(self) -> (St, Option) { + (self.stream, self.fut) + } + + /// Extract the stopping future out of the combinator. + /// The future is returned only if it isn't resolved yet, ie. if the stream isn't stopped yet. + /// Taking out the future means the combinator will be yielding + /// elements from the wrapped stream without ever stopping it. + pub fn take_future(&mut self) -> Option { + if self.fut.is_some() { + self.free = true; + } + + self.fut.take() + } + + /// Once the stopping future is resolved, this method can be used + /// to extract the value returned by the stopping future. + /// + /// This may be used to retrieve arbitrary data from the stopping + /// future, for example a reason why the stream was stopped. + /// + /// This method will return `None` if the future isn't resovled yet, + /// or if the result was already taken out. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future; + /// use futures::stream::{self, StreamExt}; + /// use futures::task::Poll; + /// + /// let stream = stream::iter(1..=10); + /// + /// let mut i = 0; + /// let stop_fut = future::poll_fn(|_cx| { + /// i += 1; + /// if i <= 5 { + /// Poll::Pending + /// } else { + /// Poll::Ready("reason") + /// } + /// }); + /// + /// let mut stream = stream.take_until(stop_fut); + /// let _ = stream.by_ref().collect::>().await; + /// + /// let result = stream.take_result().unwrap(); + /// assert_eq!(result, "reason"); + /// # }); + /// ``` + pub fn take_result(&mut self) -> Option { + self.fut_result.take() + } + + /// Whether the stream was stopped yet by the stopping future + /// being resolved. + pub fn is_stopped(&self) -> bool { + !self.free && self.fut.is_none() + } +} + +impl Stream for TakeUntil +where + St: Stream, + Fut: Future, +{ + type Item = St::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Some(fut) = self.as_mut().fut().as_pin_mut() { + if let Poll::Ready(result) = fut.poll(cx) { + self.as_mut().fut().set(None); + self.as_mut().fut_result().set(Some(result)); + } + } + + if self.is_stopped() { + // Future resolved, inner stream stopped + Poll::Ready(None) + } else { + // Future either not resolved yet or taken out by the user + let item = ready!(self.as_mut().stream().poll_next(cx)); + if item.is_none() { + self.as_mut().fut().set(None); + } + Poll::Ready(item) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.is_stopped() { + return (0, Some(0)); + } + + self.stream.size_hint() + } +} + +impl FusedStream for TakeUntil +where + St: Stream, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.is_stopped() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TakeUntil +where + S: Stream + Sink, + Fut: Future, +{ + type Error = S::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index f0652d06f1..a29e542035 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,5 +1,7 @@ +use futures::future::{self, Future}; use futures::executor::block_on; use futures::stream::{self, StreamExt}; +use futures::task::Poll; #[test] fn select() { @@ -48,3 +50,67 @@ fn scan() { ); }); } + +#[test] +fn take_until() { + fn make_stop_fut(stop_on: u32) -> impl Future { + let mut i = 0; + future::poll_fn(move |_cx| { + i += 1; + if i <= stop_on { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + } + + futures::executor::block_on(async { + // Verify stopping works: + let stream = stream::iter(1u32..=10); + let stop_fut = make_stop_fut(5); + + let stream = stream.take_until(stop_fut); + let last = stream.fold(0, |_, i| async move { i }).await; + assert_eq!(last, 5); + + // Verify take_future() works: + let stream = stream::iter(1..=10); + let stop_fut = make_stop_fut(5); + + let mut stream = stream.take_until(stop_fut); + + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, Some(2)); + + stream.take_future(); + + let last = stream.fold(0, |_, i| async move { i }).await; + assert_eq!(last, 10); + + // Verify take_future() returns None if stream is stopped: + let stream = stream::iter(1u32..=10); + let stop_fut = make_stop_fut(1); + let mut stream = stream.take_until(stop_fut); + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, None); + assert!(stream.take_future().is_none()); + + // Verify TakeUntil is fused: + let mut i = 0; + let stream = stream::poll_fn(move |_cx| { + i += 1; + match i { + 1 => Poll::Ready(Some(1)), + 2 => Poll::Ready(None), + _ => panic!("TakeUntil not fused"), + } + }); + + let stop_fut = make_stop_fut(1); + let mut stream = stream.take_until(stop_fut); + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, None); + assert_eq!(stream.next().await, None); + }); +} From 0a12553169b8da454e8e51c04a98701406ae5dad Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Tue, 17 Mar 2020 14:13:02 +0100 Subject: [PATCH 10/83] full working example for oneshot A friend of mine was confused by the current example for oneshot channel. I'm proposing more illustrative and *WORKING* version here ;) --- futures-channel/src/oneshot.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index ae7a111d5f..cdd0d57446 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -82,22 +82,22 @@ struct Inner { /// /// ``` /// use futures::channel::oneshot; -/// use futures::future::FutureExt; -/// use std::thread; -/// -/// let (sender, receiver) = oneshot::channel::(); -/// -/// # let t = -/// thread::spawn(|| { -/// let future = receiver.map(|i| { -/// println!("got: {:?}", i); +/// use std::{thread, time::Duration}; +/// +/// fn main() { +/// let (sender, receiver) = oneshot::channel::(); +/// +/// thread::spawn(|| { +/// println!("THREAD: sleeping zzz..."); +/// thread::sleep(Duration::from_millis(2000)); +/// println!("THREAD: i'm awake! sending."); +/// sender.send(3).unwrap(); /// }); -/// // ... -/// # return future; -/// }); -/// -/// sender.send(3).unwrap(); -/// # futures::executor::block_on(t.join().unwrap()); +/// +/// println!("MAIN: doing some useful stuff; and waiting..."); +/// +/// println!("MAIN: got: {:?}", futures::executor::block_on(receiver)); +/// } /// ``` pub fn channel() -> (Sender, Receiver) { let inner = Arc::new(Inner::new()); From 3b6e351bad01ed796189c11098d36686f65236da Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Wed, 18 Mar 2020 20:12:17 +0100 Subject: [PATCH 11/83] review: explicit async/await --- futures-channel/src/oneshot.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index cdd0d57446..5f76cee4b9 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -83,21 +83,22 @@ struct Inner { /// ``` /// use futures::channel::oneshot; /// use std::{thread, time::Duration}; -/// -/// fn main() { -/// let (sender, receiver) = oneshot::channel::(); -/// -/// thread::spawn(|| { -/// println!("THREAD: sleeping zzz..."); -/// thread::sleep(Duration::from_millis(2000)); -/// println!("THREAD: i'm awake! sending."); -/// sender.send(3).unwrap(); -/// }); -/// -/// println!("MAIN: doing some useful stuff; and waiting..."); -/// -/// println!("MAIN: got: {:?}", futures::executor::block_on(receiver)); -/// } +/// +/// let (sender, receiver) = oneshot::channel::(); +/// +/// thread::spawn(|| { +/// println!("THREAD: sleeping zzz..."); +/// thread::sleep(Duration::from_millis(1000)); +/// println!("THREAD: i'm awake! sending."); +/// sender.send(3).unwrap(); +/// }); +/// +/// println!("MAIN: doing some useful stuff"); +/// +/// futures::executor::block_on(async { +/// println!("MAIN: waiting for msg..."); +/// println!("MAIN: got: {:?}", receiver.await) +/// }); /// ``` pub fn channel() -> (Sender, Receiver) { let inner = Arc::new(Inner::new()); From 0769c417ac2110527b749aab714988d4dbabca2e Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Fri, 20 Mar 2020 11:34:32 +1300 Subject: [PATCH 12/83] future-utils: Avoid compile failures in tests w/o "std" This failure is not reproducable under the main futures workspace. To replicate, one must first: future-utils/: cargo package And then: cd "/target/package/future-utils-0.3.4" cargo test --no-default-features This patch remedies this issue by: - Moving the imports required for this test into the test itself - Gating the test so it only compiles when --feature=std (and thus, the #[no_std] in lib.rs doesn't activate) This approach also future proofs the code somewhat and makes it potentially easier to relocate to tests/ one day. Closes: https://github.com/rust-lang/futures-rs/issues/2102 --- futures-util/src/future/try_future/try_chain.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/futures-util/src/future/try_future/try_chain.rs b/futures-util/src/future/try_future/try_chain.rs index 662bdf2d26..ddd275a609 100644 --- a/futures-util/src/future/try_future/try_chain.rs +++ b/futures-util/src/future/try_future/try_chain.rs @@ -79,17 +79,19 @@ impl TryChain #[cfg(test)] mod tests { - use std::pin::Pin; - use std::task::Poll; - use futures_test::task::noop_context; + #[cfg(feature = "std")] // dont test with no_std + #[test] + fn try_chain_is_terminated() { + use std::pin::Pin; + use std::task::Poll; - use crate::future::ready; + use futures_test::task::noop_context; - use super::{TryChain, TryChainAction}; + use crate::future::ready; + + use super::{TryChain, TryChainAction}; - #[test] - fn try_chain_is_terminated() { let mut cx = noop_context(); let mut future = TryChain::new(ready(Ok(1)), ()); From 999820566023f8df103de3fb92c278a4acf2c9e3 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 15:30:56 +1300 Subject: [PATCH 13/83] futures: tests/abortable.rs: Guard against compile failures w/o features All tests in this file use both "alloc" and "executor" features. Subsequently, all the tests are inherently disabled without both these features turned on. "use" declaration interned into their various tests to make it cleaner to identify what tests are consuming which functionality, and keep the number of `#[cfg()]` blocks to a minimum. Tests now pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test abortable cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test abortable cargo hack --features "unstable" --each-feature \ test --test abortable cargo hack --features "unstable" --feature-powerset \ test --test abortable --- futures/tests/abortable.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/futures/tests/abortable.rs b/futures/tests/abortable.rs index 5925c9a27b..fcbabe927e 100644 --- a/futures/tests/abortable.rs +++ b/futures/tests/abortable.rs @@ -1,11 +1,10 @@ -use futures::channel::oneshot; -use futures::executor::block_on; -use futures::future::{abortable, Aborted, FutureExt}; -use futures::task::{Context, Poll}; -use futures_test::task::new_count_waker; - +#[cfg(all(feature = "alloc", feature = "executor"))] #[test] fn abortable_works() { + use futures::channel::oneshot; + use futures::future::{abortable, Aborted}; + use futures::executor::block_on; + let (_tx, a_rx) = oneshot::channel::<()>(); let (abortable_rx, abort_handle) = abortable(a_rx); @@ -13,8 +12,14 @@ fn abortable_works() { assert_eq!(Err(Aborted), block_on(abortable_rx)); } +#[cfg(all(feature = "alloc", feature = "executor"))] #[test] fn abortable_awakens() { + use futures::channel::oneshot; + use futures::future::{abortable, Aborted, FutureExt}; + use futures::task::{Context, Poll}; + use futures_test::task::new_count_waker; + let (_tx, a_rx) = oneshot::channel::<()>(); let (mut abortable_rx, abort_handle) = abortable(a_rx); @@ -28,8 +33,12 @@ fn abortable_awakens() { assert_eq!(Poll::Ready(Err(Aborted)), abortable_rx.poll_unpin(&mut cx)); } +#[cfg(all(feature = "alloc", feature = "executor"))] #[test] fn abortable_resolves() { + use futures::channel::oneshot; + use futures::future::abortable; + use futures::executor::block_on; let (tx, a_rx) = oneshot::channel::<()>(); let (abortable_rx, _abort_handle) = abortable(a_rx); From 969c3640a37873964ab1aa5b85add196a6a50ba3 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 16:41:37 +1300 Subject: [PATCH 14/83] futures: tests/arc_wake.rs: Guard against compile failures w/o alloc These tests all use 'alloc'. I could have just blocked this whole file out with a file global #![cfg(feature=alloc)] However, in consideration of future proofing, I instead opted to reduce sharing of common logic so this test can be more clearly updated in future, while also minimizing the risk of warnings about unused things. Therin, the individual test cases control the required features instead of the required features being controlled at the file level. Tests now pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test arc_wake cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test arc_wake cargo hack --features "unstable" --each-feature \ --skip unstable \ test --test arc_wake cargo hack --features "unstable" --feature-powerset \ --skip unstable \ test --test arc_wake --- futures/tests/arc_wake.rs | 107 ++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/futures/tests/arc_wake.rs b/futures/tests/arc_wake.rs index 1940e4f98b..38217f0a77 100644 --- a/futures/tests/arc_wake.rs +++ b/futures/tests/arc_wake.rs @@ -1,60 +1,79 @@ -use futures::task::{self, ArcWake, Waker}; -use std::sync::{Arc, Mutex}; +#[cfg(feature = "alloc")] +mod countingwaker { + use futures::task::{self, ArcWake, Waker}; + use std::sync::{Arc, Mutex}; -struct CountingWaker { - nr_wake: Mutex, -} + struct CountingWaker { + nr_wake: Mutex, + } -impl CountingWaker { - fn new() -> CountingWaker { - CountingWaker { - nr_wake: Mutex::new(0), + impl CountingWaker { + fn new() -> CountingWaker { + CountingWaker { + nr_wake: Mutex::new(0), + } } - } - fn wakes(&self) -> i32 { - *self.nr_wake.lock().unwrap() + fn wakes(&self) -> i32 { + *self.nr_wake.lock().unwrap() + } } -} -impl ArcWake for CountingWaker { - fn wake_by_ref(arc_self: &Arc) { - let mut lock = arc_self.nr_wake.lock().unwrap(); - *lock += 1; + impl ArcWake for CountingWaker { + fn wake_by_ref(arc_self: &Arc) { + let mut lock = arc_self.nr_wake.lock().unwrap(); + *lock += 1; + } } -} -#[test] -fn create_waker_from_arc() { - let some_w = Arc::new(CountingWaker::new()); + #[test] + fn create_from_arc() { + let some_w = Arc::new(CountingWaker::new()); - let w1: Waker = task::waker(some_w.clone()); - assert_eq!(2, Arc::strong_count(&some_w)); - w1.wake_by_ref(); - assert_eq!(1, some_w.wakes()); + let w1: Waker = task::waker(some_w.clone()); + assert_eq!(2, Arc::strong_count(&some_w)); + w1.wake_by_ref(); + assert_eq!(1, some_w.wakes()); - let w2 = w1.clone(); - assert_eq!(3, Arc::strong_count(&some_w)); + let w2 = w1.clone(); + assert_eq!(3, Arc::strong_count(&some_w)); - w2.wake_by_ref(); - assert_eq!(2, some_w.wakes()); + w2.wake_by_ref(); + assert_eq!(2, some_w.wakes()); - drop(w2); - assert_eq!(2, Arc::strong_count(&some_w)); - drop(w1); - assert_eq!(1, Arc::strong_count(&some_w)); -} + drop(w2); + assert_eq!(2, Arc::strong_count(&some_w)); + drop(w1); + assert_eq!(1, Arc::strong_count(&some_w)); + } -struct PanicWaker; + #[test] + fn ref_wake_same() { + let some_w = Arc::new(CountingWaker::new()); -impl ArcWake for PanicWaker { - fn wake_by_ref(_arc_self: &Arc) { - panic!("WAKE UP"); + let w1: Waker = task::waker(some_w.clone()); + let w2 = task::waker_ref(&some_w); + let w3 = w2.clone(); + + assert!(w1.will_wake(&w2)); + assert!(w2.will_wake(&w3)); } } +#[cfg(feature = "alloc")] #[test] fn proper_refcount_on_wake_panic() { + use futures::task::{self, ArcWake, Waker}; + use std::sync::Arc; + + struct PanicWaker; + + impl ArcWake for PanicWaker { + fn wake_by_ref(_arc_self: &Arc) { + panic!("WAKE UP"); + } + } + let some_w = Arc::new(PanicWaker); let w1: Waker = task::waker(some_w.clone()); @@ -63,15 +82,3 @@ fn proper_refcount_on_wake_panic() { drop(w1); assert_eq!(1, Arc::strong_count(&some_w)); // some_w } - -#[test] -fn waker_ref_wake_same() { - let some_w = Arc::new(CountingWaker::new()); - - let w1: Waker = task::waker(some_w.clone()); - let w2 = task::waker_ref(&some_w); - let w3 = w2.clone(); - - assert!(w1.will_wake(&w2)); - assert!(w2.will_wake(&w3)); -} From 8fe0e02316440fef27904844a3641fab5188bd3a Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 17:53:10 +1300 Subject: [PATCH 15/83] futures: tests/async_await_macros.rs: Guard against failures w/o features Tests in this file rely on a complex interaction of features. Subsequently, all tests are restructured to import their own dependencies, and then each test is cfg gated based on the features they inherently need. In 2 cases, common code definitions were moved into the test itself to avoid potentially complex code simply to avoid 'unused' warnings. These tests now pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test async_await_macros cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test async_await_macros cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test async_await_macros cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test async_await_macros --- futures/tests/async_await_macros.rs | 114 +++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index bc717df535..a90d844cd3 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -1,15 +1,12 @@ #![recursion_limit="128"] -use futures::{pending, pin_mut, poll, join, try_join, select}; -use futures::channel::{mpsc, oneshot}; -use futures::executor::block_on; -use futures::future::{self, FutureExt, poll_fn}; -use futures::sink::SinkExt; -use futures::stream::StreamExt; -use futures::task::{Context, Poll}; - +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn poll_and_pending() { + use futures::{pending, pin_mut, poll}; + use futures::executor::block_on; + use futures::task::Poll; + let pending_once = async { pending!() }; block_on(async { pin_mut!(pending_once); @@ -18,8 +15,14 @@ fn poll_and_pending() { }); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn join() { + use futures::{pin_mut, poll, join}; + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::task::Poll; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = oneshot::channel::(); @@ -38,8 +41,14 @@ fn join() { }); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select() { + use futures::select; + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; + let (tx1, rx1) = oneshot::channel::(); let (_tx2, rx2) = oneshot::channel::(); tx1.send(1).unwrap(); @@ -56,8 +65,12 @@ fn select() { assert!(ran); } +#[cfg(all(feature = "alloc", feature = "executor", feature = "async-await"))] #[test] fn select_biased() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; use futures::select_biased; let (tx1, rx1) = oneshot::channel::(); @@ -76,8 +89,15 @@ fn select_biased() { assert!(ran); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_streams() { + use futures::select; + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::sink::SinkExt; + use futures::stream::StreamExt; + let (mut tx1, rx1) = mpsc::channel::(1); let (mut tx2, rx2) = mpsc::channel::(1); let mut rx1 = rx1.fuse(); @@ -119,8 +139,14 @@ fn select_streams() { assert!(ran); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_can_move_uncompleted_futures() { + use futures::select; + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = oneshot::channel::(); tx1.send(1).unwrap(); @@ -145,8 +171,13 @@ fn select_can_move_uncompleted_futures() { assert!(ran); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_nested() { + use futures::select; + use futures::executor::block_on; + use futures::future; + let mut outer_fut = future::ready(1); let mut inner_fut = future::ready(2); let res = block_on(async { @@ -161,8 +192,12 @@ fn select_nested() { assert_eq!(res, 3); } +#[cfg(all(feature = "async-await", feature = "std"))] #[test] fn select_size() { + use futures::select; + use futures::future; + let fut = async { let mut ready = future::ready(0i32); select! { @@ -182,8 +217,13 @@ fn select_size() { assert_eq!(::std::mem::size_of_val(&fut), 40); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_on_non_unpin_expressions() { + use futures::select; + use futures::executor::block_on; + use futures::future::FutureExt; + // The returned Future is !Unpin let make_non_unpin_fut = || { async { 5 @@ -200,8 +240,13 @@ fn select_on_non_unpin_expressions() { assert_eq!(res, 5); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_on_non_unpin_expressions_with_default() { + use futures::select; + use futures::executor::block_on; + use futures::future::FutureExt; + // The returned Future is !Unpin let make_non_unpin_fut = || { async { 5 @@ -219,8 +264,12 @@ fn select_on_non_unpin_expressions_with_default() { assert_eq!(res, 5); } +#[cfg(all(feature = "async-await", feature = "std"))] #[test] fn select_on_non_unpin_size() { + use futures::select; + use futures::future::FutureExt; + // The returned Future is !Unpin let make_non_unpin_fut = || { async { 5 @@ -238,8 +287,13 @@ fn select_on_non_unpin_size() { assert_eq!(48, std::mem::size_of_val(&fut)); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_can_be_used_as_expression() { + use futures::select; + use futures::executor::block_on; + use futures::future; + block_on(async { let res = select! { x = future::ready(7) => { x }, @@ -249,8 +303,14 @@ fn select_can_be_used_as_expression() { }); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_with_default_can_be_used_as_expression() { + use futures::select; + use futures::executor::block_on; + use futures::future::{FutureExt, poll_fn}; + use futures::task::{Context, Poll}; + fn poll_always_pending(_cx: &mut Context<'_>) -> Poll { Poll::Pending } @@ -265,8 +325,13 @@ fn select_with_default_can_be_used_as_expression() { }); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_with_complete_can_be_used_as_expression() { + use futures::select; + use futures::executor::block_on; + use futures::future; + block_on(async { let res = select! { x = future::pending::() => { x }, @@ -278,11 +343,16 @@ fn select_with_complete_can_be_used_as_expression() { }); } -async fn require_mutable(_: &mut i32) {} -async fn async_noop() {} - +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_on_mutable_borrowing_future_with_same_borrow_in_block() { + use futures::select; + use futures::executor::block_on; + use futures::future::FutureExt; + + async fn require_mutable(_: &mut i32) {} + async fn async_noop() {} + block_on(async { let mut value = 234; select! { @@ -294,8 +364,16 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block() { }); } +#[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] #[test] fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { + use futures::select; + use futures::executor::block_on; + use futures::future::FutureExt; + + async fn require_mutable(_: &mut i32) {} + async fn async_noop() {} + block_on(async { let mut value = 234; select! { @@ -310,8 +388,12 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { }); } +#[cfg(feature = "async-await")] #[test] fn join_size() { + use futures::join; + use futures::future; + let fut = async { let ready = future::ready(0i32); join!(ready) @@ -326,8 +408,12 @@ fn join_size() { assert_eq!(::std::mem::size_of_val(&fut), 28); } +#[cfg(feature = "async-await")] #[test] fn try_join_size() { + use futures::try_join; + use futures::future; + let fut = async { let ready = future::ready(Ok::(0)); try_join!(ready) @@ -342,15 +428,21 @@ fn try_join_size() { assert_eq!(::std::mem::size_of_val(&fut), 28); } +#[cfg(feature = "async-await")] #[test] fn join_doesnt_require_unpin() { + use futures::join; + let _ = async { join!(async {}, async {}) }; } +#[cfg(feature = "async-await")] #[test] fn try_join_doesnt_require_unpin() { + use futures::try_join; + let _ = async { try_join!( async { Ok::<(), ()>(()) }, From 9804d82e103539fcd5de84e4a4ff87833521bb2c Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 18:06:11 +1300 Subject: [PATCH 16/83] futures: tests/atomic_waker.rs: Guard against failures w/o executor This test file has only one test, and it requires feature = executor Subsequently, all the imports have been moved inside the test to guard against accidental blind extension, and the test itself is gated with a '#[cfg()]` These tests now pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test atomic_waker cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test atomic_waker cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test atomic_waker cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test atomic_waker --- futures/tests/atomic_waker.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/futures/tests/atomic_waker.rs b/futures/tests/atomic_waker.rs index d9ce753701..5693bd01a1 100644 --- a/futures/tests/atomic_waker.rs +++ b/futures/tests/atomic_waker.rs @@ -1,14 +1,15 @@ -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::thread; - -use futures::executor::block_on; -use futures::future::poll_fn; -use futures::task::{AtomicWaker, Poll}; - +#[cfg(feature = "executor")] #[test] fn basic() { + use std::sync::atomic::AtomicUsize; + use std::sync::atomic::Ordering; + use std::sync::Arc; + use std::thread; + + use futures::executor::block_on; + use futures::future::poll_fn; + use futures::task::{AtomicWaker, Poll}; + let atomic_waker = Arc::new(AtomicWaker::new()); let atomic_waker_copy = atomic_waker.clone(); From 5f819cbcab2284771b13ec86ccff5ea971b6e61e Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 18:28:32 +1300 Subject: [PATCH 17/83] futures: tests/buffer_unordered.rs: Guard against compile fails w/o features The one test in this file requires all of: - alloc - std - executor To run, so this test is omitted without all these features present. Imports moved to inside the test case to discourage blind test extension, and to simplify avoidance of "unused" warnings. Tests now compile with the following: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test buffer_unordered cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test buffer_unordered cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test buffer_unordered cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test buffer_unordered Tests also *usually* pass with a variation of the above when passed cargo hack <...> test --test buffer_unordered \ -- -Z unstable-options --include-ignored However, I still personally see lockups occurring for no obvious reason as per https://github.com/rust-lang/futures-rs/issues/1790 --- futures/tests/buffer_unordered.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/futures/tests/buffer_unordered.rs b/futures/tests/buffer_unordered.rs index 1c559c8544..6485a1ec0f 100644 --- a/futures/tests/buffer_unordered.rs +++ b/futures/tests/buffer_unordered.rs @@ -1,13 +1,14 @@ -use futures::channel::{oneshot, mpsc}; -use futures::executor::{block_on, block_on_stream}; -use futures::sink::SinkExt; -use futures::stream::StreamExt; -use std::sync::mpsc as std_mpsc; -use std::thread; - +#[cfg(all(feature = "alloc", feature = "std", feature = "executor"))] #[test] #[ignore] // FIXME: https://github.com/rust-lang/futures-rs/issues/1790 fn works() { + use futures::channel::{oneshot, mpsc}; + use futures::executor::{block_on, block_on_stream}; + use futures::sink::SinkExt; + use futures::stream::StreamExt; + use std::sync::mpsc as std_mpsc; + use std::thread; + const N: usize = 4; let (mut tx, rx) = mpsc::channel(1); From 98141129150e4dfea704a790d14249747be393aa Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 19:57:07 +1300 Subject: [PATCH 18/83] futures: tests/eager_drop.rs: Fix compiling w/o "alloc" Some of these tests rely on the presence of the "alloc" feature. So I've reworked the imports to make ownership clearer, and reworked some tests to localise shared code, and then `#[cfg()]` gated out tests when their needed features aren't present. These tests now pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test eager_drop cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test eager_drop cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test eager_drop cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test eager_drop --- futures/tests/eager_drop.rs | 169 +++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 72 deletions(-) diff --git a/futures/tests/eager_drop.rs b/futures/tests/eager_drop.rs index 674e40121d..bfb60a7f68 100644 --- a/futures/tests/eager_drop.rs +++ b/futures/tests/eager_drop.rs @@ -1,13 +1,9 @@ -use futures::channel::oneshot; -use futures::future::{self, Future, FutureExt, TryFutureExt}; -use futures::task::{Context, Poll}; -use futures_test::future::FutureTestExt; -use pin_utils::unsafe_pinned; -use std::pin::Pin; -use std::sync::mpsc; - #[test] fn map_ok() { + use futures::future::{self, FutureExt, TryFutureExt}; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + // The closure given to `map_ok` should have been dropped by the time `map` // runs. let (tx1, rx1) = mpsc::channel::<()>(); @@ -26,6 +22,10 @@ fn map_ok() { #[test] fn map_err() { + use futures::future::{self, FutureExt, TryFutureExt}; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + // The closure given to `map_err` should have been dropped by the time `map` // runs. let (tx1, rx1) = mpsc::channel::<()>(); @@ -42,76 +42,101 @@ fn map_err() { rx2.recv().unwrap(); } -struct FutureData { - _data: T, - future: F, -} +mod channelled { + use pin_utils::unsafe_pinned; + use futures::future::Future; + use std::pin::Pin; + use futures::task::{Context,Poll}; -impl FutureData { - unsafe_pinned!(future: F); -} - -impl Future for FutureData { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future().poll(cx) + struct FutureData { + _data: T, + future: F, } -} - -#[test] -fn then_drops_eagerly() { - let (tx0, rx0) = oneshot::channel::<()>(); - let (tx1, rx1) = mpsc::channel::<()>(); - let (tx2, rx2) = mpsc::channel::<()>(); - - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } - .then(move |_| { - assert!(rx1.recv().is_err()); // tx1 should have been dropped - tx2.send(()).unwrap(); - future::ready(()) - }) - .run_in_background(); - - assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); - tx0.send(()).unwrap(); - rx2.recv().unwrap(); -} -#[test] -fn and_then_drops_eagerly() { - let (tx0, rx0) = oneshot::channel::>(); - let (tx1, rx1) = mpsc::channel::<()>(); - let (tx2, rx2) = mpsc::channel::<()>(); + impl FutureData { + unsafe_pinned!(future: F); + } - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } - .and_then(move |_| { - assert!(rx1.recv().is_err()); // tx1 should have been dropped - tx2.send(()).unwrap(); - future::ready(Ok(())) - }) - .run_in_background(); + impl Future for FutureData { + type Output = F::Output; - assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); - tx0.send(Ok(())).unwrap(); - rx2.recv().unwrap(); -} + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.future().poll(cx) + } + } -#[test] -fn or_else_drops_eagerly() { - let (tx0, rx0) = oneshot::channel::>(); - let (tx1, rx1) = mpsc::channel::<()>(); - let (tx2, rx2) = mpsc::channel::<()>(); + #[cfg(feature = "alloc")] + #[test] + fn then_drops_eagerly() { + use futures::channel::oneshot; + use futures::future::{self, FutureExt, TryFutureExt}; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + + let (tx0, rx0) = oneshot::channel::<()>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); + + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + .then(move |_| { + assert!(rx1.recv().is_err()); // tx1 should have been dropped + tx2.send(()).unwrap(); + future::ready(()) + }) + .run_in_background(); + + assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); + tx0.send(()).unwrap(); + rx2.recv().unwrap(); + } - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } - .or_else(move |_| { - assert!(rx1.recv().is_err()); // tx1 should have been dropped - tx2.send(()).unwrap(); - future::ready::>(Ok(())) - }) - .run_in_background(); + #[cfg(feature = "alloc")] + #[test] + fn and_then_drops_eagerly() { + use futures::channel::oneshot; + use futures::future::{self, TryFutureExt}; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + + let (tx0, rx0) = oneshot::channel::>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); + + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + .and_then(move |_| { + assert!(rx1.recv().is_err()); // tx1 should have been dropped + tx2.send(()).unwrap(); + future::ready(Ok(())) + }) + .run_in_background(); + + assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); + tx0.send(Ok(())).unwrap(); + rx2.recv().unwrap(); + } - assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); - tx0.send(Err(())).unwrap(); - rx2.recv().unwrap(); + #[cfg(feature = "alloc")] + #[test] + fn or_else_drops_eagerly() { + use futures::channel::oneshot; + use futures::future::{self, TryFutureExt}; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + + let (tx0, rx0) = oneshot::channel::>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); + + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + .or_else(move |_| { + assert!(rx1.recv().is_err()); // tx1 should have been dropped + tx2.send(()).unwrap(); + future::ready::>(Ok(())) + }) + .run_in_background(); + + assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); + tx0.send(Err(())).unwrap(); + rx2.recv().unwrap(); + } } From bcf61da0b87e0f9c8401b4551bfc40d058b34ba0 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 21:06:33 +1300 Subject: [PATCH 19/83] futures: tests/eventual.rs : Disable w/o executor + thread-pool This test requires both executor and thread-pool enabled to function. But as the code is all heavily shared and intertwined, it seems the most sensible thing to do is just file-level disabling. Spurious build failures no longer occur with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test eventual cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test eventual cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test eventual cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test eventual --- futures/tests/eventual.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures/tests/eventual.rs b/futures/tests/eventual.rs index bff000dd09..68355886a8 100644 --- a/futures/tests/eventual.rs +++ b/futures/tests/eventual.rs @@ -1,3 +1,4 @@ +#![cfg(all(feature = "executor", feature = "thread-pool"))] use futures::channel::oneshot; use futures::executor::ThreadPool; use futures::future::{self, ok, Future, FutureExt, TryFutureExt}; From a30e4739f09a2e85889495106188617cbeb2a1be Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 21:39:09 +1300 Subject: [PATCH 20/83] futures: tests/futures_ordered.rs : Guard against failures w/o features Tests in this file use varying combinations of "alloc" and "executor" features. Subsequently, all the imports have been relocated to inside the test functions to make ownership clear, and easily isolate the needed features, and tests have been appropriately gated with `#[cfg()]` blocks. Tests now avoid spurious compile failures with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test futures_ordered cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test futures_ordered cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test futures_ordered cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test futures_ordered --- futures/tests/futures_ordered.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/futures/tests/futures_ordered.rs b/futures/tests/futures_ordered.rs index d06b62f76c..74a220a58e 100644 --- a/futures/tests/futures_ordered.rs +++ b/futures/tests/futures_ordered.rs @@ -1,12 +1,11 @@ -use futures::channel::oneshot; -use futures::executor::{block_on, block_on_stream}; -use futures::future::{self, join, Future, FutureExt, TryFutureExt}; -use futures::stream::{StreamExt, FuturesOrdered}; -use futures_test::task::noop_context; -use std::any::Any; - +#[cfg(all(feature = "alloc", feature="executor"))] #[test] fn works_1() { + use futures::channel::oneshot; + use futures::executor::block_on_stream; + use futures::stream::{StreamExt, FuturesOrdered}; + use futures_test::task::noop_context; + let (a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -26,8 +25,14 @@ fn works_1() { assert_eq!(None, iter.next()); } +#[cfg(feature = "alloc")] #[test] fn works_2() { + use futures::channel::oneshot; + use futures::future::{join, FutureExt}; + use futures::stream::{StreamExt, FuturesOrdered}; + use futures_test::task::noop_context; + let (a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -46,8 +51,13 @@ fn works_2() { assert!(stream.poll_next_unpin(&mut cx).is_ready()); } +#[cfg(feature = "executor")] #[test] fn from_iterator() { + use futures::executor::block_on; + use futures::future; + use futures::stream::{StreamExt, FuturesOrdered}; + let stream = vec![ future::ready::(1), future::ready::(2), @@ -57,8 +67,15 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1,2,3]); } +#[cfg(feature = "alloc")] #[test] fn queue_never_unblocked() { + use futures::channel::oneshot; + use futures::future::{self, Future, TryFutureExt}; + use futures::stream::{StreamExt, FuturesOrdered}; + use futures_test::task::noop_context; + use std::any::Any; + let (_a_tx, a_rx) = oneshot::channel::>(); let (b_tx, b_rx) = oneshot::channel::>(); let (c_tx, c_rx) = oneshot::channel::>(); From 47d3637f43bdee110d88bc4bdc06c81780e7d0ac Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 23:08:01 +1300 Subject: [PATCH 21/83] futures: tests/futures_unordered.rs: Guard against compile failures w/o futures Various tests in this file require "alloc" functionality, "executor" functionality, or both. Imports relocated into relevant tests for clarity of ownership and ease of gating, and then tests are gated off with relevant `#[cfg()]` attributes. No spurious build failures (or test failures) are now seen with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test futures_unordered cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test futures_unordered cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test futures_unordered cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test futures_unordered --- futures/tests/futures_unordered.rs | 78 +++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/futures/tests/futures_unordered.rs b/futures/tests/futures_unordered.rs index 57eb98fd1b..328590307d 100644 --- a/futures/tests/futures_unordered.rs +++ b/futures/tests/futures_unordered.rs @@ -1,18 +1,11 @@ -use std::marker::Unpin; -use std::pin::Pin; -use std::sync::atomic::{AtomicBool, Ordering}; - -use futures::channel::oneshot; -use futures::executor::{block_on, block_on_stream}; -use futures::future::{self, join, Future, FutureExt}; -use futures::stream::{FusedStream, FuturesUnordered, StreamExt}; -use futures::task::{Context, Poll}; -use futures_test::future::FutureTestExt; -use futures_test::task::noop_context; -use futures_test::{assert_stream_done, assert_stream_next}; - +#[cfg(feature = "alloc")] #[test] fn is_terminated() { + use futures::future; + use futures::stream::{FusedStream, FuturesUnordered, StreamExt}; + use futures::task::Poll; + use futures_test::task::noop_context; + let mut cx = noop_context(); let mut tasks = FuturesUnordered::new(); @@ -38,8 +31,13 @@ fn is_terminated() { assert_eq!(tasks.is_terminated(), true); } +#[cfg(all(feature = "alloc", feature = "executor"))] #[test] fn works_1() { + use futures::channel::oneshot; + use futures::executor::block_on_stream; + use futures::stream::FuturesUnordered; + let (a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -60,8 +58,15 @@ fn works_1() { assert_eq!(None, iter.next()); } +#[cfg(feature = "alloc")] #[test] fn works_2() { + use futures::channel::oneshot; + use futures::future::{join, FutureExt}; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::Poll; + use futures_test::task::noop_context; + let (a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -83,8 +88,13 @@ fn works_2() { assert_eq!(stream.poll_next_unpin(&mut cx), Poll::Ready(None)); } +#[cfg(feature = "executor")] #[test] fn from_iterator() { + use futures::executor::block_on; + use futures::future; + use futures::stream::{FuturesUnordered, StreamExt}; + let stream = vec![ future::ready::(1), future::ready::(2), @@ -96,8 +106,15 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } +#[cfg(feature = "alloc")] #[test] fn finished_future() { + use std::marker::Unpin; + use futures::channel::oneshot; + use futures::future::{self, Future, FutureExt}; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures_test::task::noop_context; + let (_a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -121,8 +138,13 @@ fn finished_future() { assert!(stream.poll_next_unpin(cx).is_pending()); } +#[cfg(all(feature = "alloc", feature = "executor"))] #[test] fn iter_mut_cancel() { + use futures::channel::oneshot; + use futures::executor::block_on_stream; + use futures::stream::FuturesUnordered; + let (a_tx, a_rx) = oneshot::channel::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); @@ -147,8 +169,12 @@ fn iter_mut_cancel() { assert_eq!(iter.next(), None); } +#[cfg(feature = "alloc")] #[test] fn iter_mut_len() { + use futures::future; + use futures::stream::FuturesUnordered; + let mut stream = vec![ future::pending::<()>(), future::pending::<()>(), @@ -168,8 +194,18 @@ fn iter_mut_len() { assert!(iter_mut.next().is_none()); } +#[cfg(feature = "executor")] #[test] fn iter_cancel() { + use std::marker::Unpin; + use std::pin::Pin; + use std::sync::atomic::{AtomicBool, Ordering}; + + use futures::executor::block_on_stream; + use futures::future::{self, Future, FutureExt}; + use futures::stream::FuturesUnordered; + use futures::task::{Context, Poll}; + struct AtomicCancel { future: F, cancel: AtomicBool, @@ -213,8 +249,12 @@ fn iter_cancel() { assert_eq!(iter.next(), None); } +#[cfg(feature = "alloc")] #[test] fn iter_len() { + use futures::future; + use futures::stream::FuturesUnordered; + let stream = vec![ future::pending::<()>(), future::pending::<()>(), @@ -234,8 +274,14 @@ fn iter_len() { assert!(iter.next().is_none()); } +#[cfg(feature = "alloc")] #[test] fn futures_not_moved_after_poll() { + use futures::future; + use futures::stream::FuturesUnordered; + use futures_test::future::FutureTestExt; + use futures_test::{assert_stream_done, assert_stream_next}; + // Future that will be ready after being polled twice, // asserting that it does not move. let fut = future::ready(()).pending_once().assert_unmoved(); @@ -246,8 +292,14 @@ fn futures_not_moved_after_poll() { assert_stream_done!(stream); } +#[cfg(feature = "alloc")] #[test] fn len_valid_during_out_of_order_completion() { + use futures::channel::oneshot; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::Poll; + use futures_test::task::noop_context; + // Complete futures out-of-order and add new futures afterwards to ensure // length values remain correct. let (a_tx, a_rx) = oneshot::channel::(); From f6829e1e446032d718cdbeeb682908a132bdddd7 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sat, 14 Mar 2020 23:32:27 +1300 Subject: [PATCH 22/83] futures: tests/future_try_flatten_stream.rs: Avoid compile failures Two of the tests in this file are dependent on the "executor" feature. All imports reorganized into their nearest proximal necessary location to make import ownership clearer, and gated tests out by feature requirements with `#[cfg()]` No spurious build errors or test failures now with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test future_try_flatten_stream cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test future_try_flatten_stream cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test future_try_flatten_stream cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test future_try_flatten_stream --- futures/tests/future_try_flatten_stream.rs | 98 ++++++++++++---------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/futures/tests/future_try_flatten_stream.rs b/futures/tests/future_try_flatten_stream.rs index 082c5efa9a..aa85ed000d 100644 --- a/futures/tests/future_try_flatten_stream.rs +++ b/futures/tests/future_try_flatten_stream.rs @@ -1,13 +1,10 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures::executor::block_on_stream; -use futures::future::{ok, err, TryFutureExt}; -use futures::sink::Sink; -use futures::stream::{self, Stream, StreamExt}; -use futures::task::{Context, Poll}; - +#[cfg(feature = "executor")] #[test] fn successful_future() { + use futures::executor::block_on_stream; + use futures::future::{ok, TryFutureExt}; + use futures::stream::{self, StreamExt}; + let stream_items = vec![17, 19]; let future_of_a_stream = ok::<_, bool>(stream::iter(stream_items).map(Ok)); @@ -19,20 +16,28 @@ fn successful_future() { assert_eq!(None, iter.next()); } -struct PanickingStream { - _marker: PhantomData<(T, E)> -} +#[cfg(feature = "executor")] +#[test] +fn failed_future() { + use core::marker::PhantomData; + use core::pin::Pin; + use futures::executor::block_on_stream; + use futures::future::{err, TryFutureExt}; + use futures::stream::Stream; + use futures::task::{Context, Poll}; -impl Stream for PanickingStream { - type Item = Result; + struct PanickingStream { + _marker: PhantomData<(T, E)> + } + + impl Stream for PanickingStream { + type Item = Result; - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } -} -#[test] -fn failed_future() { let future_of_a_stream = err::, _>(10); let stream = future_of_a_stream.try_flatten_stream(); let mut iter = block_on_stream(stream); @@ -40,37 +45,44 @@ fn failed_future() { assert_eq!(None, iter.next()); } -struct StreamSink(PhantomData<(T, E, Item)>); +#[test] +fn assert_impls() { + use core::marker::PhantomData; + use core::pin::Pin; + use futures::sink::Sink; + use futures::stream::Stream; + use futures::task::{Context, Poll}; + use futures::future::{ok, TryFutureExt}; -impl Stream for StreamSink { - type Item = Result; - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() - } -} + struct StreamSink(PhantomData<(T, E, Item)>); -impl Sink for StreamSink { - type Error = E; - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() - } - fn start_send(self: Pin<&mut Self>, _: Item) -> Result<(), Self::Error> { - panic!() + impl Stream for StreamSink { + type Item = Result; + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() - } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() + + impl Sink for StreamSink { + type Error = E; + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } + fn start_send(self: Pin<&mut Self>, _: Item) -> Result<(), Self::Error> { + panic!() + } + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } -} -fn assert_stream(_: &S) {} -fn assert_sink, Item>(_: &S) {} -fn assert_stream_sink, Item>(_: &S) {} + fn assert_stream(_: &S) {} + fn assert_sink, Item>(_: &S) {} + fn assert_stream_sink, Item>(_: &S) {} -#[test] -fn assert_impls() { let s = ok(StreamSink::<(), (), ()>(PhantomData)).try_flatten_stream(); assert_stream(&s); assert_sink(&s); From c3c58e6d33291b5a85dfe5ac94b2d27fb213ecdf Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sun, 15 Mar 2020 00:11:04 +1300 Subject: [PATCH 23/83] futures: tests/inspect.rs: Avoid failures w/o "executor" - imports relocated inside the test unit - gated test unit with appropriate `#[cfg()]` Tests compile and pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test inspect cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test inspect cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test inspect cargo hack --features "unstable" \ --feature-powerset \ --skip unstable \ test --test inspect --- futures/tests/inspect.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/futures/tests/inspect.rs b/futures/tests/inspect.rs index 42f6f73634..4cbe4779aa 100644 --- a/futures/tests/inspect.rs +++ b/futures/tests/inspect.rs @@ -1,8 +1,9 @@ -use futures::executor::block_on; -use futures::future::{self, FutureExt}; - +#[cfg(feature = "executor")] #[test] fn smoke() { + use futures::executor::block_on; + use futures::future::{self, FutureExt}; + let mut counter = 0; { From 0b14c704d84f2dedd7cb910ac77b0f0332cd9acc Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sun, 15 Mar 2020 22:08:11 +1300 Subject: [PATCH 24/83] futures: tests/io_buf_reader.rs: Avoid compile failures w/o features Various tests in this file need either or both "std" and "executor" Imports and symbols pushed down as low as possible to make symbol ownership more obvious, and also make gating tests against features easier. Tests appropriately gated with `#[cfg()]` Tests now compile and pass where possible with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_buf_reader cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_buf_reader cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test io_buf_reader cargo hack --features "unstable" \ --feature-powerset \ --skip "unstable" \ test --test io_buf_reader --- futures/tests/io_buf_reader.rs | 287 +++++++++++++++++++-------------- 1 file changed, 167 insertions(+), 120 deletions(-) diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index a3d723a691..07d934d4e1 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -1,32 +1,10 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::io::{ - AsyncSeek, AsyncSeekExt, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, - AllowStdIo, BufReader, Cursor, SeekFrom, -}; -use futures::task::{Context, Poll}; -use futures_test::task::noop_context; -use std::cmp; -use std::io; -use std::pin::Pin; - -/// A dummy reader intended at testing short-reads propagation. -struct ShortReader { - lengths: Vec, -} - -impl io::Read for ShortReader { - fn read(&mut self, _: &mut [u8]) -> io::Result { - if self.lengths.is_empty() { - Ok(0) - } else { - Ok(self.lengths.remove(0)) - } - } -} - +#[cfg(any(feature = "std", feature = "executor"))] macro_rules! run_fill_buf { ($reader:expr) => {{ + use futures_test::task::noop_context; + use futures::task::Poll; + use std::pin::Pin; + let mut cx = noop_context(); loop { if let Poll::Ready(x) = Pin::new(&mut $reader).poll_fill_buf(&mut cx) { @@ -36,8 +14,83 @@ macro_rules! run_fill_buf { }}; } +#[cfg(any(feature = "std", feature = "executor"))] +mod util { + use futures::future::Future; + pub fn run(mut f: F) -> F::Output { + use futures_test::task::noop_context; + use futures::task::Poll; + use futures::future::FutureExt; + + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } +} + +#[cfg(feature = "std")] +mod maybe_pending { + use futures::task::{Context,Poll}; + use std::{cmp,io}; + use std::pin::Pin; + use futures::io::{AsyncRead,AsyncBufRead}; + + pub struct MaybePending<'a> { + inner: &'a [u8], + ready_read: bool, + ready_fill_buf: bool, + } + + impl<'a> MaybePending<'a> { + pub fn new(inner: &'a [u8]) -> Self { + Self { inner, ready_read: false, ready_fill_buf: false } + } + } + + impl AsyncRead for MaybePending<'_> { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) + -> Poll> + { + if self.ready_read { + self.ready_read = false; + Pin::new(&mut self.inner).poll_read(cx, buf) + } else { + self.ready_read = true; + Poll::Pending + } + } + } + + impl AsyncBufRead for MaybePending<'_> { + fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) + -> Poll> + { + if self.ready_fill_buf { + self.ready_fill_buf = false; + if self.inner.is_empty() { return Poll::Ready(Ok(&[])) } + let len = cmp::min(2, self.inner.len()); + Poll::Ready(Ok(&self.inner[0..len])) + } else { + self.ready_fill_buf = true; + Poll::Pending + } + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + self.inner = &self.inner[amt..]; + } + } +} + +#[cfg(feature = "executor")] #[test] fn test_buffered_reader() { + use futures::executor::block_on; + use futures::io::{AsyncReadExt, BufReader}; + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; let mut reader = BufReader::with_capacity(2, inner); @@ -73,8 +126,14 @@ fn test_buffered_reader() { assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); } +#[cfg(feature = "executor")] #[test] fn test_buffered_reader_seek() { + use futures::executor::block_on; + use futures::io::{AsyncSeekExt, AsyncBufRead, BufReader, Cursor, SeekFrom}; + use std::pin::Pin; + use util::run; + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); @@ -88,8 +147,13 @@ fn test_buffered_reader_seek() { assert_eq!(block_on(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); } +#[cfg(feature = "executor")] #[test] fn test_buffered_reader_seek_underflow() { + use futures::executor::block_on; + use futures::io::{AsyncSeekExt, AsyncBufRead, AllowStdIo, BufReader, SeekFrom}; + use std::io; + // gimmick reader that yields its position modulo 256 for each byte struct PositionReader { pos: u64 @@ -134,8 +198,28 @@ fn test_buffered_reader_seek_underflow() { assert_eq!(reader.get_ref().get_ref().pos, expected); } +#[cfg(feature = "executor")] #[test] fn test_short_reads() { + use futures::executor::block_on; + use futures::io::{AsyncReadExt, AllowStdIo, BufReader}; + use std::io; + + /// A dummy reader intended at testing short-reads propagation. + struct ShortReader { + lengths: Vec, + } + + impl io::Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + if self.lengths.is_empty() { + Ok(0) + } else { + Ok(self.lengths.remove(0)) + } + } + } + let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; let mut reader = BufReader::new(AllowStdIo::new(inner)); let mut buf = [0, 0]; @@ -148,63 +232,13 @@ fn test_short_reads() { assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); } -struct MaybePending<'a> { - inner: &'a [u8], - ready_read: bool, - ready_fill_buf: bool, -} - -impl<'a> MaybePending<'a> { - fn new(inner: &'a [u8]) -> Self { - Self { inner, ready_read: false, ready_fill_buf: false } - } -} - -impl AsyncRead for MaybePending<'_> { - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { - if self.ready_read { - self.ready_read = false; - Pin::new(&mut self.inner).poll_read(cx, buf) - } else { - self.ready_read = true; - Poll::Pending - } - } -} - -impl AsyncBufRead for MaybePending<'_> { - fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { - if self.ready_fill_buf { - self.ready_fill_buf = false; - if self.inner.is_empty() { return Poll::Ready(Ok(&[])) } - let len = cmp::min(2, self.inner.len()); - Poll::Ready(Ok(&self.inner[0..len])) - } else { - self.ready_fill_buf = true; - Poll::Pending - } - } - - fn consume(mut self: Pin<&mut Self>, amt: usize) { - self.inner = &self.inner[amt..]; - } -} - -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - +#[cfg(feature = "std")] #[test] fn maybe_pending() { + use futures::io::{AsyncReadExt, BufReader}; + use util::run; + use maybe_pending::MaybePending; + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; let mut reader = BufReader::with_capacity(2, MaybePending::new(inner)); @@ -240,8 +274,13 @@ fn maybe_pending() { assert_eq!(run(reader.read(&mut buf)).unwrap(), 0); } +#[cfg(feature = "std")] #[test] fn maybe_pending_buf_read() { + use futures::io::{AsyncBufReadExt, BufReader}; + use util::run; + use maybe_pending::MaybePending; + let inner = MaybePending::new(&[0, 1, 2, 3, 1, 0]); let mut reader = BufReader::with_capacity(2, inner); let mut v = Vec::new(); @@ -258,55 +297,63 @@ fn maybe_pending_buf_read() { assert_eq!(v, []); } -struct MaybePendingSeek<'a> { - inner: Cursor<&'a [u8]>, - ready: bool, -} - -impl<'a> MaybePendingSeek<'a> { - fn new(inner: &'a [u8]) -> Self { - Self { inner: Cursor::new(inner), ready: true } +// https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 +#[cfg(feature = "std")] +#[test] +fn maybe_pending_seek() { + use futures::io::{AsyncBufRead, AsyncSeek, AsyncSeekExt, AsyncRead, BufReader, + Cursor, SeekFrom + }; + use futures::task::{Context,Poll}; + use std::io; + use std::pin::Pin; + use util::run; + pub struct MaybePendingSeek<'a> { + inner: Cursor<&'a [u8]>, + ready: bool, } -} -impl AsyncRead for MaybePendingSeek<'_> { - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { - Pin::new(&mut self.inner).poll_read(cx, buf) + impl<'a> MaybePendingSeek<'a> { + pub fn new(inner: &'a [u8]) -> Self { + Self { inner: Cursor::new(inner), ready: true } + } } -} -impl AsyncBufRead for MaybePendingSeek<'_> { - fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { - let this: *mut Self = &mut *self as *mut _; - Pin::new(&mut unsafe { &mut *this }.inner).poll_fill_buf(cx) + impl AsyncRead for MaybePendingSeek<'_> { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) + -> Poll> + { + Pin::new(&mut self.inner).poll_read(cx, buf) + } } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + impl AsyncBufRead for MaybePendingSeek<'_> { + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) + -> Poll> + { + let this: *mut Self = &mut *self as *mut _; + Pin::new(&mut unsafe { &mut *this }.inner).poll_fill_buf(cx) + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } } -} -impl AsyncSeek for MaybePendingSeek<'_> { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { - if self.ready { - self.ready = false; - Pin::new(&mut self.inner).poll_seek(cx, pos) - } else { - self.ready = true; - Poll::Pending + impl AsyncSeek for MaybePendingSeek<'_> { + fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) + -> Poll> + { + if self.ready { + self.ready = false; + Pin::new(&mut self.inner).poll_seek(cx, pos) + } else { + self.ready = true; + Poll::Pending + } } } -} -// https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 -#[test] -fn maybe_pending_seek() { let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; let mut reader = BufReader::with_capacity(2, MaybePendingSeek::new(inner)); From 1d57bf53e16da8535b6c0d8baef58d2de5fe6b61 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sun, 15 Mar 2020 22:58:35 +1300 Subject: [PATCH 25/83] futures: tests/io_buf_writer.rs : Avoid compile failures w/o features Tests in this file use combinations of "std" and "executor" features. Imports and symbols moved to deeper lexical scopes to make owernship clearer, and to facilitate easy feature gating. Tests gated as appropriate with `#[cfg()]` Tests now compile and pass with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_buf_writer cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_buf_writer cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test "io_buf_writer" cargo hack --features "unstable" \ --feature-powerset \ --skip "unstable" \ test --test "io_buf_writer" --- futures/tests/io_buf_writer.rs | 222 +++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 91 deletions(-) diff --git a/futures/tests/io_buf_writer.rs b/futures/tests/io_buf_writer.rs index 7bdcd16df0..935335be93 100644 --- a/futures/tests/io_buf_writer.rs +++ b/futures/tests/io_buf_writer.rs @@ -1,13 +1,70 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::io::{AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter, Cursor, SeekFrom}; -use futures::task::{Context, Poll}; -use futures_test::task::noop_context; -use std::io; -use std::pin::Pin; +#[cfg(feature = "std")] +mod maybe_pending { + use futures::io::AsyncWrite; + use futures::task::{Context, Poll}; + use std::io; + use std::pin::Pin; + + pub struct MaybePending { + pub inner: Vec, + ready: bool, + } + + impl MaybePending { + pub fn new(inner: Vec) -> Self { + Self { inner, ready: false } + } + } + + impl AsyncWrite for MaybePending { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if self.ready { + self.ready = false; + Pin::new(&mut self.inner).poll_write(cx, buf) + } else { + self.ready = true; + Poll::Pending + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } + } +} + +#[cfg(any(feature = "std", feature = "executor"))] +mod util { + use futures::future::Future; + + pub fn run(mut f: F) -> F::Output { + use futures::future::FutureExt; + use futures::task::Poll; + use futures_test::task::noop_context; + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } +} + +#[cfg(feature = "executor")] #[test] fn buf_writer() { + use futures::executor::block_on; + use futures::io::{AsyncWriteExt, BufWriter}; + let mut writer = BufWriter::with_capacity(2, Vec::new()); block_on(writer.write(&[0, 1])).unwrap(); @@ -48,8 +105,12 @@ fn buf_writer() { assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); } +#[cfg(feature = "executor")] #[test] fn buf_writer_inner_flushes() { + use futures::executor::block_on; + use futures::io::{AsyncWriteExt, BufWriter}; + let mut w = BufWriter::with_capacity(3, Vec::new()); block_on(w.write(&[0, 1])).unwrap(); assert_eq!(*w.get_ref(), []); @@ -58,8 +119,12 @@ fn buf_writer_inner_flushes() { assert_eq!(w, [0, 1]); } +#[cfg(feature = "executor")] #[test] fn buf_writer_seek() { + use futures::executor::block_on; + use futures::io::{AsyncSeekExt, AsyncWriteExt, BufWriter, Cursor, SeekFrom}; + // FIXME: when https://github.com/rust-lang/futures-rs/issues/1510 fixed, // use `Vec::new` instead of `vec![0; 8]`. let mut w = BufWriter::with_capacity(3, Cursor::new(vec![0; 8])); @@ -73,52 +138,14 @@ fn buf_writer_seek() { assert_eq!(&w.into_inner().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); } -struct MaybePending { - inner: Vec, - ready: bool, -} - -impl MaybePending { - fn new(inner: Vec) -> Self { - Self { inner, ready: false } - } -} - -impl AsyncWrite for MaybePending { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - if self.ready { - self.ready = false; - Pin::new(&mut self.inner).poll_write(cx, buf) - } else { - self.ready = true; - Poll::Pending - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx) - } -} - -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - +#[cfg(feature = "std")] #[test] fn maybe_pending_buf_writer() { + use futures::io::{AsyncWriteExt, BufWriter}; + + use maybe_pending::MaybePending; + use util::run; + let mut writer = BufWriter::with_capacity(2, MaybePending::new(Vec::new())); run(writer.write(&[0, 1])).unwrap(); @@ -159,8 +186,14 @@ fn maybe_pending_buf_writer() { assert_eq!(&writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); } +#[cfg(feature = "std")] #[test] fn maybe_pending_buf_writer_inner_flushes() { + use futures::io::{AsyncWriteExt, BufWriter}; + + use maybe_pending::MaybePending; + use util::run; + let mut w = BufWriter::with_capacity(3, MaybePending::new(Vec::new())); run(w.write(&[0, 1])).unwrap(); assert_eq!(&w.get_ref().inner, &[]); @@ -169,59 +202,66 @@ fn maybe_pending_buf_writer_inner_flushes() { assert_eq!(w, [0, 1]); } +#[cfg(feature = "std")] +#[test] +fn maybe_pending_buf_writer_seek() { + use futures::io::{AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter, Cursor, SeekFrom}; + use futures::task::{Context, Poll}; + use std::io; + use std::pin::Pin; -struct MaybePendingSeek { - inner: Cursor>, - ready_write: bool, - ready_seek: bool, -} + use util::run; -impl MaybePendingSeek { - fn new(inner: Vec) -> Self { - Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } + struct MaybePendingSeek { + inner: Cursor>, + ready_write: bool, + ready_seek: bool, } -} -impl AsyncWrite for MaybePendingSeek { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - if self.ready_write { - self.ready_write = false; - Pin::new(&mut self.inner).poll_write(cx, buf) - } else { - self.ready_write = true; - Poll::Pending + impl MaybePendingSeek { + fn new(inner: Vec) -> Self { + Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } + impl AsyncWrite for MaybePendingSeek { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if self.ready_write { + self.ready_write = false; + Pin::new(&mut self.inner).poll_write(cx, buf) + } else { + self.ready_write = true; + Poll::Pending + } + } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } } -} -impl AsyncSeek for MaybePendingSeek { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { - if self.ready_seek { - self.ready_seek = false; - Pin::new(&mut self.inner).poll_seek(cx, pos) - } else { - self.ready_seek = true; - Poll::Pending + impl AsyncSeek for MaybePendingSeek { + fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) + -> Poll> + { + if self.ready_seek { + self.ready_seek = false; + Pin::new(&mut self.inner).poll_seek(cx, pos) + } else { + self.ready_seek = true; + Poll::Pending + } } } -} -#[test] -fn maybe_pending_buf_writer_seek() { // FIXME: when https://github.com/rust-lang/futures-rs/issues/1510 fixed, // use `Vec::new` instead of `vec![0; 8]`. let mut w = BufWriter::with_capacity(3, MaybePendingSeek::new(vec![0; 8])); From 43ffd53cf6716a42294f51dedbd4eaab4eb80b35 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sun, 15 Mar 2020 23:14:32 +1300 Subject: [PATCH 26/83] futures: tests/io_cursor.rs : Avoid breaking w/o features All the tests in this file consume "std" and "executor" features. As a future proofing step, and to clarify import ownership and simplify feature gating, imports have been pushed into the tests themselves. And all tests have been individually gated with `#[cfg()]`. Tests now compile and pass (where compiled) with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_cursor cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_cursor cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test io_cursor cargo hack --features "unstable" \ --feature-powerset \ --skip "unstable" \ test --test io_cursor --- futures/tests/io_cursor.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/futures/tests/io_cursor.rs b/futures/tests/io_cursor.rs index 4f80a75a37..0a93c8362c 100644 --- a/futures/tests/io_cursor.rs +++ b/futures/tests/io_cursor.rs @@ -1,11 +1,12 @@ -use assert_matches::assert_matches; -use futures::future::lazy; -use futures::io::{AsyncWrite, Cursor}; -use futures::task::Poll; -use std::pin::Pin; - +#[cfg(all(feature = "std", feature = "executor"))] #[test] fn cursor_asyncwrite_vec() { + use assert_matches::assert_matches; + use futures::future::lazy; + use futures::io::{AsyncWrite, Cursor}; + use futures::task::Poll; + use std::pin::Pin; + let mut cursor = Cursor::new(vec![0; 5]); futures::executor::block_on(lazy(|cx| { assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); @@ -16,8 +17,15 @@ fn cursor_asyncwrite_vec() { assert_eq!(cursor.into_inner(), [1, 2, 3, 4, 5, 6, 6, 7]); } +#[cfg(all(feature = "std", feature = "executor"))] #[test] fn cursor_asyncwrite_box() { + use assert_matches::assert_matches; + use futures::future::lazy; + use futures::io::{AsyncWrite, Cursor}; + use futures::task::Poll; + use std::pin::Pin; + let mut cursor = Cursor::new(vec![0; 5].into_boxed_slice()); futures::executor::block_on(lazy(|cx| { assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); From 4d3e529b8ae70faa9bf9c55decbc932cf627bfb6 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Sun, 15 Mar 2020 23:45:34 +1300 Subject: [PATCH 27/83] futures: tests/io_lines.rs : Avoid compile failures w/o features Tests in this file require either "executor" or "std" features. Imports and test related symbols pushed to deep isolation to simplify owership and test gating. Tests are gated as appropriate with `#[cfg()]` Tests now compile and pass (where relevant) with: cargo hack --each-feature \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_lines cargo hack --feature-powerset \ --skip bilock \ --skip cfg-target-has-atomic \ --skip read-initializer \ test --test io_lines cargo hack --features "unstable" \ --each-feature \ --skip "unstable" \ test --test io_lines cargo hack --features "unstable" \ --feature-powerset \ --skip "unstable" \ test --test io_lines --- futures/tests/io_lines.rs | 67 +++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs index 39eafa9a66..e10edd08f9 100644 --- a/futures/tests/io_lines.rs +++ b/futures/tests/io_lines.rs @@ -1,19 +1,34 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; -use futures::io::{AsyncBufReadExt, Cursor}; -use futures::task::Poll; -use futures_test::io::AsyncReadTestExt; -use futures_test::task::noop_context; - -macro_rules! block_on_next { - ($expr:expr) => { - block_on($expr.next()).unwrap().unwrap() - }; +#[cfg(any(feature = "std", feature = "executor"))] +mod util { + use futures::future::Future; + + pub fn run(mut f: F) -> F::Output { + use futures_test::task::noop_context; + use futures::task::Poll; + use futures::future::FutureExt; + + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } } +#[cfg(feature = "executor")] #[test] fn lines() { + use futures::executor::block_on; + use futures::stream::StreamExt; + use futures::io::{AsyncBufReadExt, Cursor}; + + macro_rules! block_on_next { + ($expr:expr) => { + block_on($expr.next()).unwrap().unwrap() + }; + } + let buf = Cursor::new(&b"12\r"[..]); let mut s = buf.lines(); assert_eq!(block_on_next!(s), "12\r".to_string()); @@ -26,23 +41,21 @@ fn lines() { assert!(block_on(s.next()).is_none()); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - -macro_rules! run_next { - ($expr:expr) => { - run($expr.next()).unwrap().unwrap() - }; -} - +#[cfg(feature = "std")] #[test] fn maybe_pending() { + use futures::stream::{self, StreamExt, TryStreamExt}; + use futures::io::AsyncBufReadExt; + use futures_test::io::AsyncReadTestExt; + + use util::run; + + macro_rules! run_next { + ($expr:expr) => { + run($expr.next()).unwrap().unwrap() + }; + } + let buf = stream::iter(vec![&b"12"[..], &b"\r"[..]]) .map(Ok) .into_async_read() From 804e039411bb729a97fefedab0c6000fb384aeb1 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:24:13 +1300 Subject: [PATCH 28/83] futures: tests/io_read_exact.rs: Don't fail w/o executor Reflow test to move imports into test function, and then gate entire test function on "executor" feature. cargo test --test io_read_exact Now compiles and passes in every valid combination of features ( see previous commits by me in tests/ for the command ) i --- futures/tests/io_read_exact.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/futures/tests/io_read_exact.rs b/futures/tests/io_read_exact.rs index 4941773cb5..a772e344ad 100644 --- a/futures/tests/io_read_exact.rs +++ b/futures/tests/io_read_exact.rs @@ -1,8 +1,9 @@ -use futures::executor::block_on; -use futures::io::AsyncReadExt; - +#[cfg(feature = "executor")] #[test] fn read_exact() { + use futures::executor::block_on; + use futures::io::AsyncReadExt; + let mut reader: &[u8] = &[1, 2, 3, 4, 5]; let mut out = [0u8; 3]; From a8ceb4b64465db35b579c6b1719a8680cdcfef19 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 29/83] futures: tests/io_read_line.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_read_line.rs | 39 +++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs index d1dba5e6d2..ab25f264e8 100644 --- a/futures/tests/io_read_line.rs +++ b/futures/tests/io_read_line.rs @@ -1,13 +1,9 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; -use futures::io::{AsyncBufReadExt, Cursor}; -use futures::task::Poll; -use futures_test::io::AsyncReadTestExt; -use futures_test::task::noop_context; - +#[cfg(feature = "executor")] #[test] fn read_line() { + use futures::executor::block_on; + use futures::io::{AsyncBufReadExt, Cursor}; + let mut buf = Cursor::new(b"12"); let mut v = String::new(); assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 2); @@ -25,17 +21,28 @@ fn read_line() { assert_eq!(v, ""); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; +#[cfg(feature = "std")] +#[test] +fn maybe_pending() { + use futures::future::Future; + + fn run(mut f: F) -> F::Output { + use futures::future::FutureExt; + use futures::task::Poll; + use futures_test::task::noop_context; + + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } } } -} -#[test] -fn maybe_pending() { + use futures::stream::{self, StreamExt, TryStreamExt}; + use futures::io::AsyncBufReadExt; + use futures_test::io::AsyncReadTestExt; + let mut buf = b"12".interleave_pending(); let mut v = String::new(); assert_eq!(run(buf.read_line(&mut v)).unwrap(), 2); From 37152fee61af14547bf2f4b8bf11cacbc788bbe4 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 30/83] futures: tests/io_read.rs: Don't break w/o "std" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_read.rs | 58 +++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/futures/tests/io_read.rs b/futures/tests/io_read.rs index f99c4edbb6..ba68fcc3ed 100644 --- a/futures/tests/io_read.rs +++ b/futures/tests/io_read.rs @@ -1,33 +1,44 @@ -use futures::io::AsyncRead; -use futures_test::task::panic_context; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; +#[cfg(feature = "std")] +mod mock_reader { + use futures::io::AsyncRead; + use std::io; + use std::pin::Pin; + use std::task::{Context, Poll}; -struct MockReader { - fun: Box Poll>>, -} + pub struct MockReader { + fun: Box Poll>>, + } -impl MockReader { - pub fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { - MockReader { fun: Box::new(fun) } + impl MockReader { + pub fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { + MockReader { fun: Box::new(fun) } + } } -} -impl AsyncRead for MockReader { - fn poll_read( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &mut [u8] - ) -> Poll> { - (self.get_mut().fun)(buf) + impl AsyncRead for MockReader { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8] + ) -> Poll> { + (self.get_mut().fun)(buf) + } } } /// Verifies that the default implementation of `poll_read_vectored` /// calls `poll_read` with an empty slice if no buffers are provided. +#[cfg(feature = "std")] #[test] fn read_vectored_no_buffers() { + use futures::io::AsyncRead; + use futures_test::task::panic_context; + use std::io; + use std::pin::Pin; + use std::task::Poll; + + use mock_reader::MockReader; + let mut reader = MockReader::new(|buf| { assert_eq!(buf, b""); Err(io::ErrorKind::BrokenPipe.into()).into() @@ -42,8 +53,17 @@ fn read_vectored_no_buffers() { /// Verifies that the default implementation of `poll_read_vectored` /// calls `poll_read` with the first non-empty buffer. +#[cfg(feature = "std")] #[test] fn read_vectored_first_non_empty() { + use futures::io::AsyncRead; + use futures_test::task::panic_context; + use std::io; + use std::pin::Pin; + use std::task::Poll; + + use mock_reader::MockReader; + let mut reader = MockReader::new(|buf| { assert_eq!(buf.len(), 4); buf.copy_from_slice(b"four"); From 5698ed47d4c4001ab085b6f88af29a3c9f507168 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 31/83] futures: tests/io_read_to_string.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_read_to_string.rs | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/futures/tests/io_read_to_string.rs b/futures/tests/io_read_to_string.rs index db825af21c..0a79a22c76 100644 --- a/futures/tests/io_read_to_string.rs +++ b/futures/tests/io_read_to_string.rs @@ -1,13 +1,9 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; -use futures::io::{AsyncReadExt, Cursor}; -use futures::task::Poll; -use futures_test::io::AsyncReadTestExt; -use futures_test::task::noop_context; - +#[cfg(all(feature = "std", feature = "executor"))] #[test] fn read_to_string() { + use futures::executor::block_on; + use futures::io::{AsyncReadExt, Cursor}; + let mut c = Cursor::new(&b""[..]); let mut v = String::new(); assert_eq!(block_on(c.read_to_string(&mut v)).unwrap(), 0); @@ -23,17 +19,26 @@ fn read_to_string() { assert!(block_on(c.read_to_string(&mut v)).is_err()); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - +#[cfg(feature = "std")] #[test] fn interleave_pending() { + use futures::future::Future; + use futures::stream::{self, StreamExt, TryStreamExt}; + use futures::io::AsyncReadExt; + use futures_test::io::AsyncReadTestExt; + + fn run(mut f: F) -> F::Output { + use futures::future::FutureExt; + use futures_test::task::noop_context; + use futures::task::Poll; + + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } let mut buf = stream::iter(vec![&b"12"[..], &b"33"[..], &b"3"[..]]) .map(Ok) .into_async_read() From 073a6529c4c4e0431a83030004ae40ee30869737 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 32/83] futures: tests/io_read_until.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_read_until.rs | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/futures/tests/io_read_until.rs b/futures/tests/io_read_until.rs index 5152281795..1e018b735e 100644 --- a/futures/tests/io_read_until.rs +++ b/futures/tests/io_read_until.rs @@ -1,13 +1,9 @@ -use futures::executor::block_on; -use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; -use futures::io::{AsyncBufReadExt, Cursor}; -use futures::task::Poll; -use futures_test::io::AsyncReadTestExt; -use futures_test::task::noop_context; - +#[cfg(feature = "executor")] #[test] fn read_until() { + use futures::executor::block_on; + use futures::io::{AsyncBufReadExt, Cursor}; + let mut buf = Cursor::new(b"12"); let mut v = Vec::new(); assert_eq!(block_on(buf.read_until(b'3', &mut v)).unwrap(), 2); @@ -25,17 +21,29 @@ fn read_until() { assert_eq!(v, []); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} +#[cfg(feature = "std")] #[test] fn maybe_pending() { + use futures::future::Future; + + fn run(mut f: F) -> F::Output { + use futures::future::FutureExt; + use futures_test::task::noop_context; + use futures::task::Poll; + + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } + + use futures::stream::{self, StreamExt, TryStreamExt}; + use futures::io::AsyncBufReadExt; + use futures_test::io::AsyncReadTestExt; + let mut buf = b"12".interleave_pending(); let mut v = Vec::new(); assert_eq!(run(buf.read_until(b'3', &mut v)).unwrap(), 2); From 2448dbd8fe7198d178c1b7446a34cd28a08cd582 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 33/83] futures: tests/io_read_until.rs: Disable w/o "std" This whole test file is simple enough, and uses only the one import, so the whole file has been gated out when "std" is missing via a file-global `#![cfg()]` Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures/tests/io_window.rs b/futures/tests/io_window.rs index 98df69c83b..8cc41a3084 100644 --- a/futures/tests/io_window.rs +++ b/futures/tests/io_window.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "std")] use futures::io::Window; #[test] From 0a8b2be555c98acac72cef8132926b2df9bdba15 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 34/83] futures: tests/io_write.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/io_write.rs | 70 +++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/futures/tests/io_write.rs b/futures/tests/io_write.rs index b96344446c..af0d5c3b16 100644 --- a/futures/tests/io_write.rs +++ b/futures/tests/io_write.rs @@ -1,41 +1,52 @@ -use futures::io::AsyncWrite; -use futures_test::task::panic_context; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; +#[cfg(feature = "std")] +mod mock_writer { + use futures::io::AsyncWrite; + use std::io; + use std::pin::Pin; + use std::task::{Context, Poll}; -struct MockWriter { - fun: Box Poll>>, -} - -impl MockWriter { - pub fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { - MockWriter { fun: Box::new(fun) } + pub struct MockWriter { + fun: Box Poll>>, } -} -impl AsyncWrite for MockWriter { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - (self.get_mut().fun)(buf) + impl MockWriter { + pub fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { + MockWriter { fun: Box::new(fun) } + } } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - panic!() - } + impl AsyncWrite for MockWriter { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + (self.get_mut().fun)(buf) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + panic!() + } - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - panic!() + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + panic!() + } } } /// Verifies that the default implementation of `poll_write_vectored` /// calls `poll_write` with an empty slice if no buffers are provided. +#[cfg(feature = "std")] #[test] fn write_vectored_no_buffers() { + use futures::io::AsyncWrite; + use futures_test::task::panic_context; + use std::io; + use std::pin::Pin; + use std::task::Poll; + + use mock_writer::MockWriter; + let mut writer = MockWriter::new(|buf| { assert_eq!(buf, b""); Err(io::ErrorKind::BrokenPipe.into()).into() @@ -50,8 +61,17 @@ fn write_vectored_no_buffers() { /// Verifies that the default implementation of `poll_write_vectored` /// calls `poll_write` with the first non-empty buffer. +#[cfg(feature = "std")] #[test] fn write_vectored_first_non_empty() { + use futures::io::AsyncWrite; + use futures_test::task::panic_context; + use std::io; + use std::pin::Pin; + use std::task::Poll; + + use mock_writer::MockWriter; + let mut writer = MockWriter::new(|buf| { assert_eq!(buf, b"four"); Poll::Ready(Ok(4)) From a333388973b240093c74c34b6ec23a76d421ff11 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 35/83] futures: tests/join_all.rs: Don't break w/o "executor" feature - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/join_all.rs | 44 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/futures/tests/join_all.rs b/futures/tests/join_all.rs index 63967bf987..0d8fbf2ece 100644 --- a/futures/tests/join_all.rs +++ b/futures/tests/join_all.rs @@ -1,29 +1,38 @@ -use futures_util::future::*; -use std::future::Future; -use futures::executor::block_on; -use std::fmt::Debug; - -fn assert_done(actual_fut: F, expected: T) -where - T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, -{ - let output = block_on(actual_fut()); - assert_eq!(output, expected); +#[cfg(feature = "executor")] +mod util { + use std::future::Future; + use std::fmt::Debug; + + pub fn assert_done(actual_fut: F, expected: T) + where + T: PartialEq + Debug, + F: FnOnce() -> Box + Unpin>, + { + use futures::executor::block_on; + + let output = block_on(actual_fut()); + assert_eq!(output, expected); + } } +#[cfg(feature = "executor")] #[test] fn collect_collects() { - assert_done(|| Box::new(join_all(vec![ready(1), ready(2)])), vec![1, 2]); - assert_done(|| Box::new(join_all(vec![ready(1)])), vec![1]); + use futures_util::future::{join_all,ready}; + + util::assert_done(|| Box::new(join_all(vec![ready(1), ready(2)])), vec![1, 2]); + util::assert_done(|| Box::new(join_all(vec![ready(1)])), vec![1]); // REVIEW: should this be implemented? // assert_done(|| Box::new(join_all(Vec::::new())), vec![]); // TODO: needs more tests } +#[cfg(feature = "executor")] #[test] fn join_all_iter_lifetime() { + use futures_util::future::{join_all,ready}; + use std::future::Future; // In futures-rs version 0.1, this function would fail to typecheck due to an overly // conservative type parameterization of `JoinAll`. fn sizes<'a>(bufs: Vec<&'a [u8]>) -> Box> + Unpin> { @@ -31,12 +40,15 @@ fn join_all_iter_lifetime() { Box::new(join_all(iter)) } - assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), vec![3 as usize, 0, 1]); + util::assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), vec![3 as usize, 0, 1]); } +#[cfg(feature = "executor")] #[test] fn join_all_from_iter() { - assert_done( + use futures_util::future::{JoinAll,ready}; + + util::assert_done( || Box::new(vec![ready(1), ready(2)].into_iter().collect::>()), vec![1, 2], ) From 983a16eeb6a6651d09c92a1d61fda563da910f6e Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 36/83] futures: tests/macro_comma_support.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/macro_comma_support.rs | 37 +++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/futures/tests/macro_comma_support.rs b/futures/tests/macro_comma_support.rs index 111f65af4e..e6a609b8a6 100644 --- a/futures/tests/macro_comma_support.rs +++ b/futures/tests/macro_comma_support.rs @@ -1,29 +1,41 @@ -#[macro_use] -extern crate futures; - -use futures::{ - executor::block_on, - future::{self, FutureExt}, - task::Poll, -}; - +#[cfg(feature = "executor")] #[test] fn ready() { + use futures::{ + executor::block_on, + future, + task::Poll, + ready, + }; + block_on(future::poll_fn(|_| { ready!(Poll::Ready(()),); Poll::Ready(()) })) } +#[cfg(all(feature = "executor", feature = "async-await"))] #[test] fn poll() { + use futures::{ + executor::block_on, + future::FutureExt, + poll, + }; + block_on(async { let _ = poll!(async {}.boxed(),); }) } +#[cfg(all(feature = "executor", feature = "async-await"))] #[test] fn join() { + use futures::{ + executor::block_on, + join + }; + block_on(async { let future1 = async { 1 }; let future2 = async { 2 }; @@ -31,8 +43,15 @@ fn join() { }) } +#[cfg(all(feature = "executor", feature = "async-await"))] #[test] fn try_join() { + use futures::{ + executor::block_on, + future::FutureExt, + try_join, + }; + block_on(async { let future1 = async { 1 }.never_error(); let future2 = async { 2 }.never_error(); From 26db3d5e63cfe34a42983f50219167bc630408fe Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 37/83] futures: tests/mutex.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/mutex.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/futures/tests/mutex.rs b/futures/tests/mutex.rs index bad53a9b8f..7ee9f41fdb 100644 --- a/futures/tests/mutex.rs +++ b/futures/tests/mutex.rs @@ -1,23 +1,24 @@ -use futures::channel::mpsc; -use futures::executor::block_on; -use futures::future::{ready, FutureExt}; -use futures::lock::Mutex; -use futures::stream::StreamExt; -use futures::task::{Context, SpawnExt}; -use futures_test::future::FutureTestExt; -use futures_test::task::{new_count_waker, panic_context}; -use std::sync::Arc; - +#[cfg(all(feature = "alloc", feature = "std"))] #[test] fn mutex_acquire_uncontested() { + use futures::future::FutureExt; + use futures::lock::Mutex; + use futures_test::task::panic_context; + let mutex = Mutex::new(()); for _ in 0..10 { assert!(mutex.lock().poll_unpin(&mut panic_context()).is_ready()); } } +#[cfg(all(feature = "alloc", feature = "std"))] #[test] fn mutex_wakes_waiters() { + use futures::future::FutureExt; + use futures::lock::Mutex; + use futures::task::Context; + use futures_test::task::{new_count_waker, panic_context}; + let mutex = Mutex::new(()); let (waker, counter) = new_count_waker(); let lock = mutex.lock().poll_unpin(&mut panic_context()); @@ -34,8 +35,18 @@ fn mutex_wakes_waiters() { assert!(waiter.poll_unpin(&mut panic_context()).is_ready()); } +#[cfg(feature = "thread-pool")] #[test] fn mutex_contested() { + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::future::ready; + use futures::lock::Mutex; + use futures::stream::StreamExt; + use futures::task::SpawnExt; + use futures_test::future::FutureTestExt; + use std::sync::Arc; + let (tx, mut rx) = mpsc::unbounded(); let pool = futures::executor::ThreadPool::builder() .pool_size(16) From 8b0cc7ed139993749fb275ce275b559735006055 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 38/83] futures: tests/object_safety.rs: Don't break w/o "std" Fortunately, the author of this test used the same style I've been employing in my other patches to tests/ Therefor, fixing this to work properly without the "std" feature is a simple one line change. Tests now pass for this in all valid feature combinations. Hat-tip to @taiki-e :) --- futures/tests/object_safety.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures/tests/object_safety.rs b/futures/tests/object_safety.rs index 30c892f5e6..b49ee88764 100644 --- a/futures/tests/object_safety.rs +++ b/futures/tests/object_safety.rs @@ -28,6 +28,7 @@ fn sink() { assert_is_object_safe::<&dyn Sink<(), Error = ()>>(); } +#[cfg(feature = "std")] // futures::io #[test] fn io() { // `AsyncReadExt`, `AsyncWriteExt`, `AsyncSeekExt` and `AsyncBufReadExt` are not object safe. From ad473c0c9c3bc0c53fa875882ca853b2634a1bed Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 39/83] futures: tests/oneshot.rs: Don't break w/o "alloc" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/oneshot.rs | 43 ++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/futures/tests/oneshot.rs b/futures/tests/oneshot.rs index 58951ec581..302160b101 100644 --- a/futures/tests/oneshot.rs +++ b/futures/tests/oneshot.rs @@ -1,11 +1,12 @@ -use futures::channel::oneshot; -use futures::future::{FutureExt, TryFutureExt}; -use futures_test::future::FutureTestExt; -use std::sync::mpsc; -use std::thread; - +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_send1() { + use futures::channel::oneshot; + use futures::future::TryFutureExt; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + use std::thread; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); @@ -15,8 +16,15 @@ fn oneshot_send1() { t.join().unwrap(); } +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_send2() { + use futures::channel::oneshot; + use futures::future::TryFutureExt; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + use std::thread; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); @@ -25,8 +33,15 @@ fn oneshot_send2() { assert_eq!(1, rx2.recv().unwrap()); } +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_send3() { + use futures::channel::oneshot; + use futures::future::TryFutureExt; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + use std::thread; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); @@ -35,8 +50,14 @@ fn oneshot_send3() { assert_eq!(1, rx2.recv().unwrap()); } +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_drop_tx1() { + use futures::channel::oneshot; + use futures::future::FutureExt; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); @@ -46,8 +67,15 @@ fn oneshot_drop_tx1() { assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); } +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_drop_tx2() { + use futures::channel::oneshot; + use futures::future::FutureExt; + use futures_test::future::FutureTestExt; + use std::sync::mpsc; + use std::thread; + let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); @@ -58,8 +86,11 @@ fn oneshot_drop_tx2() { assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); } +#[cfg(feature = "alloc")] // channel #[test] fn oneshot_drop_rx() { + use futures::channel::oneshot; + let (tx, rx) = oneshot::channel::(); drop(rx); assert_eq!(Err(2), tx.send(2)); From 0157c822e7b7c7a81a9ce3a9d3e54e18ffc5a77f Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 40/83] futures: tests/ready_queue.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/ready_queue.rs | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/futures/tests/ready_queue.rs b/futures/tests/ready_queue.rs index 15a0befda8..be6ccc3f2c 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -1,18 +1,20 @@ -use futures::channel::oneshot; -use futures::executor::{block_on, block_on_stream}; -use futures::future; -use futures::stream::{FuturesUnordered, StreamExt}; -use futures::task::Poll; -use futures_test::task::noop_context; -use std::panic::{self, AssertUnwindSafe}; -use std::sync::{Arc, Barrier}; -use std::thread; - -trait AssertSendSync: Send + Sync {} -impl AssertSendSync for FuturesUnordered<()> {} +#[cfg(feature = "alloc")] // FuturesUnordered +mod assert_send_sync { + use futures::stream::FuturesUnordered; + pub trait AssertSendSync: Send + Sync {} + impl AssertSendSync for FuturesUnordered<()> {} +} + +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn basic_usage() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::Poll; + block_on(future::lazy(move |cx| { let mut queue = FuturesUnordered::new(); let (tx1, rx1) = oneshot::channel(); @@ -39,8 +41,15 @@ fn basic_usage() { })); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn resolving_errors() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::Poll; + block_on(future::lazy(move |cx| { let mut queue = FuturesUnordered::new(); let (tx1, rx1) = oneshot::channel(); @@ -67,8 +76,15 @@ fn resolving_errors() { })); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn dropping_ready_queue() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future; + use futures::stream::FuturesUnordered; + use futures_test::task::noop_context; + block_on(future::lazy(move |_| { let queue = FuturesUnordered::new(); let (mut tx1, rx1) = oneshot::channel::<()>(); @@ -94,8 +110,15 @@ fn dropping_ready_queue() { })); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn stress() { + use futures::channel::oneshot; + use futures::executor::block_on_stream; + use futures::stream::FuturesUnordered; + use std::sync::{Arc, Barrier}; + use std::thread; + const ITER: usize = 300; for i in 0..ITER { @@ -137,8 +160,15 @@ fn stress() { } } +#[cfg(feature = "executor")] // executor #[test] fn panicking_future_dropped() { + use futures::executor::block_on; + use futures::future; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::Poll; + use std::panic::{self, AssertUnwindSafe}; + block_on(future::lazy(move |cx| { let mut queue = FuturesUnordered::new(); queue.push(future::poll_fn(|_| -> Poll> { panic!() })); From b7c5bcf63c9c6393c05d892455ae6d6b3d4148f8 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 41/83] futures: tests/ready_queue.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/recurse.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/futures/tests/recurse.rs b/futures/tests/recurse.rs index 2920a41a59..d3f4124159 100644 --- a/futures/tests/recurse.rs +++ b/futures/tests/recurse.rs @@ -1,10 +1,11 @@ -use futures::executor::block_on; -use futures::future::{self, FutureExt, BoxFuture}; -use std::sync::mpsc; -use std::thread; - +#[cfg(feature = "executor")] // executor:: #[test] fn lots() { + use futures::executor::block_on; + use futures::future::{self, FutureExt, BoxFuture}; + use std::sync::mpsc; + use std::thread; + fn do_it(input: (i32, i32)) -> BoxFuture<'static, i32> { let (n, x) = input; if n == 0 { From e15e3e0d9dce13dee00f40314d0bee43f479e325 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 42/83] futures: tests/select_all.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/select_all.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/futures/tests/select_all.rs b/futures/tests/select_all.rs index aad977d351..9a6d7367ae 100644 --- a/futures/tests/select_all.rs +++ b/futures/tests/select_all.rs @@ -1,9 +1,10 @@ -use futures::executor::block_on; -use futures::future::{ready, select_all}; -use std::collections::HashSet; - +#[cfg(feature = "executor")] // executor:: #[test] fn smoke() { + use futures::executor::block_on; + use futures::future::{ready, select_all}; + use std::collections::HashSet; + let v = vec![ ready(1), ready(2), From 83b3ac036e507a8884b677cc6e0c48f764d78c84 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 43/83] futures: tests/select_ok.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/select_ok.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/futures/tests/select_ok.rs b/futures/tests/select_ok.rs index db88a95c7a..dd3703ed02 100644 --- a/futures/tests/select_ok.rs +++ b/futures/tests/select_ok.rs @@ -1,8 +1,9 @@ -use futures::executor::block_on; -use futures::future::{err, ok, select_ok}; - +#[cfg(feature = "executor")] // executor:: #[test] fn ignore_err() { + use futures::executor::block_on; + use futures::future::{err, ok, select_ok}; + let v = vec![ err(1), err(2), @@ -21,8 +22,12 @@ fn ignore_err() { assert!(v.is_empty()); } +#[cfg(feature = "executor")] // executor:: #[test] fn last_err() { + use futures::executor::block_on; + use futures::future::{err, ok, select_ok}; + let v = vec![ ok(1), err(2), From b884db6da5f61fc1918df45b3c67cd984a643de9 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 44/83] futures: tests/shared.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/shared.rs | 74 ++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/futures/tests/shared.rs b/futures/tests/shared.rs index 8402bfe10b..d338809e5c 100644 --- a/futures/tests/shared.rs +++ b/futures/tests/shared.rs @@ -1,12 +1,23 @@ -use futures::channel::oneshot; -use futures::executor::{block_on, LocalPool}; -use futures::future::{self, FutureExt, TryFutureExt, LocalFutureObj}; -use futures::task::LocalSpawn; -use std::cell::{Cell, RefCell}; -use std::rc::Rc; -use std::thread; +mod count_clone { + use std::cell::Cell; + use std::rc::Rc; + pub struct CountClone(pub Rc>); + + impl Clone for CountClone { + fn clone(&self) -> Self { + self.0.set(self.0.get() + 1); + CountClone(self.0.clone()) + } + } +} + +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: fn send_shared_oneshot_and_wait_on_multiple_threads(threads_number: u32) { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; + use std::thread; let (tx, rx) = oneshot::channel::(); let f = rx.shared(); let join_handles = (0..threads_number) @@ -26,23 +37,32 @@ fn send_shared_oneshot_and_wait_on_multiple_threads(threads_number: u32) { } } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn one_thread() { send_shared_oneshot_and_wait_on_multiple_threads(1); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn two_threads() { send_shared_oneshot_and_wait_on_multiple_threads(2); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn many_threads() { send_shared_oneshot_and_wait_on_multiple_threads(1000); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn drop_on_one_task_ok() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::{self, FutureExt, TryFutureExt}; + use std::thread; + let (tx, rx) = oneshot::channel::(); let f1 = rx.shared(); let f2 = f1.clone(); @@ -69,8 +89,15 @@ fn drop_on_one_task_ok() { t2.join().unwrap(); } + +#[cfg(feature = "executor")] // executor:: #[test] fn drop_in_poll() { + use futures::executor::block_on; + use futures::future::{self, FutureExt, LocalFutureObj}; + use std::cell::RefCell; + use std::rc::Rc; + let slot1 = Rc::new(RefCell::new(None)); let slot2 = slot1.clone(); @@ -85,8 +112,14 @@ fn drop_in_poll() { assert_eq!(block_on(future1), 1); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn peek() { + use futures::channel::oneshot; + use futures::executor::LocalPool; + use futures::future::{FutureExt, LocalFutureObj}; + use futures::task::LocalSpawn; + let mut local_pool = LocalPool::new(); let spawn = &mut local_pool.spawner(); @@ -115,17 +148,17 @@ fn peek() { } } -struct CountClone(Rc>); - -impl Clone for CountClone { - fn clone(&self) -> Self { - self.0.set(self.0.get() + 1); - CountClone(self.0.clone()) - } -} - +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn dont_clone_in_single_owner_shared_future() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; + use std::cell::Cell; + use std::rc::Rc; + + use count_clone::CountClone; + let counter = CountClone(Rc::new(Cell::new(0))); let (tx, rx) = oneshot::channel(); @@ -136,8 +169,17 @@ fn dont_clone_in_single_owner_shared_future() { assert_eq!(block_on(rx).unwrap().0.get(), 0); } +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: #[test] fn dont_do_unnecessary_clones_on_output() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::FutureExt; + use std::cell::Cell; + use std::rc::Rc; + + use count_clone::CountClone; + let counter = CountClone(Rc::new(Cell::new(0))); let (tx, rx) = oneshot::channel(); From 876b04e89dec9997ec5fbd468f0949b73e62e8b2 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 45/83] futures: tests/sink_fanout.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/sink_fanout.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/futures/tests/sink_fanout.rs b/futures/tests/sink_fanout.rs index e57b2d8c7b..62f32f2872 100644 --- a/futures/tests/sink_fanout.rs +++ b/futures/tests/sink_fanout.rs @@ -1,11 +1,12 @@ -use futures::channel::mpsc; -use futures::executor::block_on; -use futures::future::join3; -use futures::sink::SinkExt; -use futures::stream::{self, StreamExt}; - +#[cfg(all(feature = "alloc", feature="std", feature="executor"))] // channel::mpsc, executor:: #[test] fn it_works() { + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::future::join3; + use futures::sink::SinkExt; + use futures::stream::{self, StreamExt}; + let (tx1, rx1) = mpsc::channel(1); let (tx2, rx2) = mpsc::channel(2); let tx = tx1.fanout(tx2).sink_map_err(|_| ()); From bb5a989080c02a28e2e60f7446c45d1fc3aa0d6d Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 46/83] futures: tests/sink.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/sink.rs | 594 ++++++++++++++++++++++++++---------------- 1 file changed, 374 insertions(+), 220 deletions(-) diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index f967e1b643..f6ce28c3fe 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -1,43 +1,258 @@ -use futures::channel::{mpsc, oneshot}; -use futures::executor::block_on; -use futures::future::{self, Future, FutureExt, TryFutureExt}; -use futures::never::Never; -use futures::ready; -use futures::sink::{Sink, SinkErrInto, SinkExt}; -use futures::stream::{self, Stream, StreamExt}; -use futures::task::{self, ArcWake, Context, Poll, Waker}; -use futures_test::task::panic_context; -use std::cell::{Cell, RefCell}; -use std::collections::VecDeque; -use std::fmt; -use std::mem; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; - -fn sassert_next(s: &mut S, item: S::Item) -where - S: Stream + Unpin, - S::Item: Eq + fmt::Debug, -{ - match s.poll_next_unpin(&mut panic_context()) { - Poll::Ready(None) => panic!("stream is at its end"), - Poll::Ready(Some(e)) => assert_eq!(e, item), - Poll::Pending => panic!("stream wasn't ready"), +#[allow(dead_code)] +mod sassert_next { + use futures::stream::{Stream, StreamExt}; + use futures::task::Poll; + use futures_test::task::panic_context; + use std::fmt; + + pub fn sassert_next(s: &mut S, item: S::Item) + where + S: Stream + Unpin, + S::Item: Eq + fmt::Debug, + { + match s.poll_next_unpin(&mut panic_context()) { + Poll::Ready(None) => panic!("stream is at its end"), + Poll::Ready(Some(e)) => assert_eq!(e, item), + Poll::Pending => panic!("stream wasn't ready"), + } + } +} + +#[allow(dead_code)] +mod unwrap { + use futures::task::Poll; + use std::fmt; + + pub fn unwrap(x: Poll>) -> T { + match x { + Poll::Ready(Ok(x)) => x, + Poll::Ready(Err(_)) => panic!("Poll::Ready(Err(_))"), + Poll::Pending => panic!("Poll::Pending"), + } + } +} + +#[allow(dead_code)] +#[cfg(feature = "alloc")] // ArcWake +mod flag_cx { + use futures::task::{self, ArcWake, Context}; + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + + // An Unpark struct that records unpark events for inspection + pub struct Flag(AtomicBool); + + impl Flag { + pub fn new() -> Arc { + Arc::new(Self(AtomicBool::new(false))) + } + + pub fn take(&self) -> bool { + self.0.swap(false, Ordering::SeqCst) + } + + pub fn set(&self, v: bool) { + self.0.store(v, Ordering::SeqCst) + } + } + + impl ArcWake for Flag { + fn wake_by_ref(arc_self: &Arc) { + arc_self.set(true) + } + } + + pub fn flag_cx(f: F) -> R + where + F: FnOnce(Arc, &mut Context<'_>) -> R, + { + let flag = Flag::new(); + let waker = task::waker_ref(&flag); + let cx = &mut Context::from_waker(&waker); + f(flag.clone(), cx) + } +} + +#[allow(dead_code)] +mod start_send_fut { + use futures::future::Future; + use futures::ready; + use futures::sink::Sink; + use futures::task::{Context, Poll}; + use std::pin::Pin; + + // Sends a value on an i32 channel sink + pub struct StartSendFut + Unpin, Item: Unpin>(Option, Option); + + impl + Unpin, Item: Unpin> StartSendFut { + pub fn new(sink: S, item: Item) -> Self { + Self(Some(sink), Some(item)) + } + } + + impl + Unpin, Item: Unpin> Future for StartSendFut { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self(inner, item) = self.get_mut(); + { + let mut inner = inner.as_mut().unwrap(); + ready!(Pin::new(&mut inner).poll_ready(cx))?; + Pin::new(&mut inner).start_send(item.take().unwrap())?; + } + Poll::Ready(Ok(inner.take().unwrap())) + } + } +} + +#[allow(dead_code)] +mod manual_flush { + use futures::sink::Sink; + use futures::task::{Context, Poll, Waker}; + use std::mem; + use std::pin::Pin; + + // Immediately accepts all requests to start pushing, but completion is managed + // by manually flushing + pub struct ManualFlush { + data: Vec, + waiting_tasks: Vec, + } + + impl Sink> for ManualFlush { + type Error = (); + + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn start_send(mut self: Pin<&mut Self>, item: Option) -> Result<(), Self::Error> { + if let Some(item) = item { + self.data.push(item); + } else { + self.force_flush(); + } + Ok(()) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.data.is_empty() { + Poll::Ready(Ok(())) + } else { + self.waiting_tasks.push(cx.waker().clone()); + Poll::Pending + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } + } + + impl ManualFlush { + pub fn new() -> Self { + Self { + data: Vec::new(), + waiting_tasks: Vec::new(), + } + } + + pub fn force_flush(&mut self) -> Vec { + for task in self.waiting_tasks.drain(..) { + task.wake() + } + mem::replace(&mut self.data, Vec::new()) + } } } -fn unwrap(x: Poll>) -> T { - match x { - Poll::Ready(Ok(x)) => x, - Poll::Ready(Err(_)) => panic!("Poll::Ready(Err(_))"), - Poll::Pending => panic!("Poll::Pending"), +#[allow(dead_code)] +mod allowance { + use futures::sink::Sink; + use futures::task::{Context, Poll, Waker}; + use std::cell::{Cell, RefCell}; + use std::pin::Pin; + use std::rc::Rc; + + pub struct ManualAllow { + pub data: Vec, + allow: Rc, + } + + pub struct Allow { + flag: Cell, + tasks: RefCell>, + } + + impl Allow { + pub fn new() -> Self { + Self { + flag: Cell::new(false), + tasks: RefCell::new(Vec::new()), + } + } + + pub fn check(&self, cx: &mut Context<'_>) -> bool { + if self.flag.get() { + true + } else { + self.tasks.borrow_mut().push(cx.waker().clone()); + false + } + } + + pub fn start(&self) { + self.flag.set(true); + let mut tasks = self.tasks.borrow_mut(); + for task in tasks.drain(..) { + task.wake(); + } + } + } + + impl Sink for ManualAllow { + type Error = (); + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.allow.check(cx) { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + + fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { + self.data.push(item); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + } + + pub fn manual_allow() -> (ManualAllow, Rc) { + let allow = Rc::new(Allow::new()); + let manual_allow = ManualAllow { + data: Vec::new(), + allow: allow.clone(), + }; + (manual_allow, allow) } } + +#[cfg(feature = "alloc")] // futures_sink::Sink satisfying for .left/right_sink #[test] fn either_sink() { + use futures::sink::{Sink, SinkExt}; + use std::collections::VecDeque; + use std::pin::Pin; + let mut s = if true { Vec::::new().left_sink() } else { @@ -47,8 +262,13 @@ fn either_sink() { Pin::new(&mut s).start_send(0).unwrap(); } +#[cfg(feature = "executor")] // executor:: #[test] fn vec_sink() { + use futures::executor::block_on; + use futures::sink::{Sink, SinkExt}; + use std::pin::Pin; + let mut v = Vec::new(); Pin::new(&mut v).start_send(0).unwrap(); Pin::new(&mut v).start_send(1).unwrap(); @@ -57,8 +277,13 @@ fn vec_sink() { assert_eq!(v, vec![0, 1]); } +#[cfg(feature = "alloc")] // futures_sink::Sink satisfying for .start_send() #[test] fn vecdeque_sink() { + use futures::sink::Sink; + use std::collections::VecDeque; + use std::pin::Pin; + let mut deque = VecDeque::new(); Pin::new(&mut deque).start_send(2).unwrap(); Pin::new(&mut deque).start_send(3).unwrap(); @@ -68,8 +293,12 @@ fn vecdeque_sink() { assert_eq!(deque.pop_front(), None); } +#[cfg(feature = "executor")] // executor:: #[test] fn send() { + use futures::executor::block_on; + use futures::sink::SinkExt; + let mut v = Vec::new(); block_on(v.send(0)).unwrap(); @@ -82,8 +311,13 @@ fn send() { assert_eq!(v, vec![0, 1, 2]); } +#[cfg(feature = "executor")] // executor:: #[test] fn send_all() { + use futures::executor::block_on; + use futures::sink::SinkExt; + use futures::stream::{self, StreamExt}; + let mut v = Vec::new(); block_on(v.send_all(&mut stream::iter(vec![0, 1]).map(Ok))).unwrap(); @@ -96,66 +330,20 @@ fn send_all() { assert_eq!(v, vec![0, 1, 2, 3, 4, 5]); } -// An Unpark struct that records unpark events for inspection -struct Flag(AtomicBool); - -impl Flag { - fn new() -> Arc { - Arc::new(Self(AtomicBool::new(false))) - } - - fn take(&self) -> bool { - self.0.swap(false, Ordering::SeqCst) - } - - fn set(&self, v: bool) { - self.0.store(v, Ordering::SeqCst) - } -} - -impl ArcWake for Flag { - fn wake_by_ref(arc_self: &Arc) { - arc_self.set(true) - } -} - -fn flag_cx(f: F) -> R -where - F: FnOnce(Arc, &mut Context<'_>) -> R, -{ - let flag = Flag::new(); - let waker = task::waker_ref(&flag); - let cx = &mut Context::from_waker(&waker); - f(flag.clone(), cx) -} - -// Sends a value on an i32 channel sink -struct StartSendFut + Unpin, Item: Unpin>(Option, Option); - -impl + Unpin, Item: Unpin> StartSendFut { - fn new(sink: S, item: Item) -> Self { - Self(Some(sink), Some(item)) - } -} - -impl + Unpin, Item: Unpin> Future for StartSendFut { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self(inner, item) = self.get_mut(); - { - let mut inner = inner.as_mut().unwrap(); - ready!(Pin::new(&mut inner).poll_ready(cx))?; - Pin::new(&mut inner).start_send(item.take().unwrap())?; - } - Poll::Ready(Ok(inner.take().unwrap())) - } -} - // Test that `start_send` on an `mpsc` channel does indeed block when the // channel is full +#[cfg(feature = "executor")] // executor:: #[test] fn mpsc_blocking_start_send() { + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::future::{self, FutureExt}; + + use start_send_fut::StartSendFut; + use flag_cx::flag_cx; + use sassert_next::sassert_next; + use unwrap::unwrap; + let (mut tx, mut rx) = mpsc::channel::(0); block_on(future::lazy(|_| { @@ -177,8 +365,20 @@ fn mpsc_blocking_start_send() { // test `flush` by using `with` to make the first insertion into a sink block // until a oneshot is completed +#[cfg(feature = "executor")] // executor:: #[test] fn with_flush() { + use futures::channel::oneshot; + use futures::executor::block_on; + use futures::future::{self, FutureExt, TryFutureExt}; + use futures::never::Never; + use futures::sink::{Sink, SinkExt}; + use std::mem; + use std::pin::Pin; + + use flag_cx::flag_cx; + use unwrap::unwrap; + let (tx, rx) = oneshot::channel(); let mut block = rx.boxed(); let mut sink = Vec::new().with(|elem| { @@ -203,8 +403,14 @@ fn with_flush() { } // test simple use of with to change data +#[cfg(feature = "executor")] // executor:: #[test] fn with_as_map() { + use futures::executor::block_on; + use futures::future; + use futures::never::Never; + use futures::sink::SinkExt; + let mut sink = Vec::new().with(|item| future::ok::(item * 2)); block_on(sink.send(0)).unwrap(); block_on(sink.send(1)).unwrap(); @@ -213,8 +419,13 @@ fn with_as_map() { } // test simple use of with_flat_map +#[cfg(feature = "executor")] // executor:: #[test] fn with_flat_map() { + use futures::executor::block_on; + use futures::sink::SinkExt; + use futures::stream::{self, StreamExt}; + let mut sink = Vec::new().with_flat_map(|item| stream::iter(vec![item; item]).map(Ok)); block_on(sink.send(0)).unwrap(); block_on(sink.send(1)).unwrap(); @@ -225,8 +436,19 @@ fn with_flat_map() { // Check that `with` propagates `poll_ready` to the inner sink. // Regression test for the issue #1834. +#[cfg(feature = "executor")] // executor:: #[test] fn with_propagates_poll_ready() { + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::future; + use futures::sink::{Sink, SinkExt}; + use futures::task::Poll; + use std::pin::Pin; + + use flag_cx::flag_cx; + use sassert_next::sassert_next; + let (tx, mut rx) = mpsc::channel::(0); let mut tx = tx.with(|item: i32| future::ok::(item + 10)); @@ -249,63 +471,19 @@ fn with_propagates_poll_ready() { })); } -// Immediately accepts all requests to start pushing, but completion is managed -// by manually flushing -struct ManualFlush { - data: Vec, - waiting_tasks: Vec, -} - -impl Sink> for ManualFlush { - type Error = (); - - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn start_send(mut self: Pin<&mut Self>, item: Option) -> Result<(), Self::Error> { - if let Some(item) = item { - self.data.push(item); - } else { - self.force_flush(); - } - Ok(()) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.data.is_empty() { - Poll::Ready(Ok(())) - } else { - self.waiting_tasks.push(cx.waker().clone()); - Poll::Pending - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} - -impl ManualFlush { - fn new() -> Self { - Self { - data: Vec::new(), - waiting_tasks: Vec::new(), - } - } - - fn force_flush(&mut self) -> Vec { - for task in self.waiting_tasks.drain(..) { - task.wake() - } - mem::replace(&mut self.data, Vec::new()) - } -} - // test that the `with` sink doesn't require the underlying sink to flush, // but doesn't claim to be flushed until the underlying sink is +#[cfg(feature = "alloc")] // flag_cx #[test] fn with_flush_propagate() { + use futures::future::{self, FutureExt}; + use futures::sink::{Sink, SinkExt}; + use std::pin::Pin; + + use manual_flush::ManualFlush; + use flag_cx::flag_cx; + use unwrap::unwrap; + let mut sink = ManualFlush::new().with(future::ok::, ()>); flag_cx(|flag, cx| { unwrap(Pin::new(&mut sink).poll_ready(cx)); @@ -325,8 +503,12 @@ fn with_flush_propagate() { } // test that a buffer is a no-nop around a sink that always accepts sends +#[cfg(feature = "executor")] // executor:: #[test] fn buffer_noop() { + use futures::executor::block_on; + use futures::sink::SinkExt; + let mut sink = Vec::new().buffer(0); block_on(sink.send(0)).unwrap(); block_on(sink.send(1)).unwrap(); @@ -338,80 +520,21 @@ fn buffer_noop() { assert_eq!(sink.get_ref(), &[0, 1]); } -struct ManualAllow { - data: Vec, - allow: Rc, -} - -struct Allow { - flag: Cell, - tasks: RefCell>, -} - -impl Allow { - fn new() -> Self { - Self { - flag: Cell::new(false), - tasks: RefCell::new(Vec::new()), - } - } - - fn check(&self, cx: &mut Context<'_>) -> bool { - if self.flag.get() { - true - } else { - self.tasks.borrow_mut().push(cx.waker().clone()); - false - } - } - - fn start(&self) { - self.flag.set(true); - let mut tasks = self.tasks.borrow_mut(); - for task in tasks.drain(..) { - task.wake(); - } - } -} - -impl Sink for ManualAllow { - type Error = (); - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.allow.check(cx) { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - - fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { - self.data.push(item); - Ok(()) - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -fn manual_allow() -> (ManualAllow, Rc) { - let allow = Rc::new(Allow::new()); - let manual_allow = ManualAllow { - data: Vec::new(), - allow: allow.clone(), - }; - (manual_allow, allow) -} - // test basic buffer functionality, including both filling up to capacity, // and writing out when the underlying sink is ready +#[cfg(feature = "executor")] // executor:: +#[cfg(feature = "alloc")] // flag_cx #[test] fn buffer() { + use futures::executor::block_on; + use futures::future::FutureExt; + use futures::sink::SinkExt; + + use start_send_fut::StartSendFut; + use flag_cx::flag_cx; + use unwrap::unwrap; + use allowance::manual_allow; + let (sink, allow) = manual_allow::(); let sink = sink.buffer(2); @@ -429,8 +552,13 @@ fn buffer() { }) } +#[cfg(feature = "executor")] // executor:: #[test] fn fanout_smoke() { + use futures::executor::block_on; + use futures::sink::SinkExt; + use futures::stream::{self, StreamExt}; + let sink1 = Vec::new(); let sink2 = Vec::new(); let mut sink = sink1.fanout(sink2); @@ -440,8 +568,20 @@ fn fanout_smoke() { assert_eq!(sink2, vec![1, 2, 3]); } +#[cfg(feature = "executor")] // executor:: +#[cfg(feature = "alloc")] // flag_cx #[test] fn fanout_backpressure() { + use futures::channel::mpsc; + use futures::executor::block_on; + use futures::future::FutureExt; + use futures::sink::SinkExt; + use futures::stream::StreamExt; + + use start_send_fut::StartSendFut; + use flag_cx::flag_cx; + use unwrap::unwrap; + let (left_send, mut left_recv) = mpsc::channel(0); let (right_send, mut right_recv) = mpsc::channel(0); let sink = left_send.fanout(right_send); @@ -472,8 +612,15 @@ fn fanout_backpressure() { }) } +#[cfg(all(feature = "alloc", feature = "std"))] // channel::mpsc #[test] fn sink_map_err() { + use futures::channel::mpsc; + use futures::sink::{Sink, SinkExt}; + use futures::task::Poll; + use futures_test::task::panic_context; + use std::pin::Pin; + { let cx = &mut panic_context(); let (tx, _rx) = mpsc::channel(1); @@ -489,17 +636,24 @@ fn sink_map_err() { ); } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct ErrIntoTest; - -impl From for ErrIntoTest { - fn from(_: mpsc::SendError) -> Self { - Self - } -} - +#[cfg(all(feature = "alloc", feature = "std"))] // channel::mpsc #[test] fn err_into() { + use futures::channel::mpsc; + use futures::sink::{Sink, SinkErrInto, SinkExt}; + use futures::task::Poll; + use futures_test::task::panic_context; + use std::pin::Pin; + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub struct ErrIntoTest; + + impl From for ErrIntoTest { + fn from(_: mpsc::SendError) -> Self { + Self + } + } + { let cx = &mut panic_context(); let (tx, _rx) = mpsc::channel(1); From 3284c5593e77f6c368de59ec54c25392f5ca680f Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 47/83] futures: tests/split.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/split.rs | 101 +++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/futures/tests/split.rs b/futures/tests/split.rs index 9f4f1a07e2..140cf3c57d 100644 --- a/futures/tests/split.rs +++ b/futures/tests/split.rs @@ -1,65 +1,66 @@ -use futures::executor::block_on; -use futures::sink::{Sink, SinkExt}; -use futures::stream::{self, Stream, StreamExt}; -use futures::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use std::pin::Pin; +#[cfg(feature = "executor")] // executor:: +#[test] +fn test_split() { + use futures::executor::block_on; + use futures::sink::{Sink, SinkExt}; + use futures::stream::{self, Stream, StreamExt}; + use futures::task::{Context, Poll}; + use pin_utils::unsafe_pinned; + use std::pin::Pin; -struct Join { - stream: T, - sink: U -} + struct Join { + stream: T, + sink: U + } -impl Join { - unsafe_pinned!(stream: T); - unsafe_pinned!(sink: U); -} + impl Join { + unsafe_pinned!(stream: T); + unsafe_pinned!(sink: U); + } -impl Stream for Join { - type Item = T::Item; + impl Stream for Join { + type Item = T::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().poll_next(cx) + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.stream().poll_next(cx) + } } -} -impl, Item> Sink for Join { - type Error = U::Error; + impl, Item> Sink for Join { + type Error = U::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_ready(cx) - } + fn poll_ready( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.sink().poll_ready(cx) + } - fn start_send( - self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { - self.sink().start_send(item) - } + fn start_send( + self: Pin<&mut Self>, + item: Item, + ) -> Result<(), Self::Error> { + self.sink().start_send(item) + } - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_flush(cx) - } + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.sink().poll_flush(cx) + } - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_close(cx) + fn poll_close( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.sink().poll_close(cx) + } } -} -#[test] -fn test_split() { let mut dest: Vec = Vec::new(); { let join = Join { From 81f33eb5ac079827bc4bfbdb0b72af1bbbd3db7c Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 48/83] futures: tests/stream_catch_unwind.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream_catch_unwind.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/futures/tests/stream_catch_unwind.rs b/futures/tests/stream_catch_unwind.rs index 8b23a0a7ef..94c7a7562a 100644 --- a/futures/tests/stream_catch_unwind.rs +++ b/futures/tests/stream_catch_unwind.rs @@ -1,8 +1,9 @@ -use futures::executor::block_on_stream; -use futures::stream::{self, StreamExt}; - +#[cfg(feature = "executor")] #[test] fn panic_in_the_middle_of_the_stream() { + use futures::executor::block_on_stream; + use futures::stream::{self, StreamExt}; + let stream = stream::iter(vec![Some(10), None, Some(11)]); // panic on second element @@ -14,8 +15,12 @@ fn panic_in_the_middle_of_the_stream() { assert!(iter.next().is_none()); } +#[cfg(feature = "executor")] #[test] fn no_panic() { + use futures::executor::block_on_stream; + use futures::stream::{self, StreamExt}; + let stream = stream::iter(vec![10, 11, 12]); let mut iter = block_on_stream(stream.catch_unwind()); From 53fd18eb80500c300919187eef2248598e8db762 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 49/83] futures: tests/stream_into_async_read.rs: Don't break w/o "std" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream_into_async_read.rs | 98 +++++++++++++------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/futures/tests/stream_into_async_read.rs b/futures/tests/stream_into_async_read.rs index c528af03f0..1b26233c36 100644 --- a/futures/tests/stream_into_async_read.rs +++ b/futures/tests/stream_into_async_read.rs @@ -1,51 +1,32 @@ -use core::pin::Pin; -use futures::io::{AsyncRead, AsyncBufRead}; -use futures::stream::{self, TryStreamExt}; -use futures::task::Poll; -use futures_test::{task::noop_context, stream::StreamTestExt}; - -macro_rules! assert_read { - ($reader:expr, $buf:expr, $item:expr) => { - let mut cx = noop_context(); - loop { - match Pin::new(&mut $reader).poll_read(&mut cx, $buf) { - Poll::Ready(Ok(x)) => { - assert_eq!(x, $item); - break; - } - Poll::Ready(Err(err)) => { - panic!("assertion failed: expected value but got {}", err); - } - Poll::Pending => { - continue; - } - } - } - }; -} - -macro_rules! assert_fill_buf { - ($reader:expr, $buf:expr) => { - let mut cx = noop_context(); - loop { - match Pin::new(&mut $reader).poll_fill_buf(&mut cx) { - Poll::Ready(Ok(x)) => { - assert_eq!(x, $buf); - break; - } - Poll::Ready(Err(err)) => { - panic!("assertion failed: expected value but got {}", err); - } - Poll::Pending => { - continue; +#[cfg(feature = "std")] // io:: +#[test] +fn test_into_async_read() { + use core::pin::Pin; + use futures::io::AsyncRead; + use futures::stream::{self, TryStreamExt}; + use futures::task::Poll; + use futures_test::{task::noop_context, stream::StreamTestExt}; + + macro_rules! assert_read { + ($reader:expr, $buf:expr, $item:expr) => { + let mut cx = noop_context(); + loop { + match Pin::new(&mut $reader).poll_read(&mut cx, $buf) { + Poll::Ready(Ok(x)) => { + assert_eq!(x, $item); + break; + } + Poll::Ready(Err(err)) => { + panic!("assertion failed: expected value but got {}", err); + } + Poll::Pending => { + continue; + } } } - } - }; -} + }; + } -#[test] -fn test_into_async_read() { let stream = stream::iter((1..=3).flat_map(|_| vec![Ok(vec![]), Ok(vec![1, 2, 3, 4, 5])])); let mut reader = stream.interleave_pending().into_async_read(); let mut buf = vec![0; 3]; @@ -71,8 +52,35 @@ fn test_into_async_read() { assert_read!(reader, &mut buf, 0); } +#[cfg(feature = "std")] // io:: #[test] fn test_into_async_bufread() -> std::io::Result<()> { + use core::pin::Pin; + use futures::io::AsyncBufRead; + use futures::stream::{self, TryStreamExt}; + use futures::task::Poll; + use futures_test::{task::noop_context, stream::StreamTestExt}; + + macro_rules! assert_fill_buf { + ($reader:expr, $buf:expr) => { + let mut cx = noop_context(); + loop { + match Pin::new(&mut $reader).poll_fill_buf(&mut cx) { + Poll::Ready(Ok(x)) => { + assert_eq!(x, $buf); + break; + } + Poll::Ready(Err(err)) => { + panic!("assertion failed: expected value but got {}", err); + } + Poll::Pending => { + continue; + } + } + } + }; + } + let stream = stream::iter((1..=2).flat_map(|_| vec![Ok(vec![]), Ok(vec![1, 2, 3, 4, 5])])); let mut reader = stream.interleave_pending().into_async_read(); From f0a84077cb8eb5aa55efd43e7310427178e35240 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 50/83] futures: tests/stream_peekable.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream_peekable.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/futures/tests/stream_peekable.rs b/futures/tests/stream_peekable.rs index b65a0572cb..c49cd72466 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -1,9 +1,10 @@ -use futures::executor::block_on; -use futures::pin_mut; -use futures::stream::{self, Peekable, StreamExt}; - +#[cfg(feature = "executor")] // executor:: #[test] fn peekable() { + use futures::executor::block_on; + use futures::pin_mut; + use futures::stream::{self, Peekable, StreamExt}; + block_on(async { let peekable: Peekable<_> = stream::iter(vec![1u8, 2, 3]).peekable(); pin_mut!(peekable); From 7a29dfe6eeb5559234c5a1b4b6de513bab7b9a07 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 51/83] futures: tests/stream.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index a29e542035..2d99dfd72c 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,10 +1,9 @@ -use futures::future::{self, Future}; -use futures::executor::block_on; -use futures::stream::{self, StreamExt}; -use futures::task::Poll; - +#[cfg(feature = "executor")] // executor:: #[test] fn select() { + use futures::executor::block_on; + use futures::stream::{self, StreamExt}; + fn select_and_compare(a: Vec, b: Vec, expected: Vec) { let a = stream::iter(a); let b = stream::iter(b); @@ -17,8 +16,11 @@ fn select() { select_and_compare(vec![1, 2], vec![4, 5, 6], vec![1, 4, 2, 5, 6]); } +#[cfg(feature = "executor")] // executor:: #[test] fn flat_map() { + use futures::stream::{self, StreamExt}; + futures::executor::block_on(async { let st = stream::iter(vec![ stream::iter(0..=4u8), @@ -35,8 +37,11 @@ fn flat_map() { }); } +#[cfg(feature = "executor")] // executor:: #[test] fn scan() { + use futures::stream::{self, StreamExt}; + futures::executor::block_on(async { assert_eq!( stream::iter(vec![1u8, 2, 3, 4, 6, 8, 2]) @@ -51,8 +56,13 @@ fn scan() { }); } +#[cfg(feature = "executor")] // executor:: #[test] fn take_until() { + use futures::future::{self, Future}; + use futures::stream::{self, StreamExt}; + use futures::task::Poll; + fn make_stop_fut(stop_on: u32) -> impl Future { let mut i = 0; future::poll_fn(move |_cx| { From bf3adb604dfc7a646c725af661889d36f4d50172 Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 52/83] futures: tests/stream_select_all.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream_select_all.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/futures/tests/stream_select_all.rs b/futures/tests/stream_select_all.rs index eb711dda0c..411cb73822 100644 --- a/futures/tests/stream_select_all.rs +++ b/futures/tests/stream_select_all.rs @@ -1,12 +1,11 @@ -use futures::channel::mpsc; -use futures::executor::block_on_stream; -use futures::future::{self, FutureExt}; -use futures::stream::{self, select_all, FusedStream, SelectAll, StreamExt}; -use futures::task::Poll; -use futures_test::task::noop_context; - +#[cfg(feature = "alloc")] // stream::SelectAll #[test] fn is_terminated() { + use futures::future::{self, FutureExt}; + use futures::stream::{FusedStream, SelectAll, StreamExt}; + use futures::task::Poll; + use futures_test::task::noop_context; + let mut cx = noop_context(); let mut tasks = SelectAll::new(); @@ -30,8 +29,12 @@ fn is_terminated() { assert_eq!(tasks.is_terminated(), true); } +#[cfg(feature = "executor")] // executor:: #[test] fn issue_1626() { + use futures::executor::block_on_stream; + use futures::stream; + let a = stream::iter(0..=2); let b = stream::iter(10..=14); @@ -48,8 +51,14 @@ fn issue_1626() { assert_eq!(s.next(), None); } +#[cfg(all(feature = "alloc", feature = "std"))] // channel::mpsc +#[cfg(feature = "executor")] // executor:: #[test] fn works_1() { + use futures::channel::mpsc; + use futures::executor::block_on_stream; + use futures::stream::select_all; + let (a_tx, a_rx) = mpsc::unbounded::(); let (b_tx, b_rx) = mpsc::unbounded::(); let (c_tx, c_rx) = mpsc::unbounded::(); From 438cc209b32be56a9b4df01964dd78abbb8fe06a Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 53/83] futures: tests/stream_select_next_some.rs: Don't break w/o features - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/stream_select_next_some.rs | 27 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/futures/tests/stream_select_next_some.rs b/futures/tests/stream_select_next_some.rs index 09d7e895c1..f2b3af212b 100644 --- a/futures/tests/stream_select_next_some.rs +++ b/futures/tests/stream_select_next_some.rs @@ -1,12 +1,12 @@ -use futures::{future, select}; -use futures::future::{FusedFuture, FutureExt}; -use futures::stream::{FuturesUnordered, StreamExt}; -use futures::task::{Context, Poll}; -use futures_test::future::FutureTestExt; -use futures_test::task::new_count_waker; - +#[cfg(feature = "alloc")] // stream::FuturesUnordered #[test] fn is_terminated() { + use futures::future; + use futures::future::{FusedFuture, FutureExt}; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures::task::{Context, Poll}; + use futures_test::task::new_count_waker; + let (waker, counter) = new_count_waker(); let mut cx = Context::from_waker(&waker); @@ -29,8 +29,14 @@ fn is_terminated() { assert_eq!(select_next_some.is_terminated(), true); } +#[cfg(all(feature = "async-await", feature = "std"))] // futures::select +#[cfg(feature = "executor")] // executor:: #[test] fn select() { + use futures::{future, select}; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures_test::future::FutureTestExt; + // Checks that even though `async_tasks` will yield a `None` and return // `is_terminated() == true` during the first poll, it manages to toggle // back to having items after a future is pushed into it during the second @@ -56,8 +62,15 @@ fn select() { } // Check that `select!` macro does not fail when importing from `futures_util`. +#[cfg(feature = "alloc")] // stream::FuturesUnordered +#[cfg(feature = "async-await")] // futures_util::select turned on +#[cfg(feature = "executor")] // executor:: #[test] fn futures_util_select() { + use futures::future; + use futures::stream::{FuturesUnordered, StreamExt}; + use futures_test::future::FutureTestExt; + use futures_util::select; // Checks that even though `async_tasks` will yield a `None` and return From 214fc916b062ac1908410fc17f9c2583a762933a Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 54/83] futures: tests/try_join_all.rs: Don't break w/o "executor" - Symbols/Imports pushed down / localised - Tests gated with cfg() - Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/try_join_all.rs | 42 +++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/futures/tests/try_join_all.rs b/futures/tests/try_join_all.rs index 662b866ec8..1097a36d95 100644 --- a/futures/tests/try_join_all.rs +++ b/futures/tests/try_join_all.rs @@ -1,19 +1,26 @@ -use futures_util::future::*; -use std::future::Future; -use futures::executor::block_on; -use std::fmt::Debug; - -fn assert_done(actual_fut: F, expected: T) -where - T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, -{ - let output = block_on(actual_fut()); - assert_eq!(output, expected); +#[cfg(feature = "executor")] // executor:: +mod util { + use std::future::Future; + use futures::executor::block_on; + use std::fmt::Debug; + + pub fn assert_done(actual_fut: F, expected: T) + where + T: PartialEq + Debug, + F: FnOnce() -> Box + Unpin>, + { + let output = block_on(actual_fut()); + assert_eq!(output, expected); + } } +#[cfg(feature = "executor")] // assert_done #[test] fn collect_collects() { + use futures_util::future::{err, ok, try_join_all}; + + use util::assert_done; + assert_done(|| Box::new(try_join_all(vec![ok(1), ok(2)])), Ok::<_, usize>(vec![1, 2])); assert_done(|| Box::new(try_join_all(vec![ok(1), err(2)])), Err(2)); assert_done(|| Box::new(try_join_all(vec![ok(1)])), Ok::<_, usize>(vec![1])); @@ -23,8 +30,14 @@ fn collect_collects() { // TODO: needs more tests } +#[cfg(feature = "executor")] // assert_done #[test] fn try_join_all_iter_lifetime() { + use futures_util::future::{ok, try_join_all}; + use std::future::Future; + + use util::assert_done; + // In futures-rs version 0.1, this function would fail to typecheck due to an overly // conservative type parameterization of `TryJoinAll`. fn sizes<'a>(bufs: Vec<&'a [u8]>) -> Box, ()>> + Unpin> { @@ -35,8 +48,13 @@ fn try_join_all_iter_lifetime() { assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), Ok(vec![3 as usize, 0, 1])); } +#[cfg(feature = "executor")] // assert_done #[test] fn try_join_all_from_iter() { + use futures_util::future::{ok, TryJoinAll}; + + use util::assert_done; + assert_done( || Box::new(vec![ok(1), ok(2)].into_iter().collect::>()), Ok::<_, usize>(vec![1, 2]), From c5852d324b6f1fdd606caee1f225f6864981865e Mon Sep 17 00:00:00 2001 From: Kent Fredric Date: Mon, 16 Mar 2020 08:50:04 +1300 Subject: [PATCH 55/83] futures: tests/try_join.rs: Don't break w/o features Only really appropriate action here is to gate the whole file with `#![cfg()]` Tests now build and pass with all valid feature combinations See previous commits for details and rationale --- futures/tests/try_join.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/futures/tests/try_join.rs b/futures/tests/try_join.rs index 6c6d0843d5..0861c1e860 100644 --- a/futures/tests/try_join.rs +++ b/futures/tests/try_join.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "executor")] // executor:: +#![cfg(feature = "async-await")] // try_join! #![deny(unreachable_code)] use futures::{try_join, executor::block_on}; From fc6bd694341b45349f027e6d81450a97683d0ab4 Mon Sep 17 00:00:00 2001 From: amesgen Date: Mon, 30 Mar 2020 05:16:31 +0200 Subject: [PATCH 56/83] impl Extend for SelectAll --- futures-util/src/stream/select_all.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index d25768904c..2547993a6a 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -131,3 +131,11 @@ impl FromIterator for SelectAll { select_all(iter) } } + +impl Extend for SelectAll { + fn extend>(&mut self, iter: T) { + for st in iter { + self.push(st) + } + } +} From 2f8943da9a0fcef208e86ae2f0f0152191629dcc Mon Sep 17 00:00:00 2001 From: laizy Date: Sun, 22 Mar 2020 15:12:42 +0800 Subject: [PATCH 57/83] reduce box allocation in bilock. --- futures-util/src/lock/bilock.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 374ea5032b..45d86e6a18 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -88,6 +88,7 @@ impl BiLock { /// This function will panic if called outside the context of a future's /// task. pub fn poll_lock(&self, cx: &mut Context<'_>) -> Poll> { + let mut waker = None; loop { match self.arc.state.swap(1, SeqCst) { // Woohoo, we grabbed the lock! @@ -99,12 +100,14 @@ impl BiLock { // A task was previously blocked on this lock, likely our task, // so we need to update that task. n => unsafe { - drop(Box::from_raw(n as *mut Waker)); + let mut prev = Box::from_raw(n as *mut Waker); + *prev = cx.waker().clone(); + waker = Some(prev); } } // type ascription for safety's sake! - let me: Box = Box::new(cx.waker().clone()); + let me: Box = waker.take().unwrap_or_else(||Box::new(cx.waker().clone())); let me = Box::into_raw(me) as usize; match self.arc.state.compare_exchange(1, me, SeqCst, SeqCst) { @@ -116,7 +119,7 @@ impl BiLock { // and before the compare_exchange. Deallocate what we just // allocated and go through the loop again. Err(0) => unsafe { - drop(Box::from_raw(me as *mut Waker)); + waker = Some(Box::from_raw(me as *mut Waker)); }, // The top of this loop set the previous state to 1, so if we From 3fc9310a0e7834b767bfbe42bf7bec555b47432a Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Tue, 7 Apr 2020 10:38:37 +0200 Subject: [PATCH 58/83] Update expected size of `select_on_non_unpin_size` Probably comes from https://github.com/rust-lang/rust/pull/69837 --- futures/tests/async_await_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index a90d844cd3..fd2a3497ec 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -284,7 +284,7 @@ fn select_on_non_unpin_size() { select_res }; - assert_eq!(48, std::mem::size_of_val(&fut)); + assert_eq!(32, std::mem::size_of_val(&fut)); } #[cfg(all(feature = "async-await", feature = "std", feature = "executor"))] From e679f3b54329323811401776fab3782f37356653 Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Wed, 15 Apr 2020 09:44:58 +0200 Subject: [PATCH 59/83] Allow unused_braces in cargo doc test Workaround for https://github.com/rust-lang/rust/issues/70814 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 93526353b8..96131ab204 100644 --- a/.travis.yml +++ b/.travis.yml @@ -145,7 +145,8 @@ matrix: - name: cargo doc rust: nightly script: - - RUSTDOCFLAGS=-Dwarnings cargo doc --workspace --no-deps --all-features + # TODO: Remove -Aunused_braces once https://github.com/rust-lang/rust/issues/70814 is fixed + - RUSTDOCFLAGS="-Dwarnings -Aunused_braces" cargo doc --workspace --no-deps --all-features script: - cargo test --workspace --all-features From d6cee4c13f1ad16f39292cfa538bd91731d4b5f8 Mon Sep 17 00:00:00 2001 From: Rijenkii <5338332+rijenkii@users.noreply.github.com> Date: Mon, 20 Apr 2020 02:02:36 +0700 Subject: [PATCH 60/83] Add `*_unpin` methods to `SinkExt` --- futures-util/src/sink/mod.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/futures-util/src/sink/mod.rs b/futures-util/src/sink/mod.rs index bc59ce5865..ebdb99945d 100644 --- a/futures-util/src/sink/mod.rs +++ b/futures-util/src/sink/mod.rs @@ -6,8 +6,10 @@ //! This module is only available when the `sink` feature of this //! library is activated, and it is activated by default. +use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{Stream, TryStream}; +use futures_core::task::{Context, Poll}; use crate::future::Either; #[cfg(feature = "compat")] @@ -261,4 +263,36 @@ pub trait SinkExt: Sink { { CompatSink::new(self) } + + /// A convenience method for calling [`Sink::poll_ready`] on [`Unpin`] + /// sink types. + fn poll_ready_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where Self: Unpin + { + Pin::new(self).poll_ready(cx) + } + + /// A convenience method for calling [`Sink::start_send`] on [`Unpin`] + /// sink types. + fn start_send_unpin(&mut self, item: Item) -> Result<(), Self::Error> + where Self: Unpin + { + Pin::new(self).start_send(item) + } + + /// A convenience method for calling [`Sink::poll_flush`] on [`Unpin`] + /// sink types. + fn poll_flush_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where Self: Unpin + { + Pin::new(self).poll_flush(cx) + } + + /// A convenience method for calling [`Sink::poll_close`] on [`Unpin`] + /// sink types. + fn poll_close_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where Self: Unpin + { + Pin::new(self).poll_close(cx) + } } From 6ac247c820a50ef28d3520877f414f5a1de84d93 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 10 Apr 2020 23:24:11 +0900 Subject: [PATCH 61/83] Remove unused type parameter --- futures-util/src/future/select.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index 91b467dca6..3e0a447da6 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -32,7 +32,7 @@ impl Unpin for Select {} /// /// // A poor-man's join implemented on top of select /// -/// fn join(a: A, b: B) -> impl Future +/// fn join(a: A, b: B) -> impl Future /// where A: Future + Unpin, /// B: Future + Unpin, /// { From 7da15f88946f469be46489b6a470728b7e8286f7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 11 Apr 2020 00:04:24 +0900 Subject: [PATCH 62/83] Add reunite method to ReadHalf and WriteHalf --- futures-util/src/io/mod.rs | 2 +- futures-util/src/io/split.rs | 42 +++++++++++++++++++++++++ futures-util/src/lock/bilock.rs | 7 ----- futures-util/src/stream/stream/split.rs | 4 +-- futures/src/lib.rs | 2 +- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 43f183f424..03f7b70da5 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -107,7 +107,7 @@ mod sink; pub use self::sink::{sink, Sink}; mod split; -pub use self::split::{ReadHalf, WriteHalf}; +pub use self::split::{ReadHalf, WriteHalf, ReuniteError}; mod take; pub use self::take::Take; diff --git a/futures-util/src/io/split.rs b/futures-util/src/io/split.rs index d12df867fc..ccddd04979 100644 --- a/futures-util/src/io/split.rs +++ b/futures-util/src/io/split.rs @@ -1,6 +1,7 @@ use crate::lock::BiLock; use futures_core::task::{Context, Poll}; use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut}; +use core::fmt; use std::io; use std::pin::Pin; @@ -32,6 +33,26 @@ pub(super) fn split(t: T) -> (ReadHalf, WriteHalf< (ReadHalf { handle: a }, WriteHalf { handle: b }) } +impl ReadHalf { + /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back + /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are + /// a matching pair originating from the same call to `AsyncReadExt::split`. + pub fn reunite(self, other: WriteHalf) -> Result> { + self.handle.reunite(other.handle).map_err(|err| { + ReuniteError(ReadHalf { handle: err.0 }, WriteHalf { handle: err.1 }) + }) + } +} + +impl WriteHalf { + /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back + /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are + /// a matching pair originating from the same call to `AsyncReadExt::split`. + pub fn reunite(self, other: ReadHalf) -> Result> { + other.reunite(self) + } +} + impl AsyncRead for ReadHalf { fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> @@ -67,3 +88,24 @@ impl AsyncWrite for WriteHalf { lock_and_then(&self.handle, cx, |l, cx| l.poll_close(cx)) } } + +/// Error indicating a `ReadHalf` and `WriteHalf` were not two halves +/// of a `AsyncRead + AsyncWrite`, and thus could not be `reunite`d. +pub struct ReuniteError(pub ReadHalf, pub WriteHalf); + +impl fmt::Debug for ReuniteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ReuniteError") + .field(&"...") + .finish() + } +} + +impl fmt::Display for ReuniteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "tried to reunite a ReadHalf and WriteHalf that don't form a pair") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ReuniteError {} diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 45d86e6a18..cccd0be26c 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -4,7 +4,6 @@ use futures_core::future::Future; use futures_core::task::{Context, Poll, Waker}; use core::cell::UnsafeCell; -#[cfg(any(feature = "bilock", feature = "sink"))] use core::fmt; use core::ops::{Deref, DerefMut}; use core::pin::Pin; @@ -152,7 +151,6 @@ impl BiLock { /// Attempts to put the two "halves" of a `BiLock` back together and /// recover the original value. Succeeds only if the two `BiLock`s /// originated from the same call to `BiLock::new`. - #[cfg(any(feature = "bilock", feature = "sink"))] pub fn reunite(self, other: Self) -> Result> where T: Unpin, @@ -186,7 +184,6 @@ impl BiLock { } } -#[cfg(any(feature = "bilock", feature = "sink"))] impl Inner { unsafe fn into_value(mut self) -> T { self.value.take().unwrap().into_inner() @@ -201,10 +198,8 @@ impl Drop for Inner { /// Error indicating two `BiLock`s were not two halves of a whole, and /// thus could not be `reunite`d. -#[cfg(any(feature = "bilock", feature = "sink"))] pub struct ReuniteError(pub BiLock, pub BiLock); -#[cfg(any(feature = "bilock", feature = "sink"))] impl fmt::Debug for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ReuniteError") @@ -213,14 +208,12 @@ impl fmt::Debug for ReuniteError { } } -#[cfg(any(feature = "bilock", feature = "sink"))] impl fmt::Display for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "tried to reunite two BiLocks that don't form a pair") } } -#[cfg(any(feature = "bilock", feature = "sink"))] #[cfg(feature = "std")] impl std::error::Error for ReuniteError {} diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index 4118b33462..c7237a22eb 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -16,7 +16,7 @@ impl Unpin for SplitStream {} impl SplitStream { /// Attempts to put the two "halves" of a split `Stream + Sink` back /// together. Succeeds only if the `SplitStream` and `SplitSink` are - /// a matching pair originating from the same call to `Stream::split`. + /// a matching pair originating from the same call to `StreamExt::split`. pub fn reunite(self, other: SplitSink) -> Result> where S: Sink, { @@ -53,7 +53,7 @@ impl Unpin for SplitSink {} impl + Unpin, Item> SplitSink { /// Attempts to put the two "halves" of a split `Stream + Sink` back /// together. Succeeds only if the `SplitStream` and `SplitSink` are - /// a matching pair originating from the same call to `Stream::split`. + /// a matching pair originating from the same call to `StreamExt::split`. pub fn reunite(self, other: SplitStream) -> Result> { self.lock.reunite(other.0).map_err(|err| { ReuniteError(SplitSink(err.0), SplitStream(err.1)) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index c4acbc0fd1..7494c98f05 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -332,7 +332,7 @@ pub mod io { BufReader, BufWriter, Cursor, Chain, Close, copy, Copy, copy_buf, CopyBuf, empty, Empty, Flush, IntoSink, Lines, Read, ReadExact, ReadHalf, ReadLine, ReadToEnd, ReadToString, ReadUntil, ReadVectored, repeat, - Repeat, Seek, sink, Sink, Take, Window, Write, WriteAll, WriteHalf, + Repeat, ReuniteError, Seek, sink, Sink, Take, Window, Write, WriteAll, WriteHalf, WriteVectored, }; } From d6c8bd8f35bc4e6a44f13f1657d07226a7b3e3f0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 10 Apr 2020 22:01:33 +0900 Subject: [PATCH 63/83] Support reexport for join!/try_join! macros --- Cargo.toml | 3 ++ futures-macro/src/join.rs | 66 ++++++++---------------- futures-macro/src/lib.rs | 4 +- futures-util/src/async_await/join_mod.rs | 32 +++++++++--- futures-util/src/lib.rs | 8 +++ futures/src/lib.rs | 24 +-------- futures/tests/macro-reexport/Cargo.toml | 9 ++++ futures/tests/macro-reexport/src/lib.rs | 5 ++ futures/tests/macro-tests/Cargo.toml | 10 ++++ futures/tests/macro-tests/src/main.rs | 23 +++++++++ 10 files changed, 108 insertions(+), 76 deletions(-) create mode 100644 futures/tests/macro-reexport/Cargo.toml create mode 100644 futures/tests/macro-reexport/src/lib.rs create mode 100644 futures/tests/macro-tests/Cargo.toml create mode 100644 futures/tests/macro-tests/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 56d24c2e90..f972a73d2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ members = [ "futures-util", "futures-test", + "futures/tests/macro-tests", + "futures/tests/macro-reexport", + "examples/functional", "examples/imperative", ] diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index 2a7b11ca6b..e932605a0f 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -4,15 +4,10 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; -use syn::{parenthesized, parse_quote, Expr, Ident, Token}; - -mod kw { - syn::custom_keyword!(futures_crate_path); -} +use syn::{Expr, Ident, Token}; #[derive(Default)] struct Join { - futures_crate_path: Option, fut_exprs: Vec, } @@ -20,16 +15,6 @@ impl Parse for Join { fn parse(input: ParseStream<'_>) -> syn::Result { let mut join = Join::default(); - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - join.futures_crate_path = Some(content.parse()?); - } - while !input.is_empty() { join.fut_exprs.push(input.parse::()?); @@ -43,7 +28,6 @@ impl Parse for Join { } fn bind_futures( - futures_crate: &syn::Path, fut_exprs: Vec, span: Span, ) -> (Vec, Vec) { @@ -56,7 +40,7 @@ fn bind_futures( future_let_bindings.push(quote! { // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut #name = #futures_crate::future::maybe_done(#expr); + let mut #name = __futures_crate::future::maybe_done(#expr); }); name }) @@ -69,39 +53,35 @@ fn bind_futures( pub(crate) fn join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - __all_done &= #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); + __all_done &= __futures_crate::future::Future::poll( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); } }); let take_outputs = future_names.iter().map(|fut| { quote! { - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), } }); TokenStream::from(quote! { { #( #future_let_bindings )* - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready(( + __futures_crate::task::Poll::Ready(( #( #take_outputs )* )) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) @@ -111,29 +91,25 @@ pub(crate) fn join(input: TokenStream) -> TokenStream { pub(crate) fn try_join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - if #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() + if __futures_crate::future::Future::poll( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() { __all_done = false; - } else if unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { + } else if unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { // `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce // a `T: Debug` bound. // Also, for an error type of ! any code after `err().unwrap()` is unreachable. #[allow(unreachable_code)] - return #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Err( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() + return __futures_crate::task::Poll::Ready( + __futures_crate::core_reexport::result::Result::Err( + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() ) ); } @@ -145,7 +121,7 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { // an `E: Debug` bound. // Also, for an ok type of ! any code after `ok().unwrap()` is unreachable. #[allow(unreachable_code)] - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), + unsafe { __futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), } }); @@ -153,17 +129,17 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { #( #future_let_bindings )* #[allow(clippy::diverging_sub_expression)] - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Ok(( + __futures_crate::task::Poll::Ready( + __futures_crate::core_reexport::result::Result::Ok(( #( #take_outputs )* )) ) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 932ec2e4f1..095929780d 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -23,13 +23,13 @@ mod select; /// The `join!` macro. #[proc_macro_hack] -pub fn join(input: TokenStream) -> TokenStream { +pub fn join_internal(input: TokenStream) -> TokenStream { crate::join::join(input) } /// The `try_join!` macro. #[proc_macro_hack] -pub fn try_join(input: TokenStream) -> TokenStream { +pub fn try_join_internal(input: TokenStream) -> TokenStream { crate::join::try_join(input) } diff --git a/futures-util/src/async_await/join_mod.rs b/futures-util/src/async_await/join_mod.rs index 2e27f2149a..909cd3b61b 100644 --- a/futures-util/src/async_await/join_mod.rs +++ b/futures-util/src/async_await/join_mod.rs @@ -2,8 +2,6 @@ use proc_macro_hack::proc_macro_hack; -#[doc(hidden)] -#[macro_export] macro_rules! document_join_macro { ($join:item $try_join:item) => { /// Polls multiple futures simultaneously, returning a tuple @@ -73,10 +71,32 @@ macro_rules! document_join_macro { } } +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::join_internal; + +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::try_join_internal; + document_join_macro! { - #[proc_macro_hack(support_nested)] - pub use futures_macro::join; + #[macro_export] + macro_rules! join { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::join_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::try_join; + #[macro_export] + macro_rules! try_join { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::try_join_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 21645b148d..67067a63ea 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -51,6 +51,14 @@ pub use self::async_await::*; #[doc(hidden)] pub use futures_core::core_reexport; +// Not public API. +#[cfg(feature = "async-await")] +#[doc(hidden)] +pub mod __reexport { + #[doc(hidden)] + pub use crate::*; +} + macro_rules! cfg_target_has_atomic { ($($item:item)*) => {$( #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 7494c98f05..f617aa3405 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -550,35 +550,13 @@ pub use futures_util::async_await; #[cfg(feature = "async-await")] #[doc(hidden)] pub mod inner_macro { - pub use futures_util::join; - pub use futures_util::try_join; #[cfg(feature = "std")] pub use futures_util::select; pub use futures_util::select_biased; } #[cfg(feature = "async-await")] -futures_util::document_join_macro! { - #[macro_export] - macro_rules! join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! try_join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::try_join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } -} +pub use futures_util::{join, try_join}; #[cfg(feature = "async-await")] futures_util::document_select_macro! { diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml new file mode 100644 index 0000000000..940b537ea7 --- /dev/null +++ b/futures/tests/macro-reexport/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "macro-reexport" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } diff --git a/futures/tests/macro-reexport/src/lib.rs b/futures/tests/macro-reexport/src/lib.rs new file mode 100644 index 0000000000..4866f329c6 --- /dev/null +++ b/futures/tests/macro-reexport/src/lib.rs @@ -0,0 +1,5 @@ +// normal reexport +pub use futures03::{join, try_join}; + +// reexport + rename +pub use futures03::{join as join2, try_join as try_join2}; diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml new file mode 100644 index 0000000000..3d30563868 --- /dev/null +++ b/futures/tests/macro-tests/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "macro-tests" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } +macro-reexport = { path = "../macro-reexport" } diff --git a/futures/tests/macro-tests/src/main.rs b/futures/tests/macro-tests/src/main.rs new file mode 100644 index 0000000000..3a230f1bcf --- /dev/null +++ b/futures/tests/macro-tests/src/main.rs @@ -0,0 +1,23 @@ +// Check that it works even if proc-macros are reexported. + +fn main() { + use futures03::executor::block_on; + + // join! macro + let _ = block_on(async { + let _ = futures03::join!(async {}, async {}); + let _ = macro_reexport::join!(async {}, async {}); + let _ = macro_reexport::join2!(async {}, async {}); + }); + + // try_join! macro + let _ = block_on(async { + let _ = futures03::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join2!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + Ok::<(), ()>(()) + }); + + // TODO: add select! and select_biased! + +} From 6a936af1c15a262765e71dfd74b73b0bd87a2307 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 17 Apr 2020 23:16:08 +0900 Subject: [PATCH 64/83] Support reexport for select!/select_biased! macros --- futures-macro/src/lib.rs | 4 +- futures-macro/src/select.rs | 55 ++++++++-------------- futures-util/src/async_await/select_mod.rs | 35 +++++++++++--- futures/src/lib.rs | 45 ++---------------- futures/tests/macro-reexport/src/lib.rs | 7 ++- futures/tests/macro-tests/src/main.rs | 50 +++++++++++++++++++- 6 files changed, 107 insertions(+), 89 deletions(-) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 095929780d..b565c290c6 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -35,12 +35,12 @@ pub fn try_join_internal(input: TokenStream) -> TokenStream { /// The `select!` macro. #[proc_macro_hack] -pub fn select(input: TokenStream) -> TokenStream { +pub fn select_internal(input: TokenStream) -> TokenStream { crate::select::select(input) } /// The `select_biased!` macro. #[proc_macro_hack] -pub fn select_biased(input: TokenStream) -> TokenStream { +pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index f7f688b6ae..f615b5399c 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -3,16 +3,14 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{format_ident, quote}; -use syn::{parenthesized, parse_quote, Expr, Ident, Pat, Token}; +use syn::{parse_quote, Expr, Ident, Pat, Token}; use syn::parse::{Parse, ParseStream}; mod kw { syn::custom_keyword!(complete); - syn::custom_keyword!(futures_crate_path); } struct Select { - futures_crate_path: Option, // span of `complete`, then expression after `=> ...` complete: Option, default: Option, @@ -30,23 +28,12 @@ enum CaseKind { impl Parse for Select { fn parse(input: ParseStream<'_>) -> syn::Result { let mut select = Select { - futures_crate_path: None, complete: None, default: None, normal_fut_exprs: vec![], normal_fut_handlers: vec![], }; - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - select.futures_crate_path = Some(content.parse()?); - } - while !input.is_empty() { let case_kind = if input.peek(kw::complete) { // `complete` @@ -147,8 +134,6 @@ pub(crate) fn select_biased(input: TokenStream) -> TokenStream { fn select_inner(input: TokenStream, random: bool) -> TokenStream { let parsed = syn::parse_macro_input!(input as Select); - let futures_crate: syn::Path = parsed.futures_crate_path.unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); @@ -175,8 +160,8 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // We check for this condition here in order to be able to // safely use Pin::new_unchecked(&mut #path) later on. future_let_bindings.push(quote! { - #futures_crate::async_await::assert_fused_future(&#path); - #futures_crate::async_await::assert_unpin(&#path); + __futures_crate::async_await::assert_fused_future(&#path); + __futures_crate::async_await::assert_unpin(&#path); }); path }, @@ -214,28 +199,28 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // 2. The Future is created in scope of the select! function and will // not be moved for the duration of it. It is thereby stack-pinned quote! { - let mut #variant_name = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut #variant_name = |__cx: &mut __futures_crate::task::Context<'_>| { let mut #bound_future_name = unsafe { ::core::pin::Pin::new_unchecked(&mut #bound_future_name) }; - if #futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { + if __futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { None } else { - Some(#futures_crate::future::FutureExt::poll_unpin( + Some(__futures_crate::future::FutureExt::poll_unpin( &mut #bound_future_name, __cx, ).map(#enum_ident::#variant_name)) } }; let #variant_name: &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = &mut #variant_name; + &mut __futures_crate::task::Context<'_> + ) -> Option<__futures_crate::task::Poll<_>> = &mut #variant_name; } }); let none_polled = if parsed.complete.is_some() { quote! { - #futures_crate::task::Poll::Ready(#enum_ident::Complete) + __futures_crate::task::Poll::Ready(#enum_ident::Complete) } } else { quote! { @@ -267,13 +252,13 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let await_select_fut = if parsed.default.is_some() { // For select! with default this returns the Poll result quote! { - __poll_fn(&mut #futures_crate::task::Context::from_waker( - #futures_crate::task::noop_waker_ref() + __poll_fn(&mut __futures_crate::task::Context::from_waker( + __futures_crate::task::noop_waker_ref() )) } } else { quote! { - #futures_crate::future::poll_fn(__poll_fn).await + __futures_crate::future::poll_fn(__poll_fn).await } }; @@ -281,7 +266,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // For select! with default __select_result is a Poll, otherwise not quote! { match __select_result { - #futures_crate::task::Poll::Ready(result) => match result { + __futures_crate::task::Poll::Ready(result) => match result { #branches }, _ => #default_expr @@ -297,7 +282,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let shuffle = if random { quote! { - #futures_crate::async_await::shuffle(&mut __select_arr); + __futures_crate::async_await::shuffle(&mut __select_arr); } } else { quote!() @@ -309,7 +294,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let __select_result = { #( #future_let_bindings )* - let mut __poll_fn = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut __poll_fn = |__cx: &mut __futures_crate::task::Context<'_>| { let mut __any_polled = false; #( #poll_functions )* @@ -318,12 +303,12 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { #shuffle for poller in &mut __select_arr { let poller: &mut &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = poller; + &mut __futures_crate::task::Context<'_> + ) -> Option<__futures_crate::task::Poll<_>> = poller; match poller(__cx) { - Some(x @ #futures_crate::task::Poll::Ready(_)) => + Some(x @ __futures_crate::task::Poll::Ready(_)) => return x, - Some(#futures_crate::task::Poll::Pending) => { + Some(__futures_crate::task::Poll::Pending) => { __any_polled = true; } None => {} @@ -333,7 +318,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { if !__any_polled { #none_polled } else { - #futures_crate::task::Poll::Pending + __futures_crate::task::Poll::Pending } }; diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 38153c7f7a..0471f09182 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -2,8 +2,6 @@ use proc_macro_hack::proc_macro_hack; -#[doc(hidden)] -#[macro_export] macro_rules! document_select_macro { // This branch is required for `futures 0.3.1`, from before select_biased was introduced ($select:item) => { @@ -158,7 +156,7 @@ macro_rules! document_select_macro { }; ($select:item $select_biased:item) => { - $crate::document_select_macro!($select); + document_select_macro!($select); /// Polls multiple futures and streams simultaneously, executing the branch /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready, @@ -310,11 +308,34 @@ macro_rules! document_select_macro { }; } +#[cfg(feature = "std")] +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::select_internal; + +#[doc(hidden)] +#[proc_macro_hack(support_nested)] +pub use futures_macro::select_biased_internal; + document_select_macro! { #[cfg(feature = "std")] - #[proc_macro_hack(support_nested)] - pub use futures_macro::select; + #[macro_export] + macro_rules! select { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::select_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::select_biased; + #[macro_export] + macro_rules! select_biased { + ($($tokens:tt)*) => {{ + use $crate::__reexport as __futures_crate; + $crate::select_biased_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures/src/lib.rs b/futures/src/lib.rs index f617aa3405..34f0de7229 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -537,47 +537,10 @@ pub mod never { // proc-macro re-export -------------------------------------- -// Not public API. -#[doc(hidden)] -pub use futures_core::core_reexport; - -// Not public API. -#[cfg(feature = "async-await")] -#[doc(hidden)] -pub use futures_util::async_await; - -// Not public API. -#[cfg(feature = "async-await")] -#[doc(hidden)] -pub mod inner_macro { - #[cfg(feature = "std")] - pub use futures_util::select; - pub use futures_util::select_biased; -} - #[cfg(feature = "async-await")] pub use futures_util::{join, try_join}; - +#[cfg(feature = "std")] #[cfg(feature = "async-await")] -futures_util::document_select_macro! { - #[cfg(feature = "std")] - #[macro_export] - macro_rules! select { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! select_biased { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select_biased! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } -} +pub use futures_util::select; +#[cfg(feature = "async-await")] +pub use futures_util::select_biased; diff --git a/futures/tests/macro-reexport/src/lib.rs b/futures/tests/macro-reexport/src/lib.rs index 4866f329c6..49691c9e07 100644 --- a/futures/tests/macro-reexport/src/lib.rs +++ b/futures/tests/macro-reexport/src/lib.rs @@ -1,5 +1,8 @@ // normal reexport -pub use futures03::{join, try_join}; +pub use futures03::{join, try_join, select, select_biased}; // reexport + rename -pub use futures03::{join as join2, try_join as try_join2}; +pub use futures03::{ + join as join2, try_join as try_join2, + select as select2, select_biased as select_biased2, +}; diff --git a/futures/tests/macro-tests/src/main.rs b/futures/tests/macro-tests/src/main.rs index 3a230f1bcf..5d11f60834 100644 --- a/futures/tests/macro-tests/src/main.rs +++ b/futures/tests/macro-tests/src/main.rs @@ -1,7 +1,7 @@ // Check that it works even if proc-macros are reexported. fn main() { - use futures03::executor::block_on; + use futures03::{executor::block_on, future}; // join! macro let _ = block_on(async { @@ -18,6 +18,52 @@ fn main() { Ok::<(), ()>(()) }); - // TODO: add select! and select_biased! + // select! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); + + // select_biased! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); } From 94b894af6c080e209109922fde356bcf705b7d5b Mon Sep 17 00:00:00 2001 From: Gleb Pomykalov Date: Fri, 10 Apr 2020 16:29:07 +0300 Subject: [PATCH 65/83] Introduce `ready_chunks` adaptor --- futures-util/src/stream/mod.rs | 3 + futures-util/src/stream/stream/mod.rs | 32 ++++ .../src/stream/stream/ready_chunks.rs | 146 ++++++++++++++++++ futures/src/lib.rs | 2 +- futures/tests/stream.rs | 39 +++++ 5 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 futures-util/src/stream/stream/ready_chunks.rs diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 6a57110009..2a5ecf9367 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -24,6 +24,9 @@ pub use self::stream::CatchUnwind; #[cfg(feature = "alloc")] pub use self::stream::Chunks; +#[cfg(feature = "alloc")] +pub use self::stream::ReadyChunks; + #[cfg(feature = "sink")] pub use self::stream::Forward; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 0fb5474e44..9de4514d7d 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -128,6 +128,12 @@ mod chunks; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chunks::Chunks; +#[cfg(feature = "alloc")] +mod ready_chunks; +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::ready_chunks::ReadyChunks; + mod scan; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::scan::Scan; @@ -1186,6 +1192,32 @@ pub trait StreamExt: Stream { Chunks::new(self, capacity) } + /// An adaptor for chunking up ready items of the stream inside a vector. + /// + /// This combinator will attempt to pull ready items from this stream and + /// buffer them into a local vector. At most `capacity` items will get + /// buffered before they're yielded from the returned stream. If underlying + /// stream returns `Poll::Pending`, and collected chunk is not empty, it will + /// be immediately returned. + /// + /// If the underlying stream ended and only a partial vector was created, + /// it'll be returned. Additionally if an error happens from the underlying + /// stream then the currently buffered items will be yielded. + /// + /// This method is only available when the `std` or `alloc` feature of this + /// library is activated, and it is activated by default. + /// + /// # Panics + /// + /// This method will panic if `capacity` is zero. + #[cfg(feature = "alloc")] + fn ready_chunks(self, capacity: usize) -> ReadyChunks + where + Self: Sized, + { + ReadyChunks::new(self, capacity) + } + /// A future that completes after the given stream has been fully processed /// into the sink and the sink has been flushed and closed. /// diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs new file mode 100644 index 0000000000..9a762c9e1b --- /dev/null +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -0,0 +1,146 @@ +use crate::stream::Fuse; +use futures_core::stream::{Stream, FusedStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use core::mem; +use core::pin::Pin; +use alloc::vec::Vec; + +/// Stream for the [`ready_chunks`](super::StreamExt::ready_chunks) method. +#[derive(Debug)] +#[must_use = "streams do nothing unless polled"] +pub struct ReadyChunks { + stream: Fuse, + items: Vec, + cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 +} + +impl Unpin for ReadyChunks {} + +impl ReadyChunks where St: Stream { + unsafe_unpinned!(items: Vec); + unsafe_pinned!(stream: Fuse); + + pub(super) fn new(stream: St, capacity: usize) -> ReadyChunks { + assert!(capacity > 0); + + ReadyChunks { + stream: super::Fuse::new(stream), + items: Vec::with_capacity(capacity), + cap: capacity, + } + } + + fn take(mut self: Pin<&mut Self>) -> Vec { + let cap = self.cap; + mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) + } + + /// Acquires a reference to the underlying stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &St { + self.stream.get_ref() + } + + /// Acquires a mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut St { + self.stream.get_mut() + } + + /// Acquires a pinned mutable reference to the underlying stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { + self.stream().get_pin_mut() + } + + /// Consumes this combinator, returning the underlying stream. + /// + /// Note that this may discard intermediate state of this combinator, so + /// care should be taken to avoid losing resources when this is called. + pub fn into_inner(self) -> St { + self.stream.into_inner() + } +} + +impl Stream for ReadyChunks { + type Item = Vec; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + loop { + match self.as_mut().stream().poll_next(cx) { + // Flush all collected data if underlying stream doesn't contain + // more ready values + Poll::Pending => { + return if self.items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(self.as_mut().take())) + } + } + + // Push the ready item into the buffer and check whether it is full. + // If so, replace our buffer with a new and empty one and return + // the full one. + Poll::Ready(Some(item)) => { + self.as_mut().items().push(item); + if self.items.len() >= self.cap { + return Poll::Ready(Some(self.as_mut().take())) + } + } + + // Since the underlying stream ran out of values, return what we + // have buffered, if we have anything. + Poll::Ready(None) => { + let last = if self.items.is_empty() { + None + } else { + let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + Some(full_buf) + }; + + return Poll::Ready(last); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let (lower, upper) = self.stream.size_hint(); + let lower = lower.saturating_add(chunk_len); + let upper = match upper { + Some(x) => x.checked_add(chunk_len), + None => None, + }; + (lower, upper) + } +} + +impl FusedStream for ReadyChunks { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.items.is_empty() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for ReadyChunks +where + S: Stream + Sink, +{ + type Error = S::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 34f0de7229..9b68a6a8d7 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -458,7 +458,7 @@ pub mod stream { #[cfg(feature = "alloc")] pub use futures_util::stream::{ // For StreamExt: - Chunks, + Chunks, ReadyChunks, }; #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 2d99dfd72c..09fe9e2d45 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -124,3 +124,42 @@ fn take_until() { assert_eq!(stream.next().await, None); }); } + +#[test] +#[should_panic] +fn ready_chunks_panic_on_cap_zero() { + use futures::channel::mpsc; + use futures::stream::StreamExt; + + let (_, rx1) = mpsc::channel::<()>(1); + + let _ = rx1.ready_chunks(0); +} + +#[cfg(feature = "executor")] // executor:: +#[test] +fn ready_chunks() { + use futures::channel::mpsc; + use futures::stream::StreamExt; + use futures::sink::SinkExt; + use futures::FutureExt; + use futures_test::task::noop_context; + + let (mut tx, rx1) = mpsc::channel::(16); + + let mut s = rx1.ready_chunks(2); + + let mut cx = noop_context(); + assert!(s.next().poll_unpin(&mut cx).is_pending()); + + futures::executor::block_on(async { + tx.send(1).await.unwrap(); + + assert_eq!(s.next().await.unwrap(), vec![1]); + tx.send(2).await.unwrap(); + tx.send(3).await.unwrap(); + tx.send(4).await.unwrap(); + assert_eq!(s.next().await.unwrap(), vec![2,3]); + assert_eq!(s.next().await.unwrap(), vec![4]); + }); +} \ No newline at end of file From 9c1b2d4ae72611f5373ae8209eb02965efc159de Mon Sep 17 00:00:00 2001 From: Nathan West Date: Wed, 8 Apr 2020 12:15:09 -0400 Subject: [PATCH 66/83] Code cleanup in shared.rs - Replaced several uses of `if let` with a more idiomatic `match` - added `will_wake` optimization - cleaned up verbose logic in `record_waker` - rustfmt --- futures-util/src/future/future/shared.rs | 43 +++++++++++------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 65fad2d34f..ceb66cfa89 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -1,4 +1,4 @@ -use crate::task::{ArcWake, waker_ref}; +use crate::task::{waker_ref, ArcWake}; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use slab::Slab; @@ -54,13 +54,15 @@ unsafe impl Send for Inner where Fut: Future + Send, Fut::Output: Send + Sync, -{} +{ +} unsafe impl Sync for Inner where Fut: Future + Send, Fut::Output: Send + Sync, -{} +{ +} const IDLE: usize = 0; const POLLING: usize = 1; @@ -125,27 +127,20 @@ where fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) { let mut wakers_guard = self.notifier.wakers.lock().unwrap(); - let wakers = if let Some(wakers) = wakers_guard.as_mut() { - wakers - } else { - return; + let wakers = match wakers_guard.as_mut() { + Some(wakers) => wakers, + None => return, }; + let new_waker = cx.waker(); + if *waker_key == NULL_WAKER_KEY { - *waker_key = wakers.insert(Some(cx.waker().clone())); + *waker_key = wakers.insert(Some(new_waker.clone())); } else { - let waker_slot = &mut wakers[*waker_key]; - let needs_replacement = if let Some(_old_waker) = waker_slot { - // If there's still an unwoken waker in the slot, only replace - // if the current one wouldn't wake the same task. - // TODO: This API is currently not available, so replace always - // !waker.will_wake_nonlocal(old_waker) - true - } else { - true - }; - if needs_replacement { - *waker_slot = Some(cx.waker().clone()); + match wakers[*waker_key] { + Some(ref old_waker) if new_waker.will_wake(old_waker) => {} + // Could use clone_from here, but Waker doesn't specialize it. + ref mut slot => *slot = Some(new_waker.clone()), } } debug_assert!(*waker_key != NULL_WAKER_KEY); @@ -184,7 +179,10 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; - let inner = this.inner.take().expect("Shared future polled again after completion"); + let inner = this + .inner + .take() + .expect("Shared future polled again after completion"); // Fast path for when the wrapped future has already completed if inner.notifier.state.load(Acquire) == COMPLETE { @@ -262,8 +260,7 @@ where }; unsafe { - *inner.future_or_output.get() = - FutureOrOutput::Output(output); + *inner.future_or_output.get() = FutureOrOutput::Output(output); } inner.notifier.state.store(COMPLETE, SeqCst); From 28dfa30ac649ed3dd6aa5f0216d5d5b106b8cdbc Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Sat, 4 Apr 2020 12:14:05 +0200 Subject: [PATCH 67/83] Refactor StreamExt#forward type bounds This makes the bounds easier to understand in documentation by removing bounds referring to each other. Previously, the bounds in documentation appeared as such: S: Sink, Self: TryStream>::Error>, This changes them to the following simpler to understand form. S: Sink, Self: TryStream, --- futures-util/src/stream/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 9de4514d7d..a7e312b7b8 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -1231,8 +1231,8 @@ pub trait StreamExt: Stream { #[cfg(feature = "sink")] fn forward(self, sink: S) -> Forward where - S: Sink<::Ok>, - Self: TryStream + Sized, + S: Sink, + Self: TryStream + Sized, { Forward::new(self, sink) } From d7d821620341b77320c1dd4ca43666e908069c3b Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Tue, 16 Jul 2019 18:18:15 +0200 Subject: [PATCH 68/83] Add AsyncWriteExt::write_all_vectored utility This adds a new feature to future-util, named write_all_vectored, to enable the utility since it requires the unstable io_slice_advance Rust feature. This matches the same API found in io::Write::write_all_vectored in the std lib. --- futures-util/Cargo.toml | 1 + futures-util/src/io/mod.rs | 60 ++++++- futures-util/src/io/write_all.rs | 2 +- futures-util/src/io/write_all_vectored.rs | 200 ++++++++++++++++++++++ futures-util/src/lib.rs | 1 + futures/Cargo.toml | 1 + 6 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 futures-util/src/io/write_all_vectored.rs diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index e7d532d95c..1ea067e378 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -30,6 +30,7 @@ unstable = ["futures-core/unstable", "futures-task/unstable"] cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic", "futures-task/cfg-target-has-atomic"] bilock = [] read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] +write-all-vectored = ["io"] [dependencies] futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 03f7b70da5..29b6418ae6 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -124,6 +124,11 @@ pub use self::write_vectored::WriteVectored; mod write_all; pub use self::write_all::WriteAll; +#[cfg(feature = "write_all_vectored")] +mod write_all_vectored; +#[cfg(feature = "write_all_vectored")] +pub use self::write_all_vectored::WriteAllVectored; + /// An extension trait which adds utility methods to `AsyncRead` types. pub trait AsyncReadExt: AsyncRead { /// Creates an adaptor which will chain this stream with another. @@ -460,6 +465,60 @@ pub trait AsyncWriteExt: AsyncWrite { WriteAll::new(self, buf) } + /// Attempts to write multiple buffers into this writer. + /// + /// Creates a future that will write the entire contents of `bufs` into this + /// `AsyncWrite` using [vectored writes]. + /// + /// The returned future will not complete until all the data has been + /// written. + /// + /// [vectored writes]: std::io::Write::write_vectored + /// + /// # Notes + /// + /// Unlike `io::Write::write_vectored`, this takes a *mutable* reference to + /// a slice of `IoSlice`s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this futures returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to `write_vectored` were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and + /// can be reused. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::io::AsyncWriteExt; + /// use std::io::{Cursor, IoSlice}; + /// + /// let mut writer = Cursor::new([0u8; 7]); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs).await?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 5, 6, 0]); + /// # Ok::<(), Box>(()) }).unwrap(); + /// ``` + #[cfg(feature = "write_all_vectored")] + fn write_all_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSlice<'a>], + ) -> WriteAllVectored<'a, Self> + where + Self: Unpin, + { + WriteAllVectored::new(self, bufs) + } + /// Wraps an [`AsyncWrite`] in a compatibility wrapper that allows it to be /// used as a futures 0.1 / tokio-io 0.1 `AsyncWrite`. /// Requires the `io-compat` feature to enable. @@ -470,7 +529,6 @@ pub trait AsyncWriteExt: AsyncWrite { Compat::new(self) } - /// Allow using an [`AsyncWrite`] as a [`Sink`](futures_sink::Sink)`>`. /// /// This adapter produces a sink that will write each value passed to it diff --git a/futures-util/src/io/write_all.rs b/futures-util/src/io/write_all.rs index 57f1400b0e..f9ffb49ea2 100644 --- a/futures-util/src/io/write_all.rs +++ b/futures-util/src/io/write_all.rs @@ -33,7 +33,7 @@ impl Future for WriteAll<'_, W> { this.buf = rest; } if n == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } diff --git a/futures-util/src/io/write_all_vectored.rs b/futures-util/src/io/write_all_vectored.rs new file mode 100644 index 0000000000..fbe7e73ae5 --- /dev/null +++ b/futures-util/src/io/write_all_vectored.rs @@ -0,0 +1,200 @@ +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; +use futures_io::AsyncWrite; +use futures_io::IoSlice; +use std::io; +use std::mem; +use std::pin::Pin; + +/// Future for the +/// [`write_all_vectored`](super::AsyncWriteExt::write_all_vectored) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct WriteAllVectored<'a, W: ?Sized + Unpin> { + writer: &'a mut W, + bufs: &'a mut [IoSlice<'a>], +} + +impl Unpin for WriteAllVectored<'_, W> {} + +impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAllVectored<'a, W> { + pub(super) fn new(writer: &'a mut W, bufs: &'a mut [IoSlice<'a>]) -> Self { + WriteAllVectored { writer, bufs } + } +} + +impl Future for WriteAllVectored<'_, W> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + while !this.bufs.is_empty() { + let n = ready!(Pin::new(&mut this.writer).poll_write_vectored(cx, this.bufs))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } else { + this.bufs = IoSlice::advance(mem::take(&mut this.bufs), n); + } + } + + Poll::Ready(Ok(())) + } +} + +#[cfg(test)] +mod tests { + use std::cmp::min; + use std::future::Future; + use std::io; + use std::pin::Pin; + use std::task::{Context, Poll}; + + use crate::io::{AsyncWrite, AsyncWriteExt, IoSlice}; + use crate::task::noop_waker; + + /// Create a new writer that reads from at most `n_bufs` and reads + /// `per_call` bytes (in total) per call to write. + fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { + TestWriter { + n_bufs, + per_call, + written: Vec::new(), + } + } + + // TODO: maybe move this the future-test crate? + struct TestWriter { + n_bufs: usize, + per_call: usize, + written: Vec, + } + + impl AsyncWrite for TestWriter { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.poll_write_vectored(cx, &[IoSlice::new(buf)]) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + let mut left = self.per_call; + let mut written = 0; + for buf in bufs.iter().take(self.n_bufs) { + let n = min(left, buf.len()); + self.written.extend_from_slice(&buf[0..n]); + left -= n; + written += n; + } + Poll::Ready(Ok(written)) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + } + + // TODO: maybe move this the future-test crate? + macro_rules! assert_poll_ok { + ($e:expr, $expected:expr) => { + let expected = $expected; + match $e { + Poll::Ready(Ok(ok)) if ok == expected => {} + got => panic!( + "unexpected result, got: {:?}, wanted: Ready(Ok({:?}))", + got, expected + ), + } + }; + } + + #[test] + fn test_writer_read_from_one_buf() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + let mut dst = test_writer(1, 2); + let mut dst = Pin::new(&mut dst); + + assert_poll_ok!(dst.as_mut().poll_write(&mut cx, &[]), 0); + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, &[]), 0); + + // Read at most 2 bytes. + assert_poll_ok!(dst.as_mut().poll_write(&mut cx, &[1, 1, 1]), 2); + let bufs = &[IoSlice::new(&[2, 2, 2])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 2); + + // Only read from first buf. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 1); + + assert_eq!(dst.written, &[1, 1, 2, 2, 3]); + } + + #[test] + fn test_writer_read_from_multiple_bufs() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + let mut dst = test_writer(3, 3); + let mut dst = Pin::new(&mut dst); + + // Read at most 3 bytes from two buffers. + let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 3); + + // Read at most 3 bytes from three buffers. + let bufs = &[ + IoSlice::new(&[3]), + IoSlice::new(&[4]), + IoSlice::new(&[5, 5]), + ]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 3); + + assert_eq!(dst.written, &[1, 2, 2, 3, 4, 5]); + } + + #[test] + fn test_write_all_vectored() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + #[rustfmt::skip] // Becomes unreadable otherwise. + let tests: Vec<(_, &'static [u8])> = vec![ + (vec![], &[]), + (vec![IoSlice::new(&[1])], &[1]), + (vec![IoSlice::new(&[1, 2])], &[1, 2]), + (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), + (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), + ]; + + for (mut input, wanted) in tests.into_iter() { + let mut dst = test_writer(2, 2); + { + let mut future = dst.write_all_vectored(&mut *input); + match Pin::new(&mut future).poll(&mut cx) { + Poll::Ready(Ok(())) => {} + other => panic!("unexpected result polling future: {:?}", other), + } + } + assert_eq!(&*dst.written, &*wanted); + } + } +} diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 67067a63ea..13827170b5 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -3,6 +3,7 @@ #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(feature = "read-initializer", feature(read_initializer))] +#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6619121fa4..6b30a8dde7 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -51,6 +51,7 @@ unstable = ["futures-core/unstable", "futures-task/unstable", "futures-channel/u cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic", "futures-task/cfg-target-has-atomic", "futures-channel/cfg-target-has-atomic", "futures-util/cfg-target-has-atomic"] bilock = ["futures-util/bilock"] read-initializer = ["futures-io/read-initializer", "futures-util/read-initializer"] +write-all-vectored = ["futures-util/write-all-vectored"] [package.metadata.docs.rs] all-features = true From 3df23c81bf3d4b7aa9028312b4f696e28e04de91 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Mon, 6 Apr 2020 19:15:06 +0100 Subject: [PATCH 69/83] Refactor to reduce the amount of unsafe and duplicated code. - Replace uses of `pin_utils` with the safer `pin_project` crate - Implement macros to allow building new combinators from existing ones more easily - Get rid of Chain as the building block for future combinators, and instead build from `Map` and `Flatten` primitives - Delete a lot of code which is no longer necessary. --- futures-util/Cargo.toml | 1 + futures-util/src/fns.rs | 354 ++++++++++++++++++ futures-util/src/future/abortable.rs | 10 +- futures-util/src/future/either.rs | 159 ++++---- .../src/future/future/catch_unwind.rs | 21 +- futures-util/src/future/future/chain.rs | 58 --- futures-util/src/future/future/flatten.rs | 167 +++++++-- .../src/future/future/flatten_stream.rs | 89 ----- futures-util/src/future/future/fuse.rs | 34 +- futures-util/src/future/future/inspect.rs | 47 --- futures-util/src/future/future/into_stream.rs | 43 --- futures-util/src/future/future/map.rs | 35 +- futures-util/src/future/future/mod.rs | 108 ++++-- futures-util/src/future/future/never_error.rs | 36 -- .../src/future/future/remote_handle.rs | 25 +- futures-util/src/future/future/then.rs | 46 --- futures-util/src/future/future/unit_error.rs | 35 -- futures-util/src/future/join.rs | 16 +- futures-util/src/future/join_all.rs | 53 +-- futures-util/src/future/maybe_done.rs | 41 +- futures-util/src/future/mod.rs | 13 +- futures-util/src/future/option.rs | 17 +- .../src/future/try_future/and_then.rs | 53 --- .../src/future/try_future/err_into.rs | 48 --- .../src/future/try_future/flatten_sink.rs | 76 ---- .../future/try_future/flatten_stream_sink.rs | 181 --------- .../src/future/try_future/inspect_err.rs | 53 --- .../src/future/try_future/inspect_ok.rs | 53 --- .../src/future/try_future/into_future.rs | 17 +- futures-util/src/future/try_future/map_err.rs | 52 --- futures-util/src/future/try_future/map_ok.rs | 54 --- .../src/future/try_future/map_ok_or_else.rs | 59 --- futures-util/src/future/try_future/mod.rs | 201 +++++++--- futures-util/src/future/try_future/or_else.rs | 56 --- .../src/future/try_future/try_chain.rs | 110 ------ .../src/future/try_future/try_flatten.rs | 180 +++++++++ .../src/future/try_future/try_flatten_err.rs | 61 +++ .../future/try_future/try_flatten_stream.rs | 91 ----- .../src/future/try_future/unwrap_or_else.rs | 55 --- futures-util/src/future/try_join.rs | 39 +- futures-util/src/future/try_join_all.rs | 59 +-- futures-util/src/future/try_maybe_done.rs | 99 +++++ futures-util/src/io/buf_reader.rs | 110 ++---- futures-util/src/io/buf_writer.rs | 91 +---- futures-util/src/io/chain.rs | 64 ++-- futures-util/src/io/copy.rs | 12 +- futures-util/src/io/copy_buf.rs | 22 +- futures-util/src/io/into_sink.rs | 36 +- futures-util/src/io/lines.rs | 11 +- futures-util/src/io/read_line.rs | 2 +- futures-util/src/io/read_to_string.rs | 2 +- futures-util/src/io/take.rs | 121 ++---- futures-util/src/lib.rs | 224 ++++++++++- futures-util/src/sink/buffer.rs | 66 ++-- futures-util/src/sink/err_into.rs | 42 +-- futures-util/src/sink/fanout.rs | 56 ++- futures-util/src/sink/map_err.rs | 53 +-- futures-util/src/sink/with.rs | 77 ++-- futures-util/src/sink/with_flat_map.rs | 71 +--- futures-util/src/stream/futures_ordered.rs | 15 +- futures-util/src/stream/once.rs | 21 +- futures-util/src/stream/select.rs | 21 +- .../src/stream/stream/buffer_unordered.rs | 60 +-- futures-util/src/stream/stream/buffered.rs | 65 +--- .../src/stream/stream/catch_unwind.rs | 21 +- futures-util/src/stream/stream/chain.rs | 19 +- futures-util/src/stream/stream/chunks.rs | 60 +-- futures-util/src/stream/stream/collect.rs | 22 +- futures-util/src/stream/stream/concat.rs | 20 +- futures-util/src/stream/stream/enumerate.rs | 55 +-- futures-util/src/stream/stream/filter.rs | 88 ++--- futures-util/src/stream/stream/filter_map.rs | 89 ++--- futures-util/src/stream/stream/flat_map.rs | 169 --------- futures-util/src/stream/stream/flatten.rs | 97 ++--- futures-util/src/stream/stream/fold.rs | 50 ++- futures-util/src/stream/stream/for_each.rs | 41 +- .../src/stream/stream/for_each_concurrent.rs | 34 +- futures-util/src/stream/stream/forward.rs | 84 ++--- futures-util/src/stream/stream/fuse.rs | 53 +-- futures-util/src/stream/stream/inspect.rs | 119 ------ futures-util/src/stream/stream/into_future.rs | 7 +- futures-util/src/stream/stream/map.rs | 81 ++-- futures-util/src/stream/stream/mod.rs | 33 +- futures-util/src/stream/stream/peek.rs | 110 ++---- .../src/stream/stream/ready_chunks.rs | 68 +--- futures-util/src/stream/stream/scan.rs | 84 ++--- futures-util/src/stream/stream/skip.rs | 57 +-- futures-util/src/stream/stream/skip_while.rs | 107 ++---- futures-util/src/stream/stream/take.rs | 55 +-- futures-util/src/stream/stream/take_until.rs | 67 +--- futures-util/src/stream/stream/take_while.rs | 93 ++--- futures-util/src/stream/stream/then.rs | 74 ++-- futures-util/src/stream/stream/zip.rs | 44 +-- .../src/stream/try_stream/and_then.rs | 74 ++-- .../src/stream/try_stream/err_into.rs | 98 ----- .../src/stream/try_stream/inspect_err.rs | 118 ------ .../src/stream/try_stream/inspect_ok.rs | 118 ------ .../src/stream/try_stream/into_stream.rs | 40 +- futures-util/src/stream/try_stream/map_err.rs | 112 ------ futures-util/src/stream/try_stream/map_ok.rs | 112 ------ futures-util/src/stream/try_stream/mod.rs | 55 ++- futures-util/src/stream/try_stream/or_else.rs | 79 ++-- .../stream/try_stream/try_buffer_unordered.rs | 59 +-- .../src/stream/try_stream/try_collect.rs | 28 +- .../src/stream/try_stream/try_concat.rs | 36 +- .../src/stream/try_stream/try_filter.rs | 81 ++-- .../src/stream/try_stream/try_filter_map.rs | 80 ++-- .../src/stream/try_stream/try_flatten.rs | 84 +---- .../src/stream/try_stream/try_fold.rs | 70 ++-- .../src/stream/try_stream/try_for_each.rs | 34 +- .../try_stream/try_for_each_concurrent.rs | 42 +-- .../src/stream/try_stream/try_skip_while.rs | 94 ++--- .../src/stream/try_stream/try_unfold.rs | 33 +- futures-util/src/stream/unfold.rs | 29 +- 114 files changed, 2629 insertions(+), 4995 deletions(-) create mode 100644 futures-util/src/fns.rs delete mode 100644 futures-util/src/future/future/chain.rs delete mode 100644 futures-util/src/future/future/flatten_stream.rs delete mode 100644 futures-util/src/future/future/inspect.rs delete mode 100644 futures-util/src/future/future/into_stream.rs delete mode 100644 futures-util/src/future/future/never_error.rs delete mode 100644 futures-util/src/future/future/then.rs delete mode 100644 futures-util/src/future/future/unit_error.rs delete mode 100644 futures-util/src/future/try_future/and_then.rs delete mode 100644 futures-util/src/future/try_future/err_into.rs delete mode 100644 futures-util/src/future/try_future/flatten_sink.rs delete mode 100644 futures-util/src/future/try_future/flatten_stream_sink.rs delete mode 100644 futures-util/src/future/try_future/inspect_err.rs delete mode 100644 futures-util/src/future/try_future/inspect_ok.rs delete mode 100644 futures-util/src/future/try_future/map_err.rs delete mode 100644 futures-util/src/future/try_future/map_ok.rs delete mode 100644 futures-util/src/future/try_future/map_ok_or_else.rs delete mode 100644 futures-util/src/future/try_future/or_else.rs delete mode 100644 futures-util/src/future/try_future/try_chain.rs create mode 100644 futures-util/src/future/try_future/try_flatten.rs create mode 100644 futures-util/src/future/try_future/try_flatten_err.rs delete mode 100644 futures-util/src/future/try_future/try_flatten_stream.rs delete mode 100644 futures-util/src/future/try_future/unwrap_or_else.rs create mode 100644 futures-util/src/future/try_maybe_done.rs delete mode 100644 futures-util/src/stream/stream/flat_map.rs delete mode 100644 futures-util/src/stream/stream/inspect.rs delete mode 100644 futures-util/src/stream/try_stream/err_into.rs delete mode 100644 futures-util/src/stream/try_stream/inspect_err.rs delete mode 100644 futures-util/src/stream/try_stream/inspect_ok.rs delete mode 100644 futures-util/src/stream/try_stream/map_err.rs delete mode 100644 futures-util/src/stream/try_stream/map_ok.rs diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 1ea067e378..11f77c460d 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -46,6 +46,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0-alpha.4" +pin-project = "0.4.8" [dev-dependencies] futures = { path = "../futures", version = "0.3.4", features = ["async-await", "thread-pool"] } diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs new file mode 100644 index 0000000000..2b4e3c6e56 --- /dev/null +++ b/futures-util/src/fns.rs @@ -0,0 +1,354 @@ +use core::marker::PhantomData; +use core::fmt::{self, Debug}; + +pub trait FnOnce1 { + type Output; + fn call_once(self, arg: A) -> Self::Output; +} + +impl FnOnce1 for T +where + T: FnOnce(A) -> R +{ + type Output = R; + fn call_once(self, arg: A) -> R { + self(arg) + } +} + +pub trait FnMut1: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output; +} + +impl FnMut1 for T +where + T: FnMut(A) -> R +{ + fn call_mut(&mut self, arg: A) -> R { + self(arg) + } +} + +// Not used, but present for completeness +#[allow(unreachable_pub)] +pub trait Fn1: FnMut1 { + fn call(&self, arg: A) -> Self::Output; +} + +impl Fn1 for T +where + T: Fn(A) -> R +{ + fn call(&self, arg: A) -> R { + self(arg) + } +} + +macro_rules! trivial_fn_impls { + ($name:ident <$($arg:ident),*> $t:ty = $debug:literal) => { + impl<$($arg),*> Copy for $t {} + impl<$($arg),*> Clone for $t { + fn clone(&self) -> Self { *self } + } + impl<$($arg),*> Debug for $t { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str($debug) + } + } + impl<$($arg,)* A> FnMut1 for $t where Self: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + impl<$($arg,)* A> Fn1 for $t where Self: FnOnce1 { + fn call(&self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + pub(crate) fn $name<$($arg),*>() -> $t { + Default::default() + } + } +} + +pub struct OkFn(PhantomData); + +impl Default for OkFn { + fn default() -> Self { + OkFn(PhantomData) + } +} + +impl FnOnce1 for OkFn { + type Output = Result; + fn call_once(self, arg: A) -> Self::Output { + Ok(arg) + } +} + +trivial_fn_impls!(ok_fn OkFn = "Ok"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct ChainFn(F, G); + +impl FnOnce1 for ChainFn +where + F: FnOnce1, + G: FnOnce1, +{ + type Output = G::Output; + fn call_once(self, arg: A) -> Self::Output { + self.1.call_once(self.0.call_once(arg)) + } +} +impl FnMut1 for ChainFn +where + F: FnMut1, + G: FnMut1, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.1.call_mut(self.0.call_mut(arg)) + } +} +impl Fn1 for ChainFn +where + F: Fn1, + G: Fn1, +{ + fn call(&self, arg: A) -> Self::Output { + self.1.call(self.0.call(arg)) + } +} +pub(crate) fn chain_fn(f: F, g: G) -> ChainFn { + ChainFn(f, g) +} + +#[derive(Default)] +pub struct MergeResultFn; + +impl FnOnce1> for MergeResultFn { + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + match arg { + Ok(x) => x, + Err(x) => x, + } + } +} +trivial_fn_impls!(merge_result_fn <> MergeResultFn = "merge_result"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct InspectFn(F); + +impl FnOnce1 for InspectFn +where + F: for<'a> FnOnce1<&'a A, Output=()>, +{ + type Output = A; + fn call_once(self, arg: A) -> Self::Output { + self.0.call_once(&arg); + arg + } +} +impl FnMut1 for InspectFn +where + F: for<'a> FnMut1<&'a A, Output=()>, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.0.call_mut(&arg); + arg + } +} +impl Fn1 for InspectFn +where + F: for<'a> Fn1<&'a A, Output=()>, +{ + fn call(&self, arg: A) -> Self::Output { + self.0.call(&arg); + arg + } +} +pub(crate) fn inspect_fn(f: F) -> InspectFn { + InspectFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapOkFn(F); + +impl FnOnce1> for MapOkFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapOkFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapOkFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call(x)) + } +} +pub(crate) fn map_ok_fn(f: F) -> MapOkFn { + MapOkFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapErrFn(F); + +impl FnOnce1> for MapErrFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapErrFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapErrFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call(x)) + } +} +pub(crate) fn map_err_fn(f: F) -> MapErrFn { + MapErrFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectOkFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectOkFn +where + F: FnOnce1<&'a T, Output=()> +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call_once(x) } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectOkFn +where + F: FnMut1<&'a T, Output=()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call_mut(x) } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectOkFn +where + F: Fn1<&'a T, Output=()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call(x) } + } +} +pub(crate) fn inspect_ok_fn(f: F) -> InspectOkFn { + InspectOkFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectErrFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectErrFn +where + F: FnOnce1<&'a E, Output=()> +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call_once(x) } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectErrFn +where + F: FnMut1<&'a E, Output=()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call_mut(x) } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectErrFn +where + F: Fn1<&'a E, Output=()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call(x) } + } +} +pub(crate) fn inspect_err_fn(f: F) -> InspectErrFn { + InspectErrFn(f) +} + +pub(crate) type MapOkOrElseFn = ChainFn, ChainFn, MergeResultFn>>; +pub(crate) fn map_ok_or_else_fn(f: F, g: G) -> MapOkOrElseFn { + chain_fn(map_ok_fn(f), chain_fn(map_err_fn(g), merge_result_fn())) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct UnwrapOrElseFn(F); + +impl FnOnce1> for UnwrapOrElseFn +where + F: FnOnce1, +{ + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_once(x)) + } +} +impl FnMut1> for UnwrapOrElseFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_mut(x)) + } +} +impl Fn1> for UnwrapOrElseFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call(x)) + } +} +pub(crate) fn unwrap_or_else_fn(f: F) -> UnwrapOrElseFn { + UnwrapOrElseFn(f) +} + +pub struct IntoFn(PhantomData T>); + +impl Default for IntoFn { + fn default() -> Self { + IntoFn(PhantomData) + } +} +impl FnOnce1 for IntoFn where A: Into { + type Output = T; + fn call_once(self, arg: A) -> Self::Output { + arg.into() + } +} + +trivial_fn_impls!(into_fn IntoFn = "Into::into"); diff --git a/futures-util/src/future/abortable.rs b/futures-util/src/future/abortable.rs index 281cf6b481..3a6b587091 100644 --- a/futures-util/src/future/abortable.rs +++ b/futures-util/src/future/abortable.rs @@ -1,25 +1,23 @@ use crate::task::AtomicWaker; use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; use core::fmt; use core::pin::Pin; use core::sync::atomic::{AtomicBool, Ordering}; use alloc::sync::Arc; +use pin_project::pin_project; /// A future which can be remotely short-circuited using an `AbortHandle`. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Abortable { + #[pin] future: Fut, inner: Arc, } -impl Unpin for Abortable {} - impl Abortable where Fut: Future { - unsafe_pinned!(future: Fut); - /// Creates a new `Abortable` future using an existing `AbortRegistration`. /// `AbortRegistration`s can be acquired through `AbortHandle::new`. /// @@ -144,7 +142,7 @@ impl Future for Abortable where Fut: Future { } // attempt to complete the future - if let Poll::Ready(x) = self.as_mut().future().poll(cx) { + if let Poll::Ready(x) = self.as_mut().project().future.poll(cx) { return Poll::Ready(Ok(x)) } diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 24fbbe79d8..be2882943a 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -4,15 +4,17 @@ use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; #[cfg(feature = "sink")] use futures_sink::Sink; +use pin_project::{pin_project, project}; /// Combines two different futures, streams, or sinks having the same associated types into a single /// type. +#[pin_project] #[derive(Debug, Clone)] pub enum Either { /// First branch of the type - Left(A), + Left(#[pin] A), /// Second branch of the type - Right(B), + Right(#[pin] B), } impl Either<(T, A), (T, B)> { @@ -56,12 +58,12 @@ where { type Output = A::Output; + #[project] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll(cx), - Either::Right(x) => Pin::new_unchecked(x).poll(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll(cx), + Either::Right(x) => x.poll(cx), } } } @@ -86,12 +88,12 @@ where { type Item = A::Item; + #[project] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_next(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_next(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_next(cx), + Either::Right(x) => x.poll_next(cx), } } } @@ -117,39 +119,39 @@ where { type Error = A::Error; + #[project] fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_ready(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_ready(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_ready(cx), + Either::Right(x) => x.poll_ready(cx), } } + #[project] fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).start_send(item), - Either::Right(x) => Pin::new_unchecked(x).start_send(item), - } + #[project] + match self.project() { + Either::Left(x) => x.start_send(item), + Either::Right(x) => x.start_send(item), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } + #[project] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -157,7 +159,8 @@ where #[cfg(feature = "io")] #[cfg(feature = "std")] mod if_std { - use super::Either; + use super::*; + use core::pin::Pin; use core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] @@ -179,29 +182,29 @@ mod if_std { } } + #[project] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_read(cx, buf), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_read(cx, buf), + Either::Right(x) => x.poll_read(cx, buf), } } + #[project] fn poll_read_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_read_vectored(cx, bufs), + Either::Right(x) => x.poll_read_vectored(cx, bufs), } } } @@ -211,47 +214,47 @@ mod if_std { A: AsyncWrite, B: AsyncWrite, { + #[project] fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_write(cx, buf), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_write(cx, buf), + Either::Right(x) => x.poll_write(cx, buf), } } + #[project] fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_write_vectored(cx, bufs), + Either::Right(x) => x.poll_write_vectored(cx, bufs), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } + #[project] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -261,16 +264,16 @@ mod if_std { A: AsyncSeek, B: AsyncSeek, { + #[project] fn poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - Either::Right(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_seek(cx, pos), + Either::Right(x) => x.poll_seek(cx, pos), } } } @@ -280,24 +283,24 @@ mod if_std { A: AsyncBufRead, B: AsyncBufRead, { + #[project] fn poll_fill_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_fill_buf(cx), + Either::Right(x) => x.poll_fill_buf(cx), } } + #[project] fn consume(self: Pin<&mut Self>, amt: usize) { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).consume(amt), - Either::Right(x) => Pin::new_unchecked(x).consume(amt), - } + #[project] + match self.project() { + Either::Left(x) => x.consume(amt), + Either::Right(x) => x.consume(amt), } } } diff --git a/futures-util/src/future/future/catch_unwind.rs b/futures-util/src/future/future/catch_unwind.rs index e88cce7e9d..33839f681a 100644 --- a/futures-util/src/future/future/catch_unwind.rs +++ b/futures-util/src/future/future/catch_unwind.rs @@ -1,22 +1,20 @@ +use core::any::Any; +use core::pin::Pin; +use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; + use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use std::any::Any; -use std::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; +use pin_project::pin_project; /// Future for the [`catch_unwind`](super::FutureExt::catch_unwind) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CatchUnwind { - future: Fut, -} +pub struct CatchUnwind(#[pin] Fut); impl CatchUnwind where Fut: Future + UnwindSafe { - unsafe_pinned!(future: Fut); - pub(super) fn new(future: Fut) -> CatchUnwind { - CatchUnwind { future } + CatchUnwind(future) } } @@ -26,6 +24,7 @@ impl Future for CatchUnwind type Output = Result>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + let f = self.project().0; + catch_unwind(AssertUnwindSafe(|| f.poll(cx)))?.map(Ok) } } diff --git a/futures-util/src/future/future/chain.rs b/futures-util/src/future/future/chain.rs deleted file mode 100644 index 3f248e80fe..0000000000 --- a/futures-util/src/future/future/chain.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum Chain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for Chain {} - -impl Chain { - pub(crate)fn is_terminated(&self) -> bool { - if let Chain::Empty = *self { true } else { false } - } -} - -impl Chain - where Fut1: Future, - Fut2: Future, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> Chain { - Chain::First(fut1, Some(data)) - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll - where F: FnOnce(Fut1::Output, Data) -> Fut2, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - Chain::First(fut1, data) => { - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)); - (output, data.take().unwrap()) - } - Chain::Second(fut2) => { - return unsafe { Pin::new_unchecked(fut2) }.poll(cx); - } - Chain::Empty => unreachable!() - }; - - *this = Chain::Empty; // Drop fut1 - let fut2 = (f.take().unwrap())(output, data); - *this = Chain::Second(fut2) - } - } -} diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 16b3a19de9..5e2ec686ea 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -1,56 +1,165 @@ -use super::chain::Chain; -use core::fmt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; +#[cfg(feature = "sink")] +use futures_sink::Sink; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; -/// Future for the [`flatten`](super::FutureExt::flatten) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Flatten +#[pin_project] +#[derive(Debug)] +pub enum Flatten { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl Flatten { + pub(crate) fn new(future: Fut1) -> Self { + Flatten::First(future) + } +} + +impl FusedFuture for Flatten where Fut: Future, + Fut::Output: Future, { - state: Chain, + fn is_terminated(&self) -> bool { + match self { + Flatten::Empty => true, + _ => false, + } + } } -impl Flatten +impl Future for Flatten where Fut: Future, Fut::Output: Future, { - unsafe_pinned!(state: Chain); + type Output = ::Output; - pub(super) fn new(future: Fut) -> Flatten { - Flatten { - state: Chain::new(future, ()), - } + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + let output = ready!(f.poll(cx)); + self.set(Flatten::Empty); + break output; + }, + Flatten::Empty => return Poll::Pending, + } + }) } } -impl fmt::Debug for Flatten - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, +impl FusedStream for Flatten + where Fut: Future, + Fut::Output: Stream, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Flatten") - .field("state", &self.state) - .finish() + fn is_terminated(&self) -> bool { + match self { + Flatten::Empty => true, + _ => false, + } } } -impl FusedFuture for Flatten +impl Stream for Flatten where Fut: Future, - Fut::Output: Future, + Fut::Output: Stream, { - fn is_terminated(&self) -> bool { self.state.is_terminated() } + type Item = ::Item; + + #[project] + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + let output = ready!(f.poll_next(cx)); + if output.is_none() { + self.set(Flatten::Empty); + } + break output; + }, + Flatten::Empty => break None, + } + }) + } } -impl Future for Flatten - where Fut: Future, - Fut::Output: Future, + +#[cfg(feature = "sink")] +impl Sink for Flatten +where + Fut: Future, + Fut::Output: Sink, { - type Output = ::Output; + type Error = >::Error; + + #[project] + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + break ready!(f.poll_ready(cx)); + }, + Flatten::Empty => panic!("poll_ready called after eof"), + } + }) + } - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.state().poll(cx, |a, ()| a) + #[project] + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + #[project] + match self.project() { + Flatten::First(_) => panic!("poll_ready not called first"), + Flatten::Second(f) => f.start_send(item), + Flatten::Empty => panic!("start_send called after eof"), + } + } + + #[project] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + match self.project() { + Flatten::First(_) => Poll::Ready(Ok(())), + Flatten::Second(f) => f.poll_flush(cx), + Flatten::Empty => panic!("poll_flush called after eof"), + } + } + + #[project] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + #[project] + let res = match self.as_mut().project() { + Flatten::Second(f) => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(Flatten::Empty); + } + res } } diff --git a/futures-util/src/future/future/flatten_stream.rs b/futures-util/src/future/future/flatten_stream.rs deleted file mode 100644 index d1108866ca..0000000000 --- a/futures-util/src/future/future/flatten_stream.rs +++ /dev/null @@ -1,89 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`flatten_stream`](super::FutureExt::flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct FlattenStream { - state: State, -} - -impl FlattenStream { - unsafe_pinned!(state: State); - - pub(super) fn new(future: Fut) -> FlattenStream { - FlattenStream { - state: State::Future(future) - } - } -} - -impl fmt::Debug for FlattenStream - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStream") - .field("state", &self.state) - .finish() - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream - Stream(St), -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut St>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::Stream(s) => State::Stream(unsafe { Pin::new_unchecked(s) }), - } - } -} - -impl FusedStream for FlattenStream - where Fut: Future, - Fut::Output: Stream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::Stream(stream) => stream.is_terminated(), - } - } -} - -impl Stream for FlattenStream - where Fut: Future, - Fut::Output: Stream, -{ - type Item = ::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match self.as_mut().state().get_pin_mut() { - State::Future(f) => { - let stream = ready!(f.poll(cx)); - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.as_mut().state().set(State::Stream(stream)); - } - State::Stream(s) => return s.poll_next(cx), - } - } - } -} diff --git a/futures-util/src/future/future/fuse.rs b/futures-util/src/future/future/fuse.rs index b5ef913034..69a8a6916a 100644 --- a/futures-util/src/future/future/fuse.rs +++ b/futures-util/src/future/future/fuse.rs @@ -1,24 +1,21 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Future for the [`fuse`](super::FutureExt::fuse) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Fuse { - future: Option, -} - -impl Fuse { - unsafe_pinned!(future: Option); +pub struct Fuse(#[pin] Option); +impl Fuse { pub(super) fn new(f: Fut) -> Fuse { - Fuse { - future: Some(f), - } + Fuse(Some(f)) } +} +impl Fuse { /// Creates a new `Fuse`-wrapped future which is already terminated. /// /// This can be useful in combination with looping and the `select!` @@ -65,13 +62,13 @@ impl Fuse { /// # }); /// ``` pub fn terminated() -> Fuse { - Fuse { future: None } + Fuse(None) } } impl FusedFuture for Fuse { fn is_terminated(&self) -> bool { - self.future.is_none() + self.0.is_none() } } @@ -79,12 +76,13 @@ impl Future for Fuse { type Output = Fut::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let v = match self.as_mut().future().as_pin_mut() { - Some(fut) => ready!(fut.poll(cx)), + Poll::Ready(match self.as_mut().project().0.as_pin_mut() { + Some(fut) => { + let output = ready!(fut.poll(cx)); + self.project().0.set(None); + output + }, None => return Poll::Pending, - }; - - self.as_mut().future().set(None); - Poll::Ready(v) + }) } } diff --git a/futures-util/src/future/future/inspect.rs b/futures-util/src/future/future/inspect.rs deleted file mode 100644 index d67455aa6d..0000000000 --- a/futures-util/src/future/future/inspect.rs +++ /dev/null @@ -1,47 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect`](super::FutureExt::inspect) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Inspect { - future: Fut, - f: Option, -} - -impl Inspect { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Inspect { - Inspect { - future, - f: Some(f), - } - } -} - -impl Unpin for Inspect {} - -impl FusedFuture for Inspect - where Fut: FusedFuture, - F: FnOnce(&Fut::Output), -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for Inspect - where Fut: Future, - F: FnOnce(&Fut::Output), -{ - type Output = Fut::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().poll(cx)); - let f = self.as_mut().f().take().expect("cannot poll Inspect twice"); - f(&e); - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/future/into_stream.rs b/futures-util/src/future/future/into_stream.rs deleted file mode 100644 index 616c4cbb57..0000000000 --- a/futures-util/src/future/future/into_stream.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::stream::{self, Once}; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`into_stream`](super::FutureExt::into_stream) method. -#[must_use = "streams do nothing unless polled"] -#[derive(Debug)] -pub struct IntoStream { - inner: Once -} - -impl IntoStream { - unsafe_pinned!(inner: Once); - - pub(super) fn new(future: Fut) -> IntoStream { - IntoStream { - inner: stream::once(future) - } - } -} - -impl Stream for IntoStream { - type Item = Fut::Output; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -impl FusedStream for IntoStream { - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index b5fbfb1384..046da2b77c 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,49 +1,48 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; + +use crate::fns::FnOnce1; /// Future for the [`map`](super::FutureExt::map) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Map { + #[pin] future: Fut, f: Option, } impl Map { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - /// Creates a new Map. - pub(super) fn new(future: Fut, f: F) -> Map { + pub(crate) fn new(future: Fut, f: F) -> Map { Map { future, f: Some(f) } } } -impl Unpin for Map {} - impl FusedFuture for Map where Fut: Future, - F: FnOnce(Fut::Output) -> T, + F: FnOnce1, { fn is_terminated(&self) -> bool { self.f.is_none() } } impl Future for Map where Fut: Future, - F: FnOnce(Fut::Output) -> T, + F: FnOnce1, { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut() - .future() - .poll(cx) - .map(|output| { - let f = self.f().take() - .expect("Map must not be polled after it returned `Poll::Ready`"); - f(output) - }) + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Map { future, f } = self.project(); + let output = ready!(future.poll(cx)); + let f = f.take() + .expect("Map must not be polled after it returned `Poll::Ready`"); + + Poll::Ready(f.call_once(output)) } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index e58cafc8c0..b87dae8464 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -7,6 +7,7 @@ use super::{assert_future, Either}; #[cfg(feature = "alloc")] use alloc::boxed::Box; use core::pin::Pin; + #[cfg(feature = "alloc")] use futures_core::future::{BoxFuture, LocalBoxFuture}; use futures_core::{ @@ -14,44 +15,77 @@ use futures_core::{ stream::Stream, task::{Context, Poll}, }; +use crate::never::Never; +use crate::fns::{OkFn, ok_fn, IntoFn, into_fn, InspectFn, inspect_fn}; // Combinators mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; - -mod flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_stream::FlattenStream; - mod fuse; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::fuse::Fuse; - -mod into_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::into_stream::IntoStream; - mod map; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map::Map; - -mod then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::then::Then; -mod inspect; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Flatten( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); + +delegate_all!( + /// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method. + FlattenStream( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); -mod unit_error; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unit_error::UnitError; - -mod never_error; +pub use fuse::Fuse; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::never_error::NeverError; +pub use map::Map; + +delegate_all!( + /// Stream for the [`into_stream`](FutureExt::into_stream) method. + IntoStream( + crate::stream::Once + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] +); + +delegate_all!( + /// Future for the [`map_into`](FutureExt::map_into) combinator. + MapInto( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`then`](FutureExt::then) method. + Then( + flatten::Flatten, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] +); + +delegate_all!( + /// Future for the [`inspect`](FutureExt::inspect) method. + Inspect( + map::Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] +); + +delegate_all!( + /// Future for the [`never_error`](super::FutureExt::never_error) combinator. + NeverError( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] +); + +delegate_all!( + /// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. + UnitError( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] +); #[cfg(feature = "std")] mod catch_unwind; @@ -73,11 +107,6 @@ mod shared; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::shared::Shared; -// Implementation details - -mod chain; -pub(crate) use self::chain::Chain; - impl FutureExt for T where T: Future {} /// An extension trait for `Future`s that provides a variety of convenient @@ -113,6 +142,19 @@ pub trait FutureExt: Future { assert_future::(Map::new(self, f)) } + /// Map this future's output to a different type, returning a new future of + /// the resulting type. + /// + /// This function is equivalent to calling `map(Into::into)` but allows naming + /// the return type. + fn map_into(self) -> MapInto + where + Self::Output: Into, + Self: Sized, + { + assert_future::(MapInto::new(self)) + } + /// Chain on a computation for when a future finished, passing the result of /// the future to the provided closure `f`. /// diff --git a/futures-util/src/future/future/never_error.rs b/futures-util/src/future/future/never_error.rs deleted file mode 100644 index 5a68e6f952..0000000000 --- a/futures-util/src/future/future/never_error.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::never::Never; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{self, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`never_error`](super::FutureExt::never_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct NeverError { - future: Fut, -} - -impl NeverError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> NeverError { - NeverError { future } - } -} - -impl Unpin for NeverError {} - -impl FusedFuture for NeverError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for NeverError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index ad7bff4f15..9495bec70e 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -5,7 +5,6 @@ use { future::Future, task::{Context, Poll}, }, - pin_utils::{unsafe_pinned, unsafe_unpinned}, std::{ any::Any, fmt, @@ -17,6 +16,7 @@ use { }, thread, }, + pin_project::{pin_project, project}, }; /// The handle to a remote future returned by @@ -70,10 +70,12 @@ type SendMsg = Result<::Output, Box<(dyn Any + Send + 'stati /// A future which sends its output to the corresponding `RemoteHandle`. /// Created by [`remote_handle`](crate::future::FutureExt::remote_handle). +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Remote { tx: Option>>, keep_running: Arc, + #[pin] future: CatchUnwind>, } @@ -85,29 +87,26 @@ impl fmt::Debug for Remote { } } -impl Unpin for Remote {} - -impl Remote { - unsafe_pinned!(future: CatchUnwind>); - unsafe_unpinned!(tx: Option>>); -} - impl Future for Remote { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if let Poll::Ready(_) = self.as_mut().tx().as_mut().unwrap().poll_canceled(cx) { - if !self.keep_running.load(Ordering::SeqCst) { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let Remote { tx, keep_running, future } = self.project(); + + if let Poll::Ready(_) = tx.as_mut().unwrap().poll_canceled(cx) { + if !keep_running.load(Ordering::SeqCst) { // Cancelled, bail out return Poll::Ready(()) } } - let output = ready!(self.as_mut().future().poll(cx)); + let output = ready!(future.poll(cx)); // if the receiving end has gone away then that's ok, we just ignore the // send error here. - drop(self.as_mut().tx().take().unwrap().send(output)); + drop(tx.take().unwrap().send(output)); Poll::Ready(()) } } diff --git a/futures-util/src/future/future/then.rs b/futures-util/src/future/future/then.rs deleted file mode 100644 index 9f30f09864..0000000000 --- a/futures-util/src/future/future/then.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::Chain; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`then`](super::FutureExt::then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Then { - chain: Chain, -} - -impl Then - where Fut1: Future, - Fut2: Future, -{ - unsafe_pinned!(chain: Chain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> Then { - Then { - chain: Chain::new(future, f), - } - } -} - -impl FusedFuture for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - fn is_terminated(&self) -> bool { self.chain.is_terminated() } -} - -impl Future for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - type Output = Fut2::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut().chain().poll(cx, |output, f| f(output)) - } -} diff --git a/futures-util/src/future/future/unit_error.rs b/futures-util/src/future/future/unit_error.rs deleted file mode 100644 index 679e988b16..0000000000 --- a/futures-util/src/future/future/unit_error.rs +++ /dev/null @@ -1,35 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnitError { - future: Fut, -} - -impl UnitError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> UnitError { - UnitError { future } - } -} - -impl Unpin for UnitError {} - -impl FusedFuture for UnitError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for UnitError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/join.rs b/futures-util/src/future/join.rs index 5af5b408e9..363e119955 100644 --- a/futures-util/src/future/join.rs +++ b/futures-util/src/future/join.rs @@ -5,7 +5,8 @@ use core::fmt; use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; + use super::assert_future; macro_rules! generate { @@ -14,9 +15,10 @@ macro_rules! generate { ($Join:ident, <$($Fut:ident),*>), )*) => ($( $(#[$doc])* + #[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct $Join<$($Fut: Future),*> { - $($Fut: MaybeDone<$Fut>,)* + $(#[pin] $Fut: MaybeDone<$Fut>,)* } impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*> @@ -39,24 +41,22 @@ macro_rules! generate { $($Fut: maybe_done($Fut)),* } } - $( - unsafe_pinned!($Fut: MaybeDone<$Fut>); - )* } impl<$($Fut: Future),*> Future for $Join<$($Fut),*> { type Output = ($($Fut::Output),*); fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; + let mut futures = self.project(); $( - all_done &= self.as_mut().$Fut().poll(cx).is_ready(); + all_done &= futures.$Fut.as_mut().poll(cx).is_ready(); )* if all_done { - Poll::Ready(($(self.as_mut().$Fut().take_output().unwrap()), *)) + Poll::Ready(($(futures.$Fut.take_output().unwrap()), *)) } else { Poll::Pending } diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 07408856a4..df62f3a1b5 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -10,42 +10,7 @@ use core::task::{Context, Poll}; use alloc::boxed::Box; use alloc::vec::Vec; -#[derive(Debug)] -enum ElemState -where - F: Future, -{ - Pending(F), - Done(Option), -} - -impl ElemState -where - F: Future, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState -where - F: Future + Unpin, -{ -} +use super::MaybeDone; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's @@ -62,7 +27,7 @@ pub struct JoinAll where F: Future, { - elems: Pin]>>, + elems: Pin]>>, } impl fmt::Debug for JoinAll @@ -117,7 +82,7 @@ where I: IntoIterator, I::Item: Future, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); + let elems: Box<[_]> = i.into_iter().map(MaybeDone::Future).collect(); JoinAll { elems: elems.into() } } @@ -130,20 +95,16 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut all_done = true; - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - if let Poll::Ready(output) = pending.poll(cx) { - elem.set(ElemState::Done(Some(output))); - } else { - all_done = false; - } + for elem in iter_pin_mut(self.elems.as_mut()) { + if elem.poll(cx).is_pending() { + all_done = false; } } if all_done { let mut elems = mem::replace(&mut self.elems, Box::pin([])); let result = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) + .map(|e| e.take_output().unwrap()) .collect(); Poll::Ready(result) } else { diff --git a/futures-util/src/future/maybe_done.rs b/futures-util/src/future/maybe_done.rs index f16f889781..71cb6fa320 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -4,14 +4,16 @@ use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; /// A future that may have completed. /// /// This is created by the [`maybe_done()`] function. +#[pin_project] #[derive(Debug)] pub enum MaybeDone { /// A not-yet-completed future - Future(Fut), + Future(#[pin] Fut), /// The output of the completed future Done(Fut::Output), /// The empty variant after the result of a [`MaybeDone`] has been @@ -19,9 +21,6 @@ pub enum MaybeDone { Gone, } -// Safe because we never generate `Pin<&mut Fut::Output>` -impl Unpin for MaybeDone {} - /// Wraps a future into a `MaybeDone` /// /// # Examples @@ -48,14 +47,13 @@ impl MaybeDone { /// The output of this method will be [`Some`] if and only if the inner /// future has been completed and [`take_output`](MaybeDone::take_output) /// has not yet been called. + #[project] #[inline] pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { - unsafe { - let this = self.get_unchecked_mut(); - match this { - MaybeDone::Done(res) => Some(res), - _ => None, - } + #[project] + match self.project() { + MaybeDone::Done(res) => Some(res), + _ => None, } } @@ -63,6 +61,11 @@ impl MaybeDone { /// towards completion. #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { + // Safety: we return immediately unless we are in the `Done` + // state, which does not have any pinning guarantees to uphold. + // + // Hopefully `pin_project` will support this safely soon: + // https://github.com/taiki-e/pin-project/issues/184 unsafe { let this = self.get_unchecked_mut(); match this { @@ -90,15 +93,17 @@ impl FusedFuture for MaybeDone { impl Future for MaybeDone { type Output = (); + #[project] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let res = unsafe { - match self.as_mut().get_unchecked_mut() { - MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)), - MaybeDone::Done(_) => return Poll::Ready(()), - MaybeDone::Gone => panic!("MaybeDone polled after value taken"), - } - }; - self.set(MaybeDone::Done(res)); + #[project] + match self.as_mut().project() { + MaybeDone::Future(f) => { + let res = ready!(f.poll(cx)); + self.set(MaybeDone::Done(res)); + }, + MaybeDone::Done(_) => {}, + MaybeDone::Gone => panic!("MaybeDone polled after value taken"), + } Poll::Ready(()) } } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 3f4bb01436..7962894a99 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -10,13 +10,15 @@ pub use futures_core::future::{FusedFuture, Future, TryFuture}; pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj}; // Extension traits and combinators - #[allow(clippy::module_inception)] mod future; pub use self::future::{ - Flatten, FlattenStream, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, + Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, MapInto, }; +#[deprecated(note = "This is now an alias for [Flatten](Flatten)")] +pub use self::future::FlattenStream; + #[cfg(feature = "std")] pub use self::future::CatchUnwind; @@ -29,8 +31,8 @@ pub use self::future::Shared; mod try_future; pub use self::try_future::{ - AndThen, ErrInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, - TryFutureExt, UnwrapOrElse, + AndThen, ErrInto, OkInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, + TryFutureExt, UnwrapOrElse, MapOkOrElse, TryFlatten, }; #[cfg(feature = "sink")] @@ -47,6 +49,9 @@ pub use self::pending::{pending, Pending}; mod maybe_done; pub use self::maybe_done::{maybe_done, MaybeDone}; +mod try_maybe_done; +pub use self::try_maybe_done::{try_maybe_done, TryMaybeDone}; + mod option; pub use self::option::OptionFuture; diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 21413525d0..88be0099f4 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// A future representing a value which may or may not be present. /// @@ -22,15 +22,10 @@ use pin_utils::unsafe_pinned; /// assert_eq!(a.await, None); /// # }); /// ``` +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OptionFuture { - option: Option, -} - -impl OptionFuture { - unsafe_pinned!(option: Option); -} +pub struct OptionFuture(#[pin] Option); impl Future for OptionFuture { type Output = Option; @@ -39,7 +34,7 @@ impl Future for OptionFuture { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - match self.option().as_pin_mut() { + match self.project().0.as_pin_mut() { Some(x) => x.poll(cx).map(Some), None => Poll::Ready(None), } @@ -48,7 +43,7 @@ impl Future for OptionFuture { impl FusedFuture for OptionFuture { fn is_terminated(&self) -> bool { - match &self.option { + match &self.0 { Some(x) => x.is_terminated(), None => true, } @@ -57,6 +52,6 @@ impl FusedFuture for OptionFuture { impl From> for OptionFuture { fn from(option: Option) -> Self { - OptionFuture { option } + OptionFuture(option) } } diff --git a/futures-util/src/future/try_future/and_then.rs b/futures-util/src/future/try_future/and_then.rs deleted file mode 100644 index 37333e0503..0000000000 --- a/futures-util/src/future/try_future/and_then.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`and_then`](super::TryFutureExt::and_then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct AndThen { - try_chain: TryChain, -} - -impl AndThen - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> AndThen { - AndThen { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Future(async_op(ok)), - Err(err) => TryChainAction::Output(Err(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/err_into.rs b/futures-util/src/future/try_future/err_into.rs deleted file mode 100644 index 731fcae39e..0000000000 --- a/futures-util/src/future/try_future/err_into.rs +++ /dev/null @@ -1,48 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`err_into`](super::TryFutureExt::err_into) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ErrInto { - future: Fut, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> ErrInto { - ErrInto { - future, - _marker: PhantomData, - } - } -} - -impl FusedFuture for ErrInto - where Fut: TryFuture + FusedFuture, - Fut::Error: Into, -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for ErrInto - where Fut: TryFuture, - Fut::Error: Into, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.future().try_poll(cx) - .map(|res| res.map_err(Into::into)) - } -} diff --git a/futures-util/src/future/try_future/flatten_sink.rs b/futures-util/src/future/try_future/flatten_sink.rs deleted file mode 100644 index d6863dd2cd..0000000000 --- a/futures-util/src/future/try_future/flatten_sink.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::FlattenStreamSink; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Sink for the [`flatten_sink`](super::TryFutureExt::flatten_sink) method. -#[derive(Debug)] -#[must_use = "sinks do nothing unless polled"] -pub struct FlattenSink -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl FlattenSink -where - Fut: TryFuture, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl FusedStream for FlattenSink -where - Fut: TryFuture, - S: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for FlattenSink -where - Fut: TryFuture, - S: TryStream, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -impl Sink for FlattenSink -where - Fut: TryFuture, - Si: Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/flatten_stream_sink.rs b/futures-util/src/future/try_future/flatten_stream_sink.rs deleted file mode 100644 index 5a56bf708d..0000000000 --- a/futures-util/src/future/try_future/flatten_stream_sink.rs +++ /dev/null @@ -1,181 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -#[must_use = "streams do nothing unless polled"] -pub(crate) struct FlattenStreamSink -where - Fut: TryFuture, -{ - state: State, -} - -impl Unpin for FlattenStreamSink -where - Fut: TryFuture + Unpin, - Fut::Ok: Unpin, -{ -} - -impl fmt::Debug for FlattenStreamSink -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStreamSink") - .field("state", &self.state) - .finish() - } -} - -impl FlattenStreamSink -where - Fut: TryFuture, -{ - unsafe_pinned!(state: State); - - pub(crate) fn new(future: Fut) -> Self { - Self { - state: State::Future(future), - } - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream or Sink - StreamOrSink(S), - // future resolved to error - Done, -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut S>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::StreamOrSink(s) => State::StreamOrSink(unsafe { Pin::new_unchecked(s) }), - State::Done => State::Done, - } - } -} - -impl State -where - Fut: TryFuture, -{ - fn poll_future(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let State::Future(f) = self.as_mut().get_pin_mut() { - match ready!(f.try_poll(cx)) { - Ok(s) => { - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.set(State::StreamOrSink(s)); - } - Err(e) => { - // Future resolved to error. - // We have neither a pollable stream nor a future. - self.set(State::Done); - return Poll::Ready(Err(e)); - } - } - } - Poll::Ready(Ok(())) - } -} - -impl FusedStream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::StreamOrSink(stream) => stream.is_terminated(), - State::Done => true, - } - } -} - -impl Stream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.try_poll_next(cx), - State::Done => Poll::Ready(None), - State::Future(_) => unreachable!(), - } - } -} - -#[cfg(feature = "sink")] -impl Sink for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: Sink, -{ - type Error = Fut::Error; - - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_ready(cx), - State::Done => panic!("poll_ready called after eof"), - State::Future(_) => unreachable!(), - } - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.start_send(item), - State::Future(_) => panic!("poll_ready not called first"), - State::Done => panic!("start_send called after eof"), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_flush(cx), - // if sink not yet resolved, nothing written ==> everything flushed - State::Future(_) => Poll::Ready(Ok(())), - State::Done => panic!("poll_flush called after eof"), - } - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let res = match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_close(cx), - State::Future(_) | State::Done => Poll::Ready(Ok(())), - }; - if res.is_ready() { - self.as_mut().state().set(State::Done); - } - res - } -} diff --git a/futures-util/src/future/try_future/inspect_err.rs b/futures-util/src/future/try_future/inspect_err.rs deleted file mode 100644 index 8700337bb2..0000000000 --- a/futures-util/src/future/try_future/inspect_err.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectErr { - future: Fut, - f: Option, -} - -impl Unpin for InspectErr {} - -impl InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectErr -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Error), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Err(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectErr twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/inspect_ok.rs b/futures-util/src/future/try_future/inspect_ok.rs deleted file mode 100644 index 3d0a972226..0000000000 --- a/futures-util/src/future/try_future/inspect_ok.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectOk { - future: Fut, - f: Option, -} - -impl Unpin for InspectOk {} - -impl InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectOk -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Ok), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Ok(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectOk twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/into_future.rs b/futures-util/src/future/try_future/into_future.rs index a766d5b66d..240bb1bcb9 100644 --- a/futures-util/src/future/try_future/into_future.rs +++ b/futures-util/src/future/try_future/into_future.rs @@ -1,26 +1,23 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Future for the [`into_future`](super::TryFutureExt::into_future) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IntoFuture { - future: Fut, -} +pub struct IntoFuture(#[pin] Fut); impl IntoFuture { - unsafe_pinned!(future: Fut); - #[inline] - pub(super) fn new(future: Fut) -> IntoFuture { - IntoFuture { future } + pub(crate) fn new(future: Fut) -> IntoFuture { + IntoFuture(future) } } impl FusedFuture for IntoFuture { - fn is_terminated(&self) -> bool { self.future.is_terminated() } + fn is_terminated(&self) -> bool { self.0.is_terminated() } } impl Future for IntoFuture { @@ -31,6 +28,6 @@ impl Future for IntoFuture { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - self.future().try_poll(cx) + self.project().0.try_poll(cx) } } diff --git a/futures-util/src/future/try_future/map_err.rs b/futures-util/src/future/try_future/map_err.rs deleted file mode 100644 index 8edebad86d..0000000000 --- a/futures-util/src/future/try_future/map_err.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_err`](super::TryFutureExt::map_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapErr { - future: Fut, - f: Option, -} - -impl MapErr { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapErr. - pub(super) fn new(future: Fut, f: F) -> MapErr { - MapErr { future, f: Some(f) } - } -} - -impl Unpin for MapErr {} - -impl FusedFuture for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - fn is_terminated(&self) -> bool { self.f.is_none() } -} - -impl Future for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let f = self.as_mut().f().take() - .expect("MapErr must not be polled after it returned `Poll::Ready`"); - result.map_err(f) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok.rs b/futures-util/src/future/try_future/map_ok.rs deleted file mode 100644 index ab28f1443f..0000000000 --- a/futures-util/src/future/try_future/map_ok.rs +++ /dev/null @@ -1,54 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok`](super::TryFutureExt::map_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOk { - future: Fut, - f: Option, -} - -impl MapOk { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapOk. - pub(super) fn new(future: Fut, f: F) -> MapOk { - MapOk { future, f: Some(f) } - } -} - -impl Unpin for MapOk {} - -impl FusedFuture for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("MapOk must not be polled after it returned `Poll::Ready`"); - result.map(op) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok_or_else.rs b/futures-util/src/future/try_future/map_ok_or_else.rs deleted file mode 100644 index 730b67922c..0000000000 --- a/futures-util/src/future/try_future/map_ok_or_else.rs +++ /dev/null @@ -1,59 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok_or_else`](super::TryFutureExt::map_ok_or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOkOrElse { - future: Fut, - f: Option, - e: Option, -} - -impl MapOkOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - unsafe_unpinned!(e: Option); - - /// Creates a new MapOkOrElse. - pub(super) fn new(future: Fut, e: E, f: F) -> Self { - Self { future, f: Some(f), e: Some(e) } - } -} - -impl Unpin for MapOkOrElse {} - -impl FusedFuture for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() || self.e.is_none() - } -} - -impl Future for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - type Output = T; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - match result { - Ok(i) => (self.as_mut().f().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(i), - Err(e) => (self.as_mut().e().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(e), - } - }) - } -} diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index e8e059e373..4f4a4cc1d6 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -14,65 +14,120 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; -// Combinators - -mod and_then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::and_then::AndThen; +use super::assert_future; +use crate::future::{Map, Inspect}; +use crate::fns::{ + MapOkFn, map_ok_fn, MapErrFn, map_err_fn, MapOkOrElseFn, + map_ok_or_else_fn, IntoFn, UnwrapOrElseFn, unwrap_or_else_fn, InspectOkFn, inspect_ok_fn, InspectErrFn, + inspect_err_fn, into_fn +}; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; +// Combinators +mod into_future; +mod try_flatten; +mod try_flatten_err; + +delegate_all!( + /// Future for the [`try_flatten`](TryFutureExt::try_flatten) method. + TryFlatten( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method. + TryFlattenErr( + try_flatten_err::TryFlattenErr + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method. + TryFlattenStream( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] +); #[cfg(feature = "sink")] -mod flatten_sink; -#[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_sink::FlattenSink; - -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; +delegate_all!( + /// Sink for the [`flatten_sink`](TryFutureExt::flatten_sink) method. + FlattenSink( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`and_then`](TryFutureExt::and_then) method. + AndThen( + TryFlatten, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] +); + +delegate_all!( + /// Future for the [`or_else`](TryFutureExt::or_else) method. + OrElse( + TryFlattenErr, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] +); + +delegate_all!( + /// Future for the [`err_into`](TryFutureExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`ok_into`](TryFutureExt::ok_into) method. + OkInto( + MapOk> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapOk::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] +); -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; - -mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::IntoFuture; -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; - -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; - -mod map_ok_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok_or_else::MapOkOrElse; - -mod or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::or_else::OrElse; - -mod try_flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::try_flatten_stream::TryFlattenStream; - -mod unwrap_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unwrap_or_else::UnwrapOrElse; - -// Implementation details - -mod flatten_stream_sink; -pub(crate) use self::flatten_stream_sink::FlattenStreamSink; - -mod try_chain; -pub(crate) use self::try_chain::{TryChain, TryChainAction}; +delegate_all!( + /// Future for the [`map_ok`](TryFutureExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`map_err`](TryFutureExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] +); + +delegate_all!( + /// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method. + MapOkOrElse( + Map, MapOkOrElseFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] +); + +delegate_all!( + /// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method. + UnwrapOrElse( + Map, UnwrapOrElseFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] +); impl TryFutureExt for Fut {} @@ -161,7 +216,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Ok) -> T, Self: Sized, { - MapOk::new(self, f) + assert_future::, _>(MapOk::new(self, f)) } /// Maps this future's success value to a different value, and permits for error handling resulting in the same type. @@ -202,7 +257,7 @@ pub trait TryFutureExt: TryFuture { E: FnOnce(Self::Error) -> T, Self: Sized, { - MapOkOrElse::new(self, e, f) + assert_future::(MapOkOrElse::new(self, f, e)) } /// Maps this future's error value to a different value. @@ -249,7 +304,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Error) -> E, Self: Sized, { - MapErr::new(self, f) + assert_future::, _>(MapErr::new(self, f)) } /// Maps this future's [`Error`](TryFuture::Error) to a new error type @@ -279,7 +334,17 @@ pub trait TryFutureExt: TryFuture { Self: Sized, Self::Error: Into, { - ErrInto::new(self) + assert_future::, _>(ErrInto::new(self)) + } + + /// Maps this future's [`Ok`](TryFuture::Ok) to a new type + /// using the [`Into`](std::convert::Into) trait. + fn ok_into(self) -> OkInto + where + Self: Sized, + Self::Ok: Into, + { + assert_future::, _>(OkInto::new(self)) } /// Executes another future after this one resolves successfully. The @@ -324,7 +389,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - AndThen::new(self, f) + assert_future::, _>(AndThen::new(self, f)) } /// Executes another future if this one resolves to an error. The @@ -369,7 +434,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - OrElse::new(self, f) + assert_future::, _>(OrElse::new(self, f)) } /// Do something with the success value of a future before passing it on. @@ -395,7 +460,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Ok), Self: Sized, { - InspectOk::new(self, f) + assert_future::, _>(InspectOk::new(self, f)) } /// Do something with the error value of a future before passing it on. @@ -421,7 +486,19 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Error), Self: Sized, { - InspectErr::new(self, f) + assert_future::, _>(InspectErr::new(self, f)) + } + + /// Flatten the execution of this future when the successful result of this + /// future is another future. + /// + /// This is equivalent to `future.and_then(|x| x)`. + fn try_flatten(self) -> TryFlatten + where + Self::Ok: TryFuture, + Self: Sized, + { + TryFlatten::new(self) } /// Flatten the execution of this future when the successful result of this @@ -449,7 +526,7 @@ pub trait TryFutureExt: TryFuture { /// assert_eq!(list, Ok(vec![17, 18, 19])); /// # }); /// ``` - fn try_flatten_stream(self) -> TryFlattenStream + fn try_flatten_stream(self) -> TryFlattenStream where Self::Ok: TryStream, Self: Sized, @@ -484,7 +561,7 @@ pub trait TryFutureExt: TryFuture { Self: Sized, F: FnOnce(Self::Error) -> Self::Ok, { - UnwrapOrElse::new(self, f) + assert_future::(UnwrapOrElse::new(self, f)) } /// Wraps a [`TryFuture`] into a future compatable with libraries using diff --git a/futures-util/src/future/try_future/or_else.rs b/futures-util/src/future/try_future/or_else.rs deleted file mode 100644 index a9c006fa9f..0000000000 --- a/futures-util/src/future/try_future/or_else.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`or_else`](super::TryFutureExt::or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OrElse { - try_chain: TryChain, -} - -impl OrElse - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> OrElse { - OrElse { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Output(Ok(ok)), - Err(err) => TryChainAction::Future(async_op(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/try_chain.rs b/futures-util/src/future/try_future/try_chain.rs deleted file mode 100644 index ddd275a609..0000000000 --- a/futures-util/src/future/try_future/try_chain.rs +++ /dev/null @@ -1,110 +0,0 @@ -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum TryChain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for TryChain {} - -pub(crate) enum TryChainAction - where Fut2: TryFuture, -{ - Future(Fut2), - Output(Result), -} - -impl TryChain - where Fut1: TryFuture, - Fut2: TryFuture, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> TryChain { - TryChain::First(fut1, Some(data)) - } - - pub(crate) fn is_terminated(&self) -> bool { - match self { - TryChain::First(..) | TryChain::Second(_) => false, - TryChain::Empty => true, - } - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll> - where F: FnOnce(Result, Data) -> TryChainAction, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - TryChain::First(fut1, data) => { - // Poll the first future - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.try_poll(cx)); - (output, data.take().unwrap()) - } - TryChain::Second(fut2) => { - // Poll the second future - return unsafe { Pin::new_unchecked(fut2) } - .try_poll(cx) - .map(|res| { - *this = TryChain::Empty; // Drop fut2. - res - }); - } - TryChain::Empty => { - panic!("future must not be polled after it returned `Poll::Ready`"); - } - }; - - *this = TryChain::Empty; // Drop fut1 - let f = f.take().unwrap(); - match f(output, data) { - TryChainAction::Future(fut2) => *this = TryChain::Second(fut2), - TryChainAction::Output(output) => return Poll::Ready(output), - } - } - } -} - -#[cfg(test)] -mod tests { - - #[cfg(feature = "std")] // dont test with no_std - #[test] - fn try_chain_is_terminated() { - use std::pin::Pin; - use std::task::Poll; - - use futures_test::task::noop_context; - - use crate::future::ready; - - use super::{TryChain, TryChainAction}; - - let mut cx = noop_context(); - - let mut future = TryChain::new(ready(Ok(1)), ()); - assert!(!future.is_terminated()); - - let res = Pin::new(&mut future).poll( - &mut cx, - |res: Result, ()| { - assert!(res.is_ok()); - TryChainAction::Future(ready(Ok(2))) - }, - ); - assert_eq!(res, Poll::Ready::>(Ok(2))); - assert!(future.is_terminated()); - } -} diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs new file mode 100644 index 0000000000..428377a196 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -0,0 +1,180 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::stream::{FusedStream, Stream, TryStream}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +#[pin_project] +#[derive(Debug)] +pub enum TryFlatten { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl TryFlatten { + pub(crate) fn new(future: Fut1) -> Self { + TryFlatten::First(future) + } +} + +impl FusedFuture for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlatten::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryFuture, +{ + type Output = Result<::Ok, Fut::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Err(e); + } + } + }, + TryFlatten::Second(f) => { + let output = ready!(f.try_poll(cx)); + self.set(TryFlatten::Empty); + break output; + }, + TryFlatten::Empty => return Poll::Pending, + } + }) + } +} + +impl FusedStream for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryStream, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlatten::Empty => true, + _ => false, + } + } +} + +impl Stream for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryStream, +{ + type Item = Result<::Ok, Fut::Error>; + + #[project] + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Some(Err(e)); + } + } + }, + TryFlatten::Second(f) => { + let output = ready!(f.try_poll_next(cx)); + if output.is_none() { + self.set(TryFlatten::Empty); + } + break output; + }, + TryFlatten::Empty => break None, + } + }) + } +} + + +#[cfg(feature = "sink")] +impl Sink for TryFlatten +where + Fut: TryFuture, + Fut::Ok: Sink, +{ + type Error = Fut::Error; + + #[project] + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Err(e); + } + } + }, + TryFlatten::Second(f) => { + break ready!(f.poll_ready(cx)); + }, + TryFlatten::Empty => panic!("poll_ready called after eof"), + } + }) + } + + #[project] + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + #[project] + match self.project() { + TryFlatten::First(_) => panic!("poll_ready not called first"), + TryFlatten::Second(f) => f.start_send(item), + TryFlatten::Empty => panic!("start_send called after eof"), + } + } + + #[project] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + match self.project() { + TryFlatten::First(_) => Poll::Ready(Ok(())), + TryFlatten::Second(f) => f.poll_flush(cx), + TryFlatten::Empty => panic!("poll_flush called after eof"), + } + } + + #[project] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + #[project] + let res = match self.as_mut().project() { + TryFlatten::Second(f) => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(TryFlatten::Empty); + } + res + } +} diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs new file mode 100644 index 0000000000..b4c54efb67 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -0,0 +1,61 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +#[pin_project] +#[derive(Debug)] +pub enum TryFlattenErr { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl TryFlattenErr { + pub(crate) fn new(future: Fut1) -> Self { + TryFlattenErr::First(future) + } +} + +impl FusedFuture for TryFlattenErr + where Fut: TryFuture, + Fut::Error: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlattenErr::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlattenErr + where Fut: TryFuture, + Fut::Error: TryFuture, +{ + type Output = Result::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlattenErr::First(f) => { + match ready!(f.try_poll(cx)) { + Err(f) => self.set(TryFlattenErr::Second(f)), + Ok(e) => { + self.set(TryFlattenErr::Empty); + break Ok(e); + } + } + }, + TryFlattenErr::Second(f) => { + let output = ready!(f.try_poll(cx)); + self.set(TryFlattenErr::Empty); + break output; + }, + TryFlattenErr::Empty => return Poll::Pending, + } + }) + } +} diff --git a/futures-util/src/future/try_future/try_flatten_stream.rs b/futures-util/src/future/try_future/try_flatten_stream.rs deleted file mode 100644 index 24624314c0..0000000000 --- a/futures-util/src/future/try_future/try_flatten_stream.rs +++ /dev/null @@ -1,91 +0,0 @@ -use super::FlattenStreamSink; -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`try_flatten_stream`](super::TryFutureExt::try_flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct TryFlattenStream -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl fmt::Debug for TryFlattenStream -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryFlattenStream") - .field("inner", &self.inner) - .finish() - } -} - -impl FusedStream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -#[cfg(feature = "sink")] -impl Sink for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/unwrap_or_else.rs b/futures-util/src/future/try_future/unwrap_or_else.rs deleted file mode 100644 index 286cc009fb..0000000000 --- a/futures-util/src/future/try_future/unwrap_or_else.rs +++ /dev/null @@ -1,55 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`unwrap_or_else`](super::TryFutureExt::unwrap_or_else) -/// method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnwrapOrElse { - future: Fut, - f: Option, -} - -impl UnwrapOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new UnwrapOrElse. - pub(super) fn new(future: Fut, f: F) -> UnwrapOrElse { - UnwrapOrElse { future, f: Some(f) } - } -} - -impl Unpin for UnwrapOrElse {} - -impl FusedFuture for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - type Output = Fut::Ok; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("UnwrapOrElse already returned `Poll::Ready` before"); - result.unwrap_or_else(op) - }) - } -} diff --git a/futures-util/src/future/try_join.rs b/futures-util/src/future/try_join.rs index da85eff91d..b4a3b98c1f 100644 --- a/futures-util/src/future/try_join.rs +++ b/futures-util/src/future/try_join.rs @@ -1,11 +1,11 @@ #![allow(non_snake_case)] -use crate::future::{MaybeDone, maybe_done, TryFutureExt, IntoFuture}; +use crate::future::{TryMaybeDone, try_maybe_done}; use core::fmt; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; macro_rules! generate { ($( @@ -13,10 +13,11 @@ macro_rules! generate { ($Join:ident, ), )*) => ($( $(#[$doc])* + #[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct $Join { - Fut1: MaybeDone>, - $($Fut: MaybeDone>,)* + #[pin] Fut1: TryMaybeDone, + $(#[pin] $Fut: TryMaybeDone<$Fut>,)* } impl fmt::Debug for $Join @@ -47,15 +48,10 @@ macro_rules! generate { { fn new(Fut1: Fut1, $($Fut: $Fut),*) -> $Join { $Join { - Fut1: maybe_done(TryFutureExt::into_future(Fut1)), - $($Fut: maybe_done(TryFutureExt::into_future($Fut))),* + Fut1: try_maybe_done(Fut1), + $($Fut: try_maybe_done($Fut)),* } } - - unsafe_pinned!(Fut1: MaybeDone>); - $( - unsafe_pinned!($Fut: MaybeDone>); - )* } impl Future for $Join @@ -68,29 +64,20 @@ macro_rules! generate { type Output = Result<(Fut1::Ok, $($Fut::Ok),*), Fut1::Error>; fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; - if self.as_mut().Fut1().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().Fut1().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().Fut1().take_output().unwrap().err().unwrap())); - } + let mut futures = self.project(); + all_done &= futures.Fut1.as_mut().poll(cx)?.is_ready(); $( - if self.as_mut().$Fut().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().$Fut().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().$Fut().take_output().unwrap().err().unwrap())); - } + all_done &= futures.$Fut.as_mut().poll(cx)?.is_ready(); )* if all_done { Poll::Ready(Ok(( - self.as_mut().Fut1().take_output().unwrap().ok().unwrap(), + futures.Fut1.take_output().unwrap(), $( - self.as_mut().$Fut().take_output().unwrap().ok().unwrap() + futures.$Fut.take_output().unwrap() ),* ))) } else { diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 30300e4e3e..4de0a79b94 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -10,40 +10,7 @@ use core::task::{Context, Poll}; use alloc::boxed::Box; use alloc::vec::Vec; -use super::TryFuture; - -#[derive(Debug)] -enum ElemState -where - F: TryFuture, -{ - Pending(F), - Done(Option), -} - -impl ElemState -where - F: TryFuture, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState where F: TryFuture + Unpin {} +use super::{TryFuture, TryMaybeDone}; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's @@ -66,7 +33,7 @@ pub struct TryJoinAll where F: TryFuture, { - elems: Pin]>>, + elems: Pin]>>, } impl fmt::Debug for TryJoinAll @@ -125,7 +92,7 @@ where I: IntoIterator, I::Item: TryFuture, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); + let elems: Box<[_]> = i.into_iter().map(TryMaybeDone::Future).collect(); TryJoinAll { elems: elems.into(), } @@ -140,17 +107,13 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut state = FinalState::AllDone; - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - match pending.try_poll(cx) { - Poll::Pending => state = FinalState::Pending, - Poll::Ready(output) => match output { - Ok(item) => elem.set(ElemState::Done(Some(item))), - Err(e) => { - state = FinalState::Error(e); - break; - } - } + for elem in iter_pin_mut(self.elems.as_mut()) { + match elem.try_poll(cx) { + Poll::Pending => state = FinalState::Pending, + Poll::Ready(Ok(())) => {}, + Poll::Ready(Err(e)) => { + state = FinalState::Error(e); + break; } } } @@ -160,7 +123,7 @@ where FinalState::AllDone => { let mut elems = mem::replace(&mut self.elems, Box::pin([])); let results = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) + .map(|e| e.take_output().unwrap()) .collect(); Poll::Ready(Ok(results)) }, diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs new file mode 100644 index 0000000000..a249c5ce33 --- /dev/null +++ b/futures-util/src/future/try_maybe_done.rs @@ -0,0 +1,99 @@ +//! Definition of the TryMaybeDone combinator + +use core::mem; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +/// A future that may have completed with an error. +/// +/// This is created by the [`try_maybe_done()`] function. +#[pin_project] +#[derive(Debug)] +pub enum TryMaybeDone { + /// A not-yet-completed future + Future(#[pin] Fut), + /// The output of the completed future + Done(Fut::Ok), + /// The empty variant after the result of a [`TryMaybeDone`] has been + /// taken using the [`take_output`](TryMaybeDone::take_output) method, + /// or if the future returned an error. + Gone, +} + +/// Wraps a future into a `TryMaybeDone` +pub fn try_maybe_done(future: Fut) -> TryMaybeDone { + TryMaybeDone::Future(future) +} + +impl TryMaybeDone { + /// Returns an [`Option`] containing a mutable reference to the output of the future. + /// The output of this method will be [`Some`] if and only if the inner + /// future has completed successfully and [`take_output`](TryMaybeDone::take_output) + /// has not yet been called. + #[project] + #[inline] + pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Ok> { + #[project] + match self.project() { + TryMaybeDone::Done(res) => Some(res), + _ => None, + } + } + + /// Attempt to take the output of a `TryMaybeDone` without driving it + /// towards completion. + #[inline] + pub fn take_output(self: Pin<&mut Self>) -> Option { + // Safety: we return immediately unless we are in the `Done` + // state, which does not have any pinning guarantees to uphold. + // + // Hopefully `pin_project` will support this safely soon: + // https://github.com/taiki-e/pin-project/issues/184 + unsafe { + let this = self.get_unchecked_mut(); + match this { + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Future(_) | TryMaybeDone::Gone => return None, + }; + if let TryMaybeDone::Done(output) = mem::replace(this, TryMaybeDone::Gone) { + Some(output) + } else { + unreachable!() + } + } + } +} + +impl FusedFuture for TryMaybeDone { + fn is_terminated(&self) -> bool { + match self { + TryMaybeDone::Future(_) => false, + TryMaybeDone::Done(_) | TryMaybeDone::Gone => true, + } + } +} + +impl Future for TryMaybeDone { + type Output = Result<(), Fut::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + match self.as_mut().project() { + TryMaybeDone::Future(f) => { + match ready!(f.try_poll(cx)) { + Ok(res) => self.set(TryMaybeDone::Done(res)), + Err(e) => { + self.set(TryMaybeDone::Gone); + return Poll::Ready(Err(e)); + } + } + }, + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Gone => panic!("TryMaybeDone polled after value taken"), + } + Poll::Ready(Ok(())) + } +} diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 96d3f2815e..94b3d2aefa 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,8 +1,8 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSliceMut, SeekFrom}; +use pin_project::{pin_project, project}; use std::io::{self, Read}; use std::pin::Pin; use std::{cmp, fmt}; @@ -27,19 +27,15 @@ use super::DEFAULT_BUF_SIZE; /// [`AsyncRead`]: futures_io::AsyncRead /// // TODO: Examples +#[pin_project] pub struct BufReader { + #[pin] inner: R, - buf: Box<[u8]>, + buffer: Box<[u8]>, pos: usize, cap: usize, } -impl BufReader { - unsafe_pinned!(inner: R); - unsafe_unpinned!(pos: usize); - unsafe_unpinned!(cap: usize); -} - impl BufReader { /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. @@ -55,53 +51,30 @@ impl BufReader { super::initialize(&inner, &mut buffer); Self { inner, - buf: buffer.into_boxed_slice(), + buffer: buffer.into_boxed_slice(), pos: 0, cap: 0, } } } - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes this `BufWriter`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); /// Returns a reference to the internally buffered data. /// /// Unlike `fill_buf`, this will not attempt to fill the buffer if it is empty. pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] + &self.buffer[self.pos..self.cap] } /// Invalidates all data in the internal buffer. + #[project] #[inline] - fn discard_buffer(mut self: Pin<&mut Self>) { - *self.as_mut().pos() = 0; - *self.cap() = 0; + fn discard_buffer(self: Pin<&mut Self>) { + #[project] + let BufReader { pos, cap, .. } = self.project(); + *pos = 0; + *cap = 0; } } @@ -114,8 +87,8 @@ impl AsyncRead for BufReader { // If we don't have any buffered data and we're doing a massive read // (larger than our internal buffer), bypass our internal buffer // entirely. - if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read(cx, buf)); + if self.pos == self.cap && buf.len() >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -131,8 +104,8 @@ impl AsyncRead for BufReader { bufs: &mut [IoSliceMut<'_>], ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.pos == self.cap && total_len >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + if self.pos == self.cap && total_len >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -150,12 +123,13 @@ impl AsyncRead for BufReader { } impl AsyncBufRead for BufReader { + #[project] fn poll_fill_buf( self: Pin<&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) }; + #[project] + let BufReader { inner, buffer, cap, pos } = self.project(); // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. @@ -163,48 +137,26 @@ impl AsyncBufRead for BufReader { // to tell the compiler that the pos..cap slice is always valid. if *pos >= *cap { debug_assert!(*pos == *cap); - *cap = ready!(inner.as_mut().poll_read(cx, buf))?; + *cap = ready!(inner.poll_read(cx, buffer))?; *pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&buffer[*pos..*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) { + *self.project().pos = cmp::min(self.pos + amt, self.cap); } } impl AsyncWrite for BufReader { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.inner().poll_write(cx, buf) - } - - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - self.inner().poll_write_vectored(cx, bufs) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } + delegate_async_write!(inner); } impl fmt::Debug for BufReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BufReader") .field("reader", &self.inner) - .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) + .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buffer.len())) .finish() } } @@ -242,16 +194,16 @@ impl AsyncSeek for BufReader { // support seeking by i64::min_value() so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(offset)))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(offset)))?; } else { // seek backwards by our remainder, and then by the offset - ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(-remainder)))?; + ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(-remainder)))?; self.as_mut().discard_buffer(); - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(n)))?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index b0afbd81f2..66df81f5a4 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -1,8 +1,6 @@ use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, SeekFrom}; +use pin_project::{pin_project, project}; use std::fmt; use std::io::{self, Write}; use std::pin::Pin; @@ -29,17 +27,14 @@ use super::DEFAULT_BUF_SIZE; /// [`flush`]: super::AsyncWriteExt::flush /// // TODO: Examples +#[pin_project] pub struct BufWriter { + #[pin] inner: W, buf: Vec, written: usize, } -impl BufWriter { - unsafe_pinned!(inner: W); - unsafe_unpinned!(buf: Vec); -} - impl BufWriter { /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. @@ -56,9 +51,10 @@ impl BufWriter { } } + #[project] fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, buf, written } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + #[project] + let BufWriter { mut inner, buf, written } = self.project(); let len = buf.len(); let mut ret = Ok(()); @@ -85,31 +81,7 @@ impl BufWriter { Poll::Ready(ret) } - /// Gets a reference to the underlying writer. - pub fn get_ref(&self) -> &W { - &self.inner - } - - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_mut(&mut self) -> &mut W { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { - self.inner() - } - - /// Consumes this `BufWriter`, returning the underlying writer. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> W { - self.inner - } + delegate_access_inner!(inner, W, ()); /// Returns a reference to the internally buffered data. pub fn buffer(&self) -> &[u8] { @@ -127,9 +99,9 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.project().inner.poll_write(cx, buf) } else { - Poll::Ready(self.buf().write(buf)) + Poll::Ready(self.project().buf.write(buf)) } } @@ -143,58 +115,29 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if total_len >= self.buf.capacity() { - self.inner().poll_write_vectored(cx, bufs) + self.project().inner.poll_write_vectored(cx, bufs) } else { - Poll::Ready(self.buf().write_vectored(bufs)) + Poll::Ready(self.project().buf.write_vectored(bufs)) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_flush(cx) + self.project().inner.poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_close(cx) + self.project().inner.poll_close(cx) } } impl AsyncRead for BufWriter { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - self.inner().poll_read(cx, buf) - } - - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - self.inner().poll_read_vectored(cx, bufs) - } - - // we can't skip unconditionally because of the large buffer case in read. - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } + delegate_async_read!(inner); } impl AsyncBufRead for BufWriter { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.inner().poll_fill_buf(cx) - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - self.inner().consume(amt) - } + delegate_async_buf_read!(inner); } impl fmt::Debug for BufWriter { @@ -217,6 +160,6 @@ impl AsyncSeek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.project().inner.poll_seek(cx, pos) } } diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 64bbdec87a..4e85854a41 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -2,35 +2,27 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead, IoSliceMut}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::fmt; use std::io; use std::pin::Pin; /// Reader for the [`chain`](super::AsyncReadExt::chain) method. +#[pin_project] #[must_use = "readers do nothing unless polled"] pub struct Chain { + #[pin] first: T, + #[pin] second: U, done_first: bool, } -impl Unpin for Chain -where - T: Unpin, - U: Unpin, -{ -} - impl Chain where T: AsyncRead, U: AsyncRead, { - unsafe_pinned!(first: T); - unsafe_pinned!(second: U); - unsafe_unpinned!(done_first: bool); - pub(super) fn new(first: T, second: U) -> Self { Self { first, @@ -90,34 +82,42 @@ where T: AsyncRead, U: AsyncRead, { + #[project] fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - match ready!(self.as_mut().first().poll_read(cx, buf)?) { - 0 if !buf.is_empty() => *self.as_mut().done_first() = true, + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + match ready!(first.poll_read(cx, buf)?) { + 0 if !buf.is_empty() => *done_first = true, n => return Poll::Ready(Ok(n)), } } - self.second().poll_read(cx, buf) + second.poll_read(cx, buf) } + #[project] 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 n = ready!(self.as_mut().first().poll_read_vectored(cx, bufs)?); + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + let n = ready!(first.poll_read_vectored(cx, bufs)?); if n == 0 && bufs.iter().any(|b| !b.is_empty()) { - *self.as_mut().done_first() = true + *done_first = true } else { return Poll::Ready(Ok(n)); } } - self.second().poll_read_vectored(cx, bufs) + second.poll_read_vectored(cx, bufs) } #[cfg(feature = "read-initializer")] @@ -136,14 +136,10 @@ where T: AsyncBufRead, U: AsyncBufRead, { + #[project] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - let first = unsafe { Pin::new_unchecked(first) }; - let second = unsafe { Pin::new_unchecked(second) }; + #[project] + let Chain { first, second, done_first } = self.project(); if !*done_first { match ready!(first.poll_fill_buf(cx)?) { @@ -156,11 +152,15 @@ where second.poll_fill_buf(cx) } + #[project] fn consume(self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - self.first().consume(amt) + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + first.consume(amt) } else { - self.second().consume(amt) + second.consume(amt) } } } diff --git a/futures-util/src/io/copy.rs b/futures-util/src/io/copy.rs index 9531aab996..491a680a4a 100644 --- a/futures-util/src/io/copy.rs +++ b/futures-util/src/io/copy.rs @@ -4,7 +4,7 @@ use futures_io::{AsyncRead, AsyncWrite}; use std::io; use std::pin::Pin; use super::{BufReader, copy_buf, CopyBuf}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Creates a future which copies all the bytes from one object to another. /// @@ -42,22 +42,18 @@ where } /// Future for the [`copy()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Copy<'a, R, W: ?Sized> { + #[pin] inner: CopyBuf<'a, BufReader, W>, } -impl<'a, R: AsyncRead, W: ?Sized> Unpin for Copy<'a, R, W> where CopyBuf<'a, BufReader, W>: Unpin {} - -impl<'a, R: AsyncRead, W: ?Sized> Copy<'a, R, W> { - unsafe_pinned!(inner: CopyBuf<'a, BufReader, W>); -} - impl Future for Copy<'_, R, W> { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.inner().poll(cx) + self.project().inner.poll(cx) } } diff --git a/futures-util/src/io/copy_buf.rs b/futures-util/src/io/copy_buf.rs index 98811825e0..f8bb49ea0a 100644 --- a/futures-util/src/io/copy_buf.rs +++ b/futures-util/src/io/copy_buf.rs @@ -3,6 +3,7 @@ use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncWrite}; use std::io; use std::pin::Pin; +use pin_project::{pin_project, project}; /// Creates a future which copies all the bytes from one object to another. /// @@ -42,41 +43,34 @@ where } /// Future for the [`copy_buf()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct CopyBuf<'a, R, W: ?Sized> { + #[pin] reader: R, writer: &'a mut W, amt: u64, } -impl Unpin for CopyBuf<'_, R, W> {} - -impl CopyBuf<'_, 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) - } - } -} - impl Future for CopyBuf<'_, R, W> where R: AsyncBufRead, W: AsyncWrite + Unpin + ?Sized, { type Output = io::Result; + #[project] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut reader, mut writer, amt) = self.project(); + #[project] + let CopyBuf { mut reader, mut writer, amt } = self.project(); loop { let buffer = ready!(reader.as_mut().poll_fill_buf(cx))?; if buffer.is_empty() { - ready!(writer.as_mut().poll_flush(cx))?; + ready!(Pin::new(&mut writer).poll_flush(cx))?; return Poll::Ready(Ok(*amt)); } - let i = ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = ready!(Pin::new(&mut writer).poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) } diff --git a/futures-util/src/io/into_sink.rs b/futures-util/src/io/into_sink.rs index bdc6b34140..0589f3abca 100644 --- a/futures-util/src/io/into_sink.rs +++ b/futures-util/src/io/into_sink.rs @@ -3,8 +3,7 @@ use futures_io::AsyncWrite; use futures_sink::Sink; use std::io; use std::pin::Pin; -use std::marker::Unpin; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; #[derive(Debug)] struct Block { @@ -13,40 +12,33 @@ struct Block { } /// Sink for the [`into_sink`](super::AsyncWriteExt::into_sink) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] #[derive(Debug)] pub struct IntoSink { + #[pin] writer: W, /// An outstanding block for us to push into the underlying writer, along with an offset of how /// far into this block we have written already. buffer: Option>, } -impl Unpin for IntoSink {} - impl> IntoSink { - unsafe_pinned!(writer: W); - unsafe_unpinned!(buffer: Option>); - pub(super) fn new(writer: W) -> Self { IntoSink { writer, buffer: None } } - fn project(self: Pin<&mut Self>) -> (Pin<&mut W>, &mut Option>) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.writer), &mut this.buffer) - } - } - /// If we have an outstanding block in `buffer` attempt to push it into the writer, does _not_ /// flush the writer after it succeeds in pushing the block into it. + #[project] fn poll_flush_buffer( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let (mut writer, buffer) = self.project(); + #[project] + let IntoSink { mut writer, buffer } = self.project(); + if let Some(buffer) = buffer { loop { let bytes = buffer.bytes.as_ref(); @@ -67,22 +59,22 @@ impl> Sink for IntoSink { type Error = io::Error; fn poll_ready( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - ready!(self.as_mut().poll_flush_buffer(cx))?; + ready!(self.poll_flush_buffer(cx))?; Poll::Ready(Ok(())) } #[allow(clippy::debug_assert_with_mut_call)] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - debug_assert!(self.as_mut().buffer().is_none()); - *self.as_mut().buffer() = Some(Block { offset: 0, bytes: item }); + debug_assert!(self.buffer.is_none()); + *self.project().buffer = Some(Block { offset: 0, bytes: item }); Ok(()) } @@ -92,7 +84,7 @@ impl> Sink for IntoSink { ) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_flush(cx))?; + ready!(self.project().writer.poll_flush(cx))?; Poll::Ready(Ok(())) } @@ -102,7 +94,7 @@ impl> Sink for IntoSink { ) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_close(cx))?; + ready!(self.project().writer.poll_close(cx))?; Poll::Ready(Ok(())) } } diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index 2e1261689b..af0b491c76 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -5,19 +5,21 @@ use std::io; use std::mem; use std::pin::Pin; use super::read_line::read_line_internal; +use pin_project::{pin_project, project}; /// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. + +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Lines { + #[pin] reader: R, buf: String, bytes: Vec, read: usize, } -impl Unpin for Lines {} - impl Lines { pub(super) fn new(reader: R) -> Self { Self { @@ -32,9 +34,10 @@ impl Lines { impl Stream for Lines { type Item = io::Result; + #[project] 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) }; + #[project] + let Lines { reader, buf, bytes, read } = self.project(); let n = ready!(read_line_internal(reader, cx, buf, bytes, read))?; if n == 0 && buf.is_empty() { return Poll::Ready(None) diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index d830514b92..81d8415131 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -23,7 +23,7 @@ impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadLine<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { Self { reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + bytes: mem::replace(buf, String::new()).into_bytes(), buf, read: 0, } diff --git a/futures-util/src/io/read_to_string.rs b/futures-util/src/io/read_to_string.rs index 56c95ce18d..113fe6af33 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -23,7 +23,7 @@ impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToString<'a, R> { let start_len = buf.len(); Self { reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len, } diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index b1f33fa468..39088b71ed 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -2,25 +2,22 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; use futures_io::{AsyncRead, AsyncBufRead}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::{cmp, io}; use std::pin::Pin; /// Reader for the [`take`](super::AsyncReadExt::take) method. +#[pin_project] #[derive(Debug)] #[must_use = "readers do nothing unless you `.await` or poll them"] pub struct Take { + #[pin] inner: R, // Add '_' to avoid conflicts with `limit` method. limit_: u64, } -impl Unpin for Take { } - impl Take { - unsafe_pinned!(inner: R); - unsafe_unpinned!(limit_: u64); - pub(super) fn new(inner: R, limit: u64) -> Self { Self { inner, limit_: limit } } @@ -82,101 +79,26 @@ impl Take { self.limit_ = limit } - /// Gets a reference to the underlying reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_ref = take.get_ref(); - /// assert_eq!(cursor_ref.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_mut = take.get_mut(); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes the `Take`, returning the wrapped reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor = take.into_inner(); - /// assert_eq!(cursor.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); } impl AsyncRead for Take { + #[project] fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if self.limit_ == 0 { + #[project] + let Take { inner, limit_ } = self.project(); + + if *limit_ == 0 { return Poll::Ready(Ok(0)); } - let max = std::cmp::min(buf.len() as u64, self.limit_) as usize; - let n = ready!(self.as_mut().inner().poll_read(cx, &mut buf[..max]))?; - *self.as_mut().limit_() -= n as u64; + let max = std::cmp::min(buf.len() as u64, *limit_) as usize; + let n = ready!(inner.poll_read(cx, &mut buf[..max]))?; + *limit_ -= n as u64; Poll::Ready(Ok(n)) } @@ -187,9 +109,10 @@ impl AsyncRead for Take { } impl AsyncBufRead for Take { + #[project] 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) }; + #[project] + let Take { inner, limit_ } = self.project(); // Don't call into inner reader at all at EOF because it may still block if *limit_ == 0 { @@ -201,10 +124,14 @@ impl AsyncBufRead for Take { Poll::Ready(Ok(&buf[..cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { + #[project] + fn consume(self: Pin<&mut Self>, amt: usize) { + #[project] + let Take { inner, limit_ } = 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.as_mut().limit_() -= amt as u64; - self.inner().consume(amt); + let amt = cmp::min(amt as u64, *limit_) as usize; + *limit_ -= amt as u64; + inner.consume(amt); } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 13827170b5..e8e9e0b0c3 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -7,8 +7,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] // The solution for this lint is not available on 1.39 which is the current minimum supported version. @@ -71,35 +69,238 @@ macro_rules! cfg_target_has_atomic { macro_rules! delegate_sink { ($field:ident, $item:ty) => { fn poll_ready( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_ready(cx) + self.project().$field.poll_ready(cx) } fn start_send( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, item: $item, ) -> Result<(), Self::Error> { - self.$field().start_send(item) + self.project().$field.start_send(item) } fn poll_flush( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_flush(cx) + self.project().$field.poll_flush(cx) } fn poll_close( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_close(cx) + self.project().$field.poll_close(cx) } } } +macro_rules! delegate_future { + ($field:ident) => { + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut $crate::core_reexport::task::Context<'_>, + ) -> $crate::core_reexport::task::Poll { + self.project().$field.poll(cx) + } + } +} + +macro_rules! delegate_stream { + ($field:ident) => { + fn poll_next( + self: core::pin::Pin<&mut Self>, + cx: &mut $crate::core_reexport::task::Context<'_>, + ) -> $crate::core_reexport::task::Poll> { + self.project().$field.poll_next(cx) + } + fn size_hint(&self) -> (usize, Option) { + self.$field.size_hint() + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_write { + ($field:ident) => { + fn poll_write(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &[u8]) + -> core::task::Poll> + { + self.project().$field.poll_write(cx, buf) + } + fn poll_write_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &[std::io::IoSlice<'_>]) + -> core::task::Poll> + { + self.project().$field.poll_write_vectored(cx, bufs) + } + fn poll_flush(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) + -> core::task::Poll> + { + self.project().$field.poll_flush(cx) + } + fn poll_close(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) + -> core::task::Poll> + { + self.project().$field.poll_close(cx) + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_read { + ($field:ident) => { + #[cfg(feature = "read-initializer")] + unsafe fn initializer(&self) -> $crate::io::Initializer { + self.$field.initializer() + } + + fn poll_read(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &mut [u8]) + -> core::task::Poll> + { + self.project().$field.poll_read(cx, buf) + } + + fn poll_read_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &mut [std::io::IoSliceMut<'_>]) + -> core::task::Poll> + { + self.project().$field.poll_read_vectored(cx, bufs) + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_buf_read { + ($field:ident) => { + fn poll_fill_buf( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_fill_buf(cx) + } + + fn consume(self: core::pin::Pin<&mut Self>, amt: usize) { + self.project().$field.consume(amt) + } + } +} + +macro_rules! delegate_access_inner { + ($field:ident, $inner:ty, ($($ind:tt)*)) => { + /// Acquires a reference to the underlying sink or stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &$inner { + (&self.$field) $($ind get_ref())* + } + + /// Acquires a mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut $inner { + (&mut self.$field) $($ind get_mut())* + } + + /// Acquires a pinned mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut $inner> { + self.project().$field $($ind get_pin_mut())* + } + + /// Consumes this combinator, returning the underlying sink or stream. + /// + /// Note that this may discard intermediate state of this combinator, so + /// care should be taken to avoid losing resources when this is called. + pub fn into_inner(self) -> $inner { + self.$field $($ind into_inner())* + } + } +} + +macro_rules! delegate_all { + (@trait Future $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::Future for $name<$($arg),*> where $t: futures_core::future::Future $(, $($bound)*)* { + type Output = <$t as futures_core::future::Future>::Output; + + delegate_future!(inner); + } + }; + (@trait FusedFuture $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::FusedFuture for $name<$($arg),*> where $t: futures_core::future::FusedFuture $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Stream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::Stream for $name<$($arg),*> where $t: futures_core::stream::Stream $(, $($bound)*)* { + type Item = <$t as futures_core::stream::Stream>::Item; + + delegate_stream!(inner); + } + }; + (@trait FusedStream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::FusedStream for $name<$($arg),*> where $t: futures_core::stream::FusedStream $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Sink $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + #[cfg(feature = "sink")] + impl<_Item, $($arg),*> futures_sink::Sink<_Item> for $name<$($arg),*> where $t: futures_sink::Sink<_Item> $(, $($bound)*)* { + type Error = <$t as futures_sink::Sink<_Item>>::Error; + + delegate_sink!(inner, _Item); + } + }; + (@trait Debug $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> core::fmt::Debug for $name<$($arg),*> where $t: core::fmt::Debug $(, $($bound)*)* { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.inner, f) + } + } + }; + (@trait AccessInner[$inner:ty, ($($ind:tt)*)] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + delegate_access_inner!(inner, $inner, ($($ind)*)); + } + }; + (@trait New[|$($param:ident: $paramt:ty),*| $cons:expr] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + pub(crate) fn new($($param: $paramt),*) -> Self { + Self { inner: $cons } + } + } + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($targs:tt)*])* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + #[pin_project::pin_project] + #[must_use = "futures/streams/sinks do nothing unless you `.await` or poll them"] + $(#[$attr])* + pub struct $name< $($arg),* > $(where $($bound)*)* { #[pin] inner:$t } + + impl<$($arg),*> $name< $($arg),* > $(where $($bound)*)* { + $($($item)*)* + } + + delegate_all!(@trait $ftrait $([$($targs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($ftargs:tt)*])* + $strait:ident $([$($stargs:tt)*])* $(+ $trait:ident $([$($targs:tt)*])*)* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + delegate_all!($(#[$attr])* $name<$($arg),*>($t) : $strait $([$($stargs)*])* $(+ $trait $([$($targs)*])*)* $({$($item)*})* $(where $($bound)*)*); + + delegate_all!(@trait $ftrait $([$($ftargs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; +} + pub mod future; #[doc(hidden)] pub use crate::future::{FutureExt, TryFutureExt}; @@ -125,6 +326,9 @@ pub mod io; #[cfg(feature = "std")] #[doc(hidden)] pub use crate::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt}; +mod fns; + + cfg_target_has_atomic! { #[cfg(feature = "alloc")] pub mod lock; diff --git a/futures-util/src/sink/buffer.rs b/futures-util/src/sink/buffer.rs index d2a3f9098b..c3df3b93c9 100644 --- a/futures-util/src/sink/buffer.rs +++ b/futures-util/src/sink/buffer.rs @@ -1,14 +1,16 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::pin::Pin; use alloc::collections::VecDeque; /// Sink for the [`buffer`](super::SinkExt::buffer) method. +#[pin_project] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct Buffer { + #[pin] sink: Si, buf: VecDeque, @@ -16,13 +18,7 @@ pub struct Buffer { capacity: usize, } -impl Unpin for Buffer {} - impl, Item> Buffer { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(buf: VecDeque); - unsafe_unpinned!(capacity: usize); - pub(super) fn new(sink: Si, capacity: usize) -> Self { Buffer { sink, @@ -31,38 +27,20 @@ impl, Item> Buffer { } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); + #[project] fn try_empty_buffer( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - ready!(self.as_mut().sink().poll_ready(cx))?; - while let Some(item) = self.as_mut().buf().pop_front() { - self.as_mut().sink().start_send(item)?; - if !self.buf.is_empty() { - ready!(self.as_mut().sink().poll_ready(cx))?; + #[project] + let Buffer { mut sink, buf, .. } = self.project(); + ready!(sink.as_mut().poll_ready(cx))?; + while let Some(item) = buf.pop_front() { + sink.as_mut().start_send(item)?; + if !buf.is_empty() { + ready!(sink.as_mut().poll_ready(cx))?; } } Poll::Ready(Ok(())) @@ -74,7 +52,7 @@ impl Stream for Buffer where S: Sink + Stream { type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.sink().poll_next(cx) + self.project().sink.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -96,7 +74,7 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { if self.capacity == 0 { - return self.as_mut().sink().poll_ready(cx); + return self.project().sink.poll_ready(cx); } let _ = self.as_mut().try_empty_buffer(cx)?; @@ -109,13 +87,13 @@ impl, Item> Sink for Buffer { } fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { if self.capacity == 0 { - self.as_mut().sink().start_send(item) + self.project().sink.start_send(item) } else { - self.as_mut().buf().push_back(item); + self.project().buf.push_back(item); Ok(()) } } @@ -126,8 +104,8 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_flush(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_flush(cx) } #[allow(clippy::debug_assert_with_mut_call)] @@ -136,7 +114,7 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_close(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/sink/err_into.rs b/futures-util/src/sink/err_into.rs index af198feaa0..530e1ccf42 100644 --- a/futures-util/src/sink/err_into.rs +++ b/futures-util/src/sink/err_into.rs @@ -1,14 +1,14 @@ use crate::sink::{SinkExt, SinkMapErr}; -use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; use futures_sink::{Sink}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project}; /// Sink for the [`sink_err_into`](super::SinkExt::sink_err_into) method. +#[pin_project] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct SinkErrInto, Item, E> { + #[pin] sink: SinkMapErr E>, } @@ -16,36 +16,13 @@ impl SinkErrInto where Si: Sink, Si::Error: Into, { - unsafe_pinned!(sink: SinkMapErr E>); - pub(super) fn new(sink: Si) -> Self { SinkErrInto { sink: SinkExt::sink_map_err(sink, Into::into), } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - self.sink.get_ref() - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - self.sink.get_mut() - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink.into_inner() - } + delegate_access_inner!(sink, Si, (.)); } impl Sink for SinkErrInto @@ -64,16 +41,7 @@ impl Stream for SinkErrInto { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkErrInto diff --git a/futures-util/src/sink/fanout.rs b/futures-util/src/sink/fanout.rs index 24e4de95eb..7066e216fb 100644 --- a/futures-util/src/sink/fanout.rs +++ b/futures-util/src/sink/fanout.rs @@ -2,22 +2,22 @@ use core::fmt::{Debug, Formatter, Result as FmtResult}; use core::pin::Pin; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Sink that clones incoming items and forwards them to two sinks at the same time. /// /// Backpressure from any downstream sink propagates up, which means that this sink /// can only process items as fast as its _slowest_ downstream sink. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct Fanout { + #[pin] sink1: Si1, + #[pin] sink2: Si2 } impl Fanout { - unsafe_pinned!(sink1: Si1); - unsafe_pinned!(sink2: Si2); - pub(super) fn new(sink1: Si1, sink2: Si2) -> Fanout { Fanout { sink1, sink2 } } @@ -33,11 +33,11 @@ impl Fanout { } /// Get a pinned mutable reference to the inner sinks. + #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut Si1>, Pin<&mut Si2>) { - unsafe { - let Self { sink1, sink2 } = self.get_unchecked_mut(); - (Pin::new_unchecked(sink1), Pin::new_unchecked(sink2)) - } + #[project] + let Fanout { sink1, sink2 } = self.project(); + (sink1, sink2) } /// Consumes this combinator, returning the underlying sinks. @@ -65,41 +65,57 @@ impl Sink for Fanout { type Error = Si1::Error; + #[project] fn poll_ready( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_ready(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_ready(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_ready(cx)?.is_ready(); + let sink2_ready = sink2.poll_ready(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - self.as_mut().sink1().start_send(item.clone())?; - self.as_mut().sink2().start_send(item)?; + #[project] + let Fanout { sink1, sink2 } = self.project(); + + sink1.start_send(item.clone())?; + sink2.start_send(item)?; Ok(()) } + #[project] fn poll_flush( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_flush(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_flush(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_flush(cx)?.is_ready(); + let sink2_ready = sink2.poll_flush(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } + #[project] fn poll_close( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_close(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_close(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_close(cx)?.is_ready(); + let sink2_ready = sink2.poll_close(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } diff --git a/futures-util/src/sink/map_err.rs b/futures-util/src/sink/map_err.rs index e999bea78f..29994a79c4 100644 --- a/futures-util/src/sink/map_err.rs +++ b/futures-util/src/sink/map_err.rs @@ -2,51 +2,27 @@ use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::{Sink}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; /// Sink for the [`sink_map_err`](super::SinkExt::sink_map_err) method. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "sinks do nothing unless polled"] pub struct SinkMapErr { + #[pin] sink: Si, f: Option, } -impl Unpin for SinkMapErr {} - impl SinkMapErr { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: Option); - pub(super) fn new(sink: Si, f: F) -> SinkMapErr { SinkMapErr { sink, f: Some(f) } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); fn take_f(self: Pin<&mut Self>) -> F { - self.f().take().expect("polled MapErr after completion") + self.project().f.take().expect("polled MapErr after completion") } } @@ -60,28 +36,28 @@ impl Sink for SinkMapErr mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) } fn start_send( mut self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - self.as_mut().sink().start_send(item).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.start_send(item).map_err(|e| self.as_mut().take_f()(e)) } fn poll_flush( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) } fn poll_close( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) } } @@ -89,16 +65,7 @@ impl Sink for SinkMapErr impl Stream for SinkMapErr { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkMapErr { diff --git a/futures-util/src/sink/with.rs b/futures-util/src/sink/with.rs index 5acac0176e..802123fc71 100644 --- a/futures-util/src/sink/with.rs +++ b/futures-util/src/sink/with.rs @@ -5,23 +5,20 @@ use futures_core::future::Future; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Sink for the [`with`](super::SinkExt::with) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct With { + #[pin] sink: Si, f: F, + #[pin] state: Option, _phantom: PhantomData Item>, } -impl Unpin for With -where - Si: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for With where Si: fmt::Debug, @@ -40,10 +37,6 @@ where Si: Sink, F: FnMut(U) -> Fut, Fut: Future, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(state: Option); - pub(super) fn new(sink: Si, f: F) -> Self where Fut: Future>, @@ -66,16 +59,7 @@ impl Stream for With { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl With @@ -84,40 +68,23 @@ impl With Fut: Future>, E: From, { - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); /// Completes the processing of previous item if any. + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let item = match self.as_mut().state().as_pin_mut() { + #[project] + let With { mut state, sink, .. } = self.project(); + + let item = match state.as_mut().as_pin_mut() { None => return Poll::Ready(Ok(())), Some(fut) => ready!(fut.poll(cx))?, }; - self.as_mut().state().set(None); - self.as_mut().sink().start_send(item)?; + state.set(None); + sink.start_send(item)?; Poll::Ready(Ok(())) } } @@ -135,16 +102,20 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_ready(cx)?); + ready!(self.project().sink.poll_ready(cx)?); Poll::Ready(Ok(())) } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: U, ) -> Result<(), Self::Error> { - let future = (self.as_mut().f())(item); - self.as_mut().state().set(Some(future)); + #[project] + let With { mut state, f, .. } = self.project(); + + assert!(state.is_none()); + state.set(Some(f(item))); Ok(()) } @@ -153,7 +124,7 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_flush(cx))?; + ready!(self.project().sink.poll_flush(cx)?); Poll::Ready(Ok(())) } @@ -162,7 +133,7 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_close(cx))?; + ready!(self.project().sink.poll_close(cx)?); Poll::Ready(Ok(())) } } diff --git a/futures-util/src/sink/with_flat_map.rs b/futures-util/src/sink/with_flat_map.rs index 05ab77e7b1..f260a0bb4f 100644 --- a/futures-util/src/sink/with_flat_map.rs +++ b/futures-util/src/sink/with_flat_map.rs @@ -4,24 +4,21 @@ use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Sink for the [`with_flat_map`](super::SinkExt::with_flat_map) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct WithFlatMap { + #[pin] sink: Si, f: F, + #[pin] stream: Option, buffer: Option, _marker: PhantomData, } -impl Unpin for WithFlatMap -where - Si: Unpin, - St: Unpin, -{} - impl fmt::Debug for WithFlatMap where Si: fmt::Debug, @@ -43,10 +40,6 @@ where F: FnMut(U) -> St, St: Stream>, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(stream: Option); - pub(super) fn new(sink: Si, f: F) -> Self { WithFlatMap { sink, @@ -57,37 +50,15 @@ where } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); + #[project] fn try_empty_stream( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let WithFlatMap { sink, stream, buffer, .. } = - unsafe { self.get_unchecked_mut() }; - let mut sink = unsafe { Pin::new_unchecked(sink) }; - let mut stream = unsafe { Pin::new_unchecked(stream) }; + #[project] + let WithFlatMap { mut sink, mut stream, buffer, .. } = self.project(); if buffer.is_some() { ready!(sink.as_mut().poll_ready(cx))?; @@ -119,16 +90,7 @@ where { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for WithFlatMap @@ -157,13 +119,16 @@ where self.try_empty_stream(cx) } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: U, ) -> Result<(), Self::Error> { - assert!(self.stream.is_none()); - let stream = (self.as_mut().f())(item); - self.stream().set(Some(stream)); + #[project] + let WithFlatMap { mut stream, f, .. } = self.project(); + + assert!(stream.is_none()); + stream.set(Some(f(item))); Ok(()) } @@ -172,7 +137,7 @@ where cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_flush(cx) + self.project().sink.poll_flush(cx) } fn poll_close( @@ -180,6 +145,6 @@ where cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_close(cx) + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 7cd7934a68..6dc07adf16 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -2,16 +2,18 @@ use crate::stream::{FuturesUnordered, StreamExt}; use futures_core::future::Future; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::pin::Pin; use alloc::collections::binary_heap::{BinaryHeap, PeekMut}; +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] struct OrderWrapper { + #[pin] data: T, // A future or a future's output index: usize, } @@ -37,21 +39,18 @@ impl Ord for OrderWrapper { } } -impl OrderWrapper { - unsafe_pinned!(data: T); -} - impl Future for OrderWrapper where T: Future { type Output = OrderWrapper; fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - self.as_mut().data().as_mut().poll(cx) - .map(|output| OrderWrapper { data: output, index: self.index }) + let index = self.index; + self.project().data.poll(cx) + .map(|output| OrderWrapper { data: output, index }) } } diff --git a/futures-util/src/stream/once.rs b/futures-util/src/stream/once.rs index 4f68b0cedd..21cd14b24e 100644 --- a/futures-util/src/stream/once.rs +++ b/futures-util/src/stream/once.rs @@ -2,7 +2,7 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Creates a stream of a single element. /// @@ -16,34 +16,39 @@ use pin_utils::unsafe_pinned; /// # }); /// ``` pub fn once(future: Fut) -> Once { - Once { future: Some(future) } + Once::new(future) } /// A stream which emits single element and then EOF. /// /// This stream will never block and is always ready. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Once { + #[pin] future: Option } -impl Unpin for Once {} - impl Once { - unsafe_pinned!(future: Option); + pub(crate) fn new(future: Fut) -> Self { + Self { future: Some(future) } + } } impl Stream for Once { type Item = Fut::Output; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let v = match self.as_mut().future().as_pin_mut() { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Once { mut future } = self.project(); + let v = match future.as_mut().as_pin_mut() { Some(fut) => ready!(fut.poll(cx)), None => return Poll::Ready(None), }; - self.as_mut().future().set(None); + future.set(None); Poll::Ready(Some(v)) } diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index b5fb8133b2..36503e40d5 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -2,18 +2,20 @@ use crate::stream::{StreamExt, Fuse}; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; /// Stream for the [`select()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Select { + #[pin] stream1: Fuse, + #[pin] stream2: Fuse, flag: bool, } -impl Unpin for Select {} - /// This function will attempt to pull items from both streams. Each /// stream will be polled in a round-robin fashion, and whenever a stream is /// ready to yield an item that item is yielded. @@ -56,11 +58,11 @@ impl Select { /// /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. + #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - unsafe { - let Self { stream1, stream2, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(stream1).get_pin_mut(), Pin::new_unchecked(stream2).get_pin_mut()) - } + #[project] + let Select { stream1, stream2, .. } = self.project(); + (stream1.get_pin_mut(), stream2.get_pin_mut()) } /// Consumes this combinator, returning the underlying streams. @@ -87,14 +89,13 @@ impl Stream for Select { type Item = St1::Item; + #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let Select { flag, stream1, stream2 } = - unsafe { self.get_unchecked_mut() }; - let stream1 = unsafe { Pin::new_unchecked(stream1) }; - let stream2 = unsafe { Pin::new_unchecked(stream2) }; + #[project] + let Select { flag, stream1, stream2 } = self.project(); if !*flag { poll_inner(flag, stream1, stream2, cx) diff --git a/futures-util/src/stream/stream/buffer_unordered.rs b/futures-util/src/stream/stream/buffer_unordered.rs index bea6e5b4dd..a822576f64 100644 --- a/futures-util/src/stream/stream/buffer_unordered.rs +++ b/futures-util/src/stream/stream/buffer_unordered.rs @@ -4,27 +4,24 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::fmt; use core::pin::Pin; /// Stream for the [`buffer_unordered`](super::StreamExt::buffer_unordered) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct BufferUnordered where St: Stream, { + #[pin] stream: Fuse, in_progress_queue: FuturesUnordered, max: usize, } -impl Unpin for BufferUnordered -where - St: Stream + Unpin, -{} - impl fmt::Debug for BufferUnordered where St: Stream + fmt::Debug, @@ -43,9 +40,6 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesUnordered); - pub(super) fn new(stream: St, n: usize) -> BufferUnordered where St: Stream, @@ -58,37 +52,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for BufferUnordered @@ -98,27 +62,31 @@ where { type Item = ::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let BufferUnordered { mut stream, in_progress_queue, max } = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 2445a85c52..9dff01f0ad 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -4,28 +4,24 @@ use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::fmt; use core::pin::Pin; /// Stream for the [`buffered`](super::StreamExt::buffered) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Buffered where St: Stream, St::Item: Future, { + #[pin] stream: Fuse, in_progress_queue: FuturesOrdered, max: usize, } -impl Unpin for Buffered -where - St: Stream + Unpin, - St::Item: Future, -{} - impl fmt::Debug for Buffered where St: Stream + fmt::Debug, @@ -45,9 +41,6 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesOrdered); - pub(super) fn new(stream: St, n: usize) -> Buffered { Buffered { stream: super::Fuse::new(stream), @@ -56,37 +49,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Buffered @@ -96,27 +59,31 @@ where { type Item = ::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - // Try to spawn off as many futures as possible by filling up - // our in_progress_queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + #[project] + let Buffered { mut stream, in_progress_queue, max } = self.project(); + + // First up, try to spawn off as many futures as possible by filling up + // our queue of futures. + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - let res = self.as_mut().in_progress_queue().poll_next_unpin(cx); + let res = in_progress_queue.poll_next_unpin(cx); if let Some(val) = ready!(res) { return Poll::Ready(Some(val)) } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/catch_unwind.rs b/futures-util/src/stream/stream/catch_unwind.rs index 8d2dcf7663..1bb43b2ff0 100644 --- a/futures-util/src/stream/stream/catch_unwind.rs +++ b/futures-util/src/stream/stream/catch_unwind.rs @@ -1,45 +1,50 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::any::Any; use std::pin::Pin; use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; /// Stream for the [`catch_unwind`](super::StreamExt::catch_unwind) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct CatchUnwind { + #[pin] stream: St, caught_unwind: bool, } impl CatchUnwind { - unsafe_pinned!(stream: St); - unsafe_unpinned!(caught_unwind: bool); - pub(super) fn new(stream: St) -> CatchUnwind { CatchUnwind { stream, caught_unwind: false } } + + delegate_access_inner!(stream, St, ()); } impl Stream for CatchUnwind { type Item = Result>; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.caught_unwind { + #[project] + let CatchUnwind { stream, caught_unwind } = self.project(); + + if *caught_unwind { Poll::Ready(None) } else { let res = catch_unwind(AssertUnwindSafe(|| { - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) })); match res { Ok(poll) => poll.map(|opt| opt.map(Ok)), Err(e) => { - *self.as_mut().caught_unwind() = true; + *caught_unwind = true; Poll::Ready(Some(Err(e))) }, } diff --git a/futures-util/src/stream/stream/chain.rs b/futures-util/src/stream/stream/chain.rs index b2ada69c11..720903cb1f 100644 --- a/futures-util/src/stream/stream/chain.rs +++ b/futures-util/src/stream/stream/chain.rs @@ -1,13 +1,16 @@ use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`chain`](super::StreamExt::chain) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Chain { + #[pin] first: Option, + #[pin] second: St2, } @@ -16,9 +19,6 @@ impl Chain where St1: Stream, St2: Stream, { - unsafe_pinned!(first: Option); - unsafe_pinned!(second: St2); - pub(super) fn new(stream1: St1, stream2: St2) -> Chain { Chain { first: Some(stream1), @@ -42,17 +42,20 @@ where St1: Stream, { type Item = St1::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if let Some(first) = self.as_mut().first().as_pin_mut() { + #[project] + let Chain { mut first, second } = self.project(); + if let Some(first) = first.as_mut().as_pin_mut() { if let Some(item) = ready!(first.poll_next(cx)) { return Poll::Ready(Some(item)) } } - self.as_mut().first().set(None); - self.as_mut().second().poll_next(cx) + first.set(None); + second.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index b42d1d178d..d24c31c9cb 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -3,26 +3,23 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::mem; use core::pin::Pin; use alloc::vec::Vec; /// Stream for the [`chunks`](super::StreamExt::chunks) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Chunks { + #[pin] stream: Fuse, items: Vec, cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 } -impl Unpin for Chunks {} - impl Chunks where St: Stream { - unsafe_unpinned!(items: Vec); - unsafe_pinned!(stream: Fuse); - pub(super) fn new(stream: St, capacity: usize) -> Chunks { assert!(capacity > 0); @@ -33,70 +30,43 @@ impl Chunks where St: Stream { } } - fn take(mut self: Pin<&mut Self>) -> Vec { + fn take(self: Pin<&mut Self>) -> Vec { let cap = self.cap; - mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) + mem::replace(self.project().items, Vec::with_capacity(cap)) } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Chunks { type Item = Vec; + #[project] fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let Chunks { mut stream, items, cap } = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { + match ready!(stream.as_mut().poll_next(cx)) { // Push the item into the buffer and check whether it is full. // If so, replace our buffer with a new and empty one and return // the full one. Some(item) => { - self.as_mut().items().push(item); - if self.items.len() >= self.cap { - return Poll::Ready(Some(self.as_mut().take())) + items.push(item); + if items.len() >= *cap { + return Poll::Ready(Some(self.take())) } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. None => { - let last = if self.items.is_empty() { + let last = if items.is_empty() { None } else { - let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + let full_buf = mem::replace(items, Vec::new()); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/collect.rs b/futures-util/src/stream/stream/collect.rs index 127a3f7d25..349e42d920 100644 --- a/futures-util/src/stream/stream/collect.rs +++ b/futures-util/src/stream/stream/collect.rs @@ -3,24 +3,21 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`collect`](super::StreamExt::collect) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Collect { + #[pin] stream: St, collection: C, } -impl Unpin for Collect {} - impl Collect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(collection: C); - - fn finish(mut self: Pin<&mut Self>) -> C { - mem::replace(self.as_mut().collection(), Default::default()) + fn finish(self: Pin<&mut Self>) -> C { + mem::replace(self.project().collection, Default::default()) } pub(super) fn new(stream: St) -> Collect { @@ -46,11 +43,14 @@ where St: Stream, { type Output = C; + #[project] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Collect { mut stream, collection } = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().collection().extend(Some(e)), - None => return Poll::Ready(self.as_mut().finish()), + match ready!(stream.as_mut().poll_next(cx)) { + Some(e) => collection.extend(Some(e)), + None => return Poll::Ready(self.finish()), } } } diff --git a/futures-util/src/stream/stream/concat.rs b/futures-util/src/stream/stream/concat.rs index 704efc79fd..647632b490 100644 --- a/futures-util/src/stream/stream/concat.rs +++ b/futures-util/src/stream/stream/concat.rs @@ -2,26 +2,23 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`concat`](super::StreamExt::concat) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Concat { + #[pin] stream: St, accum: Option, } -impl Unpin for Concat {} - impl Concat where St: Stream, St::Item: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - pub(super) fn new(stream: St) -> Concat { Concat { stream, @@ -37,16 +34,19 @@ where St: Stream, { type Output = St::Item; + #[project] fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { + #[project] + let Concat { mut stream, accum } = self.project(); + loop { - match ready!(self.as_mut().stream().poll_next(cx)) { + match ready!(stream.as_mut().poll_next(cx)) { None => { - return Poll::Ready(self.as_mut().accum().take().unwrap_or_default()) + return Poll::Ready(accum.take().unwrap_or_default()) } Some(e) => { - let accum = self.as_mut().accum(); if let Some(a) = accum { a.extend(e) } else { diff --git a/futures-util/src/stream/stream/enumerate.rs b/futures-util/src/stream/stream/enumerate.rs index 6366c8b7f3..477a0525e9 100644 --- a/futures-util/src/stream/stream/enumerate.rs +++ b/futures-util/src/stream/stream/enumerate.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`enumerate`](super::StreamExt::enumerate) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Enumerate { + #[pin] stream: St, count: usize, } -impl Unpin for Enumerate {} - impl Enumerate { - unsafe_pinned!(stream: St); - unsafe_unpinned!(count: usize); - pub(super) fn new(stream: St) -> Enumerate { Enumerate { stream, @@ -26,37 +23,7 @@ impl Enumerate { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Enumerate { @@ -68,15 +35,19 @@ impl FusedStream for Enumerate { impl Stream for Enumerate { type Item = (usize, St::Item); + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match ready!(self.as_mut().stream().poll_next(cx)) { + #[project] + let Enumerate { stream, count } = self.project(); + + match ready!(stream.poll_next(cx)) { Some(item) => { - let count = self.count; - *self.as_mut().count() += 1; - Poll::Ready(Some((count, item))) + let prev_count = *count; + *count += 1; + Poll::Ready(Some((prev_count, item))) } None => Poll::Ready(None), } diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index 06335f1ee0..c04c84aaa7 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -5,25 +5,23 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; +use crate::fns::FnMut1; /// Stream for the [`filter`](super::StreamExt::filter) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Filter where St: Stream, { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, } -impl Unpin for Filter -where - St: Stream + Unpin, - Fut: Unpin, -{} - impl fmt::Debug for Filter where St: Stream + fmt::Debug, @@ -41,14 +39,9 @@ where impl Filter where St: Stream, - F: FnMut(&St::Item) -> Fut, + F: for<'a> FnMut1<&'a St::Item, Output=Fut>, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - pub(super) fn new(stream: St, f: F) -> Filter { Filter { stream, @@ -58,37 +51,7 @@ where St: Stream, } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Filter @@ -103,34 +66,31 @@ impl FusedStream for Filter impl Stream for Filter where St: Stream, - F: FnMut(&St::Item) -> Fut, + F: for<'a> FnMut1<&'a St::Item, Output=Fut>, Fut: Future, { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); + #[project] + let Filter { mut stream, f, mut pending_fut, pending_item } = self.project(); + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + if ready!(fut.poll(cx)) { + break pending_item.take(); + } + *pending_item = None; + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f.call_mut(&item))); + *pending_item = Some(item); + } else { + break None; } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(item)); - } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/filter_map.rs b/futures-util/src/stream/stream/filter_map.rs index 532e6cad96..2d098ee5d6 100644 --- a/futures-util/src/stream/stream/filter_map.rs +++ b/futures-util/src/stream/stream/filter_map.rs @@ -5,22 +5,20 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; +use crate::fns::FnMut1; /// Stream for the [`filter_map`](super::StreamExt::filter_map) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct FilterMap { + #[pin] stream: St, f: F, + #[pin] pending: Option, } -impl Unpin for FilterMap -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for FilterMap where St: fmt::Debug, @@ -39,50 +37,16 @@ impl FilterMap F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - pub(super) fn new(stream: St, f: F) -> FilterMap { FilterMap { stream, f, pending: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for FilterMap where St: Stream + FusedStream, - F: FnMut(St::Item) -> Fut, + F: FnMut1, Fut: Future>, { fn is_terminated(&self) -> bool { @@ -92,31 +56,34 @@ impl FusedStream for FilterMap impl Stream for FilterMap where St: Stream, - F: FnMut(St::Item) -> Fut, + F: FnMut1, Fut: Future>, { type Item = T; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let item = ready!(self.as_mut().pending().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending().set(None); - if item.is_some() { - return Poll::Ready(item); + #[project] + let FilterMap { mut stream, f, mut pending } = self.project(); + Poll::Ready(loop { + if let Some(p) = pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let item = ready!(p.poll(cx)); + pending.set(None); + if item.is_some() { + break item; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + // No item in progress, but the stream is still going + pending.set(Some(f.call_mut(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -134,7 +101,7 @@ impl Stream for FilterMap #[cfg(feature = "sink")] impl Sink for FilterMap where S: Stream + Sink, - F: FnMut(S::Item) -> Fut, + F: FnMut1, Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/flat_map.rs b/futures-util/src/stream/stream/flat_map.rs deleted file mode 100644 index e92bb853e5..0000000000 --- a/futures-util/src/stream/stream/flat_map.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::Map; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`flat_map`](super::StreamExt::flat_map) method. -#[must_use = "streams do nothing unless polled"] -pub struct FlatMap { - stream: Map, - inner_stream: Option, -} - -impl Unpin for FlatMap {} - -impl fmt::Debug for FlatMap -where - St: fmt::Debug, - U: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlatMap") - .field("stream", &self.stream) - .field("inner_stream", &self.inner_stream) - .finish() - } -} - -impl FlatMap -where - St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, -{ - unsafe_pinned!(stream: Map); - unsafe_pinned!(inner_stream: Option); - - pub(super) fn new(stream: St, f: F) -> FlatMap { - FlatMap { - stream: Map::new(stream, f), - inner_stream: None, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } -} - -impl FusedStream for FlatMap -where - St: FusedStream, - U: FusedStream, - F: FnMut(St::Item) -> U, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - && self - .inner_stream - .as_ref() - .map(FusedStream::is_terminated) - .unwrap_or(true) - } -} - -impl Stream for FlatMap -where - St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, -{ - type Item = U::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if let Some(inner_stream) = self.as_mut().inner_stream().as_pin_mut() { - let next = ready!(inner_stream.poll_next(cx)); - - if next.is_some() { - break Poll::Ready(next); - } else { - self.as_mut().inner_stream().set(None); - } - } - - let next_stream = ready!(self.as_mut().stream().poll_next(cx)); - - if next_stream.is_some() { - self.as_mut().inner_stream().set(next_stream); - } else { - break Poll::Ready(None); - } - } - } - - fn size_hint(&self) -> (usize, Option) { - let stream_size_hint = self.stream.size_hint(); - let no_stream_items_left = stream_size_hint.1 == Some(0); - - if let Some(inner_stream_size_hint) = self.inner_stream.as_ref().map(|st| st.size_hint()) { - ( - stream_size_hint - .0 - .checked_add(inner_stream_size_hint.0) - .unwrap_or(stream_size_hint.0), - if no_stream_items_left { - inner_stream_size_hint.1 - } else { - // Can't know upper bound because next items are `Stream`s - None - }, - ) - } else { - ( - stream_size_hint.0, - if no_stream_items_left { - Some(0) - } else { - // Can't know upper bound because next items are `Stream`s - None - }, - ) - } - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for FlatMap -where - St: Stream + Sink, - U: Stream, - F: FnMut(St::Item) -> U, -{ - type Error = St::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/stream/flatten.rs b/futures-util/src/stream/stream/flatten.rs index b19ffc036e..4db77e1326 100644 --- a/futures-util/src/stream/stream/flatten.rs +++ b/futures-util/src/stream/stream/flatten.rs @@ -3,77 +3,28 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`flatten`](super::StreamExt::flatten) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] -pub struct Flatten -where - St: Stream, -{ +pub struct Flatten { + #[pin] stream: St, - next: Option, -} - -impl Unpin for Flatten -where - St: Stream + Unpin, - St::Item: Unpin, -{ + #[pin] + next: Option, } -impl Flatten -where - St: Stream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); -} - -impl Flatten -where - St: Stream, - St::Item: Stream, -{ +impl Flatten { pub(super) fn new(stream: St) -> Self { Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Flatten +impl FusedStream for Flatten where St: FusedStream, St::Item: Stream, @@ -83,34 +34,36 @@ where } } -impl Stream for Flatten +impl Stream for Flatten where St: Stream, St::Item: Stream, { type Item = ::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Flatten { mut stream, mut next } = self.project(); + Poll::Ready(loop { + if let Some(s) = next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.poll_next(cx)) { + break Some(item); + } else { + next.set(None); } - } - - if let Some(item) = ready!(self.as_mut().next().as_pin_mut().unwrap().poll_next(cx)) { - return Poll::Ready(Some(item)); + } else if let Some(s) = ready!(stream.as_mut().poll_next(cx)) { + next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Flatten +impl Sink for Flatten where S: Stream + Sink, { diff --git a/futures-util/src/stream/stream/fold.rs b/futures-util/src/stream/stream/fold.rs index e92a72ed90..d4bec25311 100644 --- a/futures-util/src/stream/stream/fold.rs +++ b/futures-util/src/stream/stream/fold.rs @@ -3,19 +3,20 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`fold`](super::StreamExt::fold) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Fold { + #[pin] stream: St, f: F, accum: Option, + #[pin] future: Option, } -impl Unpin for Fold {} - impl fmt::Debug for Fold where St: fmt::Debug, @@ -36,11 +37,6 @@ where St: Stream, F: FnMut(T, St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F, t: T) -> Fold { Fold { stream, @@ -68,25 +64,27 @@ impl Future for Fold { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } - - let item = ready!(self.as_mut().stream().poll_next(cx)); - let accum = self.as_mut().accum().take() - .expect("Fold polled after completion"); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Fold { mut stream, f, accum, mut future } = self.project(); + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + *accum = Some(ready!(fut.poll(cx))); + future.set(None); + } else if accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(stream.as_mut().poll_next(cx)); + let a = accum.take().unwrap(); + if let Some(item) = res { + future.set(Some(f(a, item))); + } else { + break a; + } } else { - return Poll::Ready(accum) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/stream/for_each.rs b/futures-util/src/stream/stream/for_each.rs index f8adcb2927..fb3f40fe3c 100644 --- a/futures-util/src/stream/stream/for_each.rs +++ b/futures-util/src/stream/stream/for_each.rs @@ -3,22 +3,19 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`for_each`](super::StreamExt::for_each) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ForEach { + #[pin] stream: St, f: F, + #[pin] future: Option, } -impl Unpin for ForEach -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEach where St: fmt::Debug, @@ -37,10 +34,6 @@ where St: Stream, F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F) -> ForEach { ForEach { stream, @@ -67,22 +60,20 @@ impl Future for ForEach { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let ForEach { mut stream, f, mut future } = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.poll(cx)); - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); - } - None => { - return Poll::Ready(()); - } + if let Some(fut) = future.as_mut().as_pin_mut() { + ready!(fut.poll(cx)); + future.set(None); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + future.set(Some(f(item))); + } else { + break; } } + Poll::Ready(()) } } diff --git a/futures-util/src/stream/stream/for_each_concurrent.rs b/futures-util/src/stream/stream/for_each_concurrent.rs index 18ca4bd34e..88ff2d39e5 100644 --- a/futures-util/src/stream/stream/for_each_concurrent.rs +++ b/futures-util/src/stream/stream/for_each_concurrent.rs @@ -5,23 +5,20 @@ use core::num::NonZeroUsize; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`for_each_concurrent`](super::StreamExt::for_each_concurrent) /// method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ForEachConcurrent { + #[pin] stream: Option, f: F, futures: FuturesUnordered, limit: Option, } -impl Unpin for ForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEachConcurrent where St: fmt::Debug, @@ -41,11 +38,6 @@ where St: Stream, F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - pub(super) fn new(stream: St, limit: Option, f: F) -> ForEachConcurrent { ForEachConcurrent { stream: Some(stream), @@ -74,16 +66,17 @@ impl Future for ForEachConcurrent { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let ForEachConcurrent { mut stream, f, futures, limit } = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { + if limit.map(|limit| limit.get() > futures.len()).unwrap_or(true) { let mut stream_completed = false; - let elem = if let Some(stream) = self.as_mut().stream().as_pin_mut() { + let elem = if let Some(stream) = stream.as_mut().as_pin_mut() { match stream.poll_next(cx) { Poll::Ready(Some(elem)) => { made_progress_this_iter = true; @@ -99,18 +92,17 @@ impl Future for ForEachConcurrent None }; if stream_completed { - self.as_mut().stream().set(None); + stream.set(None); } if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + futures.push(f(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match futures.poll_next_unpin(cx) { Poll::Ready(Some(())) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { + if stream.is_none() { return Poll::Ready(()) } }, diff --git a/futures-util/src/stream/stream/forward.rs b/futures-util/src/stream/stream/forward.rs index fd89625093..9776056b54 100644 --- a/futures-util/src/stream/stream/forward.rs +++ b/futures-util/src/stream/stream/forward.rs @@ -1,59 +1,34 @@ -use crate::stream::{StreamExt, Fuse}; +use crate::stream::Fuse; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; -use futures_core::stream::{Stream, TryStream}; +use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -const INVALID_POLL: &str = "polled `Forward` after completion"; +use pin_project::{pin_project, project}; /// Future for the [`forward`](super::StreamExt::forward) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Forward { +pub struct Forward { + #[pin] sink: Option, + #[pin] stream: Fuse, - buffered_item: Option, + buffered_item: Option, } -impl Unpin for Forward {} - -impl Forward -where - Si: Sink, - St: TryStream + Stream, -{ - unsafe_pinned!(sink: Option); - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(buffered_item: Option); - - pub(super) fn new(stream: St, sink: Si) -> Self { +impl Forward { + pub(crate) fn new(stream: St, sink: Si) -> Self { Forward { sink: Some(sink), - stream: stream.fuse(), - buffered_item: None, + stream: Fuse::new(stream), + buffered_item: None, } } - - fn try_start_send( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - item: St::Ok, - ) -> Poll> { - debug_assert!(self.buffered_item.is_none()); - { - let mut sink = self.as_mut().sink().as_pin_mut().unwrap(); - if sink.as_mut().poll_ready(cx)?.is_ready() { - return Poll::Ready(sink.start_send(item)); - } - } - *self.as_mut().buffered_item() = Some(item); - Poll::Pending - } } -impl FusedFuture for Forward +impl FusedFuture for Forward where Si: Sink, St: Stream>, @@ -63,34 +38,41 @@ where } } -impl Future for Forward +impl Future for Forward where Si: Sink, St: Stream>, { type Output = Result<(), E>; + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - // If we've got an item buffered already, we need to write it to the - // sink before we can do anything else - if let Some(item) = self.as_mut().buffered_item().take() { - ready!(self.as_mut().try_start_send(cx, item))?; - } + #[project] + let Forward { mut sink, mut stream, buffered_item } = self.project(); + let mut si = sink.as_mut().as_pin_mut().expect("polled `Forward` after completion"); loop { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(item)) => - ready!(self.as_mut().try_start_send(cx, item))?, + // If we've got an item buffered already, we need to write it to the + // sink before we can do anything else + if buffered_item.is_some() { + ready!(si.as_mut().poll_ready(cx))?; + si.as_mut().start_send(buffered_item.take().unwrap())?; + } + + match stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(item)) => { + *buffered_item = Some(item); + } Poll::Ready(None) => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_close(cx))?; - self.as_mut().sink().set(None); + ready!(si.poll_close(cx))?; + sink.set(None); return Poll::Ready(Ok(())) } Poll::Pending => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_flush(cx))?; + ready!(si.poll_flush(cx))?; return Poll::Pending } } diff --git a/futures-util/src/stream/stream/fuse.rs b/futures-util/src/stream/stream/fuse.rs index 9085dc553c..971fe60c29 100644 --- a/futures-util/src/stream/stream/fuse.rs +++ b/futures-util/src/stream/stream/fuse.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`fuse`](super::StreamExt::fuse) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Fuse { + #[pin] stream: St, done: bool, } -impl Unpin for Fuse {} - impl Fuse { - unsafe_pinned!(stream: St); - unsafe_unpinned!(done: bool); - pub(super) fn new(stream: St) -> Fuse { Fuse { stream, done: false } } @@ -32,37 +29,7 @@ impl Fuse { self.done } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Fuse { @@ -74,17 +41,21 @@ impl FusedStream for Fuse { impl Stream for Fuse { type Item = S::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done { + #[project] + let Fuse { stream, done } = self.project(); + + if *done { return Poll::Ready(None); } - let item = ready!(self.as_mut().stream().poll_next(cx)); + let item = ready!(stream.poll_next(cx)); if item.is_none() { - *self.as_mut().done() = true; + *done = true; } Poll::Ready(item) } diff --git a/futures-util/src/stream/stream/inspect.rs b/futures-util/src/stream/stream/inspect.rs deleted file mode 100644 index e34970ae65..0000000000 --- a/futures-util/src/stream/stream/inspect.rs +++ /dev/null @@ -1,119 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect`](super::StreamExt::inspect) method. -#[must_use = "streams do nothing unless polled"] -pub struct Inspect { - stream: St, - f: F, -} - -impl Unpin for Inspect {} - -impl fmt::Debug for Inspect -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Inspect") - .field("stream", &self.stream) - .finish() - } -} - -impl Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Inspect { - Inspect { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for Inspect - where St: FusedStream, - F: FnMut(&St::Item), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -// used by `TryStreamExt::{inspect_ok, inspect_err}` -#[inline] -pub(crate) fn inspect(x: T, mut f: F) -> T { - f(&x); - x -} - -impl Stream for Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - type Item = St::Item; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|e| inspect(e, self.as_mut().f()))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for Inspect - where S: Stream + Sink, - F: FnMut(&S::Item), -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/stream/into_future.rs b/futures-util/src/stream/stream/into_future.rs index abae98c0c9..0d49384bea 100644 --- a/futures-util/src/stream/stream/into_future.rs +++ b/futures-util/src/stream/stream/into_future.rs @@ -3,7 +3,6 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; /// Future for the [`into_future`](super::StreamExt::into_future) method. #[derive(Debug)] @@ -12,11 +11,7 @@ pub struct StreamFuture { stream: Option, } -impl Unpin for StreamFuture {} - impl StreamFuture { - unsafe_pinned!(stream: Option); - pub(super) fn new(stream: St) -> StreamFuture { StreamFuture { stream: Some(stream) } } @@ -57,7 +52,7 @@ impl StreamFuture { /// in order to return it to the caller of `Future::poll` if the stream yielded /// an element. pub fn get_pin_mut(self: Pin<&mut Self>) -> Option> { - self.stream().as_pin_mut() + Pin::get_mut(self).stream.as_mut().map(Pin::new) } /// Consumes this combinator, returning the underlying stream. diff --git a/futures-util/src/stream/stream/map.rs b/futures-util/src/stream/stream/map.rs index 81194342c4..755f53a7cb 100644 --- a/futures-util/src/stream/stream/map.rs +++ b/futures-util/src/stream/stream/map.rs @@ -4,17 +4,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; + +use crate::fns::FnMut1; /// Stream for the [`map`](super::StreamExt::map) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Map { + #[pin] stream: St, f: F, } -impl Unpin for Map {} - impl fmt::Debug for Map where St: fmt::Debug, @@ -26,73 +28,38 @@ where } } -impl Map - where St: Stream, - F: FnMut(St::Item) -> T, -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Map { +impl Map { + pub(crate) fn new(stream: St, f: F) -> Map { Map { stream, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Map +impl FusedStream for Map where St: FusedStream, - F: FnMut(St::Item) -> T, + F: FnMut1, { fn is_terminated(&self) -> bool { self.stream.is_terminated() } } -impl Stream for Map +impl Stream for Map where St: Stream, - F: FnMut(St::Item) -> T, + F: FnMut1, { - type Item = T; + type Item = F::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|x| self.as_mut().f()(x))) + ) -> Poll> { + #[project] + let Map { stream, f } = self.project(); + let res = ready!(stream.poll_next(cx)); + Poll::Ready(res.map(|x| f.call_mut(x))) } fn size_hint(&self) -> (usize, Option) { @@ -102,11 +69,11 @@ impl Stream for Map // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Map - where S: Stream + Sink, - F: FnMut(S::Item) -> T, +impl Sink for Map + where St: Stream + Sink, + F: FnMut1, { - type Error = S::Error; + type Error = St::Error; delegate_sink!(stream, Item); } diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index a7e312b7b8..548dd83979 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -19,6 +19,8 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; +use crate::fns::{InspectFn, inspect_fn}; + mod chain; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chain::Chain; @@ -53,9 +55,15 @@ pub use self::fold::Fold; #[cfg(feature = "sink")] mod forward; + #[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::forward::Forward; +delegate_all!( + /// Future for the [`forward`](super::StreamExt::forward) method. + Forward( + forward::Forward + ): Debug + Future + Sink + Stream + FusedStream + New[|x: St, y: Si| forward::Forward::new(x, y)] + where St: TryStream +); mod for_each; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -69,18 +77,23 @@ mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::StreamFuture; -mod inspect; -pub(crate) use self::inspect::inspect; // used by `TryStreamExt::{inspect_ok, inspect_err}` -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; +delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + Inspect( + map::Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] +); mod map; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::map::Map; -mod flat_map; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flat_map::FlatMap; +delegate_all!( + /// Stream for the [`flat_map`](StreamExt::flat_map) method. + FlatMap( + Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Flatten::new(Map::new(x, f))] +); mod next; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -550,7 +563,7 @@ pub trait StreamExt: Stream { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # }); /// ``` - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self::Item: Stream, Self: Sized, diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index 9272bafe90..fb0f8740d1 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -1,4 +1,3 @@ -use crate::future::Either; use crate::stream::{Fuse, StreamExt}; use core::fmt; use core::pin::Pin; @@ -7,26 +6,23 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// A `Stream` that implements a `peek` method. /// /// The `peek` method can be used to retrieve a reference /// to the next `Stream::Item` if available. A subsequent /// call to `poll` will return the owned item. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Peekable { + #[pin] stream: Fuse, peeked: Option, } -impl Unpin for Peekable {} - impl Peekable { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(peeked: Option); - pub(super) fn new(stream: St) -> Peekable { Peekable { stream: stream.fuse(), @@ -34,37 +30,7 @@ impl Peekable { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); /// Produces a `Peek` future which retrieves a reference to the next item /// in the stream, or `None` if the underlying stream terminates. @@ -72,42 +38,27 @@ impl Peekable { Peek { inner: Some(self) } } - /// Attempt to poll the underlying stream, and return the mutable borrow - /// in case that is desirable to try for another time. - /// In case a peeking poll is successful, the reference to the next item - /// will be in the `Either::Right` variant; otherwise, the mutable borrow - /// will be in the `Either::Left` variant. - fn do_poll_peek( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Either, Option<&St::Item>> { - if self.peeked.is_some() { - let this: &Self = self.into_ref().get_ref(); - return Either::Right(this.peeked.as_ref()); - } - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(None) => Either::Right(None), - Poll::Ready(Some(item)) => { - *self.as_mut().peeked() = Some(item); - let this: &Self = self.into_ref().get_ref(); - Either::Right(this.peeked.as_ref()) - } - _ => Either::Left(self), - } - } - /// Peek retrieves a reference to the next item in the stream. /// /// This method polls the underlying stream and return either a reference /// to the next item if the stream is ready or passes through any errors. + #[project] pub fn poll_peek( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match self.do_poll_peek(cx) { - Either::Left(_) => Poll::Pending, - Either::Right(poll) => Poll::Ready(poll), - } + #[project] + let Peekable { mut stream, peeked } = self.project(); + + Poll::Ready(loop { + if peeked.is_some() { + break peeked.as_ref(); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + *peeked = Some(item); + } else { + break None; + } + }) } } @@ -120,11 +71,14 @@ impl FusedStream for Peekable { impl Stream for Peekable { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(item) = self.as_mut().peeked().take() { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Peekable { stream, peeked } = self.project(); + if let Some(item) = peeked.take() { return Poll::Ready(Some(item)); } - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -151,13 +105,12 @@ where } /// Future for the [`Peekable::peek()`](self::Peekable::peek) function from [`Peekable`] +#[pin_project] #[must_use = "futures do nothing unless polled"] pub struct Peek<'a, St: Stream> { inner: Option>>, } -impl Unpin for Peek<'_, St> {} - impl fmt::Debug for Peek<'_, St> where St: Stream + fmt::Debug, @@ -181,15 +134,12 @@ where St: Stream, { type Output = Option<&'a St::Item>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(peekable) = self.inner.take() { - match peekable.do_poll_peek(cx) { - Either::Left(peekable) => { - self.inner = Some(peekable); - Poll::Pending - } - Either::Right(peek) => Poll::Ready(peek), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some(peekable) = inner { + ready!(peekable.as_mut().poll_peek(cx)); + + inner.take().unwrap().poll_peek(cx) } else { panic!("Peek polled after completion") } diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 9a762c9e1b..2152cb72f0 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -3,26 +3,23 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::mem; use core::pin::Pin; use alloc::vec::Vec; /// Stream for the [`ready_chunks`](super::StreamExt::ready_chunks) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct ReadyChunks { + #[pin] stream: Fuse, items: Vec, cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 } -impl Unpin for ReadyChunks {} - impl ReadyChunks where St: Stream { - unsafe_unpinned!(items: Vec); - unsafe_pinned!(stream: Fuse); - pub(super) fn new(stream: St, capacity: usize) -> ReadyChunks { assert!(capacity > 0); @@ -33,60 +30,29 @@ impl ReadyChunks where St: Stream { } } - fn take(mut self: Pin<&mut Self>) -> Vec { - let cap = self.cap; - mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for ReadyChunks { type Item = Vec; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let ReadyChunks { items, cap, mut stream } = self.project(); + loop { - match self.as_mut().stream().poll_next(cx) { + match stream.as_mut().poll_next(cx) { // Flush all collected data if underlying stream doesn't contain // more ready values Poll::Pending => { - return if self.items.is_empty() { + return if items.is_empty() { Poll::Pending } else { - Poll::Ready(Some(self.as_mut().take())) + Poll::Ready(Some(mem::replace(items, Vec::with_capacity(*cap)))) } } @@ -94,19 +60,19 @@ impl Stream for ReadyChunks { // If so, replace our buffer with a new and empty one and return // the full one. Poll::Ready(Some(item)) => { - self.as_mut().items().push(item); - if self.items.len() >= self.cap { - return Poll::Ready(Some(self.as_mut().take())) + items.push(item); + if items.len() >= *cap { + return Poll::Ready(Some(mem::replace(items, Vec::with_capacity(*cap)))) } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. Poll::Ready(None) => { - let last = if self.items.is_empty() { + let last = if items.is_empty() { None } else { - let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + let full_buf = mem::replace(items, Vec::new()); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/scan.rs b/futures-util/src/stream/stream/scan.rs index 4f937f4fd9..0cdfcbca40 100644 --- a/futures-util/src/stream/stream/scan.rs +++ b/futures-util/src/stream/stream/scan.rs @@ -5,7 +5,7 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; struct StateFn { state: S, @@ -13,15 +13,16 @@ struct StateFn { } /// Stream for the [`scan`](super::StreamExt::scan) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Scan { + #[pin] stream: St, state_f: Option>, + #[pin] future: Option, } -impl Unpin for Scan {} - impl fmt::Debug for Scan where St: Stream + fmt::Debug, @@ -40,10 +41,6 @@ where } impl Scan { - unsafe_pinned!(stream: St); - unsafe_unpinned!(state_f: Option>); - unsafe_pinned!(future: Option); - /// Checks if internal state is `None`. fn is_done_taking(&self) -> bool { self.state_f.is_none() @@ -67,37 +64,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Scan @@ -108,29 +75,32 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.is_done_taking() { return Poll::Ready(None); } - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let state_f = self.as_mut().state_f().as_mut().unwrap(); - let fut = (state_f.f)(&mut state_f.state, item); - self.as_mut().future().set(Some(fut)); - } - - let item = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - if item.is_none() { - self.as_mut().state_f().take(); - } - - Poll::Ready(item) + #[project] + let Scan { mut stream, state_f, mut future } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.poll(cx)); + future.set(None); + + if item.is_none() { + *state_f = None; + } + + break item; + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + let state_f = state_f.as_mut().unwrap(); + future.set(Some((state_f.f)(&mut state_f.state, item))) + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/skip.rs b/futures-util/src/stream/stream/skip.rs index 0b7c632daf..c0f6611e25 100644 --- a/futures-util/src/stream/stream/skip.rs +++ b/futures-util/src/stream/stream/skip.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`skip`](super::StreamExt::skip) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Skip { + #[pin] stream: St, remaining: usize, } -impl Unpin for Skip {} - impl Skip { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - pub(super) fn new(stream: St, n: usize) -> Skip { Skip { stream, @@ -26,37 +23,7 @@ impl Skip { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Skip { @@ -68,18 +35,22 @@ impl FusedStream for Skip { impl Stream for Skip { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - while self.remaining > 0 { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(_) => *self.as_mut().remaining() -= 1, - None => return Poll::Ready(None), + #[project] + let Skip { mut stream, remaining } = self.project(); + while *remaining > 0 { + if ready!(stream.as_mut().poll_next(cx)).is_some() { + *remaining -= 1; + } else { + return Poll::Ready(None); } } - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/skip_while.rs b/futures-util/src/stream/stream/skip_while.rs index 666d9deabc..3d664f2732 100644 --- a/futures-util/src/stream/stream/skip_while.rs +++ b/futures-util/src/stream/stream/skip_while.rs @@ -5,20 +5,21 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`skip_while`](super::StreamExt::skip_while) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct SkipWhile where St: Stream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_skipping: bool, } -impl Unpin for SkipWhile {} - impl fmt::Debug for SkipWhile where St: Stream + fmt::Debug, @@ -40,12 +41,6 @@ impl SkipWhile F: FnMut(&St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - pub(super) fn new(stream: St, f: F) -> SkipWhile { SkipWhile { stream, @@ -56,37 +51,7 @@ impl SkipWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for SkipWhile @@ -106,44 +71,48 @@ impl Stream for SkipWhile { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().poll_next(cx); - } - - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } + #[project] + let SkipWhile { mut stream, f, mut pending_fut, pending_item, done_skipping } = self.project(); - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *done_skipping { + return stream.poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(item)) + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let skipped = ready!(fut.poll(cx)); + let item = pending_item.take(); + pending_fut.set(None); + if !skipped { + *done_skipping = true; + break item; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; - let (_, upper) = self.stream.size_hint(); - let upper = match upper { - Some(x) => x.checked_add(pending_len), - None => None, - }; - (0, upper) // can't know a lower bound, due to the predicate + if self.done_skipping { + self.stream.size_hint() + } else { + let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let (_, upper) = self.stream.size_hint(); + let upper = match upper { + Some(x) => x.checked_add(pending_len), + None => None, + }; + (0, upper) // can't know a lower bound, due to the predicate + } } } diff --git a/futures-util/src/stream/stream/take.rs b/futures-util/src/stream/stream/take.rs index 1109a4a0f0..4a68920c4e 100644 --- a/futures-util/src/stream/stream/take.rs +++ b/futures-util/src/stream/stream/take.rs @@ -4,22 +4,19 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`take`](super::StreamExt::take) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Take { + #[pin] stream: St, remaining: usize, } -impl Unpin for Take {} - impl Take { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - pub(super) fn new(stream: St, n: usize) -> Take { Take { stream, @@ -27,37 +24,7 @@ impl Take { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Take @@ -65,17 +32,21 @@ impl Stream for Take { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { if self.remaining == 0 { Poll::Ready(None) } else { - let next = ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + #[project] + let Take { stream, remaining } = self.project(); + let next = ready!(stream.poll_next(cx)); + if next.is_some() { + *remaining -= 1; + } else { + *remaining = 0; } Poll::Ready(next) } diff --git a/futures-util/src/stream/stream/take_until.rs b/futures-util/src/stream/stream/take_until.rs index 6fbd605c74..3662620a58 100644 --- a/futures-util/src/stream/stream/take_until.rs +++ b/futures-util/src/stream/stream/take_until.rs @@ -5,16 +5,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; // FIXME: docs, tests /// Stream for the [`take_until`](super::StreamExt::take_until) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TakeUntil { + #[pin] stream: St, /// Contains the inner Future on start and None once the inner Future is resolved /// or taken out by the user. + #[pin] fut: Option, /// Contains fut's return value once fut is resolved fut_result: Option, @@ -22,8 +25,6 @@ pub struct TakeUntil { free: bool, } -impl Unpin for TakeUntil {} - impl fmt::Debug for TakeUntil where St: Stream + fmt::Debug, @@ -38,16 +39,6 @@ where } } -impl TakeUntil -where - St: Stream, - Fut: Future, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(fut: Option); - unsafe_pinned!(fut_result: Option); -} - impl TakeUntil where St: Stream, @@ -62,35 +53,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream and the stopping - /// future, if it isn't resolved yet. - pub fn into_inner(self) -> (St, Option) { - (self.stream, self.fut) - } + delegate_access_inner!(stream, St, ()); /// Extract the stopping future out of the combinator. /// The future is returned only if it isn't resolved yet, ie. if the stream isn't stopped yet. @@ -158,22 +121,26 @@ where { type Item = St::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(fut) = self.as_mut().fut().as_pin_mut() { - if let Poll::Ready(result) = fut.poll(cx) { - self.as_mut().fut().set(None); - self.as_mut().fut_result().set(Some(result)); + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let TakeUntil { stream, mut fut, fut_result, free } = self.project(); + + if let Some(f) = fut.as_mut().as_pin_mut() { + if let Poll::Ready(result) = f.poll(cx) { + fut.set(None); + *fut_result = Some(result); } } - if self.is_stopped() { + if !*free && fut.is_none() { // Future resolved, inner stream stopped Poll::Ready(None) } else { // Future either not resolved yet or taken out by the user - let item = ready!(self.as_mut().stream().poll_next(cx)); + let item = ready!(stream.poll_next(cx)); if item.is_none() { - self.as_mut().fut().set(None); + fut.set(None); } Poll::Ready(item) } diff --git a/futures-util/src/stream/stream/take_while.rs b/futures-util/src/stream/stream/take_while.rs index 68606ec263..d90061e8b2 100644 --- a/futures-util/src/stream/stream/take_while.rs +++ b/futures-util/src/stream/stream/take_while.rs @@ -5,20 +5,21 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`take_while`](super::StreamExt::take_while) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] -pub struct TakeWhile { +pub struct TakeWhile { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_taking: bool, } -impl Unpin for TakeWhile {} - impl fmt::Debug for TakeWhile where St: Stream + fmt::Debug, @@ -35,14 +36,6 @@ where } } -impl TakeWhile { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_taking: bool); -} - impl TakeWhile where St: Stream, F: FnMut(&St::Item) -> Fut, @@ -58,37 +51,7 @@ impl TakeWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TakeWhile @@ -98,34 +61,36 @@ impl Stream for TakeWhile { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { if self.done_taking { return Poll::Ready(None); } - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let take = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if take { - Poll::Ready(Some(item)) - } else { - *self.as_mut().done_taking() = true; - Poll::Ready(None) - } + #[project] + let TakeWhile { mut stream, f, mut pending_fut, pending_item, done_taking } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let take = ready!(fut.poll(cx)); + let item = pending_item.take(); + pending_fut.set(None); + if take { + break item; + } else { + *done_taking = true; + break None; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/then.rs b/futures-util/src/stream/stream/then.rs index 39843b2504..d54512ecc0 100644 --- a/futures-util/src/stream/stream/then.rs +++ b/futures-util/src/stream/stream/then.rs @@ -5,18 +5,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`then`](super::StreamExt::then) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Then { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for Then {} - impl fmt::Debug for Then where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl Then { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl Then where St: Stream, F: FnMut(St::Item) -> Fut, @@ -48,37 +43,7 @@ impl Then } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Then @@ -98,22 +63,25 @@ impl Stream for Then { type Item = Fut::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let Then { mut stream, f, mut future } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.poll(cx)); + future.set(None); + break Some(item); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + future.set(Some(f(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index f97ac17d35..6c148fb94a 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -3,33 +3,22 @@ use core::cmp; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`zip`](super::StreamExt::zip) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Zip { + #[pin] stream1: Fuse, + #[pin] stream2: Fuse, queued1: Option, queued2: Option, } -#[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4323 -impl Unpin for Zip -where - St1: Stream, - Fuse: Unpin, - St2: Stream, - Fuse: Unpin, -{} - impl Zip { - unsafe_pinned!(stream1: Fuse); - unsafe_pinned!(stream2: Fuse); - unsafe_unpinned!(queued1: Option); - unsafe_unpinned!(queued2: Option); - pub(super) fn new(stream1: St1, stream2: St2) -> Zip { Zip { stream1: stream1.fuse(), @@ -88,28 +77,31 @@ impl Stream for Zip { type Item = (St1::Item, St2::Item); + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.queued1.is_none() { - match self.as_mut().stream1().poll_next(cx) { - Poll::Ready(Some(item1)) => *self.as_mut().queued1() = Some(item1), + #[project] + let Zip { mut stream1, mut stream2, queued1, queued2 } = self.project(); + + if queued1.is_none() { + match stream1.as_mut().poll_next(cx) { + Poll::Ready(Some(item1)) => *queued1 = Some(item1), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued2.is_none() { - match self.as_mut().stream2().poll_next(cx) { - Poll::Ready(Some(item2)) => *self.as_mut().queued2() = Some(item2), + if queued2.is_none() { + match stream2.as_mut().poll_next(cx) { + Poll::Ready(Some(item2)) => *queued2 = Some(item2), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued1.is_some() && self.queued2.is_some() { - let pair = (self.as_mut().queued1().take().unwrap(), - self.as_mut().queued2().take().unwrap()); + if queued1.is_some() && queued2.is_some() { + let pair = (queued1.take().unwrap(), queued2.take().unwrap()); Poll::Ready(Some(pair)) - } else if self.stream1.is_done() || self.stream2.is_done() { + } else if stream1.is_done() || stream2.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/try_stream/and_then.rs b/futures-util/src/stream/try_stream/and_then.rs index 809c32a94b..563ed34dd0 100644 --- a/futures-util/src/stream/try_stream/and_then.rs +++ b/futures-util/src/stream/try_stream/and_then.rs @@ -5,18 +5,19 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`and_then`](super::TryStreamExt::and_then) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct AndThen { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for AndThen {} - impl fmt::Debug for AndThen where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl AndThen { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl AndThen where St: TryStream, F: FnMut(St::Ok) -> Fut, @@ -45,37 +40,7 @@ impl AndThen Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for AndThen @@ -85,22 +50,25 @@ impl Stream for AndThen { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let AndThen { mut stream, mut future, f } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.try_poll(cx)); + future.set(None); + break Some(item); + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + future.set(Some(f(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/err_into.rs b/futures-util/src/stream/try_stream/err_into.rs deleted file mode 100644 index f5d92945f3..0000000000 --- a/futures-util/src/stream/try_stream/err_into.rs +++ /dev/null @@ -1,98 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`err_into`](super::TryStreamExt::err_into) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct ErrInto { - stream: St, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(stream: St); - - pub(super) fn new(stream: St) -> Self { - ErrInto { stream, _marker: PhantomData } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for ErrInto -where - St: TryStream + FusedStream, - St::Error: Into, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for ErrInto -where - St: TryStream, - St::Error: Into, -{ - type Item = Result; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().try_poll_next(cx) - .map(|res| res.map(|some| some.map_err(Into::into))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for ErrInto -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_err.rs b/futures-util/src/stream/try_stream/inspect_err.rs deleted file mode 100644 index 3c23ae0395..0000000000 --- a/futures-util/src/stream/try_stream/inspect_err.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectErr { - stream: St, - f: F, -} - -impl Unpin for InspectErr {} - -impl fmt::Debug for InspectErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectErr") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectErr -where - St: TryStream + FusedStream, - F: FnMut(&St::Error), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_ok.rs b/futures-util/src/stream/try_stream/inspect_ok.rs deleted file mode 100644 index 89fb459be9..0000000000 --- a/futures-util/src/stream/try_stream/inspect_ok.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectOk { - stream: St, - f: F, -} - -impl Unpin for InspectOk {} - -impl fmt::Debug for InspectOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectOk") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectOk -where - St: TryStream + FusedStream, - F: FnMut(&St::Ok), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/into_stream.rs b/futures-util/src/stream/try_stream/into_stream.rs index b0fa07aa79..370a327943 100644 --- a/futures-util/src/stream/try_stream/into_stream.rs +++ b/futures-util/src/stream/try_stream/into_stream.rs @@ -3,54 +3,24 @@ use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Stream for the [`into_stream`](super::TryStreamExt::into_stream) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct IntoStream { + #[pin] stream: St, } impl IntoStream { - unsafe_pinned!(stream: St); - #[inline] pub(super) fn new(stream: St) -> Self { IntoStream { stream } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for IntoStream { @@ -67,7 +37,7 @@ impl Stream for IntoStream { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.stream().try_poll_next(cx) + self.project().stream.try_poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/map_err.rs b/futures-util/src/stream/try_stream/map_err.rs deleted file mode 100644 index 1b98d6b4bc..0000000000 --- a/futures-util/src/stream/try_stream/map_err.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_err`](super::TryStreamExt::map_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapErr { - stream: St, - f: F, -} - -impl Unpin for MapErr {} - -impl fmt::Debug for MapErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapErr") - .field("stream", &self.stream) - .finish() - } -} - -impl MapErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapErr. - pub(super) fn new(stream: St, f: F) -> Self { - MapErr { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapErr -where - St: TryStream + FusedStream, - F: FnMut(St::Error) -> E, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapErr -where - St: TryStream, - F: FnMut(St::Error) -> E, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| self.as_mut().f()(e)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/map_ok.rs b/futures-util/src/stream/try_stream/map_ok.rs deleted file mode 100644 index 19d01be459..0000000000 --- a/futures-util/src/stream/try_stream/map_ok.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapOk { - stream: St, - f: F, -} - -impl Unpin for MapOk {} - -impl fmt::Debug for MapOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapOk") - .field("stream", &self.stream) - .finish() - } -} - -impl MapOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapOk. - pub(super) fn new(stream: St, f: F) -> Self { - MapOk { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapOk -where - St: TryStream + FusedStream, - F: FnMut(St::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapOk -where - St: TryStream, - F: FnMut(St::Ok) -> T, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|x| self.as_mut().f()(x)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 45a57ed7d1..bb736484a8 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -11,34 +11,53 @@ use futures_core::{ stream::TryStream, task::{Context, Poll}, }; +use crate::fns::{ + InspectOkFn, inspect_ok_fn, InspectErrFn, inspect_err_fn, MapErrFn, map_err_fn, IntoFn, into_fn, MapOkFn, map_ok_fn, +}; +use crate::stream::{Map, Inspect}; mod and_then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::and_then::AndThen; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; - -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; - -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; +delegate_all!( + /// Stream for the [`err_into`](super::TryStreamExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] +); + +delegate_all!( + /// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] +); mod into_stream; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_stream::IntoStream; -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; - -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; +delegate_all!( + /// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] +); + +delegate_all!( + /// Stream for the [`map_err`](super::TryStreamExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] +); mod or_else; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 diff --git a/futures-util/src/stream/try_stream/or_else.rs b/futures-util/src/stream/try_stream/or_else.rs index 33310d1ce3..0bba0d0d53 100644 --- a/futures-util/src/stream/try_stream/or_else.rs +++ b/futures-util/src/stream/try_stream/or_else.rs @@ -5,18 +5,19 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`or_else`](super::TryStreamExt::or_else) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct OrElse { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for OrElse {} - impl fmt::Debug for OrElse where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl OrElse { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl OrElse where St: TryStream, F: FnMut(St::Error) -> Fut, @@ -45,37 +40,7 @@ impl OrElse Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for OrElse @@ -85,23 +50,29 @@ impl Stream for OrElse { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - None => return Poll::Ready(None), - Some(Ok(e)) => return Poll::Ready(Some(Ok(e))), - Some(Err(e)) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let OrElse { mut stream, mut future, f } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.try_poll(cx)); + future.set(None); + break Some(item); + } else { + match ready!(stream.as_mut().try_poll_next(cx)) { + Some(Ok(item)) => break Some(Ok(item)), + Some(Err(e)) => { + future.set(Some(f(e))); + }, + None => break None, + } + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_buffer_unordered.rs b/futures-util/src/stream/try_stream/try_buffer_unordered.rs index d11e1b4bae..566868b7f1 100644 --- a/futures-util/src/stream/try_stream/try_buffer_unordered.rs +++ b/futures-util/src/stream/try_stream/try_buffer_unordered.rs @@ -5,32 +5,27 @@ use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::pin::Pin; /// Stream for the /// [`try_buffer_unordered`](super::TryStreamExt::try_buffer_unordered) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct TryBufferUnordered where St: TryStream { + #[pin] stream: Fuse>, in_progress_queue: FuturesUnordered>, max: usize, } -impl Unpin for TryBufferUnordered - where St: TryStream + Unpin -{} - impl TryBufferUnordered where St: TryStream, St::Ok: TryFuture, { - unsafe_pinned!(stream: Fuse>); - unsafe_unpinned!(in_progress_queue: FuturesUnordered>); - pub(super) fn new(stream: St, n: usize) -> Self { TryBufferUnordered { stream: IntoStream::new(stream).fuse(), @@ -39,37 +34,7 @@ impl TryBufferUnordered } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref().get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut().get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner().into_inner() - } + delegate_access_inner!(stream, St, (. .)); } impl Stream for TryBufferUnordered @@ -78,27 +43,31 @@ impl Stream for TryBufferUnordered { type Item = Result<::Ok, St::Error>; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let TryBufferUnordered { mut stream, in_progress_queue, max } = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. Propagate errors from the stream immediately. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut.into_future()), + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut.into_future()), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/try_stream/try_collect.rs b/futures-util/src/stream/try_stream/try_collect.rs index d22e8e8543..3c9aee2a55 100644 --- a/futures-util/src/stream/try_stream/try_collect.rs +++ b/futures-util/src/stream/try_stream/try_collect.rs @@ -3,34 +3,27 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, TryStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_collect`](super::TryStreamExt::try_collect) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryCollect { + #[pin] stream: St, items: C, } impl TryCollect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(items: C); - pub(super) fn new(s: St) -> TryCollect { TryCollect { stream: s, items: Default::default(), } } - - fn finish(self: Pin<&mut Self>) -> C { - mem::replace(self.items(), Default::default()) - } } -impl Unpin for TryCollect {} - impl FusedFuture for TryCollect where St: TryStream + FusedStream, @@ -48,15 +41,18 @@ where { type Output = Result; + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => self.as_mut().items().extend(Some(x)), - None => return Poll::Ready(Ok(self.as_mut().finish())), + #[project] + let TryCollect { mut stream, items } = self.project(); + Poll::Ready(Ok(loop { + match ready!(stream.as_mut().try_poll_next(cx)?) { + Some(x) => items.extend(Some(x)), + None => break mem::replace(items, Default::default()), } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_concat.rs b/futures-util/src/stream/try_stream/try_concat.rs index 395f166c6b..8c9710b217 100644 --- a/futures-util/src/stream/try_stream/try_concat.rs +++ b/futures-util/src/stream/try_stream/try_concat.rs @@ -2,26 +2,23 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_concat`](super::TryStreamExt::try_concat) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryConcat { + #[pin] stream: St, accum: Option, } -impl Unpin for TryConcat {} - impl TryConcat where St: TryStream, St::Ok: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - pub(super) fn new(stream: St) -> TryConcat { TryConcat { stream, @@ -37,21 +34,20 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => { - let accum = self.as_mut().accum(); - if let Some(a) = accum { - a.extend(x) - } else { - *accum = Some(x) - } - }, - None => { - return Poll::Ready(Ok(self.as_mut().accum().take().unwrap_or_default())) + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryConcat { mut stream, accum } = self.project(); + Poll::Ready(Ok(loop { + if let Some(x) = ready!(stream.as_mut().try_poll_next(cx)?) { + if let Some(a) = accum { + a.extend(x) + } else { + *accum = Some(x) } + } else { + break accum.take().unwrap_or_default(); } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 24a9c3275a..4694f7cdef 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -5,24 +5,23 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_filter`](super::TryStreamExt::try_filter) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryFilter where St: TryStream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, } -impl Unpin for TryFilter - where St: TryStream + Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilter where St: TryStream + fmt::Debug, @@ -41,11 +40,6 @@ where impl TryFilter where St: TryStream { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - pub(super) fn new(stream: St, f: F) -> Self { TryFilter { stream, @@ -55,37 +49,7 @@ impl TryFilter } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilter @@ -105,29 +69,26 @@ impl Stream for TryFilter { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(Ok(item))); + #[project] + let TryFilter { mut stream, f, mut pending_fut, pending_item } = self.project(); + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + if ready!(fut.poll(cx)) { + break pending_item.take().map(Ok); + } + *pending_item = None; + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_filter_map.rs b/futures-util/src/stream/try_stream/try_filter_map.rs index ed7eeb227e..ba8e43a2cd 100644 --- a/futures-util/src/stream/try_stream/try_filter_map.rs +++ b/futures-util/src/stream/try_stream/try_filter_map.rs @@ -5,21 +5,20 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_filter_map`](super::TryStreamExt::try_filter_map) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryFilterMap { + #[pin] stream: St, f: F, + #[pin] pending: Option, } -impl Unpin for TryFilterMap - where St: Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilterMap where St: fmt::Debug, @@ -34,45 +33,11 @@ where } impl TryFilterMap { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - pub(super) fn new(stream: St, f: F) -> Self { TryFilterMap { stream, f, pending: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilterMap @@ -92,26 +57,29 @@ impl Stream for TryFilterMap { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let result = ready!(self.as_mut().pending().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().pending().set(None); - if let Some(x) = result? { - return Poll::Ready(Some(Ok(x))); + #[project] + let TryFilterMap { mut stream, f, mut pending } = self.project(); + Poll::Ready(loop { + if let Some(p) = pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let item = ready!(p.try_poll(cx)?); + pending.set(None); + if item.is_some() { + break item.map(Ok); + } + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + // No item in progress, but the stream is still going + pending.set(Some(f(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_flatten.rs b/futures-util/src/stream/try_stream/try_flatten.rs index 5f81b22b4c..a528639cae 100644 --- a/futures-util/src/stream/try_stream/try_flatten.rs +++ b/futures-util/src/stream/try_stream/try_flatten.rs @@ -3,34 +3,22 @@ use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`try_flatten`](super::TryStreamExt::try_flatten) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct TryFlatten where St: TryStream, { + #[pin] stream: St, + #[pin] next: Option, } -impl Unpin for TryFlatten -where - St: TryStream + Unpin, - St::Ok: Unpin, -{ -} - -impl TryFlatten -where - St: TryStream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); -} - impl TryFlatten where St: TryStream, @@ -41,37 +29,7 @@ where Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFlatten @@ -93,27 +51,23 @@ where { type Item = Result<::Ok, ::Error>; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let TryFlatten { mut stream, mut next } = self.project(); + Poll::Ready(loop { + if let Some(s) = next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.try_poll_next(cx)?) { + break Some(Ok(item)); + } else { + next.set(None); } - } - - if let Some(item) = ready!(self - .as_mut() - .next() - .as_pin_mut() - .unwrap() - .try_poll_next(cx)?) - { - return Poll::Ready(Some(Ok(item))); + } else if let Some(s) = ready!(stream.as_mut().try_poll_next(cx)?) { + next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_fold.rs b/futures-util/src/stream/try_stream/try_fold.rs index b8b8dc24f1..d85c1fe598 100644 --- a/futures-util/src/stream/try_stream/try_fold.rs +++ b/futures-util/src/stream/try_stream/try_fold.rs @@ -3,19 +3,20 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_fold`](super::TryStreamExt::try_fold) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryFold { + #[pin] stream: St, f: F, accum: Option, + #[pin] future: Option, } -impl Unpin for TryFold {} - impl fmt::Debug for TryFold where St: fmt::Debug, @@ -36,11 +37,6 @@ where St: TryStream, F: FnMut(T, St::Ok) -> Fut, Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F, t: T) -> TryFold { TryFold { stream, @@ -68,43 +64,31 @@ impl Future for TryFold { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = match ready!( - self.as_mut().future().as_pin_mut() - .expect("TryFold polled after completion") - .try_poll(cx) - ) { - Ok(accum) => accum, - Err(e) => { - // Indicate that the future can no longer be polled. - self.as_mut().future().set(None); - return Poll::Ready(Err(e)); - } - }; - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } - - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - Some(Ok(item)) => Some(item), - Some(Err(e)) => { - // Indicate that the future can no longer be polled. - *self.as_mut().accum() = None; - return Poll::Ready(Err(e)); + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryFold { mut stream, f, accum, mut future } = self.project(); + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + let res = ready!(fut.try_poll(cx)); + future.set(None); + match res { + Ok(a) => *accum = Some(a), + Err(e) => break Err(e), + } + } else if accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(stream.as_mut().try_poll_next(cx)); + let a = accum.take().unwrap(); + match res { + Some(Ok(item)) => future.set(Some(f(a, item))), + Some(Err(e)) => break Err(e), + None => break Ok(a), } - None => None, - }; - let accum = self.as_mut().accum().take().unwrap(); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); } else { - return Poll::Ready(Ok(accum)) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_for_each.rs b/futures-util/src/stream/try_stream/try_for_each.rs index 2c71107646..5fc91df6d7 100644 --- a/futures-util/src/stream/try_stream/try_for_each.rs +++ b/futures-util/src/stream/try_stream/try_for_each.rs @@ -3,18 +3,19 @@ use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_for_each`](super::TryStreamExt::try_for_each) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryForEach { + #[pin] stream: St, f: F, + #[pin] future: Option, } -impl Unpin for TryForEach {} - impl fmt::Debug for TryForEach where St: fmt::Debug, @@ -33,10 +34,6 @@ where St: TryStream, F: FnMut(St::Ok) -> Fut, Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F) -> TryForEach { TryForEach { stream, @@ -53,20 +50,21 @@ impl Future for TryForEach { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryForEach { mut stream, f, mut future } = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.try_poll(cx))?; - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); + if let Some(fut) = future.as_mut().as_pin_mut() { + ready!(fut.try_poll(cx))?; + future.set(None); + } else { + match ready!(stream.as_mut().try_poll_next(cx)?) { + Some(e) => future.set(Some(f(e))), + None => break, } - None => return Poll::Ready(Ok(())), } } + Poll::Ready(Ok(())) } } diff --git a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs index 19c3e5b89a..87fd465fa8 100644 --- a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs +++ b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs @@ -6,24 +6,21 @@ use core::num::NonZeroUsize; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the /// [`try_for_each_concurrent`](super::TryStreamExt::try_for_each_concurrent) /// method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryForEachConcurrent { + #[pin] stream: Option, f: F, futures: FuturesUnordered, limit: Option, } -impl Unpin for TryForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for TryForEachConcurrent where St: fmt::Debug, @@ -53,11 +50,6 @@ where St: TryStream, F: FnMut(St::Ok) -> Fut, Fut: Future>, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - pub(super) fn new(stream: St, limit: Option, f: F) -> TryForEachConcurrent { TryForEachConcurrent { stream: Some(stream), @@ -76,15 +68,16 @@ impl Future for TryForEachConcurrent { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryForEachConcurrent { mut stream, f, futures, limit } = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { - let poll_res = match self.as_mut().stream().as_pin_mut() { + if limit.map(|limit| limit.get() > futures.len()).unwrap_or(true) { + let poll_res = match stream.as_mut().as_pin_mut() { Some(stream) => stream.try_poll_next(cx), None => Poll::Ready(None), }; @@ -95,29 +88,28 @@ impl Future for TryForEachConcurrent Some(elem) }, Poll::Ready(None) => { - self.as_mut().stream().set(None); + stream.set(None); None } Poll::Pending => None, Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + stream.set(None); + drop(mem::replace(futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } }; if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + futures.push(f(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match futures.poll_next_unpin(cx) { Poll::Ready(Some(Ok(()))) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { + if stream.is_none() { return Poll::Ready(Ok(())) } }, @@ -125,8 +117,8 @@ impl Future for TryForEachConcurrent Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + stream.set(None); + drop(mem::replace(futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } } diff --git a/futures-util/src/stream/try_stream/try_skip_while.rs b/futures-util/src/stream/try_stream/try_skip_while.rs index a3d6803a1b..624380f32d 100644 --- a/futures-util/src/stream/try_stream/try_skip_while.rs +++ b/futures-util/src/stream/try_stream/try_skip_while.rs @@ -5,21 +5,22 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_skip_while`](super::TryStreamExt::try_skip_while) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TrySkipWhile where St: TryStream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_skipping: bool, } -impl Unpin for TrySkipWhile {} - impl fmt::Debug for TrySkipWhile where St: TryStream + fmt::Debug, @@ -36,22 +37,11 @@ where } } -impl TrySkipWhile - where St: TryStream, -{ - unsafe_pinned!(stream: St); -} - impl TrySkipWhile where St: TryStream, F: FnMut(&St::Ok) -> Fut, Fut: TryFuture, { - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - pub(super) fn new(stream: St, f: F) -> TrySkipWhile { TrySkipWhile { stream, @@ -62,37 +52,7 @@ impl TrySkipWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TrySkipWhile @@ -102,34 +62,34 @@ impl Stream for TrySkipWhile { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().try_poll_next(cx); - } + #[project] + let TrySkipWhile { mut stream, f, mut pending_fut, pending_item, done_skipping } = self.project(); - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().try_poll(cx)?); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *done_skipping { + return stream.try_poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(Ok(item))) + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let skipped = ready!(fut.try_poll(cx)?); + let item = pending_item.take(); + pending_fut.set(None); + if !skipped { + *done_skipping = true; + break item.map(Ok); + } + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_unfold.rs b/futures-util/src/stream/try_stream/try_unfold.rs index 6266274cd5..8da1248449 100644 --- a/futures-util/src/stream/try_stream/try_unfold.rs +++ b/futures-util/src/stream/try_stream/try_unfold.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Creates a `TryStream` from a seed and a closure returning a `TryFuture`. /// @@ -67,15 +67,15 @@ where } /// Stream for the [`try_unfold`] function. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryUnfold { f: F, state: Option, + #[pin] fut: Option, } -impl Unpin for TryUnfold {} - impl fmt::Debug for TryUnfold where T: fmt::Debug, @@ -89,12 +89,6 @@ where } } -impl TryUnfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl Stream for TryUnfold where F: FnMut(T) -> Fut, @@ -102,27 +96,30 @@ where { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + #[project] + let TryUnfold {f, state, mut fut } = self.project(); + + if let Some(state) = state.take() { + fut.set(Some(f(state))); } - match self.as_mut().fut().as_pin_mut() { + match fut.as_mut().as_pin_mut() { None => { // The future previously errored Poll::Ready(None) } - Some(fut) => { - let step = ready!(fut.try_poll(cx)); - self.as_mut().fut().set(None); + Some(future) => { + let step = ready!(future.try_poll(cx)); + fut.set(None); match step { Ok(Some((item, next_state))) => { - *self.as_mut().state() = Some(next_state); + *state = Some(next_state); Poll::Ready(Some(Ok(item))) } Ok(None) => Poll::Ready(None), diff --git a/futures-util/src/stream/unfold.rs b/futures-util/src/stream/unfold.rs index 3153f83711..0279571af8 100644 --- a/futures-util/src/stream/unfold.rs +++ b/futures-util/src/stream/unfold.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Creates a `Stream` from a seed and a closure returning a `Future`. /// @@ -56,15 +56,15 @@ pub fn unfold(init: T, f: F) -> Unfold } /// Stream for the [`unfold`] function. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Unfold { f: F, state: Option, + #[pin] fut: Option, } -impl Unpin for Unfold {} - impl fmt::Debug for Unfold where T: fmt::Debug, @@ -78,12 +78,6 @@ where } } -impl Unfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl FusedStream for Unfold where F: FnMut(T) -> Fut, Fut: Future>, @@ -99,21 +93,24 @@ impl Stream for Unfold { type Item = Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + #[project] + let Unfold { state, f, mut fut } = self.project(); + + if let Some(state) = state.take() { + fut.set(Some(f(state))); } - let step = ready!(self.as_mut().fut().as_pin_mut() + let step = ready!(fut.as_mut().as_pin_mut() .expect("Unfold must not be polled after it returned `Poll::Ready(None)`").poll(cx)); - self.as_mut().fut().set(None); + fut.set(None); if let Some((item, next_state)) = step { - *self.as_mut().state() = Some(next_state); + *state = Some(next_state); Poll::Ready(Some(item)) } else { Poll::Ready(None) From b5a6a5b25f2aaf027eef949d3383cf9f1c6fd4ed Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Fri, 24 Apr 2020 21:32:28 +0100 Subject: [PATCH 70/83] Address review comments --- futures-util/src/future/future/map.rs | 71 +++++++++++++++---- futures-util/src/future/future/mod.rs | 25 ++++--- futures-util/src/future/try_future/mod.rs | 35 ++++----- .../src/future/try_future/try_flatten.rs | 2 +- .../src/future/try_future/try_flatten_err.rs | 2 +- futures-util/src/stream/stream/filter.rs | 4 +- futures-util/src/stream/stream/mod.rs | 20 ++++-- futures-util/src/stream/try_stream/mod.rs | 10 +-- .../src/stream/try_stream/try_filter.rs | 4 +- 9 files changed, 115 insertions(+), 58 deletions(-) diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index 046da2b77c..080f87109a 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,24 +1,39 @@ use core::pin::Pin; +use core::ptr; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; use pin_project::{pin_project, project}; use crate::fns::FnOnce1; -/// Future for the [`map`](super::FutureExt::map) method. +/// Internal Map future #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Map { - #[pin] - future: Fut, - f: Option, +pub enum Map { + Incomplete { + #[pin] + future: Fut, + f: F, + }, + Complete, +} + +// Helper type to mark a `Map` as complete without running its destructor. +struct UnsafeMarkAsComplete(*mut Map); + +impl Drop for UnsafeMarkAsComplete { + fn drop(&mut self) { + unsafe { + ptr::write(self.0, Map::Complete); + } + } } impl Map { /// Creates a new Map. pub(crate) fn new(future: Fut, f: F) -> Map { - Map { future, f: Some(f) } + Map::Incomplete { future, f } } } @@ -26,7 +41,12 @@ impl FusedFuture for Map where Fut: Future, F: FnOnce1, { - fn is_terminated(&self) -> bool { self.f.is_none() } + fn is_terminated(&self) -> bool { + match self { + Map::Incomplete { .. } => false, + Map::Complete => true, + } + } } impl Future for Map @@ -36,13 +56,34 @@ impl Future for Map type Output = T; #[project] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - #[project] - let Map { future, f } = self.project(); - let output = ready!(future.poll(cx)); - let f = f.take() - .expect("Map must not be polled after it returned `Poll::Ready`"); - - Poll::Ready(f.call_once(output)) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { + // Store this pointer for later... + let self_ptr: *mut Self = self.as_mut().get_unchecked_mut(); + + match &mut *self_ptr { + Map::Incomplete { future, f } => { + let mut future = Pin::new_unchecked(future); + let output = match future.as_mut().poll(cx) { + Poll::Ready(x) => x, + Poll::Pending => return Poll::Pending, + }; + + // Here be dragons + let f = ptr::read(f); + { + // The ordering here is important, the call to `drop_in_place` must be + // last as it may panic. Other lines must not panic. + let _cleanup = UnsafeMarkAsComplete(self_ptr); + ptr::drop_in_place(future.get_unchecked_mut()); + }; + + // Phew, everything is back to normal, and we should be in the + // `Complete` state! + Poll::Ready(f.call_once(output)) + }, + Map::Complete => panic!("Map must not be polled after it returned `Poll::Ready`"), + } + } } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index b87dae8464..c3e035bfaa 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -28,7 +28,7 @@ delegate_all!( /// Future for the [`flatten`](super::FutureExt::flatten) method. Flatten( flatten::Flatten::Output> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + ): Debug + Future + FusedFuture + New[|x: F| flatten::Flatten::new(x)] where F: Future ); @@ -36,55 +36,60 @@ delegate_all!( /// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method. FlattenStream( flatten::Flatten::Output> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + ): Debug + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] where F: Future ); #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use fuse::Fuse; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use map::Map; + +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Map( + map::Map + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)] +); delegate_all!( /// Stream for the [`into_stream`](FutureExt::into_stream) method. IntoStream( crate::stream::Once - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] + ): Debug + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] ); delegate_all!( /// Future for the [`map_into`](FutureExt::map_into) combinator. MapInto( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, into_fn())] ); delegate_all!( /// Future for the [`then`](FutureExt::then) method. Then( flatten::Flatten, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] + ): Debug + Future + FusedFuture + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] ); delegate_all!( /// Future for the [`inspect`](FutureExt::inspect) method. Inspect( map::Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] ); delegate_all!( /// Future for the [`never_error`](super::FutureExt::never_error) combinator. NeverError( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] ); delegate_all!( /// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. UnitError( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] ); #[cfg(feature = "std")] diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index 4f4a4cc1d6..bd1ab33d1f 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -31,21 +31,22 @@ delegate_all!( /// Future for the [`try_flatten`](TryFutureExt::try_flatten) method. TryFlatten( try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten::TryFlatten::new(x)] + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten::TryFlatten::new(x)] ); delegate_all!( /// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method. TryFlattenErr( try_flatten_err::TryFlattenErr - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] ); delegate_all!( /// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method. - TryFlattenStream( - try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + TryFlattenStream( + try_flatten::TryFlatten + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + where Fut: TryFuture ); #[cfg(feature = "sink")] @@ -53,49 +54,49 @@ delegate_all!( /// Sink for the [`flatten_sink`](TryFutureExt::flatten_sink) method. FlattenSink( try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] ); delegate_all!( /// Future for the [`and_then`](TryFutureExt::and_then) method. AndThen( TryFlatten, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] ); delegate_all!( /// Future for the [`or_else`](TryFutureExt::or_else) method. OrElse( TryFlattenErr, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] ); delegate_all!( /// Future for the [`err_into`](TryFutureExt::err_into) method. ErrInto( MapErr> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapErr::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| MapErr::new(x, into_fn())] ); delegate_all!( /// Future for the [`ok_into`](TryFutureExt::ok_into) method. OkInto( MapOk> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapOk::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| MapOk::new(x, into_fn())] ); delegate_all!( /// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. InspectOk( Inspect, InspectOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] ); delegate_all!( /// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. InspectErr( Inspect, InspectErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] ); #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -105,28 +106,28 @@ delegate_all!( /// Future for the [`map_ok`](TryFutureExt::map_ok) method. MapOk( Map, MapOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] ); delegate_all!( /// Future for the [`map_err`](TryFutureExt::map_err) method. MapErr( Map, MapErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] ); delegate_all!( /// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method. MapOkOrElse( Map, MapOkOrElseFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] ); delegate_all!( /// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method. UnwrapOrElse( Map, UnwrapOrElseFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] ); impl TryFutureExt for Fut {} @@ -526,7 +527,7 @@ pub trait TryFutureExt: TryFuture { /// assert_eq!(list, Ok(vec![17, 18, 19])); /// # }); /// ``` - fn try_flatten_stream(self) -> TryFlattenStream + fn try_flatten_stream(self) -> TryFlattenStream where Self::Ok: TryStream, Self: Sized, diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs index 428377a196..661d3adf88 100644 --- a/futures-util/src/future/try_future/try_flatten.rs +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -57,7 +57,7 @@ impl Future for TryFlatten self.set(TryFlatten::Empty); break output; }, - TryFlatten::Empty => return Poll::Pending, + TryFlatten::Empty => panic!("TryFlatten polled after completion"), } }) } diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs index b4c54efb67..fbb413daac 100644 --- a/futures-util/src/future/try_future/try_flatten_err.rs +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -54,7 +54,7 @@ impl Future for TryFlattenErr self.set(TryFlattenErr::Empty); break output; }, - TryFlattenErr::Empty => return Poll::Pending, + TryFlattenErr::Empty => panic!("TryFlattenErr polled after completion"), } }) } diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index c04c84aaa7..9d848ad717 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -80,7 +80,9 @@ impl Stream for Filter let Filter { mut stream, f, mut pending_fut, pending_item } = self.project(); Poll::Ready(loop { if let Some(fut) = pending_fut.as_mut().as_pin_mut() { - if ready!(fut.poll(cx)) { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { break pending_item.take(); } *pending_item = None; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 548dd83979..359bb2f222 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -46,8 +46,14 @@ mod filter_map; pub use self::filter_map::FilterMap; mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; + +delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + Flatten( + flatten::Flatten + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| flatten::Flatten::new(x)] + where St: Stream +); mod fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -61,7 +67,7 @@ delegate_all!( /// Future for the [`forward`](super::StreamExt::forward) method. Forward( forward::Forward - ): Debug + Future + Sink + Stream + FusedStream + New[|x: St, y: Si| forward::Forward::new(x, y)] + ): Debug + Future + FusedFuture + New[|x: St, y: Si| forward::Forward::new(x, y)] where St: TryStream ); @@ -81,7 +87,7 @@ delegate_all!( /// Stream for the [`inspect`](StreamExt::inspect) method. Inspect( map::Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] ); mod map; @@ -91,8 +97,8 @@ pub use self::map::Map; delegate_all!( /// Stream for the [`flat_map`](StreamExt::flat_map) method. FlatMap( - Flatten, U> - ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Flatten::new(Map::new(x, f))] + flatten::Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| flatten::Flatten::new(Map::new(x, f))] ); mod next; @@ -563,7 +569,7 @@ pub trait StreamExt: Stream { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # }); /// ``` - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self::Item: Stream, Self: Sized, diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index bb736484a8..99d5a6d4c1 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -24,21 +24,21 @@ delegate_all!( /// Stream for the [`err_into`](super::TryStreamExt::err_into) method. ErrInto( MapErr> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] ); delegate_all!( /// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. InspectOk( Inspect, InspectOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] ); delegate_all!( /// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. InspectErr( Inspect, InspectErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] ); mod into_stream; @@ -49,14 +49,14 @@ delegate_all!( /// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. MapOk( Map, MapOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] ); delegate_all!( /// Stream for the [`map_err`](super::TryStreamExt::map_err) method. MapErr( Map, MapErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] ); mod or_else; diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 4694f7cdef..310f99164d 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -78,7 +78,9 @@ impl Stream for TryFilter let TryFilter { mut stream, f, mut pending_fut, pending_item } = self.project(); Poll::Ready(loop { if let Some(fut) = pending_fut.as_mut().as_pin_mut() { - if ready!(fut.poll(cx)) { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { break pending_item.take().map(Ok); } *pending_item = None; From dfc66739469a4fe849987a676c9b3be6011f1fb8 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Tue, 28 Apr 2020 21:13:29 +0100 Subject: [PATCH 71/83] Update futures-util/src/future/future/flatten.rs Co-Authored-By: Taiki Endo --- futures-util/src/future/future/flatten.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 5e2ec686ea..f59464c5be 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -52,7 +52,7 @@ impl Future for Flatten self.set(Flatten::Empty); break output; }, - Flatten::Empty => return Poll::Pending, + Flatten::Empty => panic!("Flatten polled after completion"), } }) } From 127b244ecd779b4be0e59218986c2b3708f862f9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 3 May 2020 22:28:25 +0900 Subject: [PATCH 72/83] Allow pending! and poll! in no_std --- futures/src/lib.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 9b68a6a8d7..0049b79945 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -118,9 +118,11 @@ compile_error!("The `read-initializer` feature requires the `unstable` feature a // Macro reexports pub use futures_core::ready; // Readiness propagation pub use futures_util::pin_mut; +#[cfg(feature = "async-await")] +pub use futures_util::{pending, poll, join, try_join, select_biased}; // Async-await #[cfg(feature = "std")] #[cfg(feature = "async-await")] -pub use futures_util::{pending, poll}; // Async-await +pub use futures_util::select; #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] @@ -534,13 +536,3 @@ pub mod never { pub use futures_util::never::Never; } - -// proc-macro re-export -------------------------------------- - -#[cfg(feature = "async-await")] -pub use futures_util::{join, try_join}; -#[cfg(feature = "std")] -#[cfg(feature = "async-await")] -pub use futures_util::select; -#[cfg(feature = "async-await")] -pub use futures_util::select_biased; From dc00793b38acfb5cf001ad975b60bb94ef0d45ae Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 26 Apr 2020 00:14:48 +0900 Subject: [PATCH 73/83] Make ready and lazy's panic messages easy to understand --- futures-util/src/future/lazy.rs | 2 +- futures-util/src/future/ready.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-util/src/future/lazy.rs b/futures-util/src/future/lazy.rs index 5e72218d1f..409717a2ab 100644 --- a/futures-util/src/future/lazy.rs +++ b/futures-util/src/future/lazy.rs @@ -49,6 +49,6 @@ impl Future for Lazy type Output = R; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Poll::Ready((self.f.take().unwrap())(cx)) + Poll::Ready((self.f.take().expect("Lazy polled after completion"))(cx)) } } diff --git a/futures-util/src/future/ready.rs b/futures-util/src/future/ready.rs index 48661b3d84..35f01c9b16 100644 --- a/futures-util/src/future/ready.rs +++ b/futures-util/src/future/ready.rs @@ -28,7 +28,7 @@ impl Future for Ready { #[inline] fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - Poll::Ready(self.0.take().unwrap()) + Poll::Ready(self.0.take().expect("Ready polled after completion")) } } From dc06edaf5428009fb4189af5a64cc2d23bd3955c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 23 Apr 2020 03:25:48 +0900 Subject: [PATCH 74/83] Update pin-utils to 0.1 --- futures-test/Cargo.toml | 2 +- futures-util/Cargo.toml | 2 +- futures/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0fed77c845..0d4aebdb70 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -17,7 +17,7 @@ futures-task = { version = "0.3.4", path = "../futures-task", default-features = futures-io = { version = "0.3.4", path = "../futures-io", default-features = false } futures-util = { version = "0.3.4", path = "../futures-util", default-features = false } futures-executor = { version = "0.3.4", path = "../futures-executor", default-features = false } -pin-utils = { version = "0.1.0-alpha.4", default-features = false } +pin-utils = { version = "0.1.0", default-features = false } once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } [dev-dependencies] diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 1ea067e378..c8d6d207c5 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -45,7 +45,7 @@ slab = { version = "0.4.2", optional = true } memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } -pin-utils = "0.1.0-alpha.4" +pin-utils = "0.1.0" [dev-dependencies] futures = { path = "../futures", version = "0.3.4", features = ["async-await", "thread-pool"] } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6b30a8dde7..2c8628ec90 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -28,7 +28,7 @@ futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = futures-util = { path = "../futures-util", version = "0.3.4", default-features = false, features = ["sink"] } [dev-dependencies] -pin-utils = "0.1.0-alpha.4" +pin-utils = "0.1.0" futures-executor = { path = "../futures-executor", version = "0.3.4", features = ["thread-pool"] } futures-test = { path = "../futures-test", version = "0.3.4" } tokio = "0.1.11" From 32005e3f74f2b48a9ca0e97a63ff25976c863703 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 24 Apr 2020 11:58:01 +0200 Subject: [PATCH 75/83] fix: Shared must relinquish control to the executor if repolled If the wrapped future requires that another future runs before it stops waking itself while returning pending the current Shared will loop forever. This removes the `REPOLL` case and returns pending immediately, since the current task is recorded and therefore woken through `Notifier`'s `wake_by_ref` the `Shared` future will still be polled again Fixes #2130 --- futures-util/src/future/future/shared.rs | 25 +++++------------ futures/tests/shared.rs | 35 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index ceb66cfa89..79ad5c37a4 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -66,9 +66,8 @@ where const IDLE: usize = 0; const POLLING: usize = 1; -const REPOLL: usize = 2; -const COMPLETE: usize = 3; -const POISONED: usize = 4; +const COMPLETE: usize = 2; +const POISONED: usize = 3; const NULL_WAKER_KEY: usize = usize::max_value(); @@ -196,7 +195,7 @@ where IDLE => { // Lock acquired, fall through } - POLLING | REPOLL => { + POLLING => { // Another task is currently polling, at this point we just want // to ensure that the waker for this task is registered this.inner = Some(inner); @@ -227,7 +226,7 @@ where let _reset = Reset(&inner.notifier.state); - let output = loop { + let output = { let future = unsafe { match &mut *inner.future_or_output.get() { FutureOrOutput::Future(fut) => Pin::new_unchecked(fut), @@ -235,27 +234,19 @@ where } }; - let poll = future.poll(&mut cx); - - match poll { + match future.poll(&mut cx) { Poll::Pending => { - let state = &inner.notifier.state; - match state.compare_and_swap(POLLING, IDLE, SeqCst) { + match inner.notifier.state.compare_and_swap(POLLING, IDLE, SeqCst) { POLLING => { // Success drop(_reset); this.inner = Some(inner); return Poll::Pending; } - REPOLL => { - // Was woken since: Gotta poll again! - let prev = state.swap(POLLING, SeqCst); - assert_eq!(prev, REPOLL); - } _ => unreachable!(), } } - Poll::Ready(output) => break output, + Poll::Ready(output) => output, } }; @@ -313,8 +304,6 @@ where impl ArcWake for Notifier { fn wake_by_ref(arc_self: &Arc) { - arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst); - let wakers = &mut *arc_self.wakers.lock().unwrap(); if let Some(wakers) = wakers.as_mut() { for (_key, opt_waker) in wakers { diff --git a/futures/tests/shared.rs b/futures/tests/shared.rs index d338809e5c..21e80fe690 100644 --- a/futures/tests/shared.rs +++ b/futures/tests/shared.rs @@ -89,7 +89,6 @@ fn drop_on_one_task_ok() { t2.join().unwrap(); } - #[cfg(feature = "executor")] // executor:: #[test] fn drop_in_poll() { @@ -104,7 +103,8 @@ fn drop_in_poll() { let future1 = future::lazy(move |_| { slot2.replace(None); // Drop future 1 - }).shared(); + }) + .shared(); let future2 = LocalFutureObj::new(Box::new(future1.clone())); slot1.replace(Some(future2)); @@ -141,7 +141,9 @@ fn peek() { } // Once the Shared has been polled, the value is peekable on the clone. - spawn.spawn_local_obj(LocalFutureObj::new(Box::new(f1.map(|_| ())))).unwrap(); + spawn + .spawn_local_obj(LocalFutureObj::new(Box::new(f1.map(|_| ())))) + .unwrap(); local_pool.run(); for _ in 0..2 { assert_eq!(*f2.peek().unwrap(), Ok(42)); @@ -191,3 +193,30 @@ fn dont_do_unnecessary_clones_on_output() { assert_eq!(block_on(rx.clone()).unwrap().0.get(), 2); assert_eq!(block_on(rx).unwrap().0.get(), 2); } + +#[cfg(all(feature = "alloc", feature = "executor"))] // channel:: + executor:: +#[test] +fn shared_future_that_wakes_itself_until_pending_is_returned() { + use futures::executor::block_on; + use futures::future::FutureExt; + use std::cell::Cell; + use std::task::Poll; + + let proceed = Cell::new(false); + let fut = futures::future::poll_fn(|cx| { + if proceed.get() { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + }) + .shared(); + + // The join future can only complete if the second future gets a chance to run after the first + // has returned pending + assert_eq!( + block_on(futures::future::join(fut, async { proceed.set(true) })), + ((), ()) + ); +} From 4b930bb4702b1f57cabe3ae6d9c499833bd7d520 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Thu, 7 May 2020 11:00:08 +0200 Subject: [PATCH 76/83] Implement Send + Sync for AssertUnmoved --- futures-test/src/future/assert_unmoved.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/futures-test/src/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs index 74f92c626b..a262a55910 100644 --- a/futures-test/src/future/assert_unmoved.rs +++ b/futures-test/src/future/assert_unmoved.rs @@ -17,6 +17,11 @@ pub struct AssertUnmoved { _pinned: PhantomPinned, } +// Safety: having a raw pointer in a struct makes it `!Send`, however the +// pointer is never dereferenced so this is safe. +unsafe impl Send for AssertUnmoved {} +unsafe impl Sync for AssertUnmoved {} + impl AssertUnmoved { unsafe_pinned!(future: Fut); unsafe_unpinned!(this_ptr: *const Self); @@ -33,10 +38,7 @@ impl AssertUnmoved { impl Future for AssertUnmoved { type Output = Fut::Output; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let cur_this = &*self as *const Self; if self.this_ptr.is_null() { // First time being polled @@ -69,6 +71,12 @@ mod tests { use super::AssertUnmoved; + #[test] + fn assert_send_sync() { + fn assert() {} + assert::>(); + } + #[test] fn dont_panic_when_not_polled() { // This shouldn't panic. From 5b91728e10a47742d3913501fc13ff0da240bdd5 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Thu, 7 May 2020 10:55:53 +0000 Subject: [PATCH 77/83] Drop unneeded Send/Sync bound from AssertUnmoved Co-authored-by: Nemo157 --- futures-test/src/future/assert_unmoved.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-test/src/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs index a262a55910..89e7f50350 100644 --- a/futures-test/src/future/assert_unmoved.rs +++ b/futures-test/src/future/assert_unmoved.rs @@ -19,8 +19,8 @@ pub struct AssertUnmoved { // Safety: having a raw pointer in a struct makes it `!Send`, however the // pointer is never dereferenced so this is safe. -unsafe impl Send for AssertUnmoved {} -unsafe impl Sync for AssertUnmoved {} +unsafe impl Send for AssertUnmoved {} +unsafe impl Sync for AssertUnmoved {} impl AssertUnmoved { unsafe_pinned!(future: Fut); From 4ceafb384c18e664a507892b16c98322a6e240a1 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Fri, 8 May 2020 15:53:53 -0700 Subject: [PATCH 78/83] Stage 0.3.5 --- CHANGELOG.md | 17 +++++++++++++++++ README.md | 2 +- examples/functional/Cargo.toml | 6 +++--- examples/imperative/Cargo.toml | 6 +++--- futures-channel/Cargo.toml | 12 ++++++------ futures-channel/src/lib.rs | 2 +- futures-core/Cargo.toml | 6 +++--- futures-core/src/lib.rs | 2 +- futures-executor/Cargo.toml | 12 ++++++------ futures-executor/src/lib.rs | 2 +- futures-io/Cargo.toml | 4 ++-- futures-io/src/lib.rs | 2 +- futures-macro/Cargo.toml | 4 ++-- futures-macro/src/lib.rs | 2 +- futures-sink/Cargo.toml | 4 ++-- futures-sink/src/lib.rs | 2 +- futures-task/Cargo.toml | 6 +++--- futures-task/src/lib.rs | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-test/src/lib.rs | 2 +- futures-util/Cargo.toml | 20 ++++++++++---------- futures-util/src/lib.rs | 2 +- futures/Cargo.toml | 22 +++++++++++----------- futures/src/lib.rs | 2 +- 24 files changed, 87 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdf208870f..8ad9fd1d1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# 0.3.5 - 2020-05-08 +* Added `StreamExt::flat_map`. +* Added `StreamExt::ready_chunks`. +* Added `*_unpin` methods to `SinkExt`. +* Added a `cancellation()` future to `oneshot::Sender`. +* Added `reunite` method to `ReadHalf` and `WriteHalf`. +* Added `Extend` implementations for `Futures(Un)Ordered` and `SelectAll`. +* Added support for reexporting the `join!` and `select!` macros. +* Added `no_std` support for the `pending!` and `poll!` macros. +* Added `Send` and `Sync` support for `AssertUnmoved`. +* Fixed a bug where `Shared` wasn't relinquishing control to the executor. +* Removed the `Send` bound on the output of `RemoteHandle`. +* Relaxed bounds on `FuturesUnordered`. +* Reorganized internal tests to work under different `--feature`s. +* Reorganized the bounds on `StreamExt::forward`. +* Removed and replaced a large amount of internal `unsafe`. + # 0.3.4 - 2020-02-06 * Fixed missing `Drop` for `UnboundedReceiver` (#2064) diff --git a/README.md b/README.md index 5c1bdcbc98..fa1e0922fd 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ a `#[no_std]` environment, use: ```toml [dependencies] -futures = { version = "0.3.4", default-features = false } +futures = { version = "0.3.5", default-features = false } ``` # License diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml index 468a45432d..d0c09b61c2 100644 --- a/examples/functional/Cargo.toml +++ b/examples/functional/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "futures-example-functional" edition = "2018" -version = "0.3.0" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" +documentation = "https://docs.rs/futures/0.3.5" description = """ An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. @@ -17,4 +17,4 @@ categories = ["asynchronous"] publish = false [dependencies] -futures = { path = "../../futures", version = "0.3.0", features = ["thread-pool"] } +futures = { path = "../../futures", version = "0.3.5", features = ["thread-pool"] } diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml index b5d47c0076..beb212c9c3 100644 --- a/examples/imperative/Cargo.toml +++ b/examples/imperative/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "futures-example-imperative" edition = "2018" -version = "0.3.0" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" +documentation = "https://docs.rs/futures/0.3.5" description = """ An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. @@ -17,4 +17,4 @@ categories = ["asynchronous"] publish = false [dependencies] -futures = { path = "../../futures", version = "0.3.0", features = ["thread-pool"] } +futures = { path = "../../futures", version = "0.3.5", features = ["thread-pool"] } diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index a15c98abca..9a6913926c 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-channel" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-channel/0.3.0" +documentation = "https://docs.rs/futures-channel/0.3.5" description = """ Channels for asynchronous communication using futures-rs. """ @@ -24,12 +24,12 @@ unstable = ["futures-core/unstable"] cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.5", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.5", default-features = false, optional = true } [dev-dependencies] -futures = { path = "../futures", version = "0.3.4", default-features = true } -futures-test = { path = "../futures-test", version = "0.3.4", default-features = true } +futures = { path = "../futures", version = "0.3.5", default-features = true } +futures-test = { path = "../futures-test", version = "0.3.5", default-features = true } [package.metadata.docs.rs] all-features = true diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index d33c919b75..f3d93d8169 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -17,7 +17,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-channel/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-channel/0.3.5")] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index f6afa0028f..56d74aae63 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-core" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-core/0.3.0" +documentation = "https://docs.rs/futures-core/0.3.5" description = """ The core traits and types in for the `futures` library. """ @@ -25,7 +25,7 @@ cfg-target-has-atomic = [] [dependencies] [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures", version = "0.3.5" } [package.metadata.docs.rs] all-features = true diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 85eff3e843..f37624df29 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -11,7 +11,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-core/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-core/0.3.5")] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 1682a8f5a2..21f15f35e9 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-executor" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-executor/0.3.0" +documentation = "https://docs.rs/futures-executor/0.3.5" description = """ Executors for asynchronous tasks based on the futures-rs library. """ @@ -17,13 +17,13 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.4", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.5", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.5", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.5", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures", version = "0.3.5" } [package.metadata.docs.rs] all-features = true diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index f267876ce5..8be8b0fdba 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -12,7 +12,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-executor/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-executor/0.3.5")] #[cfg(feature = "std")] mod local_pool; diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 76f2565cb2..de0385d0ab 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-io" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-io/0.3.0" +documentation = "https://docs.rs/futures-io/0.3.5" description = """ The `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` traits for the futures-rs library. """ diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index c1c64d86da..2a7d8e1244 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -19,7 +19,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-io/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-io/0.3.5")] #[cfg(all(feature = "read-initializer", not(feature = "unstable")))] compile_error!("The `read-initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index ccc1715ca7..f305e8fce6 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-macro" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-macro/0.3.0" +documentation = "https://docs.rs/futures-macro/0.3.5" description = """ The futures-rs procedural macro implementations. """ diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index b565c290c6..d47b0aba0c 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -8,7 +8,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-join-macro/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-join-macro/0.3.5")] // Since https://github.com/rust-lang/cargo/pull/7700 `proc_macro` is part of the prelude for // proc-macro crates, but to support older compilers we still need this explicit `extern crate`. diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index fadb6b04f3..3fc773375a 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-sink" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-sink/0.3.0" +documentation = "https://docs.rs/futures-sink/0.3.5" description = """ The asynchronous `Sink` trait for the futures-rs library. """ diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index fea98fac97..9308af4717 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -11,7 +11,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-sink/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-sink/0.3.5")] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index b74d244bfb..7967674247 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-task" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://rust-lang.github.io/futures-api-docs/0.3.0-alpha.18/futures_core" +documentation = "https://docs.rs/futures-task/0.3.5" description = """ Tools for working with tasks. """ @@ -26,7 +26,7 @@ cfg-target-has-atomic = [] once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures", version = "0.3.5" } [package.metadata.docs.rs] all-features = true diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index b18a33a83f..5f919f8410 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -11,7 +11,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-task/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-task/0.3.5")] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0d4aebdb70..afc7927879 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,27 +1,27 @@ [package] name = "futures-test" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-test/0.3.0" +documentation = "https://docs.rs/futures-test/0.3.5" description = """ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.4", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.4", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.4", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.4", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.4", path = "../futures-executor", default-features = false } +futures-core = { version = "0.3.5", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.5", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.5", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.5", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.5", path = "../futures-executor", default-features = false } pin-utils = { version = "0.1.0", default-features = false } once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } [dev-dependencies] -futures = { version = "0.3.4", path = "../futures", default-features = false, features = ["std", "executor"] } +futures = { version = "0.3.5", path = "../futures", default-features = false, features = ["std", "executor"] } [features] default = ["std"] diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index da018fe5b9..3c5d9c9828 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -7,7 +7,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-test/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-test/0.3.5")] #[cfg(not(feature = "std"))] compile_error!("`futures-test` must have the `std` feature activated, this is a default-active feature"); diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 2ddb2d997b..5cfadc9f53 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "futures-util" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-util/0.3.0" +documentation = "https://docs.rs/futures-util/0.3.5" description = """ Common utilities and extension traits for the futures-rs library. """ @@ -33,12 +33,12 @@ read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] write-all-vectored = ["io"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.4", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.4", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "0.3.4", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.5", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.5", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.5", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.5", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.5", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "0.3.5", default-features = false, optional = true } proc-macro-hack = { version = "0.5.9", optional = true } proc-macro-nested = { version = "0.1.2", optional = true } slab = { version = "0.4.2", optional = true } @@ -49,8 +49,8 @@ pin-utils = "0.1.0" pin-project = "0.4.8" [dev-dependencies] -futures = { path = "../futures", version = "0.3.4", features = ["async-await", "thread-pool"] } -futures-test = { path = "../futures-test", version = "0.3.4" } +futures = { path = "../futures", version = "0.3.5", features = ["async-await", "thread-pool"] } +futures-test = { path = "../futures-test", version = "0.3.5" } tokio = "0.1.11" [package.metadata.docs.rs] diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index e8e9e0b0c3..68b954ac81 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -16,7 +16,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures-util/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures-util/0.3.5")] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 2c8628ec90..c7288edbcc 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "futures" edition = "2018" -version = "0.3.4" +version = "0.3.5" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" +documentation = "https://docs.rs/futures/0.3.5" description = """ An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. @@ -19,18 +19,18 @@ categories = ["asynchronous"] travis-ci = { repository = "rust-lang/futures-rs" } [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.4", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.4", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.4", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.4", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.5", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.5", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.5", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.5", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.5", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.5", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.5", default-features = false, features = ["sink"] } [dev-dependencies] pin-utils = "0.1.0" -futures-executor = { path = "../futures-executor", version = "0.3.4", features = ["thread-pool"] } -futures-test = { path = "../futures-test", version = "0.3.4" } +futures-executor = { path = "../futures-executor", version = "0.3.5", features = ["thread-pool"] } +futures-test = { path = "../futures-test", version = "0.3.5" } tokio = "0.1.11" assert_matches = "1.3.0" diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 0049b79945..e6380d2d35 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -90,7 +90,7 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![doc(html_root_url = "https://docs.rs/futures/0.3.0")] +#![doc(html_root_url = "https://docs.rs/futures/0.3.5")] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); From e69ee62a09f34aea776878e79047d71f007ccb67 Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Sun, 10 May 2020 13:59:14 -0400 Subject: [PATCH 79/83] Fix documentation on futures::future::Map The documentation was pointing at `futures::future::FutureExt::flatten` instead of at `futures::future::FutureExt::map`. --- futures-util/src/future/future/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index c3e035bfaa..25ad1d1523 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -44,7 +44,7 @@ delegate_all!( pub use fuse::Fuse; delegate_all!( - /// Future for the [`flatten`](super::FutureExt::flatten) method. + /// Future for the [`map`](super::FutureExt::map) method. Map( map::Map ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)] From 3c1dfca4035743dc53706f3d09cfb3b38c635936 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 9 May 2020 08:55:03 +0900 Subject: [PATCH 80/83] Reduce pin related unsafe code --- futures-test/Cargo.toml | 1 + futures-test/src/future/assert_unmoved.rs | 17 ++-- futures-test/src/future/pending_once.rs | 16 ++-- futures-test/src/interleave_pending.rs | 92 +++++++++---------- futures-test/src/io/limited.rs | 32 +++---- futures-util/Cargo.toml | 2 +- futures-util/src/fns.rs | 3 + futures-util/src/future/future/map.rs | 55 +++-------- futures-util/src/future/future/mod.rs | 12 +-- futures-util/src/future/maybe_done.rs | 30 +++--- futures-util/src/future/try_maybe_done.rs | 30 +++--- futures-util/src/io/chain.rs | 6 +- futures-util/src/lib.rs | 4 +- futures-util/src/sink/fanout.rs | 6 +- futures-util/src/stream/select.rs | 6 +- futures-util/src/stream/stream/filter.rs | 2 + futures-util/src/stream/stream/into_future.rs | 2 +- futures-util/src/stream/stream/zip.rs | 6 +- futures/Cargo.toml | 1 + futures/tests/eager_drop.rs | 12 +-- futures/tests/split.rs | 22 ++--- 21 files changed, 150 insertions(+), 207 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index afc7927879..0dbd3f1d40 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -19,6 +19,7 @@ futures-util = { version = "0.3.5", path = "../futures-util", default-features = futures-executor = { version = "0.3.5", path = "../futures-executor", default-features = false } pin-utils = { version = "0.1.0", default-features = false } once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } +pin-project = "0.4.10" [dev-dependencies] futures = { version = "0.3.5", path = "../futures", default-features = false, features = ["std", "executor"] } diff --git a/futures-test/src/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs index 89e7f50350..114bfb3f33 100644 --- a/futures-test/src/future/assert_unmoved.rs +++ b/futures-test/src/future/assert_unmoved.rs @@ -1,6 +1,6 @@ use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, pinned_drop}; use std::marker::PhantomPinned; use std::pin::Pin; use std::ptr; @@ -9,11 +9,14 @@ use std::thread::panicking; /// Combinator for the /// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved) /// method. +#[pin_project(PinnedDrop)] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct AssertUnmoved { + #[pin] future: Fut, this_ptr: *const AssertUnmoved, + #[pin] _pinned: PhantomPinned, } @@ -23,9 +26,6 @@ unsafe impl Send for AssertUnmoved {} unsafe impl Sync for AssertUnmoved {} impl AssertUnmoved { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(this_ptr: *const Self); - pub(super) fn new(future: Fut) -> Self { Self { future, @@ -42,16 +42,17 @@ impl Future for AssertUnmoved { let cur_this = &*self as *const Self; if self.this_ptr.is_null() { // First time being polled - *self.as_mut().this_ptr() = cur_this; + *self.as_mut().project().this_ptr = cur_this; } else { assert_eq!(self.this_ptr, cur_this, "Future moved between poll calls"); } - self.as_mut().future().poll(cx) + self.project().future.poll(cx) } } -impl Drop for AssertUnmoved { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for AssertUnmoved { + fn drop(self: Pin<&mut Self>) { // If the thread is panicking then we can't panic again as that will // cause the process to be aborted. if !panicking() && !self.this_ptr.is_null() { diff --git a/futures-test/src/future/pending_once.rs b/futures-test/src/future/pending_once.rs index f803f3a543..b36af6fe39 100644 --- a/futures-test/src/future/pending_once.rs +++ b/futures-test/src/future/pending_once.rs @@ -1,7 +1,7 @@ use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; use std::pin::Pin; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; /// Combinator that guarantees one [`Poll::Pending`] before polling its inner /// future. @@ -9,17 +9,16 @@ use pin_utils::{unsafe_pinned, unsafe_unpinned}; /// This is created by the /// [`FutureTestExt::pending_once`](super::FutureTestExt::pending_once) /// method. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct PendingOnce { + #[pin] future: Fut, polled_before: bool, } impl PendingOnce { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(polled_before: bool); - pub(super) fn new(future: Fut) -> Self { Self { future, @@ -32,13 +31,14 @@ impl Future for PendingOnce { type Output = Fut::Output; fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - if self.polled_before { - self.as_mut().future().poll(cx) + let this = self.project(); + if *this.polled_before { + this.future.poll(cx) } else { - *self.as_mut().polled_before() = true; + *this.polled_before = true; cx.waker().wake_by_ref(); Poll::Pending } diff --git a/futures-test/src/interleave_pending.rs b/futures-test/src/interleave_pending.rs index 35c599dcdb..59de2879a9 100644 --- a/futures-test/src/interleave_pending.rs +++ b/futures-test/src/interleave_pending.rs @@ -1,7 +1,7 @@ use futures_core::future::{Future, FusedFuture}; use futures_core::stream::{Stream, FusedStream}; use futures_io::{self as io, AsyncBufRead, AsyncRead, AsyncWrite}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; use std::{ pin::Pin, task::{Context, Poll}, @@ -14,18 +14,15 @@ use std::{ /// * [`StreamTestExt`](crate::stream::StreamTestExt::interleave_pending) /// * [`AsyncReadTestExt`](crate::io::AsyncReadTestExt::interleave_pending) /// * [`AsyncWriteTestExt`](crate::io::AsyncWriteTestExt::interleave_pending_write) +#[pin_project] #[derive(Debug)] pub struct InterleavePending { + #[pin] inner: T, pended: bool, } -impl Unpin for InterleavePending {} - impl InterleavePending { - unsafe_pinned!(inner: T); - unsafe_unpinned!(pended: bool); - pub(crate) fn new(inner: T) -> Self { Self { inner, @@ -48,38 +45,32 @@ impl InterleavePending { /// Acquires a pinned mutable reference to the underlying I/O object that /// this adaptor is wrapping. pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().0 + self.project().inner } /// Consumes this adaptor returning the underlying I/O object. pub fn into_inner(self) -> T { self.inner } - - fn project(self: Pin<&mut Self>) -> (Pin<&mut T>, &mut bool) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.inner), &mut this.pended) - } - } } impl Future for InterleavePending { type Output = Fut::Output; fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - if *self.as_mut().pended() { - let next = self.as_mut().inner().poll(cx); + let this = self.project(); + if *this.pended { + let next = this.inner.poll(cx); if next.is_ready() { - *self.pended() = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *self.pended() = true; + *this.pended = true; Poll::Pending } } @@ -95,18 +86,19 @@ impl Stream for InterleavePending { type Item = St::Item; fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if *self.as_mut().pended() { - let next = self.as_mut().inner().poll_next(cx); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_next(cx); if next.is_ready() { - *self.pended() = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *self.pended() = true; + *this.pended = true; Poll::Pending } } @@ -128,16 +120,16 @@ impl AsyncWrite for InterleavePending { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_write(cx, buf); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_write(cx, buf); if next.is_ready() { - *pended = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *pended = true; + *this.pended = true; Poll::Pending } } @@ -146,16 +138,16 @@ impl AsyncWrite for InterleavePending { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_flush(cx); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_flush(cx); if next.is_ready() { - *pended = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *pended = true; + *this.pended = true; Poll::Pending } } @@ -164,16 +156,16 @@ impl AsyncWrite for InterleavePending { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_close(cx); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_close(cx); if next.is_ready() { - *pended = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *pended = true; + *this.pended = true; Poll::Pending } } @@ -185,16 +177,16 @@ impl AsyncRead for InterleavePending { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let (reader, pended) = self.project(); - if *pended { - let next = reader.poll_read(cx, buf); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_read(cx, buf); if next.is_ready() { - *pended = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *pended = true; + *this.pended = true; Poll::Pending } } @@ -205,21 +197,21 @@ impl AsyncBufRead for InterleavePending { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let (reader, pended) = self.project(); - if *pended { - let next = reader.poll_fill_buf(cx); + let this = self.project(); + if *this.pended { + let next = this.inner.poll_fill_buf(cx); if next.is_ready() { - *pended = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *pended = true; + *this.pended = true; Poll::Pending } } fn consume(self: Pin<&mut Self>, amount: usize) { - self.inner().consume(amount) + self.project().inner.consume(amount) } } diff --git a/futures-test/src/io/limited.rs b/futures-test/src/io/limited.rs index 68ba674d4d..41ac80d223 100644 --- a/futures-test/src/io/limited.rs +++ b/futures-test/src/io/limited.rs @@ -1,5 +1,5 @@ use futures_io::{self as io, AsyncBufRead, AsyncRead, AsyncWrite}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; use std::{ cmp, pin::Pin, @@ -12,18 +12,15 @@ use std::{ /// /// [`limited`]: super::AsyncReadTestExt::limited /// [`limited_write`]: super::AsyncWriteTestExt::limited_write +#[pin_project] #[derive(Debug)] pub struct Limited { + #[pin] io: Io, limit: usize, } -impl Unpin for Limited {} - impl Limited { - unsafe_pinned!(io: Io); - unsafe_unpinned!(limit: usize); - pub(crate) fn new(io: Io, limit: usize) -> Limited { Limited { io, limit } } @@ -43,7 +40,7 @@ impl Limited { /// Acquires a pinned mutable reference to the underlying I/O object that /// this adaptor is wrapping. pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Io> { - self.io() + self.project().io } /// Consumes this adaptor returning the underlying I/O object. @@ -54,37 +51,38 @@ impl Limited { impl AsyncWrite for Limited { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let limit = *self.as_mut().limit(); - self.io().poll_write(cx, &buf[..cmp::min(limit, buf.len())]) + let this = self.project(); + this.io.poll_write(cx, &buf[..cmp::min(*this.limit, buf.len())]) } fn poll_flush( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.io().poll_flush(cx) + self.project().io.poll_flush(cx) } fn poll_close( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.io().poll_close(cx) + self.project().io.poll_close(cx) } } impl AsyncRead for Limited { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let limit = cmp::min(*self.as_mut().limit(), buf.len()); - self.io().poll_read(cx, &mut buf[..limit]) + let this = self.project(); + let limit = cmp::min(*this.limit, buf.len()); + this.io.poll_read(cx, &mut buf[..limit]) } } @@ -93,10 +91,10 @@ impl AsyncBufRead for Limited { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.io().poll_fill_buf(cx) + self.project().io.poll_fill_buf(cx) } fn consume(self: Pin<&mut Self>, amount: usize) { - self.io().consume(amount) + self.project().io.consume(amount) } } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 5cfadc9f53..937323b85b 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -46,7 +46,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0" -pin-project = "0.4.8" +pin-project = "0.4.10" [dev-dependencies] futures = { path = "../futures", version = "0.3.5", features = ["async-await", "thread-pool"] } diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs index 2b4e3c6e56..6908bff5f8 100644 --- a/futures-util/src/fns.rs +++ b/futures-util/src/fns.rs @@ -140,6 +140,7 @@ trivial_fn_impls!(merge_result_fn <> MergeResultFn = "merge_result"); #[derive(Debug, Copy, Clone, Default)] pub struct InspectFn(F); +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl FnOnce1 for InspectFn where F: for<'a> FnOnce1<&'a A, Output=()>, @@ -150,6 +151,7 @@ where arg } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl FnMut1 for InspectFn where F: for<'a> FnMut1<&'a A, Output=()>, @@ -159,6 +161,7 @@ where arg } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Fn1 for InspectFn where F: for<'a> Fn1<&'a A, Output=()>, diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index 080f87109a..e1eeab9be5 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,13 +1,12 @@ use core::pin::Pin; -use core::ptr; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_project::{pin_project, project}; +use pin_project::{pin_project, project, project_replace}; use crate::fns::FnOnce1; /// Internal Map future -#[pin_project] +#[pin_project(Replace)] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub enum Map { @@ -19,17 +18,6 @@ pub enum Map { Complete, } -// Helper type to mark a `Map` as complete without running its destructor. -struct UnsafeMarkAsComplete(*mut Map); - -impl Drop for UnsafeMarkAsComplete { - fn drop(&mut self) { - unsafe { - ptr::write(self.0, Map::Complete); - } - } -} - impl Map { /// Creates a new Map. pub(crate) fn new(future: Fut, f: F) -> Map { @@ -56,34 +44,19 @@ impl Future for Map type Output = T; #[project] + #[project_replace] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - // Store this pointer for later... - let self_ptr: *mut Self = self.as_mut().get_unchecked_mut(); - - match &mut *self_ptr { - Map::Incomplete { future, f } => { - let mut future = Pin::new_unchecked(future); - let output = match future.as_mut().poll(cx) { - Poll::Ready(x) => x, - Poll::Pending => return Poll::Pending, - }; - - // Here be dragons - let f = ptr::read(f); - { - // The ordering here is important, the call to `drop_in_place` must be - // last as it may panic. Other lines must not panic. - let _cleanup = UnsafeMarkAsComplete(self_ptr); - ptr::drop_in_place(future.get_unchecked_mut()); - }; - - // Phew, everything is back to normal, and we should be in the - // `Complete` state! - Poll::Ready(f.call_once(output)) - }, - Map::Complete => panic!("Map must not be polled after it returned `Poll::Ready`"), - } + #[project] + match self.as_mut().project() { + Map::Incomplete { future, .. } => { + let output = ready!(future.poll(cx)); + #[project_replace] + match self.project_replace(Map::Complete) { + Map::Incomplete { f, .. } => Poll::Ready(f.call_once(output)), + Map::Complete => unreachable!(), + } + }, + Map::Complete => panic!("Map must not be polled after it returned `Poll::Ready`"), } } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index 25ad1d1523..fcefc4f064 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -17,6 +17,7 @@ use futures_core::{ }; use crate::never::Never; use crate::fns::{OkFn, ok_fn, IntoFn, into_fn, InspectFn, inspect_fn}; +use pin_utils::pin_mut; // Combinators @@ -585,19 +586,16 @@ pub trait FutureExt: Future { /// /// assert_eq!(future_ready.now_or_never().expect("Future not ready"), "foobar"); /// ``` - fn now_or_never(mut self) -> Option + fn now_or_never(self) -> Option where Self: Sized, { let noop_waker = crate::task::noop_waker(); let mut cx = Context::from_waker(&noop_waker); - // SAFETY: This is safe because this method consumes the future, so `poll` is - // only going to be called once. Thus it doesn't matter to us if the - // future is `Unpin` or not. - let pinned = unsafe { Pin::new_unchecked(&mut self) }; - - match pinned.poll(&mut cx) { + let this = self; + pin_mut!(this); + match this.poll(&mut cx) { Poll::Ready(x) => Some(x), _ => None, } diff --git a/futures-util/src/future/maybe_done.rs b/futures-util/src/future/maybe_done.rs index 71cb6fa320..97b80c8e65 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -1,15 +1,14 @@ //! Definition of the MaybeDone combinator -use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_project::{pin_project, project}; +use pin_project::{pin_project, project, project_replace}; /// A future that may have completed. /// /// This is created by the [`maybe_done()`] function. -#[pin_project] +#[pin_project(Replace)] #[derive(Debug)] pub enum MaybeDone { /// A not-yet-completed future @@ -59,24 +58,17 @@ impl MaybeDone { /// Attempt to take the output of a `MaybeDone` without driving it /// towards completion. + #[project_replace] #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { - // Safety: we return immediately unless we are in the `Done` - // state, which does not have any pinning guarantees to uphold. - // - // Hopefully `pin_project` will support this safely soon: - // https://github.com/taiki-e/pin-project/issues/184 - unsafe { - let this = self.get_unchecked_mut(); - match this { - MaybeDone::Done(_) => {}, - MaybeDone::Future(_) | MaybeDone::Gone => return None, - }; - if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) { - Some(output) - } else { - unreachable!() - } + match &*self { + MaybeDone::Done(_) => {}, + MaybeDone::Future(_) | MaybeDone::Gone => return None, + } + #[project_replace] + match self.project_replace(MaybeDone::Gone) { + MaybeDone::Done(output) => Some(output), + _ => unreachable!() } } } diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs index a249c5ce33..7f55e77433 100644 --- a/futures-util/src/future/try_maybe_done.rs +++ b/futures-util/src/future/try_maybe_done.rs @@ -1,15 +1,14 @@ //! Definition of the TryMaybeDone combinator -use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_project::{pin_project, project}; +use pin_project::{pin_project, project, project_replace}; /// A future that may have completed with an error. /// /// This is created by the [`try_maybe_done()`] function. -#[pin_project] +#[pin_project(Replace)] #[derive(Debug)] pub enum TryMaybeDone { /// A not-yet-completed future @@ -44,24 +43,17 @@ impl TryMaybeDone { /// Attempt to take the output of a `TryMaybeDone` without driving it /// towards completion. + #[project_replace] #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { - // Safety: we return immediately unless we are in the `Done` - // state, which does not have any pinning guarantees to uphold. - // - // Hopefully `pin_project` will support this safely soon: - // https://github.com/taiki-e/pin-project/issues/184 - unsafe { - let this = self.get_unchecked_mut(); - match this { - TryMaybeDone::Done(_) => {}, - TryMaybeDone::Future(_) | TryMaybeDone::Gone => return None, - }; - if let TryMaybeDone::Done(output) = mem::replace(this, TryMaybeDone::Gone) { - Some(output) - } else { - unreachable!() - } + match &*self { + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Future(_) | TryMaybeDone::Gone => return None, + } + #[project_replace] + match self.project_replace(TryMaybeDone::Gone) { + TryMaybeDone::Done(output) => Some(output), + _ => unreachable!() } } } diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 4e85854a41..04bfcdff4d 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -51,10 +51,8 @@ where /// underlying readers as doing so may corrupt the internal state of this /// `Chain`. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut T>, Pin<&mut U>) { - unsafe { - let Self { first, second, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(first), Pin::new_unchecked(second)) - } + let this = self.project(); + (this.first, this.second) } /// Consumes the `Chain`, returning the wrapped readers. diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 68b954ac81..83c798d4eb 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -7,6 +7,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +// It cannot be included in the published code because this lints have false positives in the minimum required version. +#![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] // The solution for this lint is not available on 1.39 which is the current minimum supported version. @@ -183,7 +185,7 @@ macro_rules! delegate_async_buf_read { ) -> core::task::Poll> { self.project().$field.poll_fill_buf(cx) } - + fn consume(self: core::pin::Pin<&mut Self>, amt: usize) { self.project().$field.consume(amt) } diff --git a/futures-util/src/sink/fanout.rs b/futures-util/src/sink/fanout.rs index 7066e216fb..560013167e 100644 --- a/futures-util/src/sink/fanout.rs +++ b/futures-util/src/sink/fanout.rs @@ -33,11 +33,9 @@ impl Fanout { } /// Get a pinned mutable reference to the inner sinks. - #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut Si1>, Pin<&mut Si2>) { - #[project] - let Fanout { sink1, sink2 } = self.project(); - (sink1, sink2) + let this = self.project(); + (this.sink1, this.sink2) } /// Consumes this combinator, returning the underlying sinks. diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index 36503e40d5..65a4c95b5a 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -58,11 +58,9 @@ impl Select { /// /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. - #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - #[project] - let Select { stream1, stream2, .. } = self.project(); - (stream1.get_pin_mut(), stream2.get_pin_mut()) + let this = self.project(); + (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) } /// Consumes this combinator, returning the underlying streams. diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index 9d848ad717..fb96ec057f 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -37,6 +37,7 @@ where } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Filter where St: Stream, F: for<'a> FnMut1<&'a St::Item, Output=Fut>, @@ -64,6 +65,7 @@ impl FusedStream for Filter } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Stream for Filter where St: Stream, F: for<'a> FnMut1<&'a St::Item, Output=Fut>, diff --git a/futures-util/src/stream/stream/into_future.rs b/futures-util/src/stream/stream/into_future.rs index 0d49384bea..8aa2b1e8fc 100644 --- a/futures-util/src/stream/stream/into_future.rs +++ b/futures-util/src/stream/stream/into_future.rs @@ -52,7 +52,7 @@ impl StreamFuture { /// in order to return it to the caller of `Future::poll` if the stream yielded /// an element. pub fn get_pin_mut(self: Pin<&mut Self>) -> Option> { - Pin::get_mut(self).stream.as_mut().map(Pin::new) + self.get_mut().stream.as_mut().map(Pin::new) } /// Consumes this combinator, returning the underlying stream. diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index 6c148fb94a..51ee0cf773 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -49,10 +49,8 @@ impl Zip { /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - unsafe { - let Self { stream1, stream2, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(stream1).get_pin_mut(), Pin::new_unchecked(stream2).get_pin_mut()) - } + let this = self.project(); + (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) } /// Consumes this combinator, returning the underlying streams. diff --git a/futures/Cargo.toml b/futures/Cargo.toml index c7288edbcc..200313f9bf 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -33,6 +33,7 @@ futures-executor = { path = "../futures-executor", version = "0.3.5", features = futures-test = { path = "../futures-test", version = "0.3.5" } tokio = "0.1.11" assert_matches = "1.3.0" +pin-project = "0.4.10" [features] default = ["std", "async-await", "executor"] diff --git a/futures/tests/eager_drop.rs b/futures/tests/eager_drop.rs index bfb60a7f68..2472c59b76 100644 --- a/futures/tests/eager_drop.rs +++ b/futures/tests/eager_drop.rs @@ -43,25 +43,23 @@ fn map_err() { } mod channelled { - use pin_utils::unsafe_pinned; use futures::future::Future; - use std::pin::Pin; use futures::task::{Context,Poll}; + use pin_project::pin_project; + use std::pin::Pin; + #[pin_project] struct FutureData { _data: T, + #[pin] future: F, } - impl FutureData { - unsafe_pinned!(future: F); - } - impl Future for FutureData { type Output = F::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future().poll(cx) + self.project().future.poll(cx) } } diff --git a/futures/tests/split.rs b/futures/tests/split.rs index 140cf3c57d..2cbb888c9b 100644 --- a/futures/tests/split.rs +++ b/futures/tests/split.rs @@ -5,17 +5,15 @@ fn test_split() { use futures::sink::{Sink, SinkExt}; use futures::stream::{self, Stream, StreamExt}; use futures::task::{Context, Poll}; - use pin_utils::unsafe_pinned; + use pin_project::pin_project; use std::pin::Pin; + #[pin_project] struct Join { + #[pin] stream: T, - sink: U - } - - impl Join { - unsafe_pinned!(stream: T); - unsafe_pinned!(sink: U); + #[pin] + sink: U, } impl Stream for Join { @@ -25,7 +23,7 @@ fn test_split() { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.stream().poll_next(cx) + self.project().stream.poll_next(cx) } } @@ -36,28 +34,28 @@ fn test_split() { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.sink().poll_ready(cx) + self.project().sink.poll_ready(cx) } fn start_send( self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - self.sink().start_send(item) + self.project().sink.start_send(item) } fn poll_flush( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.sink().poll_flush(cx) + self.project().sink.poll_flush(cx) } fn poll_close( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.sink().poll_close(cx) + self.project().sink.poll_close(cx) } } From a82e24b6f9262cd434c18baf4f758455f5600d31 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 10 May 2020 03:56:13 +0900 Subject: [PATCH 81/83] Use pin_project(!Unpin), instead of using the #[pin] for PhantomPinned --- futures-test/Cargo.toml | 2 +- futures-test/src/future/assert_unmoved.rs | 6 +----- futures-util/Cargo.toml | 2 +- futures/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0dbd3f1d40..193f90b839 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -19,7 +19,7 @@ futures-util = { version = "0.3.5", path = "../futures-util", default-features = futures-executor = { version = "0.3.5", path = "../futures-executor", default-features = false } pin-utils = { version = "0.1.0", default-features = false } once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } -pin-project = "0.4.10" +pin-project = "0.4.14" [dev-dependencies] futures = { version = "0.3.5", path = "../futures", default-features = false, features = ["std", "executor"] } diff --git a/futures-test/src/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs index 114bfb3f33..531fd4d818 100644 --- a/futures-test/src/future/assert_unmoved.rs +++ b/futures-test/src/future/assert_unmoved.rs @@ -1,7 +1,6 @@ use futures_core::future::Future; use futures_core::task::{Context, Poll}; use pin_project::{pin_project, pinned_drop}; -use std::marker::PhantomPinned; use std::pin::Pin; use std::ptr; use std::thread::panicking; @@ -9,15 +8,13 @@ use std::thread::panicking; /// Combinator for the /// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved) /// method. -#[pin_project(PinnedDrop)] +#[pin_project(PinnedDrop, !Unpin)] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct AssertUnmoved { #[pin] future: Fut, this_ptr: *const AssertUnmoved, - #[pin] - _pinned: PhantomPinned, } // Safety: having a raw pointer in a struct makes it `!Send`, however the @@ -30,7 +27,6 @@ impl AssertUnmoved { Self { future, this_ptr: ptr::null(), - _pinned: PhantomPinned, } } } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 937323b85b..8061962735 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -46,7 +46,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0" -pin-project = "0.4.10" +pin-project = "0.4.14" [dev-dependencies] futures = { path = "../futures", version = "0.3.5", features = ["async-await", "thread-pool"] } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 200313f9bf..03b8501a7c 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -33,7 +33,7 @@ futures-executor = { path = "../futures-executor", version = "0.3.5", features = futures-test = { path = "../futures-test", version = "0.3.5" } tokio = "0.1.11" assert_matches = "1.3.0" -pin-project = "0.4.10" +pin-project = "0.4.14" [features] default = ["std", "async-await", "executor"] From bc91b6d24f512b1383230bf5b2c6a49907583c5c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 11 May 2020 01:50:37 +0900 Subject: [PATCH 82/83] Update pin-project to 0.4.15 --- futures-test/Cargo.toml | 2 +- futures-util/Cargo.toml | 2 +- futures-util/src/future/future/map.rs | 3 +-- futures/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 193f90b839..b7f9ab08d1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -19,7 +19,7 @@ futures-util = { version = "0.3.5", path = "../futures-util", default-features = futures-executor = { version = "0.3.5", path = "../futures-executor", default-features = false } pin-utils = { version = "0.1.0", default-features = false } once_cell = { version = "1.3.1", default-features = false, features = ["std"], optional = true } -pin-project = "0.4.14" +pin-project = "0.4.15" [dev-dependencies] futures = { version = "0.3.5", path = "../futures", default-features = false, features = ["std", "executor"] } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 8061962735..be9cb8f399 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -46,7 +46,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0" -pin-project = "0.4.14" +pin-project = "0.4.15" [dev-dependencies] futures = { path = "../futures", version = "0.3.5", features = ["async-await", "thread-pool"] } diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index e1eeab9be5..9c3877aca9 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,7 +1,7 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_project::{pin_project, project, project_replace}; +use pin_project::{pin_project, project}; use crate::fns::FnOnce1; @@ -44,7 +44,6 @@ impl Future for Map type Output = T; #[project] - #[project_replace] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { #[project] match self.as_mut().project() { diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 03b8501a7c..627613b1c6 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -33,7 +33,7 @@ futures-executor = { path = "../futures-executor", version = "0.3.5", features = futures-test = { path = "../futures-test", version = "0.3.5" } tokio = "0.1.11" assert_matches = "1.3.0" -pin-project = "0.4.14" +pin-project = "0.4.15" [features] default = ["std", "async-await", "executor"] From 8a95e9a84896fb3ce944801c12115b3784cf40ac Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 9 May 2020 11:12:24 +0900 Subject: [PATCH 83/83] Clarify stream doc's "bad behavior" --- futures-core/src/stream.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs index 1f0f9a6e45..4a13e3bd7d 100644 --- a/futures-core/src/stream.rs +++ b/futures-core/src/stream.rs @@ -50,11 +50,19 @@ pub trait Stream { /// /// # Panics /// - /// Once a stream is finished, i.e. `Ready(None)` has been returned, further - /// calls to `poll_next` may result in a panic or other "bad behavior". If - /// this is difficult to guard against then the `fuse` adapter can be used + /// Once a stream has finished (returned `Ready(None)` from `poll_next`), calling its + /// `poll_next` method again may panic, block forever, or cause other kinds of + /// problems; the `Stream` trait places no requirements on the effects of + /// such a call. However, as the `poll_next` 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 stream's state. + /// + /// If this is difficult to guard against then the [`fuse`] adapter can be used /// to ensure that `poll_next` always returns `Ready(None)` in subsequent /// calls. + /// + /// [`fuse`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.fuse fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>,