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 diff --git a/CHANGELOG.md b/CHANGELOG.md index b93b0ae557..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) @@ -239,7 +256,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/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/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-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index d9f21d3083..5f76cee4b9 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -82,22 +82,23 @@ struct Inner { /// /// ``` /// use futures::channel::oneshot; -/// use futures::future::FutureExt; -/// use std::thread; +/// use std::{thread, time::Duration}; /// /// let (sender, receiver) = oneshot::channel::(); /// -/// # let t = /// thread::spawn(|| { -/// let future = receiver.map(|i| { -/// println!("got: {:?}", i); -/// }); -/// // ... -/// # return future; +/// println!("THREAD: sleeping zzz..."); +/// thread::sleep(Duration::from_millis(1000)); +/// println!("THREAD: i'm awake! sending."); +/// sender.send(3).unwrap(); /// }); /// -/// sender.send(3).unwrap(); -/// # futures::executor::block_on(t.join().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()); @@ -358,6 +359,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 +385,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(); } 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-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<'_>, 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-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 { 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/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..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`. @@ -23,24 +23,24 @@ 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) } /// 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-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 a387026472..7967674247 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,19 +1,19 @@ [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. """ [features] default = ["std"] -std = ["alloc"] +std = ["alloc", "once_cell"] alloc = [] # Unstable features @@ -23,9 +23,10 @@ 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" } +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-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..b7f9ab08d1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,30 +1,32 @@ [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 } -pin-utils = { version = "0.1.0-alpha.4", 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 } +pin-project = "0.4.15" [dev-dependencies] -futures = { version = "0.3.4", path = "../futures", default-features = false, features = ["std"] } +futures = { version = "0.3.5", 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/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs index 74f92c626b..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_utils::{unsafe_pinned, unsafe_unpinned}; -use std::marker::PhantomPinned; +use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; use std::ptr; use std::thread::panicking; @@ -9,23 +8,25 @@ use std::thread::panicking; /// Combinator for the /// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved) /// method. +#[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, - _pinned: PhantomPinned, } -impl AssertUnmoved { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(this_ptr: *const Self); +// 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 { pub(super) fn new(future: Fut) -> Self { Self { future, this_ptr: ptr::null(), - _pinned: PhantomPinned, } } } @@ -33,23 +34,21 @@ 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 - *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() { @@ -69,6 +68,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. 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-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-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() }) } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index e7d532d95c..be9cb8f399 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. """ @@ -30,25 +30,27 @@ 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 } -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 } 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" +pin-project = "0.4.15" [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/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/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-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 6615043e5e..5116d03d0a 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -380,7 +380,6 @@ mod io { /// [`AsyncRead`](futures_io::AsyncRead). /// /// ``` - /// # #![allow(incomplete_features)] /// # futures::executor::block_on(async { /// use futures::io::AsyncReadExt; /// use futures_util::compat::AsyncRead01CompatExt; @@ -390,7 +389,8 @@ mod io { /// } /// /// let input = b"Hello World!"; - /// let mut reader = create_async_read_cursor(input); + /// 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(); diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs new file mode 100644 index 0000000000..6908bff5f8 --- /dev/null +++ b/futures-util/src/fns.rs @@ -0,0 +1,357 @@ +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); + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +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 + } +} +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +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 + } +} +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +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..f59464c5be 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 => panic!("Flatten polled after completion"), + } + }) } } -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..9c3877aca9 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,49 +1,61 @@ 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}; -/// Future for the [`map`](super::FutureExt::map) method. +use crate::fns::FnOnce1; + +/// Internal Map future +#[pin_project(Replace)] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Map { - future: Fut, - f: Option, +pub enum Map { + Incomplete { + #[pin] + future: Fut, + f: F, + }, + Complete, } impl Map { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - /// Creates a new Map. - pub(super) fn new(future: Fut, f: F) -> Map { - Map { future, f: Some(f) } + pub(crate) fn new(future: Fut, f: F) -> Map { + Map::Incomplete { future, 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() } + fn is_terminated(&self) -> bool { + match self { + Map::Incomplete { .. } => false, + Map::Complete => true, + } + } } impl Future for Map where Fut: Future, - F: FnOnce(Fut::Output) -> T, + F: FnOnce1, { type Output = T; + #[project] 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] + 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 e58cafc8c0..fcefc4f064 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,83 @@ use futures_core::{ stream::Stream, task::{Context, Poll}, }; +use crate::never::Never; +use crate::fns::{OkFn, ok_fn, IntoFn, into_fn, InspectFn, inspect_fn}; +use pin_utils::pin_mut; // 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; -mod unit_error; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unit_error::UnitError; +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Flatten( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + 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 + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); -mod never_error; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::never_error::NeverError; +pub use fuse::Fuse; + +delegate_all!( + /// Future for the [`map`](super::FutureExt::map) 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 + 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 + New[|x: Fut| Map::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`then`](FutureExt::then) method. + Then( + flatten::Flatten, Fut2> + ): 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 + 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 + 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 + New[|x: Fut| Map::new(x, ok_fn())] +); #[cfg(feature = "std")] mod catch_unwind; @@ -73,11 +113,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 +148,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`. /// @@ -538,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/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 06a6b0d1e0..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 @@ -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 { @@ -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/shared.rs b/futures-util/src/future/future/shared.rs index 65fad2d34f..79ad5c37a4 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,19 +54,20 @@ 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; -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(); @@ -125,27 +126,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 +178,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 { @@ -198,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); @@ -229,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), @@ -237,33 +234,24 @@ 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, } }; unsafe { - *inner.future_or_output.get() = - FutureOrOutput::Output(output); + *inner.future_or_output.get() = FutureOrOutput::Output(output); } inner.notifier.state.store(COMPLETE, SeqCst); @@ -316,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-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/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/maybe_done.rs b/futures-util/src/future/maybe_done.rs index f16f889781..97b80c8e65 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -1,17 +1,18 @@ //! 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, project_replace}; /// A future that may have completed. /// /// This is created by the [`maybe_done()`] function. +#[pin_project(Replace)] #[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 +20,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,32 +46,29 @@ 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, } } /// 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 { - 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!() } } } @@ -90,15 +85,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/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")) } } 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, /// { 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..bd1ab33d1f 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -14,65 +14,121 @@ 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 + 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 + 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 + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + where Fut: TryFuture +); #[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 + 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 + 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 + 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 + New[|x: Fut| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`ok_into`](TryFutureExt::ok_into) method. + OkInto( + MapOk> + ): 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 + 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 + 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 + 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 + 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 + 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 + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] +); impl TryFutureExt for Fut {} @@ -161,7 +217,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 +258,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 +305,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 +335,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 +390,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 +435,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 +461,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 +487,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 @@ -484,7 +562,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 662bdf2d26..0000000000 --- a/futures-util/src/future/try_future/try_chain.rs +++ /dev/null @@ -1,108 +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 { - use std::pin::Pin; - use std::task::Poll; - - use futures_test::task::noop_context; - - 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)), ()); - 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..661d3adf88 --- /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 => panic!("TryFlatten polled after completion"), + } + }) + } +} + +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..fbb413daac --- /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 => panic!("TryFlattenErr polled after completion"), + } + }) + } +} 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..7f55e77433 --- /dev/null +++ b/futures-util/src/future/try_maybe_done.rs @@ -0,0 +1,91 @@ +//! Definition of the TryMaybeDone combinator + +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::task::{Context, Poll}; +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(Replace)] +#[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. + #[project_replace] + #[inline] + pub fn take_output(self: Pin<&mut Self>) -> Option { + match &*self { + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Future(_) | TryMaybeDone::Gone => return None, + } + #[project_replace] + match self.project_replace(TryMaybeDone::Gone) { + TryMaybeDone::Done(output) => Some(output), + _ => 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..04bfcdff4d 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, @@ -59,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. @@ -90,34 +80,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 +134,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 +150,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/mod.rs b/futures-util/src/io/mod.rs index 43f183f424..29b6418ae6 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; @@ -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/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/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/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/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 21645b148d..83c798d4eb 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)] @@ -17,7 +18,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"); @@ -51,6 +52,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"))] @@ -62,35 +71,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}; @@ -116,6 +328,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/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 374ea5032b..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; @@ -88,6 +87,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 +99,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 +118,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 @@ -149,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, @@ -183,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() @@ -198,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") @@ -210,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/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..560013167e 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 } } @@ -34,10 +34,8 @@ impl Fanout { /// Get a pinned mutable reference to the inner sinks. 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)) - } + let this = self.project(); + (this.sink1, this.sink2) } /// Consumes this combinator, returning the underlying sinks. @@ -65,41 +63,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/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) + } } 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 a30cbaa40f..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 }) } } @@ -203,3 +202,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 5d93a646a0..2b7d70455f 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, @@ -622,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); + } + } +} diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 3fb26d5762..10d5c01961 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, TakeUntil, Then, Zip, }; #[cfg(feature = "std")] @@ -24,12 +24,15 @@ 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; #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] -pub use self::stream::{BufferUnordered, FlatMapUnordered, Buffered, ForEachConcurrent}; +pub use self::stream::{BufferUnordered, FlatMapUnordered, FlattenUnordered, Buffered, ForEachConcurrent}; #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "sink")] 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..65a4c95b5a 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. @@ -57,10 +59,8 @@ impl Select { /// 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. @@ -87,14 +87,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/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) + } + } +} 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..fb96ec057f 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, @@ -39,16 +37,12 @@ where } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 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 +52,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 @@ -101,36 +65,36 @@ impl FusedStream for Filter } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 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() { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { + 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/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/flat_map_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs similarity index 59% rename from futures-util/src/stream/stream/flat_map_unordered.rs rename to futures-util/src/stream/stream/flatten_unordered.rs index 99eb4addb1..83d3feb0cf 100644 --- a/futures-util/src/stream/stream/flat_map_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -1,4 +1,4 @@ -use crate::stream::{Map, FuturesUnordered}; +use crate::stream::FuturesUnordered; use core::fmt; use core::num::NonZeroUsize; use core::pin::Pin; @@ -11,8 +11,8 @@ use futures_core::task::{Context, Poll, Waker}; #[cfg(feature = "sink")] use futures_sink::Sink; use futures_task::{waker, ArcWake}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; use core::cell::UnsafeCell; +use pin_project::pin_project; /// Indicates that there is nothing to poll and stream isn't being polled at /// the moment. @@ -30,6 +30,9 @@ const NEED_TO_POLL: u8 = NEED_TO_POLL_FUTURES | NEED_TO_POLL_STREAM; /// Indicates that current stream is polled at the moment. const POLLING: u8 = 0b100; +// Indicates that we already called one of wakers. +const WOKEN: u8 = 0b1000; + /// State which used to determine what needs to be polled, and are we polling /// stream at the moment or not. #[derive(Clone, Debug)] @@ -39,8 +42,8 @@ struct SharedPollState { impl SharedPollState { /// Constructs new `SharedPollState` with given state. - fn new(state: u8) -> Self { - Self { + fn new(state: u8) -> SharedPollState { + SharedPollState { state: Arc::new(AtomicU8::new(state)), } } @@ -53,14 +56,14 @@ impl SharedPollState { /// Performs bitwise or with `to_poll` and given state, returning /// previous state. fn set_or(&self, to_poll: u8) -> u8 { - self.state.fetch_or(to_poll, Ordering::Relaxed) + self.state.fetch_or(to_poll, Ordering::AcqRel) } /// Performs bitwise or with `to_poll` and current state, stores result /// with non-`POLLING` state, and returns disjunction result. fn end_polling(&self, to_poll: u8) -> u8 { let to_poll = to_poll | self.state.load(Ordering::Acquire); - self.state.store(to_poll & !POLLING, Ordering::Release); + self.state.store(to_poll & !POLLING & !WOKEN, Ordering::Release); to_poll } } @@ -77,12 +80,28 @@ unsafe impl Send for PollWaker {} unsafe impl Sync for PollWaker {} +impl PollWaker { + /// Replaces given waker's inner_waker for polling stream/futures which will + /// update poll state on `wake_by_ref` call. Use only if you need several + /// contexts. + /// + /// ## Safety + /// + /// This function will modify waker's `inner_waker` via `UnsafeCell`, so + /// it should be used only during `POLLING` phase. + unsafe fn replace_waker(self_arc: &mut Arc, ctx: &Context<'_>) -> Waker { + *self_arc.inner_waker.get() = ctx.waker().clone().into(); + waker(self_arc.clone()) + } +} + impl ArcWake for PollWaker { fn wake_by_ref(self_arc: &Arc) { let poll_state_value = self_arc.poll_state.set_or(self_arc.need_to_poll); // Only call waker if stream isn't being polled because it will be called // at the end of polling if state was changed. - if poll_state_value & POLLING == NONE { + if poll_state_value & (POLLING | WOKEN) == NONE { + self_arc.poll_state.set_or(WOKEN); if let Some(Some(inner_waker)) = unsafe { self_arc.inner_waker.get().as_ref() } { inner_waker.wake_by_ref(); } @@ -95,14 +114,14 @@ impl ArcWake for PollWaker { /// case of `Poll::Ready(Some(...))` or `None` in case of `Poll::Ready(None)`. /// If `poll_next` will return `Poll::Pending`, it will be forwared to /// the future, and current task will be notified by waker. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] struct PollStreamFut { + #[pin] stream: Option, } impl PollStreamFut { - unsafe_pinned!(stream: Option); - /// Constructs new `PollStreamFut` using given `stream`. fn new(stream: impl Into>) -> Self { Self { @@ -111,13 +130,13 @@ impl PollStreamFut { } } -impl Unpin for PollStreamFut {} - impl Future for PollStreamFut { type Output = Option<(St::Item, PollStreamFut)>; - fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - let item = if let Some(stream) = self.as_mut().stream().as_pin_mut() { + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + let mut stream = self.project().stream; + + let item = if let Some(stream) = stream.as_mut().as_pin_mut() { ready!(stream.poll_next(ctx)) } else { None @@ -126,7 +145,7 @@ impl Future for PollStreamFut { Poll::Ready(item.map(|item| { ( item, - PollStreamFut::new(unsafe { self.get_unchecked_mut().stream.take() }), + PollStreamFut::new(unsafe { stream.get_unchecked_mut().take() }), ) })) } @@ -134,33 +153,27 @@ impl Future for PollStreamFut { /// Stream for the [`flat_map_unordered`](super::StreamExt::flat_map_unordered) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] -pub struct FlatMapUnordered U> { - poll_state: SharedPollState, +pub struct FlattenUnordered { + #[pin] futures: FuturesUnordered>, - stream: Map, + #[pin] + stream: St, + poll_state: SharedPollState, limit: Option, is_stream_done: bool, futures_waker: Arc, stream_waker: Arc } -impl Unpin for FlatMapUnordered -where - St: Stream + Unpin, - U: Stream + Unpin, - F: FnMut(St::Item) -> U, -{ -} - -impl fmt::Debug for FlatMapUnordered +impl fmt::Debug for FlattenUnordered where St: Stream + fmt::Debug, - U: Stream + fmt::Debug, - F: FnMut(St::Item) -> U, + St::Item: Stream + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlatMapUnordered") + f.debug_struct("FlattenUnordered") .field("poll_state", &self.poll_state) .field("futures", &self.futures) .field("limit", &self.limit) @@ -170,27 +183,19 @@ where } } -impl FlatMapUnordered +impl FlattenUnordered where St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, + St::Item: Stream, { - unsafe_pinned!(futures: FuturesUnordered>); - unsafe_pinned!(stream: Map); - unsafe_unpinned!(is_stream_done: bool); - unsafe_unpinned!(limit: Option); - unsafe_unpinned!(poll_state: SharedPollState); - unsafe_unpinned!(futures_waker: Arc); - unsafe_unpinned!(stream_waker: Arc); - - pub(super) fn new(stream: St, limit: Option, f: F) -> FlatMapUnordered { + pub(super) fn new(stream: St, limit: Option) -> FlattenUnordered { // Because to create first future, it needs to get inner // stream from `stream` let poll_state = SharedPollState::new(NEED_TO_POLL_STREAM); - FlatMapUnordered { + + FlattenUnordered { futures: FuturesUnordered::new(), - stream: Map::new(stream, f), + stream, is_stream_done: false, limit: limit.and_then(NonZeroUsize::new), futures_waker: Arc::new(PollWaker { @@ -207,111 +212,56 @@ 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() - } - - /// Creates special waker for polling stream which will set poll state - /// to poll `stream` on `wake_by_ref` call. Use only if you need several - /// contexts. - /// - /// ## Safety - /// - /// This function will modify current `stream_waker`'s `inner_waker` - /// via `UnsafeCell`, so it should be used only in `POLLING` phase. - unsafe fn create_poll_stream_waker(mut self: Pin<&mut Self>, ctx: &Context<'_>) -> Waker { - *self.as_mut().stream_waker.inner_waker.get() = ctx.waker().clone().into(); - waker(self.stream_waker.clone()) - } - - /// Creates special waker for polling futures which willset poll state - /// to poll `futures` on `wake_by_ref` call. Use only if you need several - /// contexts. - /// - /// ## Safety - /// - /// This function will modify current `futures_waker`'s `inner_waker` - /// via `UnsafeCell`, so it should be used only in `POLLING` phase. - unsafe fn create_poll_futures_waker(mut self: Pin<&mut Self>, ctx: &Context<'_>) -> Waker { - *self.as_mut().futures_waker.inner_waker.get() = ctx.waker().clone().into(); - waker(self.futures_waker.clone()) - } - /// Checks if current `futures` size is less than optional limit. - fn not_exceeded_limit(&self) -> bool { + fn is_exceeded_limit(&self) -> bool { self.limit - .map(|limit| self.futures.len() < limit.get()) - .unwrap_or(true) + .map(|limit| self.futures.len() >= limit.get()) + .unwrap_or(false) } + + delegate_access_inner!(stream, St, ()); } -impl FusedStream for FlatMapUnordered +impl FusedStream for FlattenUnordered where St: FusedStream, - U: FusedStream, - F: FnMut(St::Item) -> U, + St::Item: FusedStream, { fn is_terminated(&self) -> bool { self.futures.is_empty() && self.stream.is_terminated() } } -impl Stream for FlatMapUnordered +impl Stream for FlattenUnordered where St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, + St::Item: Stream, { - type Item = U::Item; + type Item = ::Item; - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - let mut poll_state_value = self.as_mut().poll_state().begin_polling(); + fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { let mut next_item = None; let mut need_to_poll_next = NONE; - let mut stream_will_be_woken_or_polled_later = !self.not_exceeded_limit(); + let mut stream_will_be_woken = self.is_exceeded_limit(); let mut futures_will_be_woken = false; - let mut polling_with_two_wakers = poll_state_value & NEED_TO_POLL == NEED_TO_POLL && !stream_will_be_woken_or_polled_later; + + let mut this = self.project(); + + let mut poll_state_value = this.poll_state.begin_polling(); + let mut polling_with_two_wakers = poll_state_value & NEED_TO_POLL == NEED_TO_POLL && !stream_will_be_woken; if poll_state_value & NEED_TO_POLL_STREAM != NONE { - if !stream_will_be_woken_or_polled_later { + if !stream_will_be_woken { match if polling_with_two_wakers { // Safety: now state is `POLLING`. - let waker = unsafe { self.as_mut().create_poll_stream_waker(ctx) }; + let waker = unsafe { PollWaker::replace_waker(this.stream_waker, ctx) }; let mut ctx = Context::from_waker(&waker); - self.as_mut().stream().poll_next(&mut ctx) + this.stream.as_mut().poll_next(&mut ctx) } else { - self.as_mut().stream().poll_next(ctx) + this.stream.as_mut().poll_next(ctx) } { Poll::Ready(Some(inner_stream)) => { - self.as_mut().futures().push(PollStreamFut::new(inner_stream)); + this.futures.as_mut().push(PollStreamFut::new(inner_stream)); need_to_poll_next |= NEED_TO_POLL_STREAM; // Polling futures in current iteration with the same context // is ok because we already received `Poll::Ready` from @@ -320,14 +270,14 @@ where polling_with_two_wakers = false; } Poll::Ready(None) => { - *self.as_mut().is_stream_done() = true; + *this.is_stream_done = true; // Polling futures in current iteration with the same context // is ok because we already received `Poll::Ready` from // stream polling_with_two_wakers = false; } Poll::Pending => { - stream_will_be_woken_or_polled_later = true; + stream_will_be_woken = true; if !polling_with_two_wakers { need_to_poll_next |= NEED_TO_POLL_STREAM; } @@ -341,14 +291,14 @@ where if poll_state_value & NEED_TO_POLL_FUTURES != NONE { match if polling_with_two_wakers { // Safety: now state is `POLLING`. - let waker = unsafe { self.as_mut().create_poll_futures_waker(ctx) }; + let waker = unsafe { PollWaker::replace_waker(this.futures_waker, ctx) }; let mut ctx = Context::from_waker(&waker); - self.as_mut().futures().poll_next(&mut ctx) + this.futures.as_mut().poll_next(&mut ctx) } else { - self.as_mut().futures().poll_next(ctx) + this.futures.as_mut().poll_next(ctx) } { Poll::Ready(Some(Some((item, next_item_fut)))) => { - self.as_mut().futures().push(next_item_fut); + this.futures.as_mut().push(next_item_fut); next_item = Some(item); need_to_poll_next |= NEED_TO_POLL_FUTURES; } @@ -361,23 +311,25 @@ where need_to_poll_next |= NEED_TO_POLL_FUTURES; } } - _ => { + Poll::Ready(None) => { need_to_poll_next &= !NEED_TO_POLL_FUTURES; } } } - let poll_state_value = self.as_mut().poll_state().end_polling(need_to_poll_next); + let poll_state_value = this.poll_state.end_polling(need_to_poll_next); + + let is_done = this.futures.is_empty() && *this.is_stream_done; - if poll_state_value & NEED_TO_POLL != NONE + if !is_done && poll_state_value & WOKEN == NONE && poll_state_value & NEED_TO_POLL != NONE && (polling_with_two_wakers || poll_state_value & NEED_TO_POLL_FUTURES != NONE && !futures_will_be_woken - || poll_state_value & NEED_TO_POLL_STREAM != NONE && !stream_will_be_woken_or_polled_later) + || poll_state_value & NEED_TO_POLL_STREAM != NONE && !stream_will_be_woken) { ctx.waker().wake_by_ref(); } - if next_item.is_some() || self.futures.is_empty() && self.is_stream_done { + if next_item.is_some() || is_done { Poll::Ready(next_item) } else { Poll::Pending @@ -387,11 +339,10 @@ where // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for FlatMapUnordered +impl Sink for FlattenUnordered where S: Stream + Sink, - U: Stream, - F: FnMut(S::Item) -> U, + S::Item: Stream, { type Error = S::Error; 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..8aa2b1e8fc 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() + self.get_mut().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 7176364087..ed36c21e9d 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::{inspect_fn, InspectFn}; + mod chain; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chain::Chain; @@ -44,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 [`flatten`](StreamExt::flatten) 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 @@ -53,9 +61,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 + FusedFuture + 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,15 +83,24 @@ 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 + 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; +delegate_all!( + /// Stream for the [`flat_map`](StreamExt::flat_map) method. + FlatMap( + flatten::Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| flatten::Flatten::new(Map::new(x, f))] +); + mod next; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::next::Next; @@ -106,6 +129,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; @@ -120,6 +147,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; @@ -132,10 +165,25 @@ cfg_target_has_atomic! { pub use self::buffer_unordered::BufferUnordered; #[cfg(feature = "alloc")] - mod flat_map_unordered; + mod flatten_unordered; + #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::flat_map_unordered::FlatMapUnordered; + delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + FlattenUnordered( + flatten_unordered::FlattenUnordered + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, limit: Option| flatten_unordered::FlattenUnordered::new(x, limit)] + where St: Stream, St::Item: Stream + ); + + #[cfg(feature = "alloc")] + delegate_all!( + /// Stream for the [`flat_map`](StreamExt::flat_map) method. + FlatMapUnordered( + FlattenUnordered> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, limit: Option, f: F| FlattenUnordered::new(Map::new(x, f), limit)] + where St: Stream, U: Stream, F: FnMut(St::Item) -> U + ); #[cfg(feature = "alloc")] mod buffered; @@ -550,6 +598,84 @@ pub trait StreamExt: Stream { Flatten::new(self) } + /// Flattens a stream of streams into just one continuous stream. Polls + /// inner streams concurrently. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::mpsc; + /// use futures::stream::StreamExt; + /// use std::thread; + /// + /// let (tx1, rx1) = mpsc::unbounded(); + /// let (tx2, rx2) = mpsc::unbounded(); + /// let (tx3, rx3) = mpsc::unbounded(); + /// + /// thread::spawn(move || { + /// tx1.unbounded_send(1).unwrap(); + /// tx1.unbounded_send(2).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx2.unbounded_send(3).unwrap(); + /// tx2.unbounded_send(4).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx3.unbounded_send(rx1).unwrap(); + /// tx3.unbounded_send(rx2).unwrap(); + /// }); + /// + /// let mut output = rx3.flatten_unordered(None).collect::>().await; + /// output.sort(); + /// + /// assert_eq!(output, vec![1, 2, 3, 4]); + /// # }); + /// ``` + #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(feature = "alloc")] + fn flatten_unordered(self, limit: impl Into>) -> FlattenUnordered + where + Self::Item: Stream, + Self: Sized, + { + FlattenUnordered::new(self, limit.into()) + } + + /// 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 produces 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) + } + /// Maps a stream like [`StreamExt::map`] but flattens nested `Stream`s /// and polls them concurrently, yielding items in any order, as they made /// available. @@ -559,7 +685,7 @@ pub trait StreamExt: Stream { /// have to use something like `for_each_concurrent` and merge values /// by hand. This combinator provides ability to collect all values /// from concurrently polled streams into one stream. - /// + /// /// The first argument is an optional limit on the number of concurrently /// polled streams. If this limit is not `None`, no more than `limit` streams /// will be polled concurrently. The `limit` argument is of type @@ -567,7 +693,7 @@ pub trait StreamExt: Stream { /// `Some(10)`, or just `10`. Note: a limit of zero is interpreted as /// no limit at all, and will have the same result as passing in `None`. /// - /// The provided closure which produce inner streams is executed over + /// The provided closure which produces inner streams is executed over /// all elements of stream as next stream item is available and limit /// of concurrently processed streams isn't exceeded. /// @@ -589,7 +715,11 @@ pub trait StreamExt: Stream { /// # }); #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] - fn flat_map_unordered(self, limit: impl Into>, f: F) -> FlatMapUnordered + fn flat_map_unordered( + self, + limit: impl Into>, + f: F, + ) -> FlatMapUnordered where U: Stream, F: FnMut(Self::Item) -> U, @@ -598,7 +728,7 @@ pub trait StreamExt: Stream { FlatMapUnordered::new(self, limit.into(), f) } - /// Combinator similar to [`StreamExt::fold`] that holds internal state + /// Combinator similar to [`StreamExt::fold`] that holds internal state /// and produces a new stream. /// /// Accepts initial state and closure which will be applied to each element @@ -692,6 +822,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. /// @@ -1154,6 +1328,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. /// @@ -1167,8 +1367,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) } 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 new file mode 100644 index 0000000000..2152cb72f0 --- /dev/null +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -0,0 +1,112 @@ +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_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 ReadyChunks where St: Stream { + 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, + } + } + + delegate_access_inner!(stream, St, (.)); +} + +impl Stream for ReadyChunks { + type Item = Vec; + + #[project] + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + #[project] + let ReadyChunks { items, cap, mut stream } = self.project(); + + loop { + match stream.as_mut().poll_next(cx) { + // Flush all collected data if underlying stream doesn't contain + // more ready values + Poll::Pending => { + return if items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(mem::replace(items, Vec::with_capacity(*cap)))) + } + } + + // 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)) => { + 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 items.is_empty() { + None + } else { + let full_buf = mem::replace(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-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/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-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 new file mode 100644 index 0000000000..3662620a58 --- /dev/null +++ b/futures-util/src/stream/stream/take_until.rs @@ -0,0 +1,178 @@ +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_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, + /// Whether the future was taken out by the user. + free: bool, +} + +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, +{ + pub(super) fn new(stream: St, fut: Fut) -> TakeUntil { + TakeUntil { + stream, + fut: Some(fut), + fut_result: None, + free: false, + } + } + + 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. + /// 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; + + #[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 !*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!(stream.poll_next(cx)); + if item.is_none() { + 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-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..51ee0cf773 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(), @@ -60,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. @@ -88,28 +75,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..99d5a6d4c1 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 + 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 + 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 + 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 + 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 + 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..310f99164d 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,28 @@ 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() { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { + 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) diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6619121fa4..627613b1c6 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,20 +19,21 @@ 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-alpha.4" -futures-executor = { path = "../futures-executor", version = "0.3.4", features = ["thread-pool"] } -futures-test = { path = "../futures-test", version = "0.3.4" } +pin-utils = "0.1.0" +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" +pin-project = "0.4.15" [features] default = ["std", "async-await", "executor"] @@ -51,6 +52,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 diff --git a/futures/src/lib.rs b/futures/src/lib.rs index b8fdea3f26..33bfd4f18c 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"); @@ -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")] @@ -332,7 +334,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, }; } @@ -442,9 +444,10 @@ pub mod stream { try_unfold, TryUnfold, StreamExt, - Chain, Collect, Concat, Enumerate, Filter, FilterMap, Flatten, Fold, - Forward, ForEach, Fuse, StreamFuture, Inspect, Map, Next, SelectNextSome, - Peek, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile, Then, Zip, + 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, TryStreamExt, AndThen, ErrInto, MapOk, MapErr, OrElse, @@ -457,7 +460,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"))] @@ -468,7 +471,7 @@ pub mod stream { // For StreamExt: BufferUnordered, Buffered, ForEachConcurrent, SplitStream, SplitSink, - ReuniteError, FlatMapUnordered, + ReuniteError, FlatMapUnordered, FlattenUnordered, select_all, SelectAll, }; @@ -533,72 +536,3 @@ pub mod never { pub use futures_util::never::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 { - 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 )* - } - } - } -} - -#[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 )* - } - } - } -} 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); 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)); -} diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index bc717df535..fd2a3497ec 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 @@ -235,11 +284,16 @@ 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"))] #[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::<(), ()>(()) }, 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(); 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); diff --git a/futures/tests/eager_drop.rs b/futures/tests/eager_drop.rs index 674e40121d..2472c59b76 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,99 @@ fn map_err() { rx2.recv().unwrap(); } -struct FutureData { - _data: T, - 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) +mod channelled { + use futures::future::Future; + use futures::task::{Context,Poll}; + use pin_project::pin_project; + use std::pin::Pin; + + #[pin_project] + struct FutureData { + _data: T, + #[pin] + 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(); + impl Future for FutureData { + type Output = F::Output; - 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::<()>(); - - 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(); -} + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().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(); + } } 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}; 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); 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::>(); 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::(); 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; { 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)); 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])); 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))); 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() 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"); 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]; 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); 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() 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); 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] 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)) 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], ) 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..49691c9e07 --- /dev/null +++ b/futures/tests/macro-reexport/src/lib.rs @@ -0,0 +1,8 @@ +// normal reexport +pub use futures03::{join, try_join, select, select_biased}; + +// reexport + rename +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/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..5d11f60834 --- /dev/null +++ b/futures/tests/macro-tests/src/main.rs @@ -0,0 +1,69 @@ +// Check that it works even if proc-macros are reexported. + +fn main() { + use futures03::{executor::block_on, future}; + + // 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::<(), ()>(()) + }); + + // 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!(), + }; + }); + +} 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(); 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) 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. 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)); 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!() })); 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 { 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), 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), diff --git a/futures/tests/shared.rs b/futures/tests/shared.rs index 8402bfe10b..21e80fe690 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,15 +89,22 @@ 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(); 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)); @@ -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(); @@ -108,24 +141,26 @@ 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)); } } -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 +171,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(); @@ -149,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) })), + ((), ()) + ); +} 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); 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(|_| ()); diff --git a/futures/tests/split.rs b/futures/tests/split.rs index 9f4f1a07e2..2cbb888c9b 100644 --- a/futures/tests/split.rs +++ b/futures/tests/split.rs @@ -1,65 +1,64 @@ -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 -} +#[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_project::pin_project; + use std::pin::Pin; -impl Join { - unsafe_pinned!(stream: T); - unsafe_pinned!(sink: U); -} + #[pin_project] + struct Join { + #[pin] + stream: T, + #[pin] + 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.project().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.project().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.project().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.project().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.project().sink.poll_close(cx) + } } -} -#[test] -fn test_split() { let mut dest: Vec = Vec::new(); { let join = Join { diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index b9d7df6509..82f5671746 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,100 +1,9 @@ -use futures::executor::block_on; -use futures::stream::{self, *}; -use futures::task::*; -use std::convert::identity; -use std::pin::Pin; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::thread; -use std::time::Duration; - -struct DataStream { - data: Vec, - polled: bool, - wake_immediately: bool, - woken: Arc, -} - -impl Stream for DataStream { - type Item = u8; - - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { - if !self.polled { - if !self.wake_immediately { - let waker = ctx.waker().clone(); - let woken = self.woken.clone(); - let sleep_time = Duration::from_millis(*self.data.last().unwrap_or(&0) as u64); - thread::spawn(move || { - thread::sleep(sleep_time); - woken.swap(true, Ordering::Relaxed); - waker.wake_by_ref(); - }); - } else { - self.woken.swap(true, Ordering::Relaxed); - ctx.waker().wake_by_ref(); - } - self.polled = true; - Poll::Pending - } else { - assert!( - self.woken.swap(false, Ordering::AcqRel), - "Inner stream polled before wake!" - ); - self.polled = false; - Poll::Ready(self.data.pop()) - } - } -} - -struct Interchanger { - polled: bool, - base: u8, - wake_immediately: bool, - woken: Arc, -} - -impl Stream for Interchanger { - type Item = DataStream; - - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { - if !self.polled { - self.polled = true; - if !self.wake_immediately { - let waker = ctx.waker().clone(); - let woken = self.woken.clone(); - let sleep_time = Duration::from_millis(self.base as u64); - thread::spawn(move || { - thread::sleep(sleep_time); - woken.swap(true, Ordering::Relaxed); - waker.wake_by_ref(); - }); - } else { - self.woken.swap(true, Ordering::Relaxed); - ctx.waker().wake_by_ref(); - } - Poll::Pending - } else { - assert!( - self.woken.swap(false, Ordering::AcqRel), - "Stream polled before wake!" - ); - self.base += 1; - self.polled = false; - Poll::Ready(Some(DataStream { - polled: false, - data: vec![9, 8, 7, 6, 5] - .into_iter() - .map(|v| v * self.base) - .collect(), - wake_immediately: self.wake_immediately && self.base % 2 == 0, - woken: Arc::new(AtomicBool::new(false)), - })) - } - } -} - +#[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); @@ -107,8 +16,32 @@ 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), + 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]); + }); +} + +#[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]) @@ -123,9 +56,141 @@ fn scan() { }); } +#[cfg(feature = "executor")] // executor:: #[test] -fn flat_map_unordered() { - futures::executor::block_on(async { +fn flatten_unordered() { + use futures::executor::block_on; + use futures::stream::{self, *}; + use futures::task::*; + use std::convert::identity; + use std::pin::Pin; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + use std::thread; + use std::time::Duration; + + struct DataStream { + data: Vec, + polled: bool, + wake_immediately: bool, + woken: Arc, + } + + impl Stream for DataStream { + type Item = u8; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if !self.polled { + if !self.wake_immediately { + let waker = ctx.waker().clone(); + let woken = self.woken.clone(); + let sleep_time = Duration::from_millis(*self.data.last().unwrap_or(&0) as u64); + thread::spawn(move || { + thread::sleep(sleep_time); + woken.swap(true, Ordering::Relaxed); + waker.wake_by_ref(); + }); + } else { + self.woken.swap(true, Ordering::Relaxed); + ctx.waker().wake_by_ref(); + } + self.polled = true; + Poll::Pending + } else { + assert!( + self.woken.swap(false, Ordering::AcqRel), + "Inner stream polled before wake!" + ); + self.polled = false; + Poll::Ready(self.data.pop()) + } + } + } + + struct Interchanger { + polled: bool, + base: u8, + wake_immediately: bool, + woken: Arc, + } + + impl Stream for Interchanger { + type Item = DataStream; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if !self.polled { + self.polled = true; + if !self.wake_immediately { + let waker = ctx.waker().clone(); + let woken = self.woken.clone(); + let sleep_time = Duration::from_millis(self.base as u64); + thread::spawn(move || { + thread::sleep(sleep_time); + woken.swap(true, Ordering::Relaxed); + waker.wake_by_ref(); + }); + } else { + self.woken.swap(true, Ordering::Relaxed); + ctx.waker().wake_by_ref(); + } + Poll::Pending + } else { + assert!( + self.woken.swap(false, Ordering::AcqRel), + "Stream polled before wake!" + ); + self.base += 1; + self.polled = false; + Poll::Ready(Some(DataStream { + polled: false, + data: vec![9, 8, 7, 6, 5] + .into_iter() + .map(|v| v * self.base) + .collect(), + wake_immediately: self.wake_immediately && self.base % 2 == 0, + woken: Arc::new(AtomicBool::new(false)), + })) + } + } + } + + // concurrent tests + block_on(async { + let fm_unordered = Interchanger { + polled: false, + base: 1, + woken: Arc::new(AtomicBool::new(false)), + wake_immediately: false, + } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>() + .await; + + assert_eq!(fm_unordered.len(), 50); + }); + + // basic behaviour + block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(0..=2), + ]); + + let mut fl_unordered = st + .map(|s| s.filter(|v| futures::future::ready(v % 2 == 0))) + .flatten_unordered(1) + .collect::>() + .await; + + fl_unordered.sort(); + + assert_eq!(fl_unordered, vec![0, 0, 2, 2, 4, 6, 8, 10]); + }); + + // basic behaviour + block_on(async { let st = stream::iter(vec![ stream::iter(0..=4u8), stream::iter(6..=10), @@ -141,29 +206,26 @@ fn flat_map_unordered() { assert_eq!(fm_unordered, vec![0, 0, 2, 2, 4, 6, 8, 10]); }); -} -#[test] -fn flat_map_unordered_concurrency() { - futures::executor::block_on(async { - let fm_unordered = Interchanger { + // wake up immmediately + block_on(async { + let fl_unordered = Interchanger { polled: false, base: 1, woken: Arc::new(AtomicBool::new(false)), - wake_immediately: false, + wake_immediately: true, } .take(10) - .flat_map_unordered(10, |s| s.map(identity)) + .map(|s| s.map(identity)) + .flatten_unordered(10) .collect::>() .await; - assert_eq!(fm_unordered.len(), 50); + assert_eq!(fl_unordered.len(), 50); }); -} -#[test] -fn flat_map_unordered_concurrency_when_wake_immediately() { - futures::executor::block_on(async { + // wake up immmediately + block_on(async { let fm_unordered = Interchanger { polled: false, base: 1, @@ -178,3 +240,111 @@ fn flat_map_unordered_concurrency_when_wake_immediately() { assert_eq!(fm_unordered.len(), 50); }); } + +#[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| { + 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); + }); +} + +#[cfg(feature = "executor")] // executor:: +#[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::sink::SinkExt; + use futures::stream::StreamExt; + 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]); + }); +} 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()); 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(); 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); 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::(); 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 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}; 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]),