From 33677fe7cd8f014d78ad7708f36979af94f1c40e Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Fri, 26 Feb 2021 04:08:50 +0000 Subject: [PATCH 001/283] Add future::SelectAll::into_inner (#2363) When select all is not yet finished (for example, due to timeout), underlying futures may need to be extracted to be updated to construct a fresh select all. --- futures-util/src/future/select_all.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/futures-util/src/future/select_all.rs b/futures-util/src/future/select_all.rs index 0db90a750e..b341e83cd5 100644 --- a/futures-util/src/future/select_all.rs +++ b/futures-util/src/future/select_all.rs @@ -42,6 +42,13 @@ pub fn select_all(iter: I) -> SelectAll assert_future::<(::Output, usize, Vec), _>(ret) } +impl SelectAll { + /// Consumes this combinator, returning the underlying futures. + pub fn into_inner(self) -> Vec { + self.inner + } +} + impl Future for SelectAll { type Output = (Fut::Output, usize, Vec); From ac430ccd1392bba70b001193a609f0f2138b9afc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 26 Feb 2021 23:47:46 +0900 Subject: [PATCH 002/283] Fix clippy::manual_map warning (#2365) --- futures-util/src/stream/stream/skip.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/futures-util/src/stream/stream/skip.rs b/futures-util/src/stream/stream/skip.rs index 6ffcf57d77..631ae40ed3 100644 --- a/futures-util/src/stream/stream/skip.rs +++ b/futures-util/src/stream/stream/skip.rs @@ -57,11 +57,8 @@ impl Stream for Skip { fn size_hint(&self) -> (usize, Option) { let (lower, upper) = self.stream.size_hint(); - let lower = lower.saturating_sub(self.remaining as usize); - let upper = match upper { - Some(x) => Some(x.saturating_sub(self.remaining as usize)), - None => None, - }; + let lower = lower.saturating_sub(self.remaining); + let upper = upper.map(|x| x.saturating_sub(self.remaining)); (lower, upper) } From 9d6983562c29398dcf68b8d312d2266ae218bfef Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 27 Feb 2021 00:58:23 +0900 Subject: [PATCH 003/283] Revert import changes in tests After #2216, these redundant imports are unneeded. This reverts almost all of #2101. This also includes some minor cleanup of imports in tests. --- futures/tests/abortable.rs | 18 +- futures/tests/arc_wake.rs | 98 ++--- futures/tests/async_await_macros.rs | 125 +----- futures/tests/atomic_waker.rs | 18 +- futures/tests/buffer_unordered.rs | 14 +- futures/tests/eager_drop.rs | 179 ++++---- futures/tests/future_obj.rs | 4 +- futures/tests/future_try_flatten_stream.rs | 29 +- futures/tests/futures_ordered.rs | 55 ++- futures/tests/futures_unordered.rs | 68 +-- futures/tests/inspect.rs | 10 +- futures/tests/io_buf_reader.rs | 206 +++++---- futures/tests/io_buf_writer.rs | 148 ++++--- futures/tests/io_cursor.rs | 63 ++- futures/tests/io_lines.rs | 60 ++- futures/tests/io_read.rs | 55 +-- futures/tests/io_read_exact.rs | 8 +- futures/tests/io_read_line.rs | 39 +- futures/tests/io_read_to_end.rs | 7 +- futures/tests/io_read_to_string.rs | 20 +- futures/tests/io_read_until.rs | 39 +- futures/tests/io_write.rs | 69 ++-- futures/tests/join_all.rs | 39 +- futures/tests/macro_comma_support.rs | 32 +- futures/tests/mutex.rs | 33 +- futures/tests/oneshot.rs | 49 +-- futures/tests/ready_queue.rs | 55 +-- futures/tests/recurse.rs | 14 +- futures/tests/select_all.rs | 14 +- futures/tests/select_ok.rs | 22 +- futures/tests/shared.rs | 70 +--- futures/tests/sink.rs | 460 ++++++++------------- futures/tests/sink_fanout.rs | 12 +- futures/tests/split.rs | 43 +- futures/tests/stream.rs | 66 ++- futures/tests/stream_catch_unwind.rs | 9 +- futures/tests/stream_into_async_read.rs | 96 ++--- futures/tests/stream_peekable.rs | 8 +- futures/tests/stream_select_all.rs | 19 +- futures/tests/stream_select_next_some.rs | 26 +- futures/tests/try_join.rs | 3 +- futures/tests/try_join_all.rs | 53 ++- futures/tests/unfold.rs | 5 +- 43 files changed, 968 insertions(+), 1492 deletions(-) diff --git a/futures/tests/abortable.rs b/futures/tests/abortable.rs index 6b5a25c365..5925c9a27b 100644 --- a/futures/tests/abortable.rs +++ b/futures/tests/abortable.rs @@ -1,9 +1,11 @@ +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; + #[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,11 +15,6 @@ fn abortable_works() { #[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); @@ -33,9 +30,6 @@ fn abortable_awakens() { #[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 d19a83dfac..3c5724a239 100644 --- a/futures/tests/arc_wake.rs +++ b/futures/tests/arc_wake.rs @@ -1,69 +1,65 @@ -mod countingwaker { - use futures::task::{self, ArcWake, Waker}; - use std::sync::{Arc, Mutex}; +use futures::task::{self, ArcWake, Waker}; +use std::panic; +use std::sync::{Arc, Mutex}; - struct CountingWaker { - nr_wake: Mutex, - } +struct CountingWaker { + nr_wake: Mutex, +} - impl CountingWaker { - fn new() -> Self { - Self { - nr_wake: Mutex::new(0), - } +impl CountingWaker { + fn new() -> Self { + Self { + 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_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)); +} - #[test] - fn ref_wake_same() { - let some_w = Arc::new(CountingWaker::new()); +#[test] +fn 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(); + 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)); - } + assert!(w1.will_wake(&w2)); + assert!(w2.will_wake(&w3)); } #[test] fn proper_refcount_on_wake_panic() { - use futures::task::{self, ArcWake, Waker}; - use std::sync::Arc; - struct PanicWaker; impl ArcWake for PanicWaker { @@ -75,7 +71,13 @@ fn proper_refcount_on_wake_panic() { let some_w = Arc::new(PanicWaker); let w1: Waker = task::waker(some_w.clone()); - assert_eq!("WAKE UP", *std::panic::catch_unwind(|| w1.wake_by_ref()).unwrap_err().downcast::<&str>().unwrap()); + assert_eq!( + "WAKE UP", + *panic::catch_unwind(|| w1.wake_by_ref()) + .unwrap_err() + .downcast::<&str>() + .unwrap() + ); assert_eq!(2, Arc::strong_count(&some_w)); // some_w + w1 drop(w1); assert_eq!(1, Arc::strong_count(&some_w)); // some_w diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index bd586d6e52..3e461b50eb 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -1,9 +1,14 @@ +use futures::channel::{mpsc, oneshot}; +use futures::executor::block_on; +use futures::future::{self, poll_fn, FutureExt}; +use futures::sink::SinkExt; +use futures::stream::StreamExt; +use futures::task::{Context, Poll}; +use futures::{join, pending, pin_mut, poll, select, select_biased, try_join}; +use std::mem; + #[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); @@ -14,11 +19,6 @@ fn poll_and_pending() { #[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::(); @@ -39,11 +39,6 @@ fn join() { #[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(); @@ -62,11 +57,6 @@ fn select() { #[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::(); let (_tx2, rx2) = oneshot::channel::(); tx1.send(1).unwrap(); @@ -85,12 +75,6 @@ fn select_biased() { #[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(); @@ -134,11 +118,6 @@ fn select_streams() { #[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(); @@ -165,10 +144,6 @@ fn select_can_move_uncompleted_futures() { #[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 { @@ -185,16 +160,13 @@ fn select_nested() { #[test] fn select_size() { - use futures::select; - use futures::future; - let fut = async { let mut ready = future::ready(0i32); select! { _ = ready => {}, } }; - assert_eq!(::std::mem::size_of_val(&fut), 24); + assert_eq!(mem::size_of_val(&fut), 24); let fut = async { let mut ready1 = future::ready(0i32); @@ -204,19 +176,13 @@ fn select_size() { _ = ready2 => {}, } }; - assert_eq!(::std::mem::size_of_val(&fut), 40); + assert_eq!(mem::size_of_val(&fut), 40); } #[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 - }}; + let make_non_unpin_fut = || async { 5 }; let res = block_on(async { let select_res; @@ -231,14 +197,8 @@ fn select_on_non_unpin_expressions() { #[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 - }}; + let make_non_unpin_fut = || async { 5 }; let res = block_on(async { let select_res; @@ -254,13 +214,8 @@ fn select_on_non_unpin_expressions_with_default() { #[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 - }}; + let make_non_unpin_fut = || async { 5 }; let fut = async { let select_res; @@ -271,15 +226,11 @@ fn select_on_non_unpin_size() { select_res }; - assert_eq!(32, std::mem::size_of_val(&fut)); + assert_eq!(32, mem::size_of_val(&fut)); } #[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, @@ -291,11 +242,6 @@ fn select_can_be_used_as_expression() { #[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 } @@ -312,10 +258,6 @@ fn select_with_default_can_be_used_as_expression() { #[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, @@ -330,10 +272,6 @@ fn select_with_complete_can_be_used_as_expression() { #[test] #[allow(unused_assignments)] 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() {} @@ -351,10 +289,6 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block() { #[test] #[allow(unused_assignments)] 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() {} @@ -374,59 +308,42 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { #[test] fn join_size() { - use futures::join; - use futures::future; - let fut = async { let ready = future::ready(0i32); join!(ready) }; - assert_eq!(::std::mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 16); let fut = async { let ready1 = future::ready(0i32); let ready2 = future::ready(0i32); join!(ready1, ready2) }; - assert_eq!(::std::mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 28); } #[test] fn try_join_size() { - use futures::try_join; - use futures::future; - let fut = async { let ready = future::ready(Ok::(0)); try_join!(ready) }; - assert_eq!(::std::mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 16); let fut = async { let ready1 = future::ready(Ok::(0)); let ready2 = future::ready(Ok::(0)); try_join!(ready1, ready2) }; - assert_eq!(::std::mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 28); } #[test] fn join_doesnt_require_unpin() { - use futures::join; - - let _ = async { - join!(async {}, async {}) - }; + let _ = async { join!(async {}, async {}) }; } #[test] fn try_join_doesnt_require_unpin() { - use futures::try_join; - - let _ = async { - try_join!( - async { Ok::<(), ()>(()) }, - async { Ok::<(), ()>(()) }, - ) - }; + let _ = async { try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) },) }; } diff --git a/futures/tests/atomic_waker.rs b/futures/tests/atomic_waker.rs index bf15d0f0c7..299270e63d 100644 --- a/futures/tests/atomic_waker.rs +++ b/futures/tests/atomic_waker.rs @@ -1,14 +1,14 @@ -#[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}; +use futures::executor::block_on; +use futures::future::poll_fn; +use futures::task::{AtomicWaker, Poll}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::thread; +#[test] +fn basic() { 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 5c8b8bf7e5..9a2ee174ed 100644 --- a/futures/tests/buffer_unordered.rs +++ b/futures/tests/buffer_unordered.rs @@ -1,13 +1,13 @@ +use futures::channel::{mpsc, oneshot}; +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; + #[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 11edb1b6de..45cf191d20 100644 --- a/futures/tests/eager_drop.rs +++ b/futures/tests/eager_drop.rs @@ -1,16 +1,23 @@ +use futures::channel::oneshot; +use futures::future::{self, Future, FutureExt, TryFutureExt}; +use futures::task::{Context, Poll}; +use futures_test::future::FutureTestExt; +use pin_project::pin_project; +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::<()>(); let (tx2, rx2) = mpsc::channel::<()>(); future::ready::>(Err(1)) - .map_ok(move |_| { let _tx1 = tx1; panic!("should not run"); }) + .map_ok(move |_| { + let _tx1 = tx1; + panic!("should not run"); + }) .map(move |_| { assert!(rx1.recv().is_err()); tx2.send(()).unwrap() @@ -22,17 +29,16 @@ 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::<()>(); let (tx2, rx2) = mpsc::channel::<()>(); future::ready::>(Ok(1)) - .map_err(move |_| { let _tx1 = tx1; panic!("should not run"); }) + .map_err(move |_| { + let _tx1 = tx1; + panic!("should not run"); + }) .map(move |_| { assert!(rx1.recv().is_err()); tx2.send(()).unwrap() @@ -42,96 +48,83 @@ fn map_err() { rx2.recv().unwrap(); } -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, - } +#[pin_project] +struct FutureData { + _data: T, + #[pin] + future: F, +} - impl Future for FutureData { - type Output = F::Output; +impl Future for FutureData { + type Output = F::Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.project().future.poll(cx) - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().future.poll(cx) } +} + +#[test] +fn then_drops_eagerly() { + let (tx0, rx0) = oneshot::channel::<()>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); - #[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!()), } + .then(move |_| { + assert!(rx1.recv().is_err()); // tx1 should have been dropped + tx2.send(()).unwrap(); + future::ready(()) + }) + .run_in_background(); + + assert_eq!(Err(mpsc::TryRecvError::Empty), rx2.try_recv()); + tx0.send(()).unwrap(); + rx2.recv().unwrap(); +} + +#[test] +fn and_then_drops_eagerly() { + let (tx0, rx0) = oneshot::channel::>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); - #[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(); + 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(); +} + +#[test] +fn or_else_drops_eagerly() { + let (tx0, rx0) = oneshot::channel::>(); + let (tx1, rx1) = mpsc::channel::<()>(); + let (tx2, rx2) = mpsc::channel::<()>(); - #[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(); + 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/future_obj.rs b/futures/tests/future_obj.rs index c6b18fc85c..0e5253464e 100644 --- a/futures/tests/future_obj.rs +++ b/futures/tests/future_obj.rs @@ -1,6 +1,6 @@ -use futures::future::{Future, FutureObj, FutureExt}; -use std::pin::Pin; +use futures::future::{Future, FutureExt, FutureObj}; use futures::task::{Context, Poll}; +use std::pin::Pin; #[test] fn dropping_does_not_segfault() { diff --git a/futures/tests/future_try_flatten_stream.rs b/futures/tests/future_try_flatten_stream.rs index 4a614f9c14..82ae1baf2c 100644 --- a/futures/tests/future_try_flatten_stream.rs +++ b/futures/tests/future_try_flatten_stream.rs @@ -1,9 +1,14 @@ +use futures::executor::block_on_stream; +use futures::future::{err, ok, TryFutureExt}; +use futures::sink::Sink; +use futures::stream::Stream; +use futures::stream::{self, StreamExt}; +use futures::task::{Context, Poll}; +use std::marker::PhantomData; +use std::pin::Pin; + #[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)); @@ -17,15 +22,8 @@ fn successful_future() { #[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}; - struct PanickingStream { - _marker: PhantomData<(T, E)> + _marker: PhantomData<(T, E)>, } impl Stream for PanickingStream { @@ -45,13 +43,6 @@ fn failed_future() { #[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}; - struct StreamSink(PhantomData<(T, E, Item)>); impl Stream for StreamSink { diff --git a/futures/tests/futures_ordered.rs b/futures/tests/futures_ordered.rs index 7f21c829e3..1cdb197da4 100644 --- a/futures/tests/futures_ordered.rs +++ b/futures/tests/futures_ordered.rs @@ -1,15 +1,19 @@ +use futures::channel::oneshot; +use futures::executor::{block_on, block_on_stream}; +use futures::future::{self, join, Future, FutureExt, TryFutureExt}; +use futures::stream::{FuturesOrdered, StreamExt}; +use futures_test::task::noop_context; +use std::any::Any; + #[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::(); - let mut stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); + let mut stream = vec![a_rx, b_rx, c_rx] + .into_iter() + .collect::>(); b_tx.send(99).unwrap(); assert!(stream.poll_next_unpin(&mut noop_context()).is_pending()); @@ -26,11 +30,6 @@ fn works_1() { #[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::(); @@ -38,7 +37,9 @@ fn works_2() { let mut stream = vec![ a_rx.boxed(), join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed(), - ].into_iter().collect::>(); + ] + .into_iter() + .collect::>(); let mut cx = noop_context(); a_tx.send(33).unwrap(); @@ -51,37 +52,33 @@ fn works_2() { #[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), - future::ready::(3) - ].into_iter().collect::>(); + future::ready::(3), + ] + .into_iter() + .collect::>(); assert_eq!(stream.len(), 3); - assert_eq!(block_on(stream.collect::>()), vec![1,2,3]); + assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } #[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::>(); let mut stream = vec![ Box::new(a_rx) as Box + Unpin>, - Box::new(future::try_select(b_rx, c_rx) - .map_err(|e| e.factor_first().0) - .and_then(|e| future::ok(Box::new(e) as Box))) as _, - ].into_iter().collect::>(); + Box::new( + future::try_select(b_rx, c_rx) + .map_err(|e| e.factor_first().0) + .and_then(|e| future::ok(Box::new(e) as Box)), + ) as _, + ] + .into_iter() + .collect::>(); let cx = &mut noop_context(); for _ in 0..10 { diff --git a/futures/tests/futures_unordered.rs b/futures/tests/futures_unordered.rs index ac0817e079..e55a91bdd4 100644 --- a/futures/tests/futures_unordered.rs +++ b/futures/tests/futures_unordered.rs @@ -1,17 +1,17 @@ -use futures::future::Future; -use futures::stream::{FuturesUnordered, StreamExt}; +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, assert_stream_pending}; use std::iter::FromIterator; use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; #[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(); @@ -39,10 +39,6 @@ fn is_terminated() { #[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::(); @@ -65,12 +61,6 @@ fn works_1() { #[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::(); @@ -94,10 +84,6 @@ fn works_2() { #[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), @@ -111,12 +97,6 @@ fn from_iterator() { #[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::(); @@ -142,10 +122,6 @@ fn finished_future() { #[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::(); @@ -172,9 +148,6 @@ fn iter_mut_cancel() { #[test] fn iter_mut_len() { - use futures::future; - use futures::stream::FuturesUnordered; - let mut stream = vec![ future::pending::<()>(), future::pending::<()>(), @@ -196,15 +169,6 @@ fn iter_mut_len() { #[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, @@ -224,7 +188,10 @@ fn iter_cancel() { impl AtomicCancel { fn new(future: F) -> Self { - Self { future, cancel: AtomicBool::new(false) } + Self { + future, + cancel: AtomicBool::new(false), + } } } @@ -250,9 +217,6 @@ fn iter_cancel() { #[test] fn iter_len() { - use futures::future; - use futures::stream::FuturesUnordered; - let stream = vec![ future::pending::<()>(), future::pending::<()>(), @@ -274,11 +238,6 @@ fn iter_len() { #[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, assert_stream_pending}; - // Future that will be ready after being polled twice, // asserting that it does not move. let fut = future::ready(()).pending_once().assert_unmoved(); @@ -292,11 +251,6 @@ fn futures_not_moved_after_poll() { #[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 375778b63d..eacd1f78a2 100644 --- a/futures/tests/inspect.rs +++ b/futures/tests/inspect.rs @@ -1,12 +1,14 @@ +use futures::executor::block_on; +use futures::future::{self, FutureExt}; + #[test] fn smoke() { - use futures::executor::block_on; - use futures::future::{self, FutureExt}; - let mut counter = 0; { - let work = future::ready::(40).inspect(|val| { counter += *val; }); + let work = future::ready::(40).inspect(|val| { + counter += *val; + }); assert_eq!(block_on(work), 40); } diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index f8f9d140e3..f7d6de1cf9 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -1,9 +1,17 @@ +use futures::executor::block_on; +use futures::future::{Future, FutureExt}; +use futures::io::{ + AllowStdIo, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, + BufReader, Cursor, SeekFrom, +}; +use futures::task::{Context, Poll}; +use futures_test::task::noop_context; +use std::cmp; +use std::io; +use std::pin::Pin; + 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) { @@ -13,80 +21,69 @@ macro_rules! run_fill_buf { }}; } -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; - } +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; } } } -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, - } +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<'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 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 +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 consume(mut self: Pin<&mut Self>, amt: usize) { + self.inner = &self.inner[amt..]; } } #[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); @@ -124,17 +121,15 @@ fn test_buffered_reader() { #[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)); assert_eq!(block_on(reader.seek(SeekFrom::Start(3))).ok(), Some(3)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); + assert_eq!( + run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), + None + ); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); assert_eq!(block_on(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); @@ -144,13 +139,9 @@ fn test_buffered_reader_seek() { #[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 + pos: u64, } impl io::Read for PositionReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { @@ -181,23 +172,28 @@ fn test_buffered_reader_seek_underflow() { let mut reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(block_on(reader.seek(SeekFrom::End(-5))).ok(), Some(u64::max_value()-5)); + assert_eq!( + block_on(reader.seek(SeekFrom::End(-5))).ok(), + Some(u64::max_value() - 5) + ); assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); // the following seek will require two underlying seeks let expected = 9_223_372_036_854_775_802; - assert_eq!(block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), Some(expected)); + assert_eq!( + block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), + Some(expected) + ); assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); // seeking to 0 should empty the buffer. - assert_eq!(block_on(reader.seek(SeekFrom::Current(0))).ok(), Some(expected)); + assert_eq!( + block_on(reader.seek(SeekFrom::Current(0))).ok(), + Some(expected) + ); assert_eq!(reader.get_ref().get_ref().pos, expected); } #[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, @@ -213,7 +209,9 @@ fn test_short_reads() { } } - let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 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]; assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); @@ -227,10 +225,6 @@ fn test_short_reads() { #[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)); @@ -268,10 +262,6 @@ fn maybe_pending() { #[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(); @@ -291,36 +281,35 @@ fn maybe_pending_buf_read() { // https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 #[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> { + struct MaybePendingSeek<'a> { inner: Cursor<&'a [u8]>, ready: bool, } impl<'a> MaybePendingSeek<'a> { - pub fn new(inner: &'a [u8]) -> Self { - Self { inner: Cursor::new(inner), ready: true } + fn new(inner: &'a [u8]) -> Self { + Self { + inner: Cursor::new(inner), + ready: true, + } } } impl AsyncRead for MaybePendingSeek<'_> { - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + 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 AsyncBufRead for MaybePendingSeek<'_> { - fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + 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) } @@ -331,9 +320,11 @@ fn maybe_pending_seek() { } impl AsyncSeek for MaybePendingSeek<'_> { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + 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) @@ -349,7 +340,10 @@ fn maybe_pending_seek() { assert_eq!(run(reader.seek(SeekFrom::Current(3))).ok(), Some(3)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); + assert_eq!( + run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), + None + ); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); assert_eq!(run(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); diff --git a/futures/tests/io_buf_writer.rs b/futures/tests/io_buf_writer.rs index d58a6d801f..dec4026fae 100644 --- a/futures/tests/io_buf_writer.rs +++ b/futures/tests/io_buf_writer.rs @@ -1,67 +1,62 @@ -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, - } +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; + +struct MaybePending { + inner: Vec, + ready: bool, +} - impl MaybePending { - pub fn new(inner: Vec) -> Self { - Self { inner, ready: false } +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 - } +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_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 poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) } } -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; - } +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; } } } #[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(); @@ -104,9 +99,6 @@ fn buf_writer() { #[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(), []); @@ -117,9 +109,6 @@ fn buf_writer_inner_flushes() { #[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])); @@ -135,11 +124,6 @@ fn buf_writer_seek() { #[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(); @@ -173,20 +157,21 @@ fn maybe_pending_buf_writer() { run(writer.write(&[9, 10, 11])).unwrap(); assert_eq!(writer.buffer(), []); - assert_eq!(writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + assert_eq!( + writer.get_ref().inner, + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + ); run(writer.flush()).unwrap(); assert_eq!(writer.buffer(), []); - assert_eq!(&writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + assert_eq!( + &writer.get_ref().inner, + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + ); } #[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, &[]); @@ -197,13 +182,6 @@ fn maybe_pending_buf_writer_inner_flushes() { #[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; - - use util::run; - struct MaybePendingSeek { inner: Cursor>, ready_write: bool, @@ -212,7 +190,11 @@ fn maybe_pending_buf_writer_seek() { impl MaybePendingSeek { fn new(inner: Vec) -> Self { - Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } + Self { + inner: Cursor::new(inner), + ready_write: false, + ready_seek: false, + } } } @@ -241,9 +223,11 @@ fn maybe_pending_buf_writer_seek() { } impl AsyncSeek for MaybePendingSeek { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + 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) @@ -260,9 +244,15 @@ fn maybe_pending_buf_writer_seek() { run(w.write_all(&[0, 1, 2, 3, 4, 5])).unwrap(); run(w.write_all(&[6, 7])).unwrap(); assert_eq!(run(w.seek(SeekFrom::Current(0))).ok(), Some(8)); - assert_eq!(&w.get_ref().inner.get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); + assert_eq!( + &w.get_ref().inner.get_ref()[..], + &[0, 1, 2, 3, 4, 5, 6, 7][..] + ); assert_eq!(run(w.seek(SeekFrom::Start(2))).ok(), Some(2)); run(w.write_all(&[8, 9])).unwrap(); run(w.flush()).unwrap(); - assert_eq!(&w.into_inner().inner.into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); + assert_eq!( + &w.into_inner().inner.into_inner()[..], + &[0, 1, 8, 9, 4, 5, 6, 7] + ); } diff --git a/futures/tests/io_cursor.rs b/futures/tests/io_cursor.rs index 4ba6342525..906e00d1be 100644 --- a/futures/tests/io_cursor.rs +++ b/futures/tests/io_cursor.rs @@ -1,35 +1,54 @@ +use assert_matches::assert_matches; +use futures::executor::block_on; +use futures::future::lazy; +use futures::io::{AsyncWrite, Cursor}; +use futures::task::Poll; +use std::pin::Pin; + #[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))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(2))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[6, 7]), Poll::Ready(Ok(2))); + block_on(lazy(|cx| { + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[1, 2]), + Poll::Ready(Ok(2)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[3, 4]), + Poll::Ready(Ok(2)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[5, 6]), + Poll::Ready(Ok(2)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[6, 7]), + Poll::Ready(Ok(2)) + ); })); assert_eq!(cursor.into_inner(), [1, 2, 3, 4, 5, 6, 6, 7]); } #[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))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(1))); - assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[6, 7]), Poll::Ready(Ok(0))); + block_on(lazy(|cx| { + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[1, 2]), + Poll::Ready(Ok(2)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[3, 4]), + Poll::Ready(Ok(2)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[5, 6]), + Poll::Ready(Ok(1)) + ); + assert_matches!( + Pin::new(&mut cursor).poll_write(cx, &[6, 7]), + Poll::Ready(Ok(0)) + ); })); assert_eq!(&*cursor.into_inner(), [1, 2, 3, 4, 5]); } diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs index 2552c7c40a..06e1ac9b14 100644 --- a/futures/tests/io_lines.rs +++ b/futures/tests/io_lines.rs @@ -1,32 +1,34 @@ -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; - } +use futures::executor::block_on; +use futures::future::{Future, FutureExt}; +use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; +use futures::task::Poll; +use futures_test::io::AsyncReadTestExt; +use futures_test::task::noop_context; + +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; } } } -#[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() + }; +} - macro_rules! block_on_next { - ($expr:expr) => { - block_on($expr.next()).unwrap().unwrap() - }; - } +macro_rules! run_next { + ($expr:expr) => { + run($expr.next()).unwrap().unwrap() + }; +} +#[test] +fn lines() { let buf = Cursor::new(&b"12\r"[..]); let mut s = buf.lines(); assert_eq!(block_on_next!(s), "12\r".to_string()); @@ -41,18 +43,6 @@ fn lines() { #[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 5902ad0ed9..d39a6ea790 100644 --- a/futures/tests/io_read.rs +++ b/futures/tests/io_read.rs @@ -1,27 +1,26 @@ -mod mock_reader { - use futures::io::AsyncRead; - use std::io; - use std::pin::Pin; - use std::task::{Context, Poll}; +use futures::io::AsyncRead; +use futures_test::task::panic_context; +use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; - pub struct MockReader { - fun: Box Poll>>, - } +struct MockReader { + fun: Box Poll>>, +} - impl MockReader { - pub fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { - Self { fun: Box::new(fun) } - } +impl MockReader { + fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { + Self { 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) } } @@ -29,14 +28,6 @@ mod mock_reader { /// calls `poll_read` with an empty slice if no buffers are provided. #[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() @@ -53,14 +44,6 @@ fn read_vectored_no_buffers() { /// calls `poll_read` with the first non-empty buffer. #[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 bd4b36deaf..6582e50b80 100644 --- a/futures/tests/io_read_exact.rs +++ b/futures/tests/io_read_exact.rs @@ -1,14 +1,14 @@ +use futures::executor::block_on; +use futures::io::AsyncReadExt; + #[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]; let res = block_on(reader.read_exact(&mut out)); // read 3 bytes out assert!(res.is_ok()); - assert_eq!(out, [1,2,3]); + assert_eq!(out, [1, 2, 3]); assert_eq!(reader.len(), 2); let res = block_on(reader.read_exact(&mut out)); // read another 3 bytes, but only 2 bytes left diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs index 51e8126ada..6d82497495 100644 --- a/futures/tests/io_read_line.rs +++ b/futures/tests/io_read_line.rs @@ -1,8 +1,22 @@ +use futures::executor::block_on; +use futures::future::{Future, FutureExt}; +use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; +use futures::task::Poll; +use futures_test::io::AsyncReadTestExt; +use futures_test::task::noop_context; + +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; + } + } +} + #[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); @@ -22,25 +36,6 @@ fn read_line() { #[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; - } - } - } - - 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_end.rs b/futures/tests/io_read_to_end.rs index 892d463c2d..7122511fcb 100644 --- a/futures/tests/io_read_to_end.rs +++ b/futures/tests/io_read_to_end.rs @@ -1,4 +1,5 @@ use futures::{ + executor::block_on, io::{self, AsyncRead, AsyncReadExt}, task::{Context, Poll}, }; @@ -12,7 +13,7 @@ fn issue2310() { } impl MyRead { - pub fn new() -> Self { + fn new() -> Self { MyRead { first: false } } } @@ -39,7 +40,7 @@ fn issue2310() { } impl VecWrapper { - pub fn new() -> Self { + fn new() -> Self { VecWrapper { inner: Vec::new() } } } @@ -55,7 +56,7 @@ fn issue2310() { } } - futures::executor::block_on(async { + block_on(async { let mut vec = VecWrapper::new(); let mut read = MyRead::new(); diff --git a/futures/tests/io_read_to_string.rs b/futures/tests/io_read_to_string.rs index 2e9c00a138..ae6aaa21d8 100644 --- a/futures/tests/io_read_to_string.rs +++ b/futures/tests/io_read_to_string.rs @@ -1,8 +1,13 @@ +use futures::executor::block_on; +use futures::future::{Future, FutureExt}; +use futures::io::{AsyncReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; +use futures::task::Poll; +use futures_test::io::AsyncReadTestExt; +use futures_test::task::noop_context; + #[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); @@ -20,16 +25,7 @@ fn read_to_string() { #[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) { diff --git a/futures/tests/io_read_until.rs b/futures/tests/io_read_until.rs index 6fa22eee65..71f857f4b0 100644 --- a/futures/tests/io_read_until.rs +++ b/futures/tests/io_read_until.rs @@ -1,8 +1,22 @@ +use futures::executor::block_on; +use futures::future::{Future, FutureExt}; +use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; +use futures::task::Poll; +use futures_test::io::AsyncReadTestExt; +use futures_test::task::noop_context; + +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; + } + } +} + #[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); @@ -22,25 +36,6 @@ fn read_until() { #[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_write.rs b/futures/tests/io_write.rs index 363f32b1a6..227b4f9670 100644 --- a/futures/tests/io_write.rs +++ b/futures/tests/io_write.rs @@ -1,35 +1,34 @@ -mod mock_writer { - use futures::io::AsyncWrite; - use std::io; - use std::pin::Pin; - use std::task::{Context, Poll}; +use futures::io::AsyncWrite; +use futures_test::task::panic_context; +use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; - pub struct MockWriter { - fun: Box Poll>>, - } +struct MockWriter { + fun: Box Poll>>, +} - impl MockWriter { - pub fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { - Self { fun: Box::new(fun) } - } +impl MockWriter { + fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { + Self { fun: Box::new(fun) } } +} - impl AsyncWrite for MockWriter { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - (self.get_mut().fun)(buf) - } +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_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!() } } @@ -37,14 +36,6 @@ mod mock_writer { /// calls `poll_write` with an empty slice if no buffers are provided. #[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() @@ -61,14 +52,6 @@ fn write_vectored_no_buffers() { /// calls `poll_write` with the first non-empty buffer. #[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)) @@ -77,7 +60,7 @@ fn write_vectored_first_non_empty() { let bufs = &mut [ io::IoSlice::new(&[]), io::IoSlice::new(&[]), - io::IoSlice::new(b"four") + io::IoSlice::new(b"four"), ]; let res = Pin::new(&mut writer).poll_write_vectored(cx, bufs); diff --git a/futures/tests/join_all.rs b/futures/tests/join_all.rs index c322e58a13..ae05a21b7c 100644 --- a/futures/tests/join_all.rs +++ b/futures/tests/join_all.rs @@ -1,25 +1,20 @@ -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); - } +use futures::executor::block_on; +use futures::future::{join_all, ready, Future, JoinAll}; +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); } #[test] fn collect_collects() { - 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]); + 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]); // REVIEW: should this be implemented? // assert_done(|| Box::new(join_all(Vec::::new())), vec![]); @@ -28,8 +23,6 @@ fn collect_collects() { #[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(bufs: Vec<&[u8]>) -> Box> + Unpin> { @@ -37,14 +30,12 @@ fn join_all_iter_lifetime() { Box::new(join_all(iter)) } - util::assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), vec![3_usize, 0, 1]); + assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]); } #[test] fn join_all_from_iter() { - use futures_util::future::{JoinAll,ready}; - - util::assert_done( + assert_done( || Box::new(vec![ready(1), ready(2)].into_iter().collect::>()), vec![1, 2], ) diff --git a/futures/tests/macro_comma_support.rs b/futures/tests/macro_comma_support.rs index ca131639dd..85871e98be 100644 --- a/futures/tests/macro_comma_support.rs +++ b/futures/tests/macro_comma_support.rs @@ -1,12 +1,13 @@ +use futures::{ + executor::block_on, + future::{self, FutureExt}, + join, ready, + task::Poll, + try_join, +}; + #[test] fn ready() { - use futures::{ - executor::block_on, - future, - task::Poll, - ready, - }; - block_on(future::poll_fn(|_| { ready!(Poll::Ready(()),); Poll::Ready(()) @@ -15,11 +16,7 @@ fn ready() { #[test] fn poll() { - use futures::{ - executor::block_on, - future::FutureExt, - poll, - }; + use futures::poll; block_on(async { let _ = poll!(async {}.boxed(),); @@ -28,11 +25,6 @@ fn poll() { #[test] fn join() { - use futures::{ - executor::block_on, - join - }; - block_on(async { let future1 = async { 1 }; let future2 = async { 2 }; @@ -42,12 +34,6 @@ fn join() { #[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 68e0301426..7c33864c76 100644 --- a/futures/tests/mutex.rs +++ b/futures/tests/mutex.rs @@ -1,9 +1,15 @@ +use futures::channel::mpsc; +use futures::executor::{block_on, ThreadPool}; +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; + #[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()); @@ -12,11 +18,6 @@ fn mutex_acquire_uncontested() { #[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()); @@ -35,20 +36,8 @@ fn mutex_wakes_waiters() { #[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) - .create() - .unwrap(); + let pool = ThreadPool::builder().pool_size(16).create().unwrap(); let tx = Arc::new(tx); let mutex = Arc::new(Mutex::new(0)); diff --git a/futures/tests/oneshot.rs b/futures/tests/oneshot.rs index 2494306fad..867cb45ff1 100644 --- a/futures/tests/oneshot.rs +++ b/futures/tests/oneshot.rs @@ -1,11 +1,11 @@ +use futures::channel::oneshot; +use futures::future::{FutureExt, TryFutureExt}; +use futures_test::future::FutureTestExt; +use std::sync::mpsc; +use std::thread; + #[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(); @@ -17,65 +17,46 @@ fn oneshot_send1() { #[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(); thread::spawn(|| tx1.send(1).unwrap()).join().unwrap(); - rx1.map_ok(move |x| tx2.send(x).unwrap()).run_in_background(); + rx1.map_ok(move |x| tx2.send(x).unwrap()) + .run_in_background(); assert_eq!(1, rx2.recv().unwrap()); } #[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(); - rx1.map_ok(move |x| tx2.send(x).unwrap()).run_in_background(); + rx1.map_ok(move |x| tx2.send(x).unwrap()) + .run_in_background(); thread::spawn(|| tx1.send(1).unwrap()).join().unwrap(); assert_eq!(1, rx2.recv().unwrap()); } #[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(); drop(tx1); - rx1.map(move |result| tx2.send(result).unwrap()).run_in_background(); + rx1.map(move |result| tx2.send(result).unwrap()) + .run_in_background(); assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); } #[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(); let t = thread::spawn(|| drop(tx1)); - rx1.map(move |result| tx2.send(result).unwrap()).run_in_background(); + rx1.map(move |result| tx2.send(result).unwrap()) + .run_in_background(); t.join().unwrap(); assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); @@ -83,8 +64,6 @@ fn oneshot_drop_tx2() { #[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 9aa36362d0..b2b9bec461 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -1,18 +1,15 @@ -mod assert_send_sync { - use futures::stream::FuturesUnordered; - - pub trait AssertSendSync: Send + Sync {} - impl AssertSendSync for FuturesUnordered<()> {} -} +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; #[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(); @@ -41,12 +38,6 @@ fn basic_usage() { #[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(); @@ -61,13 +52,19 @@ fn resolving_errors() { drop(tx2); - assert_eq!(Poll::Ready(Some(Err(oneshot::Canceled))), queue.poll_next_unpin(cx)); + assert_eq!( + Poll::Ready(Some(Err(oneshot::Canceled))), + queue.poll_next_unpin(cx) + ); assert!(!queue.poll_next_unpin(cx).is_ready()); drop(tx1); tx3.send("world2").unwrap(); - assert_eq!(Poll::Ready(Some(Err(oneshot::Canceled))), queue.poll_next_unpin(cx)); + assert_eq!( + Poll::Ready(Some(Err(oneshot::Canceled))), + queue.poll_next_unpin(cx) + ); assert_eq!(Poll::Ready(Some(Ok("world2"))), queue.poll_next_unpin(cx)); assert_eq!(Poll::Ready(None), queue.poll_next_unpin(cx)); })); @@ -75,12 +72,6 @@ fn resolving_errors() { #[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::<()>(); @@ -108,12 +99,6 @@ fn dropping_ready_queue() { #[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 { @@ -157,12 +142,6 @@ fn stress() { #[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 a151f1b1d4..d81753c9d7 100644 --- a/futures/tests/recurse.rs +++ b/futures/tests/recurse.rs @@ -1,10 +1,10 @@ +use futures::executor::block_on; +use futures::future::{self, BoxFuture, FutureExt}; +use std::sync::mpsc; +use std::thread; + #[test] fn lots() { - use futures::executor::block_on; - use futures::future::{self, FutureExt, BoxFuture}; - use std::sync::mpsc; - use std::thread; - #[cfg(not(futures_sanitizer))] const N: i32 = 1_000; #[cfg(futures_sanitizer)] // If N is many, asan reports stack-overflow: https://gist.github.com/taiki-e/099446d21cbec69d4acbacf7a9646136 @@ -20,8 +20,6 @@ fn lots() { } let (tx, rx) = mpsc::channel(); - thread::spawn(|| { - block_on(do_it((N, 0)).map(move |x| tx.send(x).unwrap())) - }); + thread::spawn(|| block_on(do_it((N, 0)).map(move |x| tx.send(x).unwrap()))); assert_eq!((0..=N).sum::(), rx.recv().unwrap()); } diff --git a/futures/tests/select_all.rs b/futures/tests/select_all.rs index 540db2c410..299b479044 100644 --- a/futures/tests/select_all.rs +++ b/futures/tests/select_all.rs @@ -1,14 +1,10 @@ +use futures::executor::block_on; +use futures::future::{ready, select_all}; +use std::collections::HashSet; + #[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), - ready(3), - ]; + let v = vec![ready(1), ready(2), ready(3)]; let mut c = vec![1, 2, 3].into_iter().collect::>(); diff --git a/futures/tests/select_ok.rs b/futures/tests/select_ok.rs index 81cadb7bae..8aec00362d 100644 --- a/futures/tests/select_ok.rs +++ b/futures/tests/select_ok.rs @@ -1,14 +1,9 @@ +use futures::executor::block_on; +use futures::future::{err, ok, select_ok}; + #[test] fn ignore_err() { - use futures::executor::block_on; - use futures::future::{err, ok, select_ok}; - - let v = vec![ - err(1), - err(2), - ok(3), - ok(4), - ]; + let v = vec![err(1), err(2), ok(3), ok(4)]; let (i, v) = block_on(select_ok(v)).ok().unwrap(); assert_eq!(i, 3); @@ -23,14 +18,7 @@ fn ignore_err() { #[test] fn last_err() { - use futures::executor::block_on; - use futures::future::{err, ok, select_ok}; - - let v = vec![ - ok(1), - err(2), - err(3), - ]; + let v = vec![ok(1), err(2), err(3)]; let (i, v) = block_on(select_ok(v)).ok().unwrap(); assert_eq!(i, 1); diff --git a/futures/tests/shared.rs b/futures/tests/shared.rs index cc0c6a20cf..662798bff4 100644 --- a/futures/tests/shared.rs +++ b/futures/tests/shared.rs @@ -1,22 +1,22 @@ -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); - Self(self.0.clone()) - } +use futures::channel::oneshot; +use futures::executor::{block_on, LocalPool}; +use futures::future::{self, FutureExt, LocalFutureObj, TryFutureExt}; +use futures::task::LocalSpawn; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use std::task::Poll; +use std::thread; + +struct CountClone(Rc>); + +impl Clone for CountClone { + fn clone(&self) -> Self { + self.0.set(self.0.get() + 1); + Self(self.0.clone()) } } 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) @@ -53,11 +53,6 @@ fn many_threads() { #[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(); @@ -86,11 +81,6 @@ fn drop_on_one_task_ok() { #[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(); @@ -108,11 +98,6 @@ fn drop_in_poll() { #[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(); @@ -145,10 +130,6 @@ fn peek() { #[test] fn downgrade() { - use futures::channel::oneshot; - use futures::executor::block_on; - use futures::future::FutureExt; - let (tx, rx) = oneshot::channel::(); let shared = rx.shared(); // Since there are outstanding `Shared`s, we can get a `WeakShared`. @@ -173,14 +154,6 @@ fn downgrade() { #[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(); @@ -193,14 +166,6 @@ fn dont_clone_in_single_owner_shared_future() { #[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(); @@ -215,11 +180,6 @@ fn dont_do_unnecessary_clones_on_output() { #[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() { diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index 597ed34c7a..6c27e9ae1f 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -1,249 +1,222 @@ -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"), - } +use futures::channel::{mpsc, oneshot}; +use futures::executor::block_on; +use futures::future::{self, poll_fn, Future, FutureExt, TryFutureExt}; +use futures::never::Never; +use futures::ready; +use futures::sink::{self, 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::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +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"), } } -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"), - } +fn unwrap(x: Poll>) -> T { + match x { + Poll::Ready(Ok(x)) => x, + Poll::Ready(Err(_)) => panic!("Poll::Ready(Err(_))"), + Poll::Pending => panic!("Poll::Pending"), } } -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); +// An Unpark struct that records unpark events for inspection +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) - } +impl Flag { + fn new() -> Arc { + Arc::new(Self(AtomicBool::new(false))) + } - pub fn set(&self, v: bool) { - self.0.store(v, Ordering::SeqCst) - } + fn take(&self) -> bool { + self.0.swap(false, Ordering::SeqCst) } - impl ArcWake for Flag { - fn wake_by_ref(arc_self: &Arc) { - arc_self.set(true) - } + fn set(&self, v: bool) { + self.0.store(v, Ordering::SeqCst) } +} - 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) +impl ArcWake for Flag { + fn wake_by_ref(arc_self: &Arc) { + arc_self.set(true) } } -mod start_send_fut { - use futures::future::Future; - use futures::ready; - use futures::sink::Sink; - use futures::task::{Context, Poll}; - use std::pin::Pin; +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 - pub struct StartSendFut + Unpin, Item: Unpin>(Option, Option); +// Sends a value on an i32 channel sink +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> StartSendFut { + fn new(sink: S, item: Item) -> Self { + Self(Some(sink), Some(item)) } +} - impl + Unpin, Item: Unpin> Future for StartSendFut { - type Output = Result; +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())) + 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())) } } -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, - } +// 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 = (); +impl Sink> for ManualFlush { + type Error = (); - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + 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 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_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) - } + 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(), - } +impl ManualFlush { + 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 force_flush(&mut self) -> Vec { + for task in self.waiting_tasks.drain(..) { + task.wake() } + mem::replace(&mut self.data, Vec::new()) } } -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, - } +struct ManualAllow { + data: Vec, + allow: Rc, +} - pub struct Allow { - flag: Cell, - tasks: RefCell>, - } +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 - } +impl Allow { + fn new() -> Self { + Self { + flag: Cell::new(false), + tasks: RefCell::new(Vec::new()), } + } - pub fn start(&self) { - self.flag.set(true); - let mut tasks = self.tasks.borrow_mut(); - for task in tasks.drain(..) { - task.wake(); - } + fn check(&self, cx: &mut Context<'_>) -> bool { + if self.flag.get() { + true + } else { + self.tasks.borrow_mut().push(cx.waker().clone()); + false } } - 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(&self) { + self.flag.set(true); + let mut tasks = self.tasks.borrow_mut(); + for task in tasks.drain(..) { + task.wake(); } + } +} - fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { - self.data.push(item); - Ok(()) - } +impl Sink for ManualAllow { + type Error = (); - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.allow.check(cx) { Poll::Ready(Ok(())) + } else { + Poll::Pending } + } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { + self.data.push(item); + 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) + 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] 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 { @@ -255,10 +228,6 @@ fn either_sink() { #[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(); @@ -269,10 +238,6 @@ fn vec_sink() { #[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(); @@ -284,9 +249,6 @@ fn vecdeque_sink() { #[test] fn send() { - use futures::executor::block_on; - use futures::sink::SinkExt; - let mut v = Vec::new(); block_on(v.send(0)).unwrap(); @@ -301,10 +263,6 @@ fn send() { #[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(); @@ -321,15 +279,6 @@ fn send_all() { // channel is full #[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(|_| { @@ -353,17 +302,6 @@ fn mpsc_blocking_start_send() { // until a oneshot is completed #[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| { @@ -390,11 +328,6 @@ fn with_flush() { // test simple use of with to change data #[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(); @@ -405,10 +338,6 @@ fn with_as_map() { // test simple use of with_flat_map #[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(); @@ -421,16 +350,6 @@ fn with_flat_map() { // Regression test for the issue #1834. #[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)); @@ -457,14 +376,6 @@ fn with_propagates_poll_ready() { // but doesn't claim to be flushed until the underlying sink is #[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)); @@ -486,11 +397,6 @@ fn with_flush_propagate() { // test that `Clone` is implemented on `with` sinks #[test] fn with_implements_clone() { - use futures::channel::mpsc; - use futures::executor::block_on; - use futures::future; - use futures::{SinkExt, StreamExt}; - let (mut tx, rx) = mpsc::channel(5); { @@ -521,9 +427,6 @@ fn with_implements_clone() { // test that a buffer is a no-nop around a sink that always accepts sends #[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(); @@ -539,15 +442,6 @@ fn buffer_noop() { // and writing out when the underlying sink is ready #[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); @@ -567,10 +461,6 @@ fn buffer() { #[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); @@ -582,16 +472,6 @@ fn fanout_smoke() { #[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); @@ -624,12 +504,6 @@ fn fanout_backpressure() { #[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); @@ -647,12 +521,6 @@ fn sink_map_err() { #[test] fn sink_unfold() { - use futures::channel::mpsc; - use futures::executor::block_on; - use futures::future::poll_fn; - use futures::sink::{self, Sink, SinkExt}; - use futures::task::Poll; - block_on(poll_fn(|cx| { let (tx, mut rx) = mpsc::channel(1); let unfold = sink::unfold((), |(), i: i32| { @@ -685,14 +553,8 @@ fn sink_unfold() { #[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; + struct ErrIntoTest; impl From for ErrIntoTest { fn from(_: mpsc::SendError) -> Self { diff --git a/futures/tests/sink_fanout.rs b/futures/tests/sink_fanout.rs index 7d1fa43790..e57b2d8c7b 100644 --- a/futures/tests/sink_fanout.rs +++ b/futures/tests/sink_fanout.rs @@ -1,11 +1,11 @@ +use futures::channel::mpsc; +use futures::executor::block_on; +use futures::future::join3; +use futures::sink::SinkExt; +use futures::stream::{self, StreamExt}; + #[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 86c2fc6b82..7cee9ba6a2 100644 --- a/futures/tests/split.rs +++ b/futures/tests/split.rs @@ -1,12 +1,12 @@ +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; + #[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; - #[pin_project] struct Join { #[pin] @@ -18,10 +18,7 @@ fn test_split() { impl Stream for Join { type Item = T::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().stream.poll_next(cx) } } @@ -29,40 +26,28 @@ fn test_split() { impl, Item> Sink for Join { type Error = U::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + 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> { + 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> { + 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> { + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().sink.poll_close(cx) } } let mut dest: Vec = Vec::new(); { - let join = Join { + let join = Join { stream: stream::iter(vec![10, 20, 30]), - sink: &mut dest + sink: &mut dest, }; let (sink, stream) = join.split(); diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 14b283db7a..f2027b5d58 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,8 +1,14 @@ +use futures::channel::mpsc; +use futures::executor::block_on; +use futures::future::{self, Future}; +use futures::sink::SinkExt; +use futures::stream::{self, StreamExt}; +use futures::task::Poll; +use futures::FutureExt; +use futures_test::task::noop_context; + #[test] fn select() { - use futures::executor::block_on; - use futures::stream::{self, StreamExt}; - fn select_and_compare(a: Vec, b: Vec, expected: Vec) { let a = stream::iter(a); let b = stream::iter(b); @@ -17,9 +23,7 @@ fn select() { #[test] fn flat_map() { - use futures::stream::{self, StreamExt}; - - futures::executor::block_on(async { + block_on(async { let st = stream::iter(vec![ stream::iter(0..=4u8), stream::iter(6..=10), @@ -37,28 +41,21 @@ fn flat_map() { #[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]) - .scan(1, |state, e| { - *state += 1; - futures::future::ready(if e < *state { Some(e) } else { None }) - }) - .collect::>() - .await, - vec![1u8, 2, 3, 4] - ); + block_on(async { + let values = stream::iter(vec![1u8, 2, 3, 4, 6, 8, 2]) + .scan(1, |state, e| { + *state += 1; + futures::future::ready(if e < *state { Some(e) } else { None }) + }) + .collect::>() + .await; + + assert_eq!(values, vec![1u8, 2, 3, 4]); }); } #[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| { @@ -71,7 +68,7 @@ fn take_until() { }) } - futures::executor::block_on(async { + block_on(async { // Verify stopping works: let stream = stream::iter(1u32..=10); let stop_fut = make_stop_fut(5); @@ -123,10 +120,15 @@ fn take_until() { #[test] #[should_panic] -fn ready_chunks_panic_on_cap_zero() { - use futures::channel::mpsc; - use futures::stream::StreamExt; +fn chunks_panic_on_cap_zero() { + let (_, rx1) = mpsc::channel::<()>(1); + + let _ = rx1.chunks(0); +} +#[test] +#[should_panic] +fn ready_chunks_panic_on_cap_zero() { let (_, rx1) = mpsc::channel::<()>(1); let _ = rx1.ready_chunks(0); @@ -134,12 +136,6 @@ fn ready_chunks_panic_on_cap_zero() { #[test] fn ready_chunks() { - use futures::channel::mpsc; - use futures::stream::StreamExt; - use futures::sink::SinkExt; - use futures::FutureExt; - use futures_test::task::noop_context; - let (mut tx, rx1) = mpsc::channel::(16); let mut s = rx1.ready_chunks(2); @@ -147,14 +143,14 @@ fn ready_chunks() { let mut cx = noop_context(); assert!(s.next().poll_unpin(&mut cx).is_pending()); - futures::executor::block_on(async { + 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![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 272558cc61..8b23a0a7ef 100644 --- a/futures/tests/stream_catch_unwind.rs +++ b/futures/tests/stream_catch_unwind.rs @@ -1,8 +1,8 @@ +use futures::executor::block_on_stream; +use futures::stream::{self, StreamExt}; + #[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 @@ -16,9 +16,6 @@ fn panic_in_the_middle_of_the_stream() { #[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 222c985706..60188d3e58 100644 --- a/futures/tests/stream_into_async_read.rs +++ b/futures/tests/stream_into_async_read.rs @@ -1,31 +1,51 @@ -#[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; - } +use core::pin::Pin; +use futures::io::{AsyncBufRead, AsyncRead}; +use futures::stream::{self, TryStreamExt}; +use futures::task::Poll; +use futures_test::{stream::StreamTestExt, task::noop_context}; + +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; } } - }; - } + } + }; +} +#[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]; @@ -53,32 +73,6 @@ fn test_into_async_read() { #[test] fn test_into_async_bufread() { - 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 66a7385ae9..b65a0572cb 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -1,9 +1,9 @@ +use futures::executor::block_on; +use futures::pin_mut; +use futures::stream::{self, Peekable, StreamExt}; + #[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 6178412f4d..eb711dda0c 100644 --- a/futures/tests/stream_select_all.rs +++ b/futures/tests/stream_select_all.rs @@ -1,10 +1,12 @@ +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; + #[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,9 +32,6 @@ fn is_terminated() { #[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); @@ -51,10 +50,6 @@ fn issue_1626() { #[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 bec5262c1d..8252ad7b54 100644 --- a/futures/tests/stream_select_next_some.rs +++ b/futures/tests/stream_select_next_some.rs @@ -1,11 +1,13 @@ +use futures::executor::block_on; +use futures::future::{self, FusedFuture, FutureExt}; +use futures::select; +use futures::stream::{FuturesUnordered, StreamExt}; +use futures::task::{Context, Poll}; +use futures_test::future::FutureTestExt; +use futures_test::task::new_count_waker; + #[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); @@ -30,15 +32,11 @@ fn is_terminated() { #[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 // poll (after pending_once completes). - futures::executor::block_on(async { + block_on(async { let mut fut = future::ready(1).pending_once(); let mut async_tasks = FuturesUnordered::new(); let mut total = 0; @@ -61,17 +59,13 @@ fn select() { // Check that `select!` macro does not fail when importing from `futures_util`. #[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 // `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 // poll (after pending_once completes). - futures::executor::block_on(async { + block_on(async { let mut fut = future::ready(1).pending_once(); let mut async_tasks = FuturesUnordered::new(); let mut total = 0; diff --git a/futures/tests/try_join.rs b/futures/tests/try_join.rs index 6c6d0843d5..8b0b38c1a3 100644 --- a/futures/tests/try_join.rs +++ b/futures/tests/try_join.rs @@ -1,6 +1,6 @@ #![deny(unreachable_code)] -use futures::{try_join, executor::block_on}; +use futures::{executor::block_on, try_join}; // TODO: This abuses https://github.com/rust-lang/rust/issues/58733 in order to // test behaviour of the `try_join!` macro with the never type before it is @@ -14,7 +14,6 @@ impl MyTrait for fn() -> T { } type Never = ! as MyTrait>::Output; - #[test] fn try_join_never_error() { block_on(async { diff --git a/futures/tests/try_join_all.rs b/futures/tests/try_join_all.rs index 8e579a2800..d80a2c634a 100644 --- a/futures/tests/try_join_all.rs +++ b/futures/tests/try_join_all.rs @@ -1,27 +1,28 @@ -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); - } +use futures::executor::block_on; +use futures_util::future::{err, ok, try_join_all, TryJoinAll}; +use std::fmt::Debug; +use std::future::Future; + +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); } #[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), 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])); + assert_done( + || Box::new(try_join_all(vec![ok(1)])), + Ok::<_, usize>(vec![1]), + ); // REVIEW: should this be implemented? // assert_done(|| Box::new(try_join_all(Vec::::new())), Ok(vec![])); @@ -30,11 +31,6 @@ fn collect_collects() { #[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(bufs: Vec<&[u8]>) -> Box, ()>> + Unpin> { @@ -42,15 +38,14 @@ fn try_join_all_iter_lifetime() { Box::new(try_join_all(iter)) } - assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), Ok(vec![3_usize, 0, 1])); + assert_done( + || sizes(vec![&[1, 2, 3], &[], &[0]]), + Ok(vec![3_usize, 0, 1]), + ); } #[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]), diff --git a/futures/tests/unfold.rs b/futures/tests/unfold.rs index 95722cf8a6..16b10813b1 100644 --- a/futures/tests/unfold.rs +++ b/futures/tests/unfold.rs @@ -1,10 +1,7 @@ use futures::future; use futures::stream; - use futures_test::future::FutureTestExt; -use futures_test::{ - assert_stream_done, assert_stream_next, assert_stream_pending, -}; +use futures_test::{assert_stream_done, assert_stream_next, assert_stream_pending}; #[test] fn unfold1() { From e95a8fbfce0e38234f8b324a5cd363de7442858c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 27 Feb 2021 01:46:32 +0900 Subject: [PATCH 004/283] Unify test file naming If the test is related to a particular module, use the file name starting with the module name. --- futures/tests/{abortable.rs => future_abortable.rs} | 0 .../tests/{basic_combinators.rs => future_basic_combinators.rs} | 0 futures/tests/{fuse.rs => future_fuse.rs} | 0 futures/tests/{inspect.rs => future_inspect.rs} | 0 futures/tests/{join_all.rs => future_join_all.rs} | 0 futures/tests/{select_all.rs => future_select_all.rs} | 0 futures/tests/{select_ok.rs => future_select_ok.rs} | 0 futures/tests/{shared.rs => future_shared.rs} | 0 futures/tests/{try_join_all.rs => future_try_join_all.rs} | 0 futures/tests/{mutex.rs => lock_mutex.rs} | 0 futures/tests/{buffer_unordered.rs => stream_buffer_unordered.rs} | 0 futures/tests/{futures_ordered.rs => stream_futures_ordered.rs} | 0 .../tests/{futures_unordered.rs => stream_futures_unordered.rs} | 0 futures/tests/{split.rs => stream_split.rs} | 0 futures/tests/{try_stream.rs => stream_try_stream.rs} | 0 futures/tests/{unfold.rs => stream_unfold.rs} | 0 futures/tests/{arc_wake.rs => task_arc_wake.rs} | 0 futures/tests/{atomic_waker.rs => task_atomic_waker.rs} | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename futures/tests/{abortable.rs => future_abortable.rs} (100%) rename futures/tests/{basic_combinators.rs => future_basic_combinators.rs} (100%) rename futures/tests/{fuse.rs => future_fuse.rs} (100%) rename futures/tests/{inspect.rs => future_inspect.rs} (100%) rename futures/tests/{join_all.rs => future_join_all.rs} (100%) rename futures/tests/{select_all.rs => future_select_all.rs} (100%) rename futures/tests/{select_ok.rs => future_select_ok.rs} (100%) rename futures/tests/{shared.rs => future_shared.rs} (100%) rename futures/tests/{try_join_all.rs => future_try_join_all.rs} (100%) rename futures/tests/{mutex.rs => lock_mutex.rs} (100%) rename futures/tests/{buffer_unordered.rs => stream_buffer_unordered.rs} (100%) rename futures/tests/{futures_ordered.rs => stream_futures_ordered.rs} (100%) rename futures/tests/{futures_unordered.rs => stream_futures_unordered.rs} (100%) rename futures/tests/{split.rs => stream_split.rs} (100%) rename futures/tests/{try_stream.rs => stream_try_stream.rs} (100%) rename futures/tests/{unfold.rs => stream_unfold.rs} (100%) rename futures/tests/{arc_wake.rs => task_arc_wake.rs} (100%) rename futures/tests/{atomic_waker.rs => task_atomic_waker.rs} (100%) diff --git a/futures/tests/abortable.rs b/futures/tests/future_abortable.rs similarity index 100% rename from futures/tests/abortable.rs rename to futures/tests/future_abortable.rs diff --git a/futures/tests/basic_combinators.rs b/futures/tests/future_basic_combinators.rs similarity index 100% rename from futures/tests/basic_combinators.rs rename to futures/tests/future_basic_combinators.rs diff --git a/futures/tests/fuse.rs b/futures/tests/future_fuse.rs similarity index 100% rename from futures/tests/fuse.rs rename to futures/tests/future_fuse.rs diff --git a/futures/tests/inspect.rs b/futures/tests/future_inspect.rs similarity index 100% rename from futures/tests/inspect.rs rename to futures/tests/future_inspect.rs diff --git a/futures/tests/join_all.rs b/futures/tests/future_join_all.rs similarity index 100% rename from futures/tests/join_all.rs rename to futures/tests/future_join_all.rs diff --git a/futures/tests/select_all.rs b/futures/tests/future_select_all.rs similarity index 100% rename from futures/tests/select_all.rs rename to futures/tests/future_select_all.rs diff --git a/futures/tests/select_ok.rs b/futures/tests/future_select_ok.rs similarity index 100% rename from futures/tests/select_ok.rs rename to futures/tests/future_select_ok.rs diff --git a/futures/tests/shared.rs b/futures/tests/future_shared.rs similarity index 100% rename from futures/tests/shared.rs rename to futures/tests/future_shared.rs diff --git a/futures/tests/try_join_all.rs b/futures/tests/future_try_join_all.rs similarity index 100% rename from futures/tests/try_join_all.rs rename to futures/tests/future_try_join_all.rs diff --git a/futures/tests/mutex.rs b/futures/tests/lock_mutex.rs similarity index 100% rename from futures/tests/mutex.rs rename to futures/tests/lock_mutex.rs diff --git a/futures/tests/buffer_unordered.rs b/futures/tests/stream_buffer_unordered.rs similarity index 100% rename from futures/tests/buffer_unordered.rs rename to futures/tests/stream_buffer_unordered.rs diff --git a/futures/tests/futures_ordered.rs b/futures/tests/stream_futures_ordered.rs similarity index 100% rename from futures/tests/futures_ordered.rs rename to futures/tests/stream_futures_ordered.rs diff --git a/futures/tests/futures_unordered.rs b/futures/tests/stream_futures_unordered.rs similarity index 100% rename from futures/tests/futures_unordered.rs rename to futures/tests/stream_futures_unordered.rs diff --git a/futures/tests/split.rs b/futures/tests/stream_split.rs similarity index 100% rename from futures/tests/split.rs rename to futures/tests/stream_split.rs diff --git a/futures/tests/try_stream.rs b/futures/tests/stream_try_stream.rs similarity index 100% rename from futures/tests/try_stream.rs rename to futures/tests/stream_try_stream.rs diff --git a/futures/tests/unfold.rs b/futures/tests/stream_unfold.rs similarity index 100% rename from futures/tests/unfold.rs rename to futures/tests/stream_unfold.rs diff --git a/futures/tests/arc_wake.rs b/futures/tests/task_arc_wake.rs similarity index 100% rename from futures/tests/arc_wake.rs rename to futures/tests/task_arc_wake.rs diff --git a/futures/tests/atomic_waker.rs b/futures/tests/task_atomic_waker.rs similarity index 100% rename from futures/tests/atomic_waker.rs rename to futures/tests/task_atomic_waker.rs From 343e6c57d9de3e22cebf4b21f07dcd718f2c9b5c Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Mon, 1 Mar 2021 02:11:48 +0000 Subject: [PATCH 005/283] Document results of UnboundedReceiver::try_next --- futures-channel/src/mpsc/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index dd503436bf..854ba34935 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -1169,6 +1169,11 @@ impl UnboundedReceiver { /// only when you've otherwise arranged to be notified when the channel is /// no longer empty. /// + /// This function returns: + /// * `Ok(Some(t))` when message is fetched + /// * `Ok(None)` when channel is closed and no messages left in the queue + /// * `Err(e)` when there are no messages available, but channel is not yet closed + /// /// This function will panic if called after `try_next` or `poll_next` has /// returned `None`. pub fn try_next(&mut self) -> Result, TryRecvError> { From 53313b7e67ba4db7d4127a08f3a6bae89b55735d Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Mon, 1 Mar 2021 02:40:47 +0000 Subject: [PATCH 006/283] Add .editorconfig Add `.editorconfig` with sensible defaults. Many editors understand editorconfig, and the most important one here is IntelliJ IDEA which does not insert file trailing newline by default, but does it out of box this provided `.editorconfig`. --- .editorconfig | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..79a8d7d53f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*.rs] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 From caae2283adabcbfef13901b3ac284cff0e9780ed Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Mon, 1 Mar 2021 02:18:42 +0000 Subject: [PATCH 007/283] Allow calling UnboundedReceiver::try_next after None Allow calling `UnboundedReceiver::try_next` and `Receiver::try_next` after `None`: do not panic. Not-panicking is equally safe, and does not have negative performance implication. It is irrelevant for `Stream` implementation to panic or not (because `Stream` behavior is unspecified after `None`), but panicking in `try_next` just complicates the interface: returned `Ok(None)` is reasonable assumption to have. Consider this use case: drain the queue on drop by performing app-specific cleanup of queued messages. The obvious implementation would be: ``` impl Drop for MyReceiverWrapper { fn drop(&mut self) { while let Ok(Some(m)) self.try_next() { cleanup(m); } } } ``` Without this change, I cannot even say for sure how this code need to be implemented to avoid panicking. E. g. is `is_closed` enough or some additional checks need to be performed? --- futures-channel/src/mpsc/mod.rs | 16 ++++++++-------- futures-channel/tests/mpsc-close.rs | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 854ba34935..7442569eb3 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -1020,9 +1020,6 @@ impl Receiver { /// It is not recommended to call this function from inside of a future, /// only when you've otherwise arranged to be notified when the channel is /// no longer empty. - /// - /// This function will panic if called after `try_next` or `poll_next` has - /// returned `None`. pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { Poll::Ready(msg) => { @@ -1033,7 +1030,10 @@ impl Receiver { } fn next_message(&mut self) -> Poll> { - let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`"); + let inner = match self.inner.as_mut() { + None => return Poll::Ready(None), + Some(inner) => inner, + }; // Pop off a message match unsafe { inner.message_queue.pop_spin() } { Some(msg) => { @@ -1173,9 +1173,6 @@ impl UnboundedReceiver { /// * `Ok(Some(t))` when message is fetched /// * `Ok(None)` when channel is closed and no messages left in the queue /// * `Err(e)` when there are no messages available, but channel is not yet closed - /// - /// This function will panic if called after `try_next` or `poll_next` has - /// returned `None`. pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { Poll::Ready(msg) => { @@ -1186,7 +1183,10 @@ impl UnboundedReceiver { } fn next_message(&mut self) -> Poll> { - let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`"); + let inner = match self.inner.as_mut() { + None => return Poll::Ready(None), + Some(inner) => inner, + }; // Pop off a message match unsafe { inner.message_queue.pop_spin() } { Some(msg) => { diff --git a/futures-channel/tests/mpsc-close.rs b/futures-channel/tests/mpsc-close.rs index 9eb5296d88..0df78f7854 100644 --- a/futures-channel/tests/mpsc-close.rs +++ b/futures-channel/tests/mpsc-close.rs @@ -276,3 +276,25 @@ fn stress_try_send_as_receiver_closes() { bg.join() .expect("background thread join"); } + +#[test] +fn unbounded_try_next_after_none() { + let (tx, mut rx) = mpsc::unbounded::(); + // Drop the sender, close the channel. + drop(tx); + // Receive the end of channel. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); + // None received, check we can call `try_next` again. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); +} + +#[test] +fn bounded_try_next_after_none() { + let (tx, mut rx) = mpsc::channel::(17); + // Drop the sender, close the channel. + drop(tx); + // Receive the end of channel. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); + // None received, check we can call `try_next` again. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); +} From e21659e0ea931cfa4a17a0116922af4da8dd8ced Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Mon, 1 Mar 2021 03:18:16 +0000 Subject: [PATCH 008/283] Add Receiver::try_next return value descriptions Similar change applied to `UnboundedReceiver::try_next` a few commits ago. --- futures-channel/src/mpsc/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 7442569eb3..c32ad4b683 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -1020,6 +1020,11 @@ impl Receiver { /// It is not recommended to call this function from inside of a future, /// only when you've otherwise arranged to be notified when the channel is /// no longer empty. + /// + /// This function returns: + /// * `Ok(Some(t))` when message is fetched + /// * `Ok(None)` when channel is closed and no messages left in the queue + /// * `Err(e)` when there are no messages available, but channel is not yet closed pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { Poll::Ready(msg) => { From 48347802f2303ba3786ede0b44dccff1686650f8 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sun, 21 Mar 2021 01:39:20 +0800 Subject: [PATCH 009/283] Remove empty line from futures example --- futures/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index de29ace218..1c0f3fb8f9 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -29,7 +29,7 @@ //! # use futures::executor; ///standard executors to provide a context for futures and streams //! # use futures::executor::ThreadPool; //! # use futures::StreamExt; -//! +//! # //! fn main() { //! let pool = ThreadPool::new().expect("Failed to build pool"); //! let (tx, rx) = mpsc::unbounded::(); From 698e09a3e4bce1f0e0ea72bf639d1b81de5fe591 Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Mon, 22 Mar 2021 13:03:08 -0500 Subject: [PATCH 010/283] Reexport non-Ext traits from the root of futures_util --- futures-util/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 44823cc7da..512d793653 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -309,18 +309,18 @@ macro_rules! delegate_all { pub mod future; #[doc(hidden)] -pub use crate::future::{FutureExt, TryFutureExt}; +pub use crate::future::{Future, FutureExt, TryFuture, TryFutureExt}; pub mod stream; #[doc(hidden)] -pub use crate::stream::{StreamExt, TryStreamExt}; +pub use crate::stream::{Stream, StreamExt, TryStream, TryStreamExt}; #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub mod sink; #[cfg(feature = "sink")] #[doc(hidden)] -pub use crate::sink::SinkExt; +pub use crate::sink::{Sink, SinkExt}; pub mod task; @@ -337,7 +337,10 @@ pub mod io; #[cfg(feature = "io")] #[cfg(feature = "std")] #[doc(hidden)] -pub use crate::io::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; +pub use crate::io::{ + AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, + AsyncWriteExt, +}; #[cfg(feature = "alloc")] pub mod lock; From a4a0e05392a5bca220534918c4218ce4bcc3132d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 27 Mar 2021 01:04:23 +0900 Subject: [PATCH 011/283] Add AsyncSeekExt::stream_position --- futures-util/src/io/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 1437930f66..437aedf0c4 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -601,6 +601,17 @@ pub trait AsyncSeekExt: AsyncSeek { { assert_future::, _>(Seek::new(self, pos)) } + + /// Creates a future which will return the current seek position from the + /// start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + fn stream_position(&mut self) -> Seek<'_, Self> + where + Self: Unpin, + { + self.seek(SeekFrom::Current(0)) + } } impl AsyncSeekExt for S {} From e0d378fc227a53ec77d6630b8bc4ca0129c2261f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 27 Mar 2021 01:03:52 +0900 Subject: [PATCH 012/283] Add stream::Peekable::{next_if, next_if_eq} --- futures-util/src/stream/mod.rs | 2 +- futures-util/src/stream/stream/mod.rs | 2 +- futures-util/src/stream/stream/peek.rs | 228 ++++++++++++++++++++++++- futures/tests/stream_peekable.rs | 26 +++ 4 files changed, 248 insertions(+), 10 deletions(-) diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index f3b2baa408..ecffdc7e41 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -19,7 +19,7 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; mod stream; pub use self::stream::{ Chain, Collect, Concat, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, - Fuse, Inspect, Map, Next, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, + Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, }; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index c3340ecc65..b0f5b4b74c 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -123,7 +123,7 @@ pub use self::select_next_some::SelectNextSome; mod peek; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::peek::{Peek, Peekable}; +pub use self::peek::{Peek, Peekable, NextIf, NextIfEq}; mod skip; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index a4031102ab..21e84c110a 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -1,5 +1,7 @@ +use crate::fns::FnOnce1; use crate::stream::{Fuse, StreamExt}; use core::fmt; +use core::marker::PhantomData; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::ready; @@ -44,10 +46,7 @@ impl Peekable { /// /// 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. - pub fn poll_peek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + pub fn poll_peek(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -60,6 +59,96 @@ impl Peekable { } }) } + + /// Creates a future which will consume and return the next value of this + /// stream if a condition is true. + /// + /// If `func` returns `true` for the next value of this stream, consume and + /// return it. Otherwise, return `None`. + /// + /// # Examples + /// + /// Consume a number if it's equal to 0. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(0..5).peekable(); + /// pin_mut!(stream); + /// // The first item of the stream is 0; consume it. + /// assert_eq!(stream.as_mut().next_if(|&x| x == 0).await, Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(stream.as_mut().next_if(|&x| x == 0).await, None); + /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(stream.next().await, Some(1)); + /// # }); + /// ``` + /// + /// Consume any number less than 10. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(1..20).peekable(); + /// pin_mut!(stream); + /// // Consume all numbers less than 10 + /// while stream.as_mut().next_if(|&x| x < 10).await.is_some() {} + /// // The next value returned will be 10 + /// assert_eq!(stream.next().await, Some(10)); + /// # }); + /// ``` + pub fn next_if(self: Pin<&mut Self>, func: F) -> NextIf<'_, St, F> + where + F: FnOnce(&St::Item) -> bool, + { + NextIf { + inner: Some((self, func)), + } + } + + /// Creates a future which will consume and return the next item if it is + /// equal to `expected`. + /// + /// # Example + /// + /// Consume a number if it's equal to 0. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(0..5).peekable(); + /// pin_mut!(stream); + /// // The first item of the stream is 0; consume it. + /// assert_eq!(stream.as_mut().next_if_eq(&0).await, Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(stream.as_mut().next_if_eq(&0).await, None); + /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(stream.next().await, Some(1)); + /// # }); + /// ``` + pub fn next_if_eq<'a, T>(self: Pin<&'a mut Self>, expected: &'a T) -> NextIfEq<'a, St, T> + where + T: ?Sized, + St::Item: PartialEq, + { + NextIfEq { + inner: NextIf { + inner: Some(( + self, + NextIfEqFn { + expected, + _next: PhantomData, + }, + )), + }, + } + } } impl FusedStream for Peekable { @@ -103,7 +192,7 @@ where } pin_project! { - /// Future for the [`Peekable::peek()`](self::Peekable::peek) function from [`Peekable`] + /// Future for the [`Peekable::peek`](self::Peekable::peek) method. #[must_use = "futures do nothing unless polled"] pub struct Peek<'a, St: Stream> { inner: Option>>, @@ -116,9 +205,7 @@ where St::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Peek") - .field("inner", &self.inner) - .finish() + f.debug_struct("Peek").field("inner", &self.inner).finish() } } @@ -133,6 +220,7 @@ where St: Stream, { type Output = Option<&'a St::Item>; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let inner = self.project().inner; if let Some(peekable) = inner { @@ -144,3 +232,127 @@ where } } } + +pin_project! { + /// Future for the [`Peekable::next_if`](self::Peekable::next_if) method. + #[must_use = "futures do nothing unless polled"] + pub struct NextIf<'a, St: Stream, F> { + inner: Option<(Pin<&'a mut Peekable>, F)>, + } +} + +impl fmt::Debug for NextIf<'_, St, F> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NextIf") + .field("inner", &self.inner.as_ref().map(|(s, _f)| s)) + .finish() + } +} + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl FusedFuture for NextIf<'_, St, F> +where + St: Stream, + F: for<'a> FnOnce1<&'a St::Item, Output = bool>, +{ + fn is_terminated(&self) -> bool { + self.inner.is_none() + } +} + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl Future for NextIf<'_, St, F> +where + St: Stream, + F: for<'a> FnOnce1<&'a St::Item, Output = bool>, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some((peekable, _)) = inner { + let res = ready!(peekable.as_mut().poll_next(cx)); + + let (peekable, func) = inner.take().unwrap(); + match res { + Some(ref matched) if func.call_once(matched) => Poll::Ready(res), + other => { + let peekable = peekable.project(); + // Since we called `self.next()`, we consumed `self.peeked`. + assert!(peekable.peeked.is_none()); + *peekable.peeked = other; + Poll::Ready(None) + } + } + } else { + panic!("NextIf polled after completion") + } + } +} + +pin_project! { + /// Future for the [`Peekable::next_if_eq`](self::Peekable::next_if_eq) method. + #[must_use = "futures do nothing unless polled"] + pub struct NextIfEq<'a, St: Stream, T: ?Sized> { + #[pin] + inner: NextIf<'a, St, NextIfEqFn<'a, T, St::Item>>, + } +} + +impl fmt::Debug for NextIfEq<'_, St, T> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, + T: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NextIfEq") + .field("inner", &self.inner.inner.as_ref().map(|(s, _f)| s)) + .finish() + } +} + +impl FusedFuture for NextIfEq<'_, St, T> +where + St: Stream, + T: ?Sized, + St::Item: PartialEq, +{ + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } +} + +impl Future for NextIfEq<'_, St, T> +where + St: Stream, + T: ?Sized, + St::Item: PartialEq, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().inner.poll(cx) + } +} + +struct NextIfEqFn<'a, T: ?Sized, Item> { + expected: &'a T, + _next: PhantomData, +} + +impl FnOnce1<&Item> for NextIfEqFn<'_, T, Item> +where + T: ?Sized, + Item: PartialEq, +{ + type Output = bool; + + fn call_once(self, next: &Item) -> Self::Output { + next == self.expected + } +} diff --git a/futures/tests/stream_peekable.rs b/futures/tests/stream_peekable.rs index b65a0572cb..2fa7f3a4f5 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -11,3 +11,29 @@ fn peekable() { assert_eq!(peekable.collect::>().await, vec![1, 2, 3]); }); } + +#[test] +fn peekable_next_if_eq() { + block_on(async { + // first, try on references + let s = stream::iter(vec!["Heart", "of", "Gold"]).peekable(); + pin_mut!(s); + // try before `peek()` + assert_eq!(s.as_mut().next_if_eq(&"trillian").await, None); + assert_eq!(s.as_mut().next_if_eq(&"Heart").await, Some("Heart")); + // try after peek() + assert_eq!(s.as_mut().peek().await, Some(&"of")); + assert_eq!(s.as_mut().next_if_eq(&"of").await, Some("of")); + assert_eq!(s.as_mut().next_if_eq(&"zaphod").await, None); + // make sure `next()` still behaves + assert_eq!(s.next().await, Some("Gold")); + + // make sure comparison works for owned values + let s = stream::iter(vec![String::from("Ludicrous"), "speed".into()]).peekable(); + pin_mut!(s); + // make sure basic functionality works + assert_eq!(s.as_mut().next_if_eq("Ludicrous").await, Some("Ludicrous".into())); + assert_eq!(s.as_mut().next_if_eq("speed").await, Some("speed".into())); + assert_eq!(s.as_mut().next_if_eq("").await, None); + }); +} From 071a43419199853c95eacbd19dd3efc66dee4d80 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 10 Apr 2021 14:34:17 +0900 Subject: [PATCH 013/283] Link to modules instead of traits --- futures/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 1c0f3fb8f9..58885c9cc9 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -3,12 +3,12 @@ //! This crate provides a number of core abstractions for writing asynchronous //! code: //! -//! - [Futures](crate::future::Future) are single eventual values produced by +//! - [Futures](crate::future) are single eventual values produced by //! asynchronous computations. Some programming languages (e.g. JavaScript) //! call this concept "promise". -//! - [Streams](crate::stream::Stream) represent a series of values +//! - [Streams](crate::stream) represent a series of values //! produced asynchronously. -//! - [Sinks](crate::sink::Sink) provide support for asynchronous writing of +//! - [Sinks](crate::sink) provide support for asynchronous writing of //! data. //! - [Executors](crate::executor) are responsible for running asynchronous //! tasks. From 8aecdeb9bb8110fda6fa0941159d6431a4b9e95e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 10 Apr 2021 15:39:34 +0900 Subject: [PATCH 014/283] Release 0.3.14 --- CHANGELOG.md | 7 +++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 14 +++++++------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 41 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f5679f90..4f2aea765b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.3.14 - 2021-04-10 +- Add `future::SelectAll::into_inner` (#2363) +- Allow calling `UnboundedReceiver::try_next` after `None` (#2369) +- Reexport non-Ext traits from the root of `futures_util` (#2377) +- Add `AsyncSeekExt::stream_position` (#2380) +- Add `stream::Peekable::{next_if, next_if_eq}` (#2379) + # 0.3.13 - 2021-02-23 - Mitigated starvation issues in `FuturesUnordered` (#2333) - Fixed race with dropping `mpsc::Receiver` (#2304) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 9a33320a16..d7843e7d5e 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-channel" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -24,8 +24,8 @@ unstable = ["futures-core/unstable"] cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.13", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.13", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 55601dbb6e..e4c42f717e 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-core" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index bc1853b01e..225b7c0070 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-executor" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -17,9 +17,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.13", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.13", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.13", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.14", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 2edbdc37ba..329f09f3bf 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-io" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 090f73608d..4ab6860bb3 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-macro" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 59853a985d..6cf7802660 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-sink" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 764a692c24..68abccd6a9 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-task" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index d7a39212af..4835f447b0 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-test" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -12,12 +12,12 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.13", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.13", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.13", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.13", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.13", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.13", path = "../futures-sink", default-features = false } +futures-core = { version = "0.3.14", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.14", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.14", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.14", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.14", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.14", path = "../futures-sink", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index b7cc19370e..424e372630 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-util" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -33,12 +33,12 @@ read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] write-all-vectored = ["io"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.13", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.13", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.13", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.13", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.13", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.13", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.14", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.14", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.14", default-features = false, optional = true } proc-macro-hack = { version = "0.5.19", optional = true } proc-macro-nested = { version = "0.1.2", optional = true } slab = { version = "0.4.2", optional = true } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index d046c54215..a0a64bfece 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures" edition = "2018" -version = "0.3.13" +version = "0.3.14" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" @@ -16,13 +16,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.13", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.13", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.13", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.13", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.13", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.13", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.13", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.14", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.14", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.14", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.14", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From a21c0b7d8902e65b04e2645d82602d8f0a8cb5bf Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 24 Apr 2021 01:24:02 +0900 Subject: [PATCH 015/283] Format with rustfmt --- .rustfmt.toml | 2 +- examples/functional/src/main.rs | 6 +- examples/imperative/src/main.rs | 4 +- futures-channel/benches/sync_mpsc.rs | 15 +- futures-channel/src/lib.rs | 2 - futures-channel/tests/channel.rs | 10 +- futures-channel/tests/mpsc-close.rs | 28 ++- futures-channel/tests/mpsc.rs | 33 ++-- futures-channel/tests/oneshot.rs | 4 +- futures-core/src/future.rs | 10 +- futures-core/src/lib.rs | 8 +- futures-core/src/stream.rs | 43 ++-- .../src/task/__internal/atomic_waker.rs | 19 +- futures-core/src/task/mod.rs | 2 +- futures-core/src/task/poll.rs | 11 +- futures-executor/benches/thread_notify.rs | 5 +- futures-executor/src/lib.rs | 8 +- futures-executor/src/local_pool.rs | 14 +- futures-executor/src/thread_pool.rs | 49 ++--- futures-executor/src/unpark_mutex.rs | 27 +-- futures-executor/tests/local_pool.rs | 187 +++++++++++------- futures-io/src/lib.rs | 187 ++++++++++-------- futures-macro/src/join.rs | 5 +- futures-macro/src/select.rs | 42 ++-- futures-sink/src/lib.rs | 15 +- futures-task/src/future_obj.rs | 45 ++--- futures-task/src/lib.rs | 6 +- futures-task/src/spawn.rs | 2 +- futures-test/src/assert.rs | 12 +- futures-test/src/assert_unmoved.rs | 10 +- futures-test/src/future/pending_once.rs | 14 +- futures-test/src/interleave_pending.rs | 5 +- futures-test/src/io/limited.rs | 15 +- futures-test/src/lib.rs | 8 +- futures-test/src/task/context.rs | 2 +- futures-test/src/task/mod.rs | 2 +- futures-test/src/task/wake_counter.rs | 2 +- futures-test/src/track_closed.rs | 5 +- futures-util/benches/futures_unordered.rs | 4 +- futures-util/src/async_await/mod.rs | 4 +- futures-util/src/async_await/pending.rs | 2 +- futures-util/src/async_await/poll.rs | 2 +- futures-util/src/compat/compat01as03.rs | 75 +++---- futures-util/src/compat/compat03as01.rs | 56 ++---- futures-util/src/compat/executor.rs | 4 +- futures-util/src/compat/mod.rs | 8 +- futures-util/src/fns.rs | 61 +++--- .../src/future/future/catch_unwind.rs | 10 +- futures-util/src/future/future/flatten.rs | 47 +++-- futures-util/src/future/future/fuse.rs | 4 +- .../src/future/future/remote_handle.rs | 12 +- futures-util/src/future/future/shared.rs | 29 +-- futures-util/src/future/join.rs | 11 +- futures-util/src/future/join_all.rs | 18 +- futures-util/src/future/lazy.rs | 15 +- futures-util/src/future/mod.rs | 6 +- futures-util/src/future/option.rs | 7 +- futures-util/src/future/pending.rs | 7 +- futures-util/src/future/poll_fn.rs | 5 +- futures-util/src/future/select.rs | 18 +- futures-util/src/future/select_all.rs | 19 +- futures-util/src/future/select_ok.rs | 29 +-- .../src/future/try_future/into_future.rs | 9 +- .../src/future/try_future/try_flatten.rs | 77 ++++---- .../src/future/try_future/try_flatten_err.rs | 24 +-- futures-util/src/future/try_join_all.rs | 31 ++- futures-util/src/future/try_maybe_done.rs | 18 +- futures-util/src/future/try_select.rs | 25 +-- futures-util/src/io/allow_std.rs | 90 ++++++--- futures-util/src/io/buf_reader.rs | 17 +- futures-util/src/io/buf_writer.rs | 8 +- futures-util/src/io/chain.rs | 6 +- futures-util/src/io/copy.rs | 8 +- futures-util/src/io/copy_buf.rs | 15 +- futures-util/src/io/cursor.rs | 22 ++- futures-util/src/io/fill_buf.rs | 3 +- futures-util/src/io/flush.rs | 3 +- futures-util/src/io/into_sink.rs | 30 +-- futures-util/src/io/lines.rs | 13 +- futures-util/src/io/mod.rs | 94 +++++---- futures-util/src/io/read_exact.rs | 4 +- futures-util/src/io/read_line.rs | 11 +- futures-util/src/io/read_to_end.rs | 11 +- futures-util/src/io/read_to_string.rs | 14 +- futures-util/src/io/split.rs | 53 ++--- futures-util/src/io/take.rs | 4 +- futures-util/src/io/window.rs | 5 +- futures-util/src/io/write_all_vectored.rs | 21 +- futures-util/src/lib.rs | 7 +- futures-util/src/sink/buffer.rs | 47 ++--- futures-util/src/sink/close.rs | 10 +- futures-util/src/sink/drain.rs | 20 +- futures-util/src/sink/err_into.rs | 28 +-- futures-util/src/sink/fanout.rs | 50 +++-- futures-util/src/sink/feed.rs | 10 +- futures-util/src/sink/flush.rs | 10 +- futures-util/src/sink/map_err.rs | 29 +-- futures-util/src/sink/send.rs | 9 +- futures-util/src/sink/send_all.rs | 35 ++-- futures-util/src/sink/unfold.rs | 5 +- futures-util/src/sink/with.rs | 75 +++---- futures-util/src/sink/with_flat_map.rs | 35 +--- futures-util/src/stream/empty.rs | 6 +- futures-util/src/stream/iter.rs | 10 +- futures-util/src/stream/mod.rs | 4 +- futures-util/src/stream/once.rs | 2 +- futures-util/src/stream/repeat.rs | 11 +- futures-util/src/stream/repeat_with.rs | 5 +- futures-util/src/stream/select.rs | 36 ++-- .../src/stream/stream/catch_unwind.rs | 15 +- futures-util/src/stream/stream/chain.rs | 29 ++- futures-util/src/stream/stream/chunks.rs | 20 +- futures-util/src/stream/stream/collect.rs | 15 +- futures-util/src/stream/stream/concat.rs | 35 ++-- futures-util/src/stream/stream/cycle.rs | 5 +- futures-util/src/stream/stream/enumerate.rs | 10 +- futures-util/src/stream/stream/filter.rs | 42 ++-- futures-util/src/stream/stream/filter_map.rs | 35 ++-- futures-util/src/stream/stream/fold.rs | 28 ++- futures-util/src/stream/stream/for_each.rs | 27 ++- futures-util/src/stream/stream/forward.rs | 15 +- futures-util/src/stream/stream/fuse.rs | 5 +- futures-util/src/stream/stream/into_future.rs | 5 +- futures-util/src/stream/stream/map.rs | 24 ++- futures-util/src/stream/stream/mod.rs | 2 +- futures-util/src/stream/stream/next.rs | 5 +- futures-util/src/stream/stream/peek.rs | 23 +-- .../src/stream/stream/ready_chunks.rs | 23 ++- futures-util/src/stream/stream/scan.rs | 9 +- .../src/stream/stream/select_next_some.rs | 4 +- futures-util/src/stream/stream/skip.rs | 10 +- futures-util/src/stream/stream/skip_while.rs | 41 ++-- futures-util/src/stream/stream/take.rs | 23 +-- futures-util/src/stream/stream/take_until.rs | 12 +- futures-util/src/stream/stream/take_while.rs | 39 ++-- futures-util/src/stream/stream/then.rs | 38 ++-- futures-util/src/stream/stream/unzip.rs | 27 ++- futures-util/src/stream/stream/zip.rs | 24 +-- .../src/stream/try_stream/and_then.rs | 31 +-- .../src/stream/try_stream/into_async_read.rs | 81 +++----- .../src/stream/try_stream/into_stream.rs | 5 +- futures-util/src/stream/try_stream/mod.rs | 17 +- futures-util/src/stream/try_stream/or_else.rs | 33 ++-- .../src/stream/try_stream/try_collect.rs | 10 +- .../src/stream/try_stream/try_concat.rs | 5 +- .../src/stream/try_stream/try_filter.rs | 34 ++-- .../src/stream/try_stream/try_filter_map.rs | 26 +-- .../src/stream/try_stream/try_fold.rs | 28 ++- .../src/stream/try_stream/try_for_each.rs | 20 +- .../src/stream/try_stream/try_next.rs | 5 +- .../src/stream/try_stream/try_skip_while.rs | 39 ++-- .../src/stream/try_stream/try_take_while.rs | 8 +- .../src/stream/try_stream/try_unfold.rs | 16 +- futures-util/src/stream/unfold.rs | 13 +- futures-util/src/task/mod.rs | 9 +- futures/src/lib.rs | 3 - futures/tests/_require_features.rs | 13 +- futures/tests/compat.rs | 12 +- futures/tests/eager_drop.rs | 51 ++--- futures/tests/future_basic_combinators.rs | 14 +- futures/tests/future_shared.rs | 9 +- futures/tests/future_try_join_all.rs | 15 +- futures/tests/io_buf_reader.rs | 40 +--- futures/tests/io_buf_writer.rs | 31 +-- futures/tests/io_cursor.rs | 40 +--- futures/tests/io_lines.rs | 6 +- futures/tests/io_read_line.rs | 6 +- futures/tests/io_write.rs | 6 +- futures/tests/macro-reexport/src/lib.rs | 5 +- futures/tests/macro-tests/src/main.rs | 1 - futures/tests/oneshot.rs | 12 +- futures/tests/ready_queue.rs | 10 +- futures/tests/sink.rs | 46 ++--- futures/tests/stream.rs | 15 +- futures/tests/stream_futures_ordered.rs | 23 +-- futures/tests/stream_futures_unordered.rs | 56 ++---- futures/tests/stream_split.rs | 5 +- futures/tests/task_arc_wake.rs | 9 +- futures/tests/task_atomic_waker.rs | 1 - 179 files changed, 1593 insertions(+), 2183 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index c7ad93bafe..2a35f0230c 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1 +1 @@ -disable_all_formatting = true +use_small_heuristics = "Max" diff --git a/examples/functional/src/main.rs b/examples/functional/src/main.rs index 3ce65de66a..7dea28d674 100644 --- a/examples/functional/src/main.rs +++ b/examples/functional/src/main.rs @@ -30,9 +30,7 @@ fn main() { // responsible for transmission pool.spawn_ok(fut_tx_result); - let fut_values = rx - .map(|v| v * 2) - .collect(); + let fut_values = rx.map(|v| v * 2).collect(); // Use the executor provided to this async block to wait for the // future to complete. @@ -45,4 +43,4 @@ fn main() { let values: Vec = executor::block_on(fut_values); println!("Values={:?}", values); -} \ No newline at end of file +} diff --git a/examples/imperative/src/main.rs b/examples/imperative/src/main.rs index ff1afffe27..10ef7f4df5 100644 --- a/examples/imperative/src/main.rs +++ b/examples/imperative/src/main.rs @@ -34,7 +34,7 @@ fn main() { // of the stream to be available. while let Some(v) = rx.next().await { pending.push(v * 2); - }; + } pending }; @@ -45,4 +45,4 @@ fn main() { let values: Vec = executor::block_on(fut_values); println!("Values={:?}", values); -} \ No newline at end of file +} diff --git a/futures-channel/benches/sync_mpsc.rs b/futures-channel/benches/sync_mpsc.rs index e22fe60666..7c3c3d3a80 100644 --- a/futures-channel/benches/sync_mpsc.rs +++ b/futures-channel/benches/sync_mpsc.rs @@ -7,8 +7,8 @@ use { futures::{ channel::mpsc::{self, Sender, UnboundedSender}, ready, - stream::{Stream, StreamExt}, sink::Sink, + stream::{Stream, StreamExt}, task::{Context, Poll}, }, futures_test::task::noop_context, @@ -25,7 +25,6 @@ fn unbounded_1_tx(b: &mut Bencher) { // 1000 iterations to avoid measuring overhead of initialization // Result should be divided by 1000 for i in 0..1000 { - // Poll, not ready, park assert_eq!(Poll::Pending, rx.poll_next_unpin(&mut cx)); @@ -73,7 +72,6 @@ fn unbounded_uncontended(b: &mut Bencher) { }) } - /// A Stream that continuously sends incrementing number of the queue struct TestSender { tx: Sender, @@ -84,9 +82,7 @@ struct TestSender { impl Stream for TestSender { type Item = u32; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = &mut *self; let mut tx = Pin::new(&mut this.tx); @@ -123,12 +119,7 @@ fn bounded_100_tx(b: &mut Bencher) { // Each sender can send one item after specified capacity let (tx, mut rx) = mpsc::channel(0); - let mut tx: Vec<_> = (0..100).map(|_| { - TestSender { - tx: tx.clone(), - last: 0 - } - }).collect(); + let mut tx: Vec<_> = (0..100).map(|_| TestSender { tx: tx.clone(), last: 0 }).collect(); for i in 0..10 { for x in &mut tx { diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 22d90d8a63..41a4a19af7 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -12,9 +12,7 @@ //! library is activated, and it is activated by default. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] diff --git a/futures-channel/tests/channel.rs b/futures-channel/tests/channel.rs index 73dac645b1..5f01a8ef4c 100644 --- a/futures-channel/tests/channel.rs +++ b/futures-channel/tests/channel.rs @@ -1,8 +1,8 @@ use futures::channel::mpsc; use futures::executor::block_on; use futures::future::poll_fn; -use futures::stream::StreamExt; use futures::sink::SinkExt; +use futures::stream::StreamExt; use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; @@ -11,9 +11,7 @@ fn sequence() { let (tx, rx) = mpsc::channel(1); let amt = 20; - let t = thread::spawn(move || { - block_on(send_sequence(amt, tx)) - }); + let t = thread::spawn(move || block_on(send_sequence(amt, tx))); let list: Vec<_> = block_on(rx.collect()); let mut list = list.into_iter(); for i in (1..=amt).rev() { @@ -34,9 +32,7 @@ async fn send_sequence(n: u32, mut sender: mpsc::Sender) { fn drop_sender() { let (tx, mut rx) = mpsc::channel::(1); drop(tx); - let f = poll_fn(|cx| { - rx.poll_next_unpin(cx) - }); + let f = poll_fn(|cx| rx.poll_next_unpin(cx)); assert_eq!(block_on(f), None) } diff --git a/futures-channel/tests/mpsc-close.rs b/futures-channel/tests/mpsc-close.rs index 0df78f7854..81203d3348 100644 --- a/futures-channel/tests/mpsc-close.rs +++ b/futures-channel/tests/mpsc-close.rs @@ -13,9 +13,7 @@ use std::time::{Duration, Instant}; fn smoke() { let (mut sender, receiver) = mpsc::channel(1); - let t = thread::spawn(move || { - while let Ok(()) = block_on(sender.send(42)) {} - }); + let t = thread::spawn(move || while let Ok(()) = block_on(sender.send(42)) {}); // `receiver` needs to be dropped for `sender` to stop sending and therefore before the join. block_on(receiver.take(3).for_each(|_| futures::future::ready(()))); @@ -166,7 +164,7 @@ fn stress_try_send_as_receiver_closes() { struct TestRx { rx: mpsc::Receiver>, // The number of times to query `rx` before dropping it. - poll_count: usize + poll_count: usize, } struct TestTask { command_rx: mpsc::Receiver, @@ -190,14 +188,11 @@ fn stress_try_send_as_receiver_closes() { impl Future for TestTask { type Output = (); - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // Poll the test channel, if one is present. if let Some(rx) = &mut self.test_rx { if let Poll::Ready(v) = rx.poll_next_unpin(cx) { - let _ = v.expect("test finished unexpectedly!"); + let _ = v.expect("test finished unexpectedly!"); } self.countdown -= 1; // Busy-poll until the countdown is finished. @@ -209,9 +204,9 @@ fn stress_try_send_as_receiver_closes() { self.test_rx = Some(rx); self.countdown = poll_count; cx.waker().wake_by_ref(); - }, + } Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => {}, + Poll::Pending => {} } if self.countdown == 0 { // Countdown complete -- drop the Receiver. @@ -255,10 +250,14 @@ fn stress_try_send_as_receiver_closes() { if prev_weak.upgrade().is_none() { break; } - assert!(t0.elapsed() < Duration::from_secs(SPIN_TIMEOUT_S), + assert!( + t0.elapsed() < Duration::from_secs(SPIN_TIMEOUT_S), "item not dropped on iteration {} after \ {} sends ({} successful). spin=({})", - i, attempted_sends, successful_sends, spins + i, + attempted_sends, + successful_sends, + spins ); spins += 1; thread::sleep(Duration::from_millis(SPIN_SLEEP_MS)); @@ -273,8 +272,7 @@ fn stress_try_send_as_receiver_closes() { } } drop(cmd_tx); - bg.join() - .expect("background thread join"); + bg.join().expect("background thread join"); } #[test] diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index 61c5a50246..88cdef13d6 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -1,13 +1,13 @@ use futures::channel::{mpsc, oneshot}; use futures::executor::{block_on, block_on_stream}; -use futures::future::{FutureExt, poll_fn}; -use futures::stream::{Stream, StreamExt}; +use futures::future::{poll_fn, FutureExt}; +use futures::pin_mut; use futures::sink::{Sink, SinkExt}; +use futures::stream::{Stream, StreamExt}; use futures::task::{Context, Poll}; -use futures::pin_mut; use futures_test::task::{new_count_waker, noop_context}; -use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; use std::thread; trait AssertSend: Send {} @@ -77,7 +77,7 @@ fn send_shared_recv() { fn send_recv_threads() { let (mut tx, rx) = mpsc::channel::(16); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { block_on(tx.send(1)).unwrap(); }); @@ -204,7 +204,7 @@ fn stress_shared_unbounded() { const NTHREADS: u32 = 8; let (tx, rx) = mpsc::unbounded::(); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { let result: Vec<_> = block_on(rx.collect()); assert_eq!(result.len(), (AMT * NTHREADS) as usize); for item in result { @@ -215,7 +215,7 @@ fn stress_shared_unbounded() { for _ in 0..NTHREADS { let tx = tx.clone(); - thread::spawn(move|| { + thread::spawn(move || { for _ in 0..AMT { tx.unbounded_send(1).unwrap(); } @@ -233,7 +233,7 @@ fn stress_shared_bounded_hard() { const NTHREADS: u32 = 8; let (tx, rx) = mpsc::channel::(0); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { let result: Vec<_> = block_on(rx.collect()); assert_eq!(result.len(), (AMT * NTHREADS) as usize); for item in result { @@ -297,9 +297,9 @@ fn stress_receiver_multi_task_bounded_hard() { } Poll::Ready(None) => { *rx_opt = None; - break - }, - Poll::Pending => {}, + break; + } + Poll::Pending => {} } } } else { @@ -311,7 +311,6 @@ fn stress_receiver_multi_task_bounded_hard() { th.push(t); } - for i in 0..AMT { block_on(tx.send(i)).unwrap(); } @@ -328,7 +327,7 @@ fn stress_receiver_multi_task_bounded_hard() { /// after sender dropped. #[test] fn stress_drop_sender() { - fn list() -> impl Stream { + fn list() -> impl Stream { let (tx, rx) = mpsc::channel(1); thread::spawn(move || { block_on(send_one_two_three(tx)); @@ -407,9 +406,7 @@ fn stress_poll_ready() { let mut threads = Vec::new(); for _ in 0..NTHREADS { let sender = tx.clone(); - threads.push(thread::spawn(move || { - block_on(stress_poll_ready_sender(sender, AMT)) - })); + threads.push(thread::spawn(move || block_on(stress_poll_ready_sender(sender, AMT)))); } drop(tx); @@ -436,7 +433,7 @@ fn try_send_1() { for i in 0..N { loop { if tx.try_send(i).is_ok() { - break + break; } } } @@ -542,8 +539,8 @@ fn is_connected_to() { #[test] fn hash_receiver() { - use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; + use std::hash::Hasher; let mut hasher_a1 = DefaultHasher::new(); let mut hasher_a2 = DefaultHasher::new(); diff --git a/futures-channel/tests/oneshot.rs b/futures-channel/tests/oneshot.rs index a22d039e40..979cd8a15a 100644 --- a/futures-channel/tests/oneshot.rs +++ b/futures-channel/tests/oneshot.rs @@ -1,6 +1,6 @@ use futures::channel::oneshot::{self, Sender}; use futures::executor::block_on; -use futures::future::{FutureExt, poll_fn}; +use futures::future::{poll_fn, FutureExt}; use futures::task::{Context, Poll}; use futures_test::task::panic_waker_ref; use std::sync::mpsc; @@ -70,7 +70,7 @@ fn close() { rx.close(); block_on(poll_fn(|cx| { match rx.poll_unpin(cx) { - Poll::Ready(Err(_)) => {}, + Poll::Ready(Err(_)) => {} _ => panic!(), }; assert!(tx.poll_canceled(cx).is_ready()); diff --git a/futures-core/src/future.rs b/futures-core/src/future.rs index d9327c01e5..7540cd027e 100644 --- a/futures-core/src/future.rs +++ b/futures-core/src/future.rs @@ -67,14 +67,12 @@ pub trait TryFuture: Future + private_try_future::Sealed { /// This method is a stopgap for a compiler limitation that prevents us from /// directly inheriting from the `Future` trait; in the future it won't be /// needed. - fn try_poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>; + fn try_poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } impl TryFuture for F - where F: ?Sized + Future> +where + F: ?Sized + Future>, { type Ok = T; type Error = E; @@ -87,8 +85,8 @@ impl TryFuture for F #[cfg(feature = "alloc")] mod if_alloc { - use alloc::boxed::Box; use super::*; + use alloc::boxed::Box; impl FusedFuture for Box { fn is_terminated(&self) -> bool { diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index ec14adba22..f3bd9ab928 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,9 +1,7 @@ //! Core traits and types for asynchronous operations in Rust. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] @@ -17,10 +15,12 @@ compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feat extern crate alloc; pub mod future; -#[doc(hidden)] pub use self::future::{Future, FusedFuture, TryFuture}; +#[doc(hidden)] +pub use self::future::{FusedFuture, Future, TryFuture}; pub mod stream; -#[doc(hidden)] pub use self::stream::{Stream, FusedStream, TryStream}; +#[doc(hidden)] +pub use self::stream::{FusedStream, Stream, TryStream}; #[macro_use] pub mod task; diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs index 4a13e3bd7d..ad5350b795 100644 --- a/futures-core/src/stream.rs +++ b/futures-core/src/stream.rs @@ -63,10 +63,7 @@ pub trait Stream { /// 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<'_>, - ) -> Poll>; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Returns the bounds on the remaining length of the stream. /// @@ -103,10 +100,7 @@ pub trait Stream { impl Stream for &mut S { type Item = S::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { S::poll_next(Pin::new(&mut **self), cx) } @@ -122,10 +116,7 @@ where { type Item = ::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut().as_mut().poll_next(cx) } @@ -185,35 +176,36 @@ pub trait TryStream: Stream + private_try_stream::Sealed { /// This method is a stopgap for a compiler limitation that prevents us from /// directly inheriting from the `Stream` trait; in the future it won't be /// needed. - fn try_poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>>; + fn try_poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>>; } impl TryStream for S - where S: ?Sized + Stream> +where + S: ?Sized + Stream>, { type Ok = T; type Error = E; - fn try_poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>> - { + fn try_poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { self.poll_next(cx) } } #[cfg(feature = "alloc")] mod if_alloc { - use alloc::boxed::Box; use super::*; + use alloc::boxed::Box; impl Stream for Box { type Item = S::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self).poll_next(cx) } @@ -226,10 +218,7 @@ mod if_alloc { impl Stream for std::panic::AssertUnwindSafe { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx) } diff --git a/futures-core/src/task/__internal/atomic_waker.rs b/futures-core/src/task/__internal/atomic_waker.rs index 213355bc6b..d49d043619 100644 --- a/futures-core/src/task/__internal/atomic_waker.rs +++ b/futures-core/src/task/__internal/atomic_waker.rs @@ -1,7 +1,7 @@ use core::cell::UnsafeCell; use core::fmt; use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering::{Acquire, Release, AcqRel}; +use core::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use core::task::Waker; /// A synchronization primitive for task wakeup. @@ -202,10 +202,7 @@ impl AtomicWaker { trait AssertSync: Sync {} impl AssertSync for Waker {} - Self { - state: AtomicUsize::new(WAITING), - waker: UnsafeCell::new(None), - } + Self { state: AtomicUsize::new(WAITING), waker: UnsafeCell::new(None) } } /// Registers the waker to be notified on calls to `wake`. @@ -279,8 +276,7 @@ impl AtomicWaker { // nothing to acquire, only release. In case of concurrent // wakers, we need to acquire their releases, so success needs // to do both. - let res = self.state.compare_exchange( - REGISTERING, WAITING, AcqRel, Acquire); + let res = self.state.compare_exchange(REGISTERING, WAITING, AcqRel, Acquire); match res { Ok(_) => { @@ -344,9 +340,7 @@ impl AtomicWaker { // // We just want to maintain memory safety. It is ok to drop the // call to `register`. - debug_assert!( - state == REGISTERING || - state == REGISTERING | WAKING); + debug_assert!(state == REGISTERING || state == REGISTERING | WAKING); } } } @@ -391,9 +385,8 @@ impl AtomicWaker { // not. // debug_assert!( - state == REGISTERING || - state == REGISTERING | WAKING || - state == WAKING); + state == REGISTERING || state == REGISTERING | WAKING || state == WAKING + ); None } } diff --git a/futures-core/src/task/mod.rs b/futures-core/src/task/mod.rs index f945d5da75..19e4eaecdd 100644 --- a/futures-core/src/task/mod.rs +++ b/futures-core/src/task/mod.rs @@ -7,4 +7,4 @@ mod poll; pub mod __internal; #[doc(no_inline)] -pub use core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; diff --git a/futures-core/src/task/poll.rs b/futures-core/src/task/poll.rs index 8fe294c580..930d888d2d 100644 --- a/futures-core/src/task/poll.rs +++ b/futures-core/src/task/poll.rs @@ -3,9 +3,10 @@ /// This macro bakes in propagation of `Pending` signals by returning early. #[macro_export] macro_rules! ready { - ($e:expr $(,)?) => (match $e { - $crate::__private::Poll::Ready(t) => t, - $crate::__private::Poll::Pending => - return $crate::__private::Poll::Pending, - }) + ($e:expr $(,)?) => { + match $e { + $crate::__private::Poll::Ready(t) => t, + $crate::__private::Poll::Pending => return $crate::__private::Poll::Pending, + } + }; } diff --git a/futures-executor/benches/thread_notify.rs b/futures-executor/benches/thread_notify.rs index d8fbec4555..88d0447cf6 100644 --- a/futures-executor/benches/thread_notify.rs +++ b/futures-executor/benches/thread_notify.rs @@ -102,10 +102,7 @@ fn thread_yield_multi_thread(b: &mut Bencher) { }); b.iter(move || { - let y = Yield { - rem: NUM, - tx: tx.clone(), - }; + let y = Yield { rem: NUM, tx: tx.clone() }; block_on(y); }); diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index b6796490af..873ac7a684 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -37,13 +37,11 @@ //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] - #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(feature = "std")] @@ -51,14 +49,14 @@ mod local_pool; #[cfg(feature = "std")] pub use crate::local_pool::{block_on, block_on_stream, BlockingStream, LocalPool, LocalSpawner}; -#[cfg(feature = "thread-pool")] -#[cfg(feature = "std")] -mod unpark_mutex; #[cfg(feature = "thread-pool")] #[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] #[cfg(feature = "std")] mod thread_pool; #[cfg(feature = "thread-pool")] +#[cfg(feature = "std")] +mod unpark_mutex; +#[cfg(feature = "thread-pool")] #[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] #[cfg(feature = "std")] pub use crate::thread_pool::{ThreadPool, ThreadPoolBuilder}; diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index 156d5cc642..bee96d8db9 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -10,7 +10,10 @@ use futures_util::stream::StreamExt; use std::cell::RefCell; use std::ops::{Deref, DerefMut}; use std::rc::{Rc, Weak}; -use std::sync::{Arc, atomic::{AtomicBool, Ordering}}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; use std::thread::{self, Thread}; /// A single-threaded task pool for polling futures to completion. @@ -119,17 +122,12 @@ fn poll_executor) -> T>(mut f: F) -> T { impl LocalPool { /// Create a new, empty pool of tasks. pub fn new() -> Self { - Self { - pool: FuturesUnordered::new(), - incoming: Default::default(), - } + Self { pool: FuturesUnordered::new(), incoming: Default::default() } } /// Get a clonable handle to the pool as a [`Spawn`]. pub fn spawner(&self) -> LocalSpawner { - LocalSpawner { - incoming: Rc::downgrade(&self.incoming), - } + LocalSpawner { incoming: Rc::downgrade(&self.incoming) } } /// Run all tasks in the pool to completion. diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 741e6d9c6c..f2347dbbdf 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -2,8 +2,8 @@ use crate::enter; use crate::unpark_mutex::UnparkMutex; use futures_core::future::Future; use futures_core::task::{Context, Poll}; +use futures_task::{waker_ref, ArcWake}; use futures_task::{FutureObj, Spawn, SpawnError}; -use futures_task::{ArcWake, waker_ref}; use futures_util::future::FutureExt; use std::cmp; use std::fmt; @@ -54,9 +54,7 @@ struct PoolState { impl fmt::Debug for ThreadPool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ThreadPool") - .field("size", &self.state.size) - .finish() + f.debug_struct("ThreadPool").field("size", &self.state.size).finish() } } @@ -100,10 +98,7 @@ impl ThreadPool { pub fn spawn_obj_ok(&self, future: FutureObj<'static, ()>) { let task = Task { future, - wake_handle: Arc::new(WakeHandle { - exec: self.clone(), - mutex: UnparkMutex::new(), - }), + wake_handle: Arc::new(WakeHandle { exec: self.clone(), mutex: UnparkMutex::new() }), exec: self.clone(), }; self.state.send(Message::Run(task)); @@ -132,10 +127,7 @@ impl ThreadPool { } impl Spawn for ThreadPool { - fn spawn_obj( - &self, - future: FutureObj<'static, ()>, - ) -> Result<(), SpawnError> { + fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { self.spawn_obj_ok(future); Ok(()) } @@ -146,10 +138,12 @@ impl PoolState { self.tx.lock().unwrap().send(msg).unwrap(); } - fn work(&self, - idx: usize, - after_start: Option>, - before_stop: Option>) { + fn work( + &self, + idx: usize, + after_start: Option>, + before_stop: Option>, + ) { let _scope = enter().unwrap(); if let Some(after_start) = after_start { after_start(idx); @@ -241,7 +235,8 @@ impl ThreadPoolBuilder { /// The closure provided will receive an index corresponding to the worker /// thread it's running on. pub fn after_start(&mut self, f: F) -> &mut Self - where F: Fn(usize) + Send + Sync + 'static + where + F: Fn(usize) + Send + Sync + 'static, { self.after_start = Some(Arc::new(f)); self @@ -256,7 +251,8 @@ impl ThreadPoolBuilder { /// The closure provided will receive an index corresponding to the worker /// thread it's running on. pub fn before_stop(&mut self, f: F) -> &mut Self - where F: Fn(usize) + Send + Sync + 'static + where + F: Fn(usize) + Send + Sync + 'static, { self.before_stop = Some(Arc::new(f)); self @@ -328,14 +324,11 @@ impl Task { Poll::Pending => {} Poll::Ready(()) => return wake_handle.mutex.complete(), } - let task = Self { - future, - wake_handle: wake_handle.clone(), - exec, - }; + let task = Self { future, wake_handle: wake_handle.clone(), exec }; match wake_handle.mutex.wait(task) { Ok(()) => return, // we've waited - Err(task) => { // someone's notified us + Err(task) => { + // someone's notified us future = task.future; exec = task.exec; } @@ -347,9 +340,7 @@ impl Task { impl fmt::Debug for Task { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Task") - .field("contents", &"...") - .finish() + f.debug_struct("Task").field("contents", &"...").finish() } } @@ -372,7 +363,9 @@ mod tests { let (tx, rx) = mpsc::sync_channel(2); let _cpu_pool = ThreadPoolBuilder::new() .pool_size(2) - .after_start(move |_| tx.send(1).unwrap()).create().unwrap(); + .after_start(move |_| tx.send(1).unwrap()) + .create() + .unwrap(); // After ThreadPoolBuilder is deconstructed, the tx should be droped // so that we can use rx as an iterator. diff --git a/futures-executor/src/unpark_mutex.rs b/futures-executor/src/unpark_mutex.rs index c49c64cc39..ac5112cfa2 100644 --- a/futures-executor/src/unpark_mutex.rs +++ b/futures-executor/src/unpark_mutex.rs @@ -29,25 +29,22 @@ unsafe impl Sync for UnparkMutex {} // transitions: // The task is blocked, waiting on an event -const WAITING: usize = 0; // --> POLLING +const WAITING: usize = 0; // --> POLLING // The task is actively being polled by a thread; arrival of additional events // of interest should move it to the REPOLL state -const POLLING: usize = 1; // --> WAITING, REPOLL, or COMPLETE +const POLLING: usize = 1; // --> WAITING, REPOLL, or COMPLETE // The task is actively being polled, but will need to be re-polled upon // completion to ensure that all events were observed. -const REPOLL: usize = 2; // --> POLLING +const REPOLL: usize = 2; // --> POLLING // The task has finished executing (either successfully or with an error/panic) -const COMPLETE: usize = 3; // No transitions out +const COMPLETE: usize = 3; // No transitions out impl UnparkMutex { pub(crate) fn new() -> Self { - Self { - status: AtomicUsize::new(WAITING), - inner: UnsafeCell::new(None), - } + Self { status: AtomicUsize::new(WAITING), inner: UnsafeCell::new(None) } } /// Attempt to "notify" the mutex that a poll should occur. @@ -62,8 +59,7 @@ impl UnparkMutex { match status { // The task is idle, so try to run it immediately. WAITING => { - match self.status.compare_exchange(WAITING, POLLING, - SeqCst, SeqCst) { + match self.status.compare_exchange(WAITING, POLLING, SeqCst, SeqCst) { Ok(_) => { let data = unsafe { // SAFETY: we've ensured mutual exclusion via @@ -82,13 +78,10 @@ impl UnparkMutex { // The task is being polled, so we need to record that it should // be *repolled* when complete. - POLLING => { - match self.status.compare_exchange(POLLING, REPOLL, - SeqCst, SeqCst) { - Ok(_) => return Err(()), - Err(cur) => status = cur, - } - } + POLLING => match self.status.compare_exchange(POLLING, REPOLL, SeqCst, SeqCst) { + Ok(_) => return Err(()), + Err(cur) => status = cur, + }, // The task is already scheduled for polling, or is complete, so // we've got nothing to do. diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index b31f1034cb..56e6daa077 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -1,14 +1,14 @@ use futures::channel::oneshot; use futures::executor::LocalPool; -use futures::future::{self, Future, lazy, poll_fn}; -use futures::task::{Context, Poll, Spawn, LocalSpawn, Waker}; +use futures::future::{self, lazy, poll_fn, Future}; +use futures::task::{Context, LocalSpawn, Poll, Spawn, Waker}; use std::cell::{Cell, RefCell}; use std::pin::Pin; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread; use std::time::Duration; -use std::sync::Arc; -use std::sync::atomic::{Ordering, AtomicBool}; struct Pending(Rc<()>); @@ -52,9 +52,14 @@ fn run_until_executes_spawned() { let (tx, rx) = oneshot::channel(); let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - tx.send(()).unwrap(); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + tx.send(()).unwrap(); + })) + .into(), + ) + .unwrap(); pool.run_until(rx).unwrap(); } @@ -74,18 +79,27 @@ fn run_executes_spawned() { let spawn = pool.spawner(); let spawn2 = pool.spawner(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - spawn2.spawn_local_obj(Box::pin(lazy(move |_| { - cnt2.set(cnt2.get() + 1); - })).into()).unwrap(); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + spawn2 + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt2.set(cnt2.get() + 1); + })) + .into(), + ) + .unwrap(); + })) + .into(), + ) + .unwrap(); pool.run(); assert_eq!(cnt.get(), 1); } - #[test] fn run_spawn_many() { const ITER: usize = 200; @@ -97,9 +111,14 @@ fn run_spawn_many() { for _ in 0..ITER { let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); } pool.run(); @@ -126,9 +145,14 @@ fn try_run_one_executes_one_ready() { spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); } @@ -154,15 +178,20 @@ fn try_run_one_returns_on_no_progress() { { let cnt = cnt.clone(); let waker = waker.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |ctx| { - cnt.set(cnt.get() + 1); - waker.set(Some(ctx.waker().clone())); - if cnt.get() == ITER { - Poll::Ready(()) - } else { - Poll::Pending - } - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |ctx| { + cnt.set(cnt.get() + 1); + waker.set(Some(ctx.waker().clone())); + if cnt.get() == ITER { + Poll::Ready(()) + } else { + Poll::Pending + } + })) + .into(), + ) + .unwrap(); } for i in 0..ITER - 1 { @@ -185,16 +214,21 @@ fn try_run_one_runs_sub_futures() { let inner_spawner = spawn.clone(); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |_| { - cnt1.set(cnt1.get() + 1); - - let cnt2 = cnt1.clone(); - inner_spawner.spawn_local_obj(Box::pin(lazy(move |_|{ - cnt2.set(cnt2.get() + 1) - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |_| { + cnt1.set(cnt1.get() + 1); - Poll::Pending - })).into()).unwrap(); + let cnt2 = cnt1.clone(); + inner_spawner + .spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()) + .unwrap(); + + Poll::Pending + })) + .into(), + ) + .unwrap(); pool.try_run_one(); assert_eq!(cnt.get(), 2); @@ -214,12 +248,12 @@ fn run_until_stalled_returns_multiple_times() { let cnt = Rc::new(Cell::new(0)); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_|{ cnt1.set(cnt1.get() + 1) })).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt1.set(cnt1.get() + 1))).into()).unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 1); let cnt2 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_|{ cnt2.set(cnt2.get() + 1) })).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()).unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 2); } @@ -232,16 +266,21 @@ fn run_until_stalled_runs_spawned_sub_futures() { let inner_spawner = spawn.clone(); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |_| { - cnt1.set(cnt1.get() + 1); - - let cnt2 = cnt1.clone(); - inner_spawner.spawn_local_obj(Box::pin(lazy(move |_|{ - cnt2.set(cnt2.get() + 1) - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |_| { + cnt1.set(cnt1.get() + 1); - Poll::Pending - })).into()).unwrap(); + let cnt2 = cnt1.clone(); + inner_spawner + .spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()) + .unwrap(); + + Poll::Pending + })) + .into(), + ) + .unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 2); @@ -262,9 +301,14 @@ fn run_until_stalled_executes_all_ready() { spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); // also add some pending tasks to test if they are ignored spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); @@ -281,10 +325,15 @@ fn nesting_run() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_obj(Box::pin(lazy(|_| { - let mut pool = LocalPool::new(); - pool.run(); - })).into()).unwrap(); + spawn + .spawn_obj( + Box::pin(lazy(|_| { + let mut pool = LocalPool::new(); + pool.run(); + })) + .into(), + ) + .unwrap(); pool.run(); } @@ -295,10 +344,15 @@ fn nesting_run_run_until_stalled() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_obj(Box::pin(lazy(|_| { - let mut pool = LocalPool::new(); - pool.run_until_stalled(); - })).into()).unwrap(); + spawn + .spawn_obj( + Box::pin(lazy(|_| { + let mut pool = LocalPool::new(); + pool.run_until_stalled(); + })) + .into(), + ) + .unwrap(); pool.run(); } @@ -342,15 +396,9 @@ fn tasks_are_scheduled_fairly() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_local_obj(Box::pin(Spin { - state: state.clone(), - idx: 0, - }).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(Spin { state: state.clone(), idx: 0 }).into()).unwrap(); - spawn.spawn_local_obj(Box::pin(Spin { - state, - idx: 1, - }).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(Spin { state, idx: 1 }).into()).unwrap(); pool.run(); } @@ -363,11 +411,11 @@ fn park_unpark_independence() { let future = future::poll_fn(move |cx| { if done { - return Poll::Ready(()) + return Poll::Ready(()); } done = true; cx.waker().clone().wake(); // (*) - // some user-code that temporarily parks the thread + // some user-code that temporarily parks the thread let test = thread::current(); let latch = Arc::new(AtomicBool::new(false)); let signal = latch.clone(); @@ -384,4 +432,3 @@ fn park_unpark_independence() { futures::executor::block_on(future) } - diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index 48de896fa8..998042d299 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -9,15 +9,12 @@ //! library is activated, and it is activated by default. #![cfg_attr(all(feature = "read-initializer", feature = "std"), feature(read_initializer))] - #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] - #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "read-initializer", not(feature = "unstable")))] @@ -32,14 +29,14 @@ mod if_std { // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - #[doc(no_inline)] - pub use io::{Error, ErrorKind, Result, IoSlice, IoSliceMut, SeekFrom}; #[cfg(feature = "read-initializer")] #[cfg_attr(docsrs, doc(cfg(feature = "read-initializer")))] #[doc(no_inline)] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use io::Initializer; + #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 + #[doc(no_inline)] + pub use io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; /// Read bytes asynchronously. /// @@ -85,8 +82,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll>; + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll>; /// Attempt to read from the `AsyncRead` into `bufs` using vectored /// IO operations. @@ -110,9 +110,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { for b in bufs { if !b.is_empty() { return self.poll_read(cx, b); @@ -149,8 +151,11 @@ mod if_std { /// /// `poll_write` must try to make progress by flushing the underlying object if /// that is the only way the underlying object can become writable again. - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll>; + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll>; /// Attempt to write bytes from `bufs` into the object using vectored /// IO operations. @@ -175,9 +180,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { for b in bufs { if !b.is_empty() { return self.poll_write(cx, b); @@ -252,8 +259,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll>; + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll>; } /// Read bytes asynchronously. @@ -292,8 +302,7 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>; + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Tells this buffer that `amt` bytes have been consumed from the buffer, /// so they should no longer be returned in calls to [`poll_read`]. @@ -320,18 +329,22 @@ mod if_std { (**self).initializer() } - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Pin::new(&mut **self).poll_read(cx, buf) } - fn poll_read_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Pin::new(&mut **self).poll_read_vectored(cx, bufs) } - } + }; } impl AsyncRead for Box { @@ -352,15 +365,19 @@ mod if_std { (**self).initializer() } - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { self.get_mut().as_mut().poll_read(cx, buf) } - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { self.get_mut().as_mut().poll_read_vectored(cx, bufs) } } @@ -372,18 +389,22 @@ mod if_std { io::Read::initializer(self) } - fn poll_read(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Poll::Ready(io::Read::read(&mut *self, buf)) } - fn poll_read_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Poll::Ready(io::Read::read_vectored(&mut *self, bufs)) } - } + }; } impl AsyncRead for &[u8] { @@ -392,15 +413,19 @@ mod if_std { macro_rules! deref_async_write { () => { - fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Pin::new(&mut **self).poll_write(cx, buf) } - fn poll_write_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Pin::new(&mut **self).poll_write_vectored(cx, bufs) } @@ -411,7 +436,7 @@ mod if_std { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self).poll_close(cx) } - } + }; } impl AsyncWrite for Box { @@ -427,15 +452,19 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncWrite, { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { self.get_mut().as_mut().poll_write(cx, buf) } - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { self.get_mut().as_mut().poll_write_vectored(cx, bufs) } @@ -450,15 +479,19 @@ mod if_std { macro_rules! delegate_async_write_to_stdio { () => { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(io::Write::write(&mut *self, buf)) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) } @@ -469,7 +502,7 @@ mod if_std { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } - } + }; } impl AsyncWrite for Vec { @@ -478,12 +511,14 @@ mod if_std { macro_rules! deref_async_seek { () => { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { Pin::new(&mut **self).poll_seek(cx, pos) } - } + }; } impl AsyncSeek for Box { @@ -499,25 +534,25 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncSeek, { - fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { self.get_mut().as_mut().poll_seek(cx, pos) } } macro_rules! deref_async_buf_read { () => { - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self.get_mut()).poll_fill_buf(cx) } fn consume(mut self: Pin<&mut Self>, amt: usize) { Pin::new(&mut **self).consume(amt) } - } + }; } impl AsyncBufRead for Box { @@ -533,9 +568,7 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncBufRead, { - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut().as_mut().poll_fill_buf(cx) } @@ -546,16 +579,14 @@ mod if_std { macro_rules! delegate_async_buf_read_to_stdio { () => { - fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(io::BufRead::fill_buf(self.get_mut())) } fn consume(self: Pin<&mut Self>, amt: usize) { io::BufRead::consume(self.get_mut(), amt) } - } + }; } impl AsyncBufRead for &[u8] { diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index 048b62a6cd..d427da27a0 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -27,10 +27,7 @@ impl Parse for Join { } } -fn bind_futures( - fut_exprs: Vec, - span: Span, -) -> (Vec, Vec) { +fn bind_futures(fut_exprs: Vec, span: Span) -> (Vec, Vec) { let mut future_let_bindings = Vec::with_capacity(fut_exprs.len()); let future_names: Vec<_> = fut_exprs .into_iter() diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index 65b31877ac..0c8e5f1ca0 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -3,8 +3,8 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{format_ident, quote}; -use syn::{parse_quote, Expr, Ident, Pat, Token}; use syn::parse::{Parse, ParseStream}; +use syn::{parse_quote, Expr, Ident, Pat, Token}; mod kw { syn::custom_keyword!(complete); @@ -63,7 +63,10 @@ impl Parse for Select { // Commas after the expression are only optional if it's a `Block` // or it is the last branch in the `match`. - let is_block = match expr { Expr::Block(_) => true, _ => false }; + let is_block = match expr { + Expr::Block(_) => true, + _ => false, + }; if is_block || input.is_empty() { input.parse::>()?; } else { @@ -76,7 +79,7 @@ impl Parse for Select { CaseKind::Normal(pat, fut_expr) => { select.normal_fut_exprs.push(fut_expr); select.normal_fut_handlers.push((pat, expr)); - }, + } } } @@ -92,22 +95,16 @@ fn declare_result_enum( result_ident: Ident, variants: usize, complete: bool, - span: Span + span: Span, ) -> (Vec, syn::ItemEnum) { // "_0", "_1", "_2" let variant_names: Vec = - (0..variants) - .map(|num| format_ident!("_{}", num, span = span)) - .collect(); + (0..variants).map(|num| format_ident!("_{}", num, span = span)).collect(); let type_parameters = &variant_names; let variants = &variant_names; - let complete_variant = if complete { - Some(quote!(Complete)) - } else { - None - }; + let complete_variant = if complete { Some(quote!(Complete)) } else { None }; let enum_item = parse_quote! { enum #result_ident<#(#type_parameters,)*> { @@ -148,7 +145,9 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // bind non-`Ident` future exprs w/ `let` let mut future_let_bindings = Vec::with_capacity(parsed.normal_fut_exprs.len()); - let bound_future_names: Vec<_> = parsed.normal_fut_exprs.into_iter() + let bound_future_names: Vec<_> = parsed + .normal_fut_exprs + .into_iter() .zip(variant_names.iter()) .map(|(expr, variant_name)| { match expr { @@ -164,7 +163,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { __futures_crate::async_await::assert_unpin(&#path); }); path - }, + } _ => { // Bind and pin the resulting Future on the stack. This is // necessary to support direct select! calls on !Unpin @@ -188,8 +187,8 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // For each future, make an `&mut dyn FnMut(&mut Context<'_>) -> Option>` // to use for polling that individual future. These will then be put in an array. - let poll_functions = bound_future_names.iter().zip(variant_names.iter()) - .map(|(bound_future_name, variant_name)| { + let poll_functions = bound_future_names.iter().zip(variant_names.iter()).map( + |(bound_future_name, variant_name)| { // Below we lazily create the Pin on the Future below. // This is done in order to avoid allocating memory in the generator // for the Pin variable. @@ -216,7 +215,8 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { &mut __futures_crate::task::Context<'_> ) -> __futures_crate::Option<__futures_crate::task::Poll<_>> = &mut #variant_name; } - }); + }, + ); let none_polled = if parsed.complete.is_some() { quote! { @@ -229,13 +229,13 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { } }; - let branches = parsed.normal_fut_handlers.into_iter() - .zip(variant_names.iter()) - .map(|((pat, expr), variant_name)| { + let branches = parsed.normal_fut_handlers.into_iter().zip(variant_names.iter()).map( + |((pat, expr), variant_name)| { quote! { #enum_ident::#variant_name(#pat) => { #expr }, } - }); + }, + ); let branches = quote! { #( #branches )* }; let complete_branch = parsed.complete.map(|complete_expr| { diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index 6193f51d6b..4dc2fb3d37 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -207,7 +207,10 @@ mod if_alloc { impl + Unpin, Item> Sink for alloc::boxed::Box { type Error = S::Error; - fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_ready(cx) } @@ -215,11 +218,17 @@ mod if_alloc { Pin::new(&mut **self).start_send(item) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_flush(cx) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_close(cx) } } diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index 373be244cd..48ec12beb2 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -1,8 +1,8 @@ use core::{ - mem, fmt, future::Future, marker::PhantomData, + mem, pin::Pin, task::{Context, Poll}, }; @@ -26,16 +26,16 @@ impl Unpin for LocalFutureObj<'_, T> {} #[allow(single_use_lifetimes)] #[allow(clippy::transmute_ptr_to_ptr)] -unsafe fn remove_future_lifetime<'a, T>(ptr: *mut (dyn Future + 'a)) - -> *mut (dyn Future + 'static) -{ +unsafe fn remove_future_lifetime<'a, T>( + ptr: *mut (dyn Future + 'a), +) -> *mut (dyn Future + 'static) { mem::transmute(ptr) } #[allow(single_use_lifetimes)] -unsafe fn remove_drop_lifetime<'a, T>(ptr: unsafe fn (*mut (dyn Future + 'a))) - -> unsafe fn(*mut (dyn Future + 'static)) -{ +unsafe fn remove_drop_lifetime<'a, T>( + ptr: unsafe fn(*mut (dyn Future + 'a)), +) -> unsafe fn(*mut (dyn Future + 'static)) { mem::transmute(ptr) } @@ -65,8 +65,7 @@ impl<'a, T> LocalFutureObj<'a, T> { impl fmt::Debug for LocalFutureObj<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LocalFutureObj") - .finish() + f.debug_struct("LocalFutureObj").finish() } } @@ -82,17 +81,13 @@ impl Future for LocalFutureObj<'_, T> { #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - Pin::new_unchecked(&mut *self.future).poll(cx) - } + unsafe { Pin::new_unchecked(&mut *self.future).poll(cx) } } } impl Drop for LocalFutureObj<'_, T> { fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.future) - } + unsafe { (self.drop_fn)(self.future) } } } @@ -120,8 +115,7 @@ impl<'a, T> FutureObj<'a, T> { impl fmt::Debug for FutureObj<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureObj") - .finish() + f.debug_struct("FutureObj").finish() } } @@ -130,7 +124,7 @@ impl Future for FutureObj<'_, T> { #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new( &mut self.0 ).poll(cx) + Pin::new(&mut self.0).poll(cx) } } @@ -180,7 +174,7 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F where - F: Future + Unpin + 'a + F: Future + Unpin + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { self as *mut dyn Future @@ -189,8 +183,7 @@ where unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } -unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + Unpin + 'a) -{ +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + Unpin + 'a) { fn into_raw(self) -> *mut (dyn Future + 'a) { self as *mut dyn Future } @@ -200,7 +193,7 @@ unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin<&'a mut F> where - F: Future + 'a + F: Future + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { unsafe { self.get_unchecked_mut() as *mut dyn Future } @@ -209,8 +202,7 @@ where unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } -unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future + 'a)> -{ +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future + 'a)> { fn into_raw(self) -> *mut (dyn Future + 'a) { unsafe { self.get_unchecked_mut() as *mut dyn Future } } @@ -224,7 +216,8 @@ mod if_alloc { use alloc::boxed::Box; unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Box - where F: Future + 'a + where + F: Future + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { Box::into_raw(self) @@ -257,7 +250,7 @@ mod if_alloc { unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin> where - F: Future + 'a + F: Future + 'a, { fn into_raw(mut self) -> *mut (dyn Future + 'a) { let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ }; diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 5505e3ad49..6693c53af0 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,9 +1,7 @@ //! Tools for working with tasks. #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] @@ -24,7 +22,7 @@ macro_rules! cfg_target_has_atomic { } mod spawn; -pub use crate::spawn::{Spawn, SpawnError, LocalSpawn}; +pub use crate::spawn::{LocalSpawn, Spawn, SpawnError}; cfg_target_has_atomic! { #[cfg(feature = "alloc")] @@ -51,4 +49,4 @@ pub use crate::noop_waker::noop_waker; pub use crate::noop_waker::noop_waker_ref; #[doc(no_inline)] -pub use core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; diff --git a/futures-task/src/spawn.rs b/futures-task/src/spawn.rs index a515dd4e18..50f5d0d56a 100644 --- a/futures-task/src/spawn.rs +++ b/futures-task/src/spawn.rs @@ -126,7 +126,7 @@ impl LocalSpawn for &mut Sp { #[cfg(feature = "alloc")] mod if_alloc { use super::*; - use alloc::{ boxed::Box, rc::Rc }; + use alloc::{boxed::Box, rc::Rc}; impl Spawn for Box { fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { diff --git a/futures-test/src/assert.rs b/futures-test/src/assert.rs index 4f1cc7d31a..75d7832482 100644 --- a/futures-test/src/assert.rs +++ b/futures-test/src/assert.rs @@ -30,9 +30,7 @@ macro_rules! assert_stream_pending { $crate::__private::assert::assert_is_unpin_stream(stream); let stream = $crate::__private::Pin::new(stream); let mut cx = $crate::task::noop_context(); - let poll = $crate::__private::stream::Stream::poll_next( - stream, &mut cx, - ); + let poll = $crate::__private::stream::Stream::poll_next(stream, &mut cx); if poll.is_ready() { panic!("assertion failed: stream is not pending"); } @@ -71,13 +69,15 @@ macro_rules! assert_stream_next { assert_eq!(x, $item); } $crate::__private::task::Poll::Ready($crate::__private::None) => { - panic!("assertion failed: expected stream to provide item but stream is at its end"); + panic!( + "assertion failed: expected stream to provide item but stream is at its end" + ); } $crate::__private::task::Poll::Pending => { panic!("assertion failed: expected stream to provide item but stream wasn't ready"); } } - }} + }}; } /// Assert that the next poll to the provided stream will return an empty @@ -117,5 +117,5 @@ macro_rules! assert_stream_done { panic!("assertion failed: expected stream to be done but was pending"); } } - }} + }}; } diff --git a/futures-test/src/assert_unmoved.rs b/futures-test/src/assert_unmoved.rs index 6e5116d228..95d9a095f2 100644 --- a/futures-test/src/assert_unmoved.rs +++ b/futures-test/src/assert_unmoved.rs @@ -34,10 +34,7 @@ unsafe impl Sync for AssertUnmoved {} impl AssertUnmoved { pub(crate) fn new(inner: T) -> Self { - Self { - inner, - this_ptr: ptr::null(), - } + Self { inner, this_ptr: ptr::null() } } fn poll_with<'a, U>(mut self: Pin<&'a mut Self>, f: impl FnOnce(Pin<&'a mut T>) -> U) -> U { @@ -46,10 +43,7 @@ impl AssertUnmoved { // First time being polled *self.as_mut().project().this_ptr = cur_this; } else { - assert_eq!( - self.this_ptr, cur_this, - "AssertUnmoved moved between poll calls" - ); + assert_eq!(self.this_ptr, cur_this, "AssertUnmoved moved between poll calls"); } f(self.project().inner) } diff --git a/futures-test/src/future/pending_once.rs b/futures-test/src/future/pending_once.rs index b36af6fe39..0fc3ef0b34 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::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use std::pin::Pin; use pin_project::pin_project; +use std::pin::Pin; /// Combinator that guarantees one [`Poll::Pending`] before polling its inner /// future. @@ -20,20 +20,14 @@ pub struct PendingOnce { impl PendingOnce { pub(super) fn new(future: Fut) -> Self { - Self { - future, - polled_before: false, - } + Self { future, polled_before: false } } } impl Future for PendingOnce { type Output = Fut::Output; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); if *this.polled_before { this.future.poll(cx) diff --git a/futures-test/src/interleave_pending.rs b/futures-test/src/interleave_pending.rs index 7bc8706388..91640778b2 100644 --- a/futures-test/src/interleave_pending.rs +++ b/futures-test/src/interleave_pending.rs @@ -28,10 +28,7 @@ pub struct InterleavePending { impl InterleavePending { pub(crate) fn new(inner: T) -> Self { - Self { - inner, - pended: false, - } + Self { inner, pended: false } } /// Acquires a reference to the underlying I/O object that this adaptor is diff --git a/futures-test/src/io/limited.rs b/futures-test/src/io/limited.rs index a206160099..34b72a530e 100644 --- a/futures-test/src/io/limited.rs +++ b/futures-test/src/io/limited.rs @@ -59,17 +59,11 @@ impl AsyncWrite for Limited { this.io.poll_write(cx, &buf[..cmp::min(*this.limit, buf.len())]) } - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().io.poll_flush(cx) } - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().io.poll_close(cx) } } @@ -87,10 +81,7 @@ impl AsyncRead for Limited { } impl AsyncBufRead for Limited { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().io.poll_fill_buf(cx) } diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 6c3e60cbe1..4c26a56987 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -7,18 +7,20 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] #[cfg(not(feature = "std"))] -compile_error!("`futures-test` must have the `std` feature activated, this is a default-active feature"); +compile_error!( + "`futures-test` must have the `std` feature activated, this is a default-active feature" +); // Not public API. #[doc(hidden)] #[cfg(feature = "std")] pub mod __private { + pub use futures_core::{future, stream, task}; pub use std::{ - option::Option::{Some, None}, + option::Option::{None, Some}, pin::Pin, result::Result::{Err, Ok}, }; - pub use futures_core::{future, stream, task}; pub mod assert { pub use crate::assert::*; diff --git a/futures-test/src/task/context.rs b/futures-test/src/task/context.rs index 602127c657..b2b0dfe31e 100644 --- a/futures-test/src/task/context.rs +++ b/futures-test/src/task/context.rs @@ -1,4 +1,4 @@ -use crate::task::{panic_waker_ref, noop_waker_ref}; +use crate::task::{noop_waker_ref, panic_waker_ref}; use futures_core::task::Context; /// Create a new [`Context`](core::task::Context) where the diff --git a/futures-test/src/task/mod.rs b/futures-test/src/task/mod.rs index 89e6aef704..ef6dd1cea4 100644 --- a/futures-test/src/task/mod.rs +++ b/futures-test/src/task/mod.rs @@ -57,4 +57,4 @@ mod record_spawner; pub use self::record_spawner::RecordSpawner; mod wake_counter; -pub use self::wake_counter::{AwokenCount, new_count_waker}; +pub use self::wake_counter::{new_count_waker, AwokenCount}; diff --git a/futures-test/src/task/wake_counter.rs b/futures-test/src/task/wake_counter.rs index cf496c2ddb..52c63e1cc9 100644 --- a/futures-test/src/task/wake_counter.rs +++ b/futures-test/src/task/wake_counter.rs @@ -1,7 +1,7 @@ use futures_core::task::Waker; use futures_util::task::{self, ArcWake}; -use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; /// Number of times the waker was awoken. /// diff --git a/futures-test/src/track_closed.rs b/futures-test/src/track_closed.rs index 31e404c226..be883b1491 100644 --- a/futures-test/src/track_closed.rs +++ b/futures-test/src/track_closed.rs @@ -21,10 +21,7 @@ pub struct TrackClosed { impl TrackClosed { pub(crate) fn new(inner: T) -> Self { - Self { - inner, - closed: false, - } + Self { inner, closed: false } } /// Check whether this object has been closed. diff --git a/futures-util/benches/futures_unordered.rs b/futures-util/benches/futures_unordered.rs index 05e9eadb79..d5fe7a59de 100644 --- a/futures-util/benches/futures_unordered.rs +++ b/futures-util/benches/futures_unordered.rs @@ -6,7 +6,7 @@ use crate::test::Bencher; use futures::channel::oneshot; use futures::executor::block_on; use futures::future; -use futures::stream::{StreamExt, FuturesUnordered}; +use futures::stream::{FuturesUnordered, StreamExt}; use futures::task::Poll; use std::collections::VecDeque; use std::thread; @@ -34,7 +34,7 @@ fn oneshots(b: &mut Bencher) { block_on(future::poll_fn(move |cx| { loop { if let Poll::Ready(None) = rxs.poll_next_unpin(cx) { - break + break; } } Poll::Ready(()) diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index bdaed95b0c..5f5d4aca3f 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -3,8 +3,8 @@ //! This module contains a number of functions and combinators for working //! with `async`/`await` code. -use futures_core::future::{Future, FusedFuture}; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; #[macro_use] mod poll; diff --git a/futures-util/src/async_await/pending.rs b/futures-util/src/async_await/pending.rs index e0cf341a8b..5d7a431811 100644 --- a/futures-util/src/async_await/pending.rs +++ b/futures-util/src/async_await/pending.rs @@ -16,7 +16,7 @@ use futures_core::task::{Context, Poll}; macro_rules! pending { () => { $crate::__private::async_await::pending_once().await - } + }; } #[doc(hidden)] diff --git a/futures-util/src/async_await/poll.rs b/futures-util/src/async_await/poll.rs index b5782df130..b62f45a943 100644 --- a/futures-util/src/async_await/poll.rs +++ b/futures-util/src/async_await/poll.rs @@ -17,7 +17,7 @@ use futures_core::task::{Context, Poll}; macro_rules! poll { ($x:expr $(,)?) => { $crate::__private::async_await::poll($x).await - } + }; } #[doc(hidden)] diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index bc3aee3c0a..17239a4e57 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -1,18 +1,15 @@ use futures_01::executor::{ - spawn as spawn01, Notify as Notify01, NotifyHandle as NotifyHandle01, - Spawn as Spawn01, UnsafeNotify as UnsafeNotify01, -}; -use futures_01::{ - Async as Async01, Future as Future01, - Stream as Stream01, + spawn as spawn01, Notify as Notify01, NotifyHandle as NotifyHandle01, Spawn as Spawn01, + UnsafeNotify as UnsafeNotify01, }; +use futures_01::{Async as Async01, Future as Future01, Stream as Stream01}; #[cfg(feature = "sink")] use futures_01::{AsyncSink as AsyncSink01, Sink as Sink01}; -use futures_core::{task as task03, future::Future as Future03, stream::Stream as Stream03}; -use std::pin::Pin; -use std::task::Context; +use futures_core::{future::Future as Future03, stream::Stream as Stream03, task as task03}; #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; +use std::pin::Pin; +use std::task::Context; #[cfg(feature = "io-compat")] #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] @@ -33,9 +30,7 @@ impl Compat01As03 { /// Wraps a futures 0.1 Future, Stream, AsyncRead, or AsyncWrite /// object in a futures 0.3-compatible wrapper. pub fn new(object: T) -> Self { - Self { - inner: spawn01(object), - } + Self { inner: spawn01(object) } } fn in_notify(&mut self, cx: &mut Context<'_>, f: impl FnOnce(&mut T) -> R) -> R { @@ -157,10 +152,7 @@ fn poll_01_to_03(x: Result, E>) -> task03::Poll> { impl Future03 for Compat01As03 { type Output = Result; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> task03::Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> task03::Poll { poll_01_to_03(self.in_notify(cx, Future01::poll)) } } @@ -198,18 +190,10 @@ impl Unpin for Compat01As03Sink {} impl Compat01As03Sink { /// Wraps a futures 0.1 Sink object in a futures 0.3-compatible wrapper. pub fn new(inner: S) -> Self { - Self { - inner: spawn01(inner), - buffer: None, - close_started: false - } + Self { inner: spawn01(inner), buffer: None, close_started: false } } - fn in_notify( - &mut self, - cx: &mut Context<'_>, - f: impl FnOnce(&mut S) -> R, - ) -> R { + fn in_notify(&mut self, cx: &mut Context<'_>, f: impl FnOnce(&mut S) -> R) -> R { let notify = &WakerToHandle(cx.waker()); self.inner.poll_fn_notify(notify, 0, f) } @@ -256,10 +240,7 @@ where { type Error = S::SinkError; - fn start_send( - mut self: Pin<&mut Self>, - item: SinkItem, - ) -> Result<(), Self::Error> { + fn start_send(mut self: Pin<&mut Self>, item: SinkItem) -> Result<(), Self::Error> { debug_assert!(self.buffer.is_none()); self.buffer = Some(item); Ok(()) @@ -289,9 +270,7 @@ where match self.in_notify(cx, |f| match item { Some(i) => match f.start_send(i)? { AsyncSink01::Ready => f.poll_complete().map(|i| (i, None)), - AsyncSink01::NotReady(t) => { - Ok((Async01::NotReady, Some(t))) - } + AsyncSink01::NotReady(t) => Ok((Async01::NotReady, Some(t))), }, None => f.poll_complete().map(|i| (i, None)), })? { @@ -447,29 +426,35 @@ mod io { } } - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> task03::Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, |x| x.poll_read(buf))) } } impl AsyncWrite03 for Compat01As03 { - fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> task03::Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, |x| x.poll_write(buf))) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> task03::Poll> - { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, AsyncWrite01::poll_flush)) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> task03::Poll> - { + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, AsyncWrite01::shutdown)) } } diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 3f1eebbf1d..2573fe7a74 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -1,31 +1,19 @@ +use crate::task::{self as task03, ArcWake as ArcWake03, WakerRef}; use futures_01::{ - task as task01, Async as Async01, Future as Future01, Poll as Poll01, - Stream as Stream01, + task as task01, Async as Async01, Future as Future01, Poll as Poll01, Stream as Stream01, }; #[cfg(feature = "sink")] -use futures_01::{ - AsyncSink as AsyncSink01, Sink as Sink01, StartSend as StartSend01, -}; +use futures_01::{AsyncSink as AsyncSink01, Sink as Sink01, StartSend as StartSend01}; use futures_core::{ - task::{RawWaker, RawWakerVTable}, future::TryFuture as TryFuture03, stream::TryStream as TryStream03, + task::{RawWaker, RawWakerVTable}, }; #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; -use crate::task::{ - self as task03, - ArcWake as ArcWake03, - WakerRef, -}; #[cfg(feature = "sink")] use std::marker::PhantomData; -use std::{ - mem, - pin::Pin, - sync::Arc, - task::Context, -}; +use std::{mem, pin::Pin, sync::Arc, task::Context}; /// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture) or /// [`TryStream`](futures_core::stream::TryStream) into a futures 0.1 @@ -80,10 +68,7 @@ impl Compat { impl CompatSink { /// Creates a new [`CompatSink`]. pub fn new(inner: T) -> Self { - Self { - inner, - _phantom: PhantomData, - } + Self { inner, _phantom: PhantomData } } /// Get a reference to 0.3 Sink contained within. @@ -102,9 +87,7 @@ impl CompatSink { } } -fn poll_03_to_01(x: task03::Poll>) - -> Result, E> -{ +fn poll_03_to_01(x: task03::Poll>) -> Result, E> { match x? { task03::Poll::Ready(t) => Ok(Async01::Ready(t)), task03::Poll::Pending => Ok(Async01::NotReady), @@ -147,17 +130,10 @@ where type SinkItem = Item; type SinkError = T::Error; - fn start_send( - &mut self, - item: Self::SinkItem, - ) -> StartSend01 { - with_sink_context(self, |mut inner, cx| { - match inner.as_mut().poll_ready(cx)? { - task03::Poll::Ready(()) => { - inner.start_send(item).map(|()| AsyncSink01::Ready) - } - task03::Poll::Pending => Ok(AsyncSink01::NotReady(item)), - } + fn start_send(&mut self, item: Self::SinkItem) -> StartSend01 { + with_sink_context(self, |mut inner, cx| match inner.as_mut().poll_ready(cx)? { + task03::Poll::Ready(()) => inner.start_send(item).map(|()| AsyncSink01::Ready), + task03::Poll::Pending => Ok(AsyncSink01::NotReady(item)), }) } @@ -190,9 +166,9 @@ impl Current { // Lazily create the `Arc` only when the waker is actually cloned. // FIXME: remove `transmute` when a `Waker` -> `RawWaker` conversion // function is landed in `core`. - mem::transmute::( - task03::waker(Arc::new(ptr_to_current(ptr).clone())) - ) + mem::transmute::(task03::waker(Arc::new( + ptr_to_current(ptr).clone(), + ))) } unsafe fn drop(_: *const ()) {} unsafe fn wake(ptr: *const ()) { @@ -243,9 +219,7 @@ mod io { use futures_io::{AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03}; use tokio_io::{AsyncRead as AsyncRead01, AsyncWrite as AsyncWrite01}; - fn poll_03_to_io(x: task03::Poll>) - -> Result - { + fn poll_03_to_io(x: task03::Poll>) -> Result { match x { task03::Poll::Ready(Ok(t)) => Ok(t), task03::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), diff --git a/futures-util/src/compat/executor.rs b/futures-util/src/compat/executor.rs index 82cb496a70..e25705be16 100644 --- a/futures-util/src/compat/executor.rs +++ b/futures-util/src/compat/executor.rs @@ -66,9 +66,7 @@ where fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError03> { let future = future.unit_error().compat(); - self.executor01 - .execute(future) - .map_err(|_| SpawnError03::shutdown()) + self.executor01.execute(future).map_err(|_| SpawnError03::shutdown()) } } diff --git a/futures-util/src/compat/mod.rs b/futures-util/src/compat/mod.rs index c5edcc580c..4812803eb6 100644 --- a/futures-util/src/compat/mod.rs +++ b/futures-util/src/compat/mod.rs @@ -4,16 +4,16 @@ //! library is activated. mod executor; -pub use self::executor::{Executor01CompatExt, Executor01Future, Executor01As03}; +pub use self::executor::{Executor01As03, Executor01CompatExt, Executor01Future}; mod compat01as03; +#[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] +pub use self::compat01as03::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; pub use self::compat01as03::{Compat01As03, Future01CompatExt, Stream01CompatExt}; #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::compat01as03::{Compat01As03Sink, Sink01CompatExt}; -#[cfg(feature = "io-compat")] -#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] -pub use self::compat01as03::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; mod compat03as01; pub use self::compat03as01::Compat; diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs index cb623918dd..37ee03e6df 100644 --- a/futures-util/src/fns.rs +++ b/futures-util/src/fns.rs @@ -1,5 +1,5 @@ -use core::marker::PhantomData; use core::fmt::{self, Debug}; +use core::marker::PhantomData; pub trait FnOnce1 { type Output; @@ -8,7 +8,7 @@ pub trait FnOnce1 { impl FnOnce1 for T where - T: FnOnce(A) -> R + T: FnOnce(A) -> R, { type Output = R; fn call_once(self, arg: A) -> R { @@ -22,7 +22,7 @@ pub trait FnMut1: FnOnce1 { impl FnMut1 for T where - T: FnMut(A) -> R + T: FnMut(A) -> R, { fn call_mut(&mut self, arg: A) -> R { self(arg) @@ -37,7 +37,7 @@ pub trait Fn1: FnMut1 { impl Fn1 for T where - T: Fn(A) -> R + T: Fn(A) -> R, { fn call(&self, arg: A) -> R { self(arg) @@ -143,7 +143,7 @@ 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=()>, + F: for<'a> FnOnce1<&'a A, Output = ()>, { type Output = A; fn call_once(self, arg: A) -> Self::Output { @@ -154,7 +154,7 @@ where #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl FnMut1 for InspectFn where - F: for<'a> FnMut1<&'a A, Output=()>, + F: for<'a> FnMut1<&'a A, Output = ()>, { fn call_mut(&mut self, arg: A) -> Self::Output { self.0.call_mut(&arg); @@ -164,7 +164,7 @@ where #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Fn1 for InspectFn where - F: for<'a> Fn1<&'a A, Output=()>, + F: for<'a> Fn1<&'a A, Output = ()>, { fn call(&self, arg: A) -> Self::Output { self.0.call(&arg); @@ -244,27 +244,33 @@ pub struct InspectOkFn(F); impl<'a, F, T, E> FnOnce1<&'a Result> for InspectOkFn where - F: FnOnce1<&'a T, Output=()> + 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) } + 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=()>, + 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) } + 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=()>, + F: Fn1<&'a T, Output = ()>, { fn call(&self, arg: &'a Result) -> Self::Output { - if let Ok(x) = arg { self.0.call(x) } + if let Ok(x) = arg { + self.0.call(x) + } } } pub(crate) fn inspect_ok_fn(f: F) -> InspectOkFn { @@ -276,27 +282,33 @@ pub struct InspectErrFn(F); impl<'a, F, T, E> FnOnce1<&'a Result> for InspectErrFn where - F: FnOnce1<&'a E, Output=()> + 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) } + 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=()>, + 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) } + 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=()>, + F: Fn1<&'a E, Output = ()>, { fn call(&self, arg: &'a Result) -> Self::Output { - if let Err(x) = arg { self.0.call(x) } + if let Err(x) = arg { + self.0.call(x) + } } } pub(crate) fn inspect_err_fn(f: F) -> InspectErrFn { @@ -313,7 +325,7 @@ pub struct UnwrapOrElseFn(F); impl FnOnce1> for UnwrapOrElseFn where - F: FnOnce1, + F: FnOnce1, { type Output = T; fn call_once(self, arg: Result) -> Self::Output { @@ -322,7 +334,7 @@ where } impl FnMut1> for UnwrapOrElseFn where - F: FnMut1, + F: FnMut1, { fn call_mut(&mut self, arg: Result) -> Self::Output { arg.unwrap_or_else(|x| self.0.call_mut(x)) @@ -330,7 +342,7 @@ where } impl Fn1> for UnwrapOrElseFn where - F: Fn1, + F: Fn1, { fn call(&self, arg: Result) -> Self::Output { arg.unwrap_or_else(|x| self.0.call(x)) @@ -347,7 +359,10 @@ impl Default for IntoFn { Self(PhantomData) } } -impl FnOnce1 for IntoFn where A: Into { +impl FnOnce1 for IntoFn +where + A: Into, +{ type Output = T; fn call_once(self, arg: A) -> Self::Output { arg.into() diff --git a/futures-util/src/future/future/catch_unwind.rs b/futures-util/src/future/future/catch_unwind.rs index 3f16577788..0e09d6eeb0 100644 --- a/futures-util/src/future/future/catch_unwind.rs +++ b/futures-util/src/future/future/catch_unwind.rs @@ -1,6 +1,6 @@ use core::any::Any; use core::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; +use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use futures_core::future::Future; use futures_core::task::{Context, Poll}; @@ -16,14 +16,18 @@ pin_project! { } } -impl CatchUnwind where Fut: Future + UnwindSafe { +impl CatchUnwind +where + Fut: Future + UnwindSafe, +{ pub(super) fn new(future: Fut) -> Self { Self { future } } } impl Future for CatchUnwind - where Fut: Future + UnwindSafe, +where + Fut: Future + UnwindSafe, { type Output = Result>; diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 0c48a4f1d4..bd767af344 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -2,9 +2,9 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; pin_project! { @@ -24,8 +24,9 @@ impl Flatten { } impl FusedFuture for Flatten - where Fut: Future, - Fut::Output: Future, +where + Fut: Future, + Fut::Output: Future, { fn is_terminated(&self) -> bool { match self { @@ -36,8 +37,9 @@ impl FusedFuture for Flatten } impl Future for Flatten - where Fut: Future, - Fut::Output: Future, +where + Fut: Future, + Fut::Output: Future, { type Output = ::Output; @@ -47,12 +49,12 @@ impl Future for Flatten FlattenProj::First { f } => { let f = ready!(f.poll(cx)); self.set(Self::Second { f }); - }, + } FlattenProj::Second { f } => { let output = ready!(f.poll(cx)); self.set(Self::Empty); break output; - }, + } FlattenProj::Empty => panic!("Flatten polled after completion"), } }) @@ -60,8 +62,9 @@ impl Future for Flatten } impl FusedStream for Flatten - where Fut: Future, - Fut::Output: Stream, +where + Fut: Future, + Fut::Output: Stream, { fn is_terminated(&self) -> bool { match self { @@ -72,32 +75,32 @@ impl FusedStream for Flatten } impl Stream for Flatten - where Fut: Future, - Fut::Output: Stream, +where + Fut: Future, + Fut::Output: Stream, { type Item = ::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready(loop { match self.as_mut().project() { - FlattenProj::First { f } => { + FlattenProj::First { f } => { let f = ready!(f.poll(cx)); self.set(Self::Second { f }); - }, + } FlattenProj::Second { f } => { let output = ready!(f.poll_next(cx)); if output.is_none() { self.set(Self::Empty); } break output; - }, + } FlattenProj::Empty => break None, } }) } } - #[cfg(feature = "sink")] impl Sink for Flatten where @@ -106,19 +109,16 @@ where { type Error = >::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready(loop { match self.as_mut().project() { FlattenProj::First { f } => { let f = ready!(f.poll(cx)); self.set(Self::Second { f }); - }, + } FlattenProj::Second { f } => { break ready!(f.poll_ready(cx)); - }, + } FlattenProj::Empty => panic!("poll_ready called after eof"), } }) @@ -140,10 +140,7 @@ where } } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let res = match self.as_mut().project() { FlattenProj::Second { f } => f.poll_close(cx), _ => Poll::Ready(Ok(())), diff --git a/futures-util/src/future/future/fuse.rs b/futures-util/src/future/future/fuse.rs index f4284ba373..597aec1a40 100644 --- a/futures-util/src/future/future/fuse.rs +++ b/futures-util/src/future/future/fuse.rs @@ -1,5 +1,5 @@ use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::ready; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -86,7 +86,7 @@ impl Future for Fuse { let output = ready!(fut.poll(cx)); self.project().inner.set(None); output - }, + } None => return Poll::Pending, }) } diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index 0d33ea5189..861e4c1cd8 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -1,23 +1,23 @@ use { crate::future::{CatchUnwind, FutureExt}, - futures_channel::oneshot::{self, Sender, Receiver}, + futures_channel::oneshot::{self, Receiver, Sender}, futures_core::{ future::Future, - task::{Context, Poll}, ready, + task::{Context, Poll}, }, + pin_project_lite::pin_project, std::{ any::Any, fmt, panic::{self, AssertUnwindSafe}, pin::Pin, sync::{ - Arc, atomic::{AtomicBool, Ordering}, + Arc, }, thread, }, - pin_project_lite::pin_project, }; /// The handle to a remote future returned by @@ -85,9 +85,7 @@ pin_project! { impl fmt::Debug for Remote { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Remote") - .field(&self.future) - .finish() + f.debug_tuple("Remote").field(&self.future).finish() } } diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 74311a02c0..1f139d0af0 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -90,10 +90,7 @@ impl Shared { }), }; - Self { - inner: Some(Arc::new(inner)), - waker_key: NULL_WAKER_KEY, - } + Self { inner: Some(Arc::new(inner)), waker_key: NULL_WAKER_KEY } } } @@ -223,10 +220,7 @@ 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 { @@ -286,11 +280,7 @@ where match future.poll(&mut cx) { Poll::Pending => { - if inner - .notifier - .state - .compare_exchange(POLLING, IDLE, SeqCst, SeqCst) - .is_ok() + if inner.notifier.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok() { // Success drop(_reset); @@ -330,10 +320,7 @@ where Fut: Future, { fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - waker_key: NULL_WAKER_KEY, - } + Self { inner: self.inner.clone(), waker_key: NULL_WAKER_KEY } } } @@ -367,16 +354,12 @@ impl ArcWake for Notifier { } } -impl WeakShared -{ +impl WeakShared { /// Attempts to upgrade this [`WeakShared`] into a [`Shared`]. /// /// Returns [`None`] if all clones of the [`Shared`] have been dropped or polled /// to completion. pub fn upgrade(&self) -> Option> { - Some(Shared { - inner: Some(self.0.upgrade()?), - waker_key: NULL_WAKER_KEY, - }) + Some(Shared { inner: Some(self.0.upgrade()?), waker_key: NULL_WAKER_KEY }) } } diff --git a/futures-util/src/future/join.rs b/futures-util/src/future/join.rs index a8183433df..740ffbc988 100644 --- a/futures-util/src/future/join.rs +++ b/futures-util/src/future/join.rs @@ -213,14 +213,5 @@ where Fut5: Future, { let f = Join5::new(future1, future2, future3, future4, future5); - assert_future::< - ( - Fut1::Output, - Fut2::Output, - Fut3::Output, - Fut4::Output, - Fut5::Output, - ), - _, - >(f) + assert_future::<(Fut1::Output, Fut2::Output, Fut3::Output, Fut4::Output, Fut5::Output), _>(f) } diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 7ccf869042..427e71ce04 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -1,24 +1,22 @@ //! Definition of the `JoinAll` combinator, waiting for all of a list of futures //! to finish. +use alloc::boxed::Box; +use alloc::vec::Vec; use core::fmt; use core::future::Future; use core::iter::FromIterator; use core::mem; use core::pin::Pin; use core::task::{Context, Poll}; -use alloc::boxed::Box; -use alloc::vec::Vec; -use super::{MaybeDone, assert_future}; +use super::{assert_future, MaybeDone}; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has // the same safety as a normal field pin projection. - unsafe { slice.get_unchecked_mut() } - .iter_mut() - .map(|t| unsafe { Pin::new_unchecked(t) }) + unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) } /// Future for the [`join_all`] function. @@ -36,9 +34,7 @@ where F::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinAll") - .field("elems", &self.elems) - .finish() + f.debug_struct("JoinAll").field("elems", &self.elems).finish() } } @@ -105,9 +101,7 @@ where 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_output().unwrap()) - .collect(); + let result = iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); Poll::Ready(result) } else { Poll::Pending diff --git a/futures-util/src/future/lazy.rs b/futures-util/src/future/lazy.rs index 42812d3893..e9a8cf2fa9 100644 --- a/futures-util/src/future/lazy.rs +++ b/futures-util/src/future/lazy.rs @@ -7,7 +7,7 @@ use futures_core::task::{Context, Poll}; #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Lazy { - f: Option + f: Option, } // safe because we never generate `Pin<&mut F>` @@ -33,19 +33,24 @@ impl Unpin for Lazy {} /// # }); /// ``` pub fn lazy(f: F) -> Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { assert_future::(Lazy { f: Some(f) }) } impl FusedFuture for Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { - fn is_terminated(&self) -> bool { self.f.is_none() } + fn is_terminated(&self) -> bool { + self.f.is_none() + } } impl Future for Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { type Output = R; diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 84e457c2f5..8d9152b6c9 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -21,7 +21,7 @@ pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj}; #[allow(clippy::module_inception)] mod future; pub use self::future::{ - Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, MapInto, + Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, MapInto, NeverError, Then, UnitError, }; #[deprecated(note = "This is now an alias for [Flatten](Flatten)")] @@ -40,8 +40,8 @@ pub use self::future::{Shared, WeakShared}; mod try_future; pub use self::try_future::{ - AndThen, ErrInto, OkInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, - TryFutureExt, UnwrapOrElse, MapOkOrElse, TryFlatten, + AndThen, ErrInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, MapOkOrElse, OkInto, + OrElse, TryFlatten, TryFlattenStream, TryFutureExt, UnwrapOrElse, }; #[cfg(feature = "sink")] diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 85939d6b1a..426fe50fea 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -1,7 +1,7 @@ //! Definition of the `Option` (optional step) combinator use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -34,10 +34,7 @@ pin_project! { impl Future for OptionFuture { type Output = Option; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.project().inner.as_pin_mut() { Some(x) => x.poll(cx).map(Some), None => Poll::Ready(None), diff --git a/futures-util/src/future/pending.rs b/futures-util/src/future/pending.rs index 4311b9a228..92c78d52b8 100644 --- a/futures-util/src/future/pending.rs +++ b/futures-util/src/future/pending.rs @@ -34,9 +34,7 @@ impl FusedFuture for Pending { /// # }); /// ``` pub fn pending() -> Pending { - assert_future::(Pending { - _data: marker::PhantomData, - }) + assert_future::(Pending { _data: marker::PhantomData }) } impl Future for Pending { @@ -47,8 +45,7 @@ impl Future for Pending { } } -impl Unpin for Pending { -} +impl Unpin for Pending {} impl Clone for Pending { fn clone(&self) -> Self { diff --git a/futures-util/src/future/poll_fn.rs b/futures-util/src/future/poll_fn.rs index 6ac1ab818e..19311570b5 100644 --- a/futures-util/src/future/poll_fn.rs +++ b/futures-util/src/future/poll_fn.rs @@ -35,7 +35,7 @@ impl Unpin for PollFn {} /// ``` pub fn poll_fn(f: F) -> PollFn where - F: FnMut(&mut Context<'_>) -> Poll + F: FnMut(&mut Context<'_>) -> Poll, { assert_future::(PollFn { f }) } @@ -47,7 +47,8 @@ impl fmt::Debug for PollFn { } impl Future for PollFn - where F: FnMut(&mut Context<'_>) -> Poll, +where + F: FnMut(&mut Context<'_>) -> Poll, { type Output = T; diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index 043ed178e7..bd44f20f77 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -1,8 +1,8 @@ use super::assert_future; +use crate::future::{Either, FutureExt}; use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use crate::future::{Either, FutureExt}; /// Future for the [`select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] @@ -37,13 +37,13 @@ impl Unpin for Select {} /// future::Either, /// future::self, /// }; -/// +/// /// // These two futures have different types even though their outputs have the same type. /// let future1 = async { /// future::pending::<()>().await; // will never finish /// 1 /// }; -/// let future2 = async { +/// let future2 = async { /// future::ready(2).await /// }; /// @@ -82,9 +82,13 @@ impl Unpin for Select {} /// } /// ``` pub fn select(future1: A, future2: B) -> Select - where A: Future + Unpin, B: Future + Unpin +where + A: Future + Unpin, + B: Future + Unpin, { - assert_future::, _>(Select { inner: Some((future1, future2)) }) + assert_future::, _>(Select { + inner: Some((future1, future2)), + }) } impl Future for Select @@ -104,7 +108,7 @@ where self.inner = Some((a, b)); Poll::Pending } - } + }, } } } diff --git a/futures-util/src/future/select_all.rs b/futures-util/src/future/select_all.rs index b341e83cd5..106e50844c 100644 --- a/futures-util/src/future/select_all.rs +++ b/futures-util/src/future/select_all.rs @@ -1,9 +1,9 @@ use super::assert_future; use crate::future::FutureExt; +use alloc::vec::Vec; use core::iter::FromIterator; use core::mem; use core::pin::Pin; -use alloc::vec::Vec; use futures_core::future::Future; use futures_core::task::{Context, Poll}; @@ -32,12 +32,11 @@ impl Unpin for SelectAll {} /// /// This function will panic if the iterator specified contains no items. pub fn select_all(iter: I) -> SelectAll - where I: IntoIterator, - I::Item: Future + Unpin, +where + I: IntoIterator, + I::Item: Future + Unpin, { - let ret = SelectAll { - inner: iter.into_iter().collect() - }; + let ret = SelectAll { inner: iter.into_iter().collect() }; assert!(!ret.inner.is_empty()); assert_future::<(::Output, usize, Vec), _>(ret) } @@ -53,11 +52,9 @@ impl Future for SelectAll { type Output = (Fut::Output, usize, Vec); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| { - match f.poll_unpin(cx) { - Poll::Pending => None, - Poll::Ready(e) => Some((i, e)), - } + let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| match f.poll_unpin(cx) { + Poll::Pending => None, + Poll::Ready(e) => Some((i, e)), }); match item { Some((idx, res)) => { diff --git a/futures-util/src/future/select_ok.rs b/futures-util/src/future/select_ok.rs index 52d393c28e..0ad83c6db6 100644 --- a/futures-util/src/future/select_ok.rs +++ b/futures-util/src/future/select_ok.rs @@ -1,9 +1,9 @@ use super::assert_future; use crate::future::TryFutureExt; +use alloc::vec::Vec; use core::iter::FromIterator; use core::mem; use core::pin::Pin; -use alloc::vec::Vec; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; @@ -30,14 +30,16 @@ impl Unpin for SelectOk {} /// /// This function will panic if the iterator specified contains no items. pub fn select_ok(iter: I) -> SelectOk - where I: IntoIterator, - I::Item: TryFuture + Unpin, +where + I: IntoIterator, + I::Item: TryFuture + Unpin, { - let ret = SelectOk { - inner: iter.into_iter().collect() - }; + let ret = SelectOk { inner: iter.into_iter().collect() }; assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty"); - assert_future::::Ok, Vec), ::Error>, _>(ret) + assert_future::< + Result<(::Ok, Vec), ::Error>, + _, + >(ret) } impl Future for SelectOk { @@ -46,12 +48,11 @@ impl Future for SelectOk { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // loop until we've either exhausted all errors, a success was hit, or nothing is ready loop { - let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| { - match f.try_poll_unpin(cx) { + let item = + self.inner.iter_mut().enumerate().find_map(|(i, f)| match f.try_poll_unpin(cx) { Poll::Pending => None, Poll::Ready(e) => Some((i, e)), - } - }); + }); match item { Some((idx, res)) => { // always remove Ok or Err, if it's not the last Err continue looping @@ -59,18 +60,18 @@ impl Future for SelectOk { match res { Ok(e) => { let rest = mem::replace(&mut self.inner, Vec::new()); - return Poll::Ready(Ok((e, rest))) + return Poll::Ready(Ok((e, rest))); } Err(e) => { if self.inner.is_empty() { - return Poll::Ready(Err(e)) + return Poll::Ready(Err(e)); } } } } None => { // based on the filter above, nothing is ready, return - return Poll::Pending + return Poll::Pending; } } } diff --git a/futures-util/src/future/try_future/into_future.rs b/futures-util/src/future/try_future/into_future.rs index e88d603c0f..9f093d0e2e 100644 --- a/futures-util/src/future/try_future/into_future.rs +++ b/futures-util/src/future/try_future/into_future.rs @@ -21,17 +21,16 @@ impl IntoFuture { } impl FusedFuture for IntoFuture { - fn is_terminated(&self) -> bool { self.future.is_terminated() } + fn is_terminated(&self) -> bool { + self.future.is_terminated() + } } impl Future for IntoFuture { type Output = Result; #[inline] - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.project().future.try_poll(cx) } } diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs index 5241b2750d..1ce4559ac2 100644 --- a/futures-util/src/future/try_future/try_flatten.rs +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -2,9 +2,9 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::ready; use futures_core::stream::{FusedStream, Stream, TryStream}; +use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; pin_project! { @@ -24,8 +24,9 @@ impl TryFlatten { } impl FusedFuture for TryFlatten - where Fut: TryFuture, - Fut::Ok: TryFuture, +where + Fut: TryFuture, + Fut::Ok: TryFuture, { fn is_terminated(&self) -> bool { match self { @@ -36,28 +37,27 @@ impl FusedFuture for TryFlatten } impl Future for TryFlatten - where Fut: TryFuture, - Fut::Ok: TryFuture, +where + Fut: TryFuture, + Fut::Ok: TryFuture, { type Output = Result<::Ok, Fut::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready(loop { match self.as_mut().project() { - TryFlattenProj::First { f } => { - match ready!(f.try_poll(cx)) { - Ok(f) => self.set(Self::Second { f }), - Err(e) => { - self.set(Self::Empty); - break Err(e); - } + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Err(e); } }, TryFlattenProj::Second { f } => { let output = ready!(f.try_poll(cx)); self.set(Self::Empty); break output; - }, + } TryFlattenProj::Empty => panic!("TryFlatten polled after completion"), } }) @@ -65,8 +65,9 @@ impl Future for TryFlatten } impl FusedStream for TryFlatten - where Fut: TryFuture, - Fut::Ok: TryStream, +where + Fut: TryFuture, + Fut::Ok: TryStream, { fn is_terminated(&self) -> bool { match self { @@ -77,21 +78,20 @@ impl FusedStream for TryFlatten } impl Stream for TryFlatten - where Fut: TryFuture, - Fut::Ok: TryStream, +where + Fut: TryFuture, + Fut::Ok: TryStream, { type Item = Result<::Ok, Fut::Error>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready(loop { match self.as_mut().project() { - TryFlattenProj::First { f } => { - match ready!(f.try_poll(cx)) { - Ok(f) => self.set(Self::Second { f }), - Err(e) => { - self.set(Self::Empty); - break Some(Err(e)); - } + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Some(Err(e)); } }, TryFlattenProj::Second { f } => { @@ -100,40 +100,34 @@ impl Stream for TryFlatten self.set(Self::Empty); } break output; - }, + } TryFlattenProj::Empty => break None, } }) } } - #[cfg(feature = "sink")] impl Sink for TryFlatten where Fut: TryFuture, - Fut::Ok: Sink, + Fut::Ok: Sink, { type Error = Fut::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready(loop { match self.as_mut().project() { - TryFlattenProj::First { f } => { - match ready!(f.try_poll(cx)) { - Ok(f) => self.set(Self::Second { f }), - Err(e) => { - self.set(Self::Empty); - break Err(e); - } + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Err(e); } }, TryFlattenProj::Second { f } => { break ready!(f.poll_ready(cx)); - }, + } TryFlattenProj::Empty => panic!("poll_ready called after eof"), } }) @@ -155,10 +149,7 @@ where } } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let res = match self.as_mut().project() { TryFlattenProj::Second { f } => f.poll_close(cx), _ => Poll::Ready(Ok(())), diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs index 2e67f1104e..39b7d9f5f6 100644 --- a/futures-util/src/future/try_future/try_flatten_err.rs +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -21,8 +21,9 @@ impl TryFlattenErr { } impl FusedFuture for TryFlattenErr - where Fut: TryFuture, - Fut::Error: TryFuture, +where + Fut: TryFuture, + Fut::Error: TryFuture, { fn is_terminated(&self) -> bool { match self { @@ -33,28 +34,27 @@ impl FusedFuture for TryFlattenErr } impl Future for TryFlattenErr - where Fut: TryFuture, - Fut::Error: TryFuture, +where + Fut: TryFuture, + Fut::Error: TryFuture, { type Output = Result::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready(loop { match self.as_mut().project() { - TryFlattenErrProj::First { f } => { - match ready!(f.try_poll(cx)) { - Err(f) => self.set(Self::Second { f }), - Ok(e) => { - self.set(Self::Empty); - break Ok(e); - } + TryFlattenErrProj::First { f } => match ready!(f.try_poll(cx)) { + Err(f) => self.set(Self::Second { f }), + Ok(e) => { + self.set(Self::Empty); + break Ok(e); } }, TryFlattenErrProj::Second { f } => { let output = ready!(f.try_poll(cx)); self.set(Self::Empty); break output; - }, + } TryFlattenErrProj::Empty => panic!("TryFlattenErr polled after completion"), } }) diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 371f753f3f..29244af837 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -1,14 +1,14 @@ //! Definition of the `TryJoinAll` combinator, waiting for all of a list of //! futures to finish with either success or error. +use alloc::boxed::Box; +use alloc::vec::Vec; use core::fmt; use core::future::Future; use core::iter::FromIterator; use core::mem; use core::pin::Pin; use core::task::{Context, Poll}; -use alloc::boxed::Box; -use alloc::vec::Vec; use super::{assert_future, TryFuture, TryMaybeDone}; @@ -16,15 +16,13 @@ fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has // the same safety as a normal field pin projection. - unsafe { slice.get_unchecked_mut() } - .iter_mut() - .map(|t| unsafe { Pin::new_unchecked(t) }) + unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) } enum FinalState { Pending, AllDone, - Error(E) + Error(E), } /// Future for the [`try_join_all`] function. @@ -43,9 +41,7 @@ where F::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryJoinAll") - .field("elems", &self.elems) - .finish() + f.debug_struct("TryJoinAll").field("elems", &self.elems).finish() } } @@ -93,9 +89,9 @@ where I::Item: TryFuture, { let elems: Box<[_]> = i.into_iter().map(TryMaybeDone::Future).collect(); - assert_future::::Ok>, ::Error>, _>(TryJoinAll { - elems: elems.into(), - }) + assert_future::::Ok>, ::Error>, _>( + TryJoinAll { elems: elems.into() }, + ) } impl Future for TryJoinAll @@ -110,7 +106,7 @@ where 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(Ok(())) => {} Poll::Ready(Err(e)) => { state = FinalState::Error(e); break; @@ -122,15 +118,14 @@ where FinalState::Pending => Poll::Pending, FinalState::AllDone => { let mut elems = mem::replace(&mut self.elems, Box::pin([])); - let results = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_output().unwrap()) - .collect(); + let results = + iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); Poll::Ready(Ok(results)) - }, + } FinalState::Error(e) => { let _ = mem::replace(&mut self.elems, Box::pin([])); Poll::Ready(Err(e)) - }, + } } } } diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs index dfd290065d..24044d2c27 100644 --- a/futures-util/src/future/try_maybe_done.rs +++ b/futures-util/src/future/try_maybe_done.rs @@ -49,13 +49,13 @@ impl TryMaybeDone { #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { match &*self { - Self::Done(_) => {}, + Self::Done(_) => {} Self::Future(_) | Self::Gone => return None, } unsafe { match mem::replace(self.get_unchecked_mut(), Self::Gone) { TryMaybeDone::Done(output) => Some(output), - _ => unreachable!() + _ => unreachable!(), } } } @@ -76,16 +76,14 @@ impl Future for TryMaybeDone { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { match self.as_mut().get_unchecked_mut() { - TryMaybeDone::Future(f) => { - match ready!(Pin::new_unchecked(f).try_poll(cx)) { - Ok(res) => self.set(Self::Done(res)), - Err(e) => { - self.set(Self::Gone); - return Poll::Ready(Err(e)); - } + TryMaybeDone::Future(f) => match ready!(Pin::new_unchecked(f).try_poll(cx)) { + Ok(res) => self.set(Self::Done(res)), + Err(e) => { + self.set(Self::Gone); + return Poll::Ready(Err(e)); } }, - TryMaybeDone::Done(_) => {}, + TryMaybeDone::Done(_) => {} TryMaybeDone::Gone => panic!("TryMaybeDone polled after value taken"), } } diff --git a/futures-util/src/future/try_select.rs b/futures-util/src/future/try_select.rs index b26eed35f0..4d0b7ff135 100644 --- a/futures-util/src/future/try_select.rs +++ b/futures-util/src/future/try_select.rs @@ -1,7 +1,7 @@ +use crate::future::{Either, TryFutureExt}; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; -use crate::future::{Either, TryFutureExt}; /// Future for the [`try_select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] @@ -48,22 +48,23 @@ impl Unpin for TrySelect {} /// } /// ``` pub fn try_select(future1: A, future2: B) -> TrySelect - where A: TryFuture + Unpin, B: TryFuture + Unpin +where + A: TryFuture + Unpin, + B: TryFuture + Unpin, { - super::assert_future::, - Either<(A::Error, B), (B::Error, A)>, - >, _>(TrySelect { inner: Some((future1, future2)) }) + super::assert_future::< + Result, Either<(A::Error, B), (B::Error, A)>>, + _, + >(TrySelect { inner: Some((future1, future2)) }) } impl Future for TrySelect - where A: TryFuture, B: TryFuture +where + A: TryFuture, + B: TryFuture, { #[allow(clippy::type_complexity)] - type Output = Result< - Either<(A::Ok, B), (B::Ok, A)>, - Either<(A::Error, B), (B::Error, A)>, - >; + type Output = Result, Either<(A::Error, B), (B::Error, A)>>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let (mut a, mut b) = self.inner.take().expect("cannot poll Select twice"); @@ -77,7 +78,7 @@ impl Future for TrySelect self.inner = Some((a, b)); Poll::Pending } - } + }, } } } diff --git a/futures-util/src/io/allow_std.rs b/futures-util/src/io/allow_std.rs index 9aa8eb4e71..1d13e0c177 100644 --- a/futures-util/src/io/allow_std.rs +++ b/futures-util/src/io/allow_std.rs @@ -1,9 +1,9 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; -use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, IoSlice, IoSliceMut, SeekFrom}; -use std::{fmt, io}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; use std::pin::Pin; +use std::{fmt, io}; /// A simple wrapper type which allows types which implement only /// implement `std::io::Read` or `std::io::Write` @@ -35,7 +35,7 @@ macro_rules! try_with_interrupt { } } } - } + }; } impl AllowStdIo { @@ -60,7 +60,10 @@ impl AllowStdIo { } } -impl io::Write for AllowStdIo where T: io::Write { +impl io::Write for AllowStdIo +where + T: io::Write, +{ fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -78,16 +81,23 @@ impl io::Write for AllowStdIo where T: io::Write { } } -impl AsyncWrite for AllowStdIo where T: io::Write { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { +impl AsyncWrite for AllowStdIo +where + T: io::Write, +{ + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.write(buf)))) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.write_vectored(bufs)))) } @@ -101,7 +111,10 @@ impl AsyncWrite for AllowStdIo where T: io::Write { } } -impl io::Read for AllowStdIo where T: io::Read { +impl io::Read for AllowStdIo +where + T: io::Read, +{ fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -123,16 +136,23 @@ impl io::Read for AllowStdIo where T: io::Read { } } -impl AsyncRead for AllowStdIo where T: io::Read { - fn poll_read(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { +impl AsyncRead for AllowStdIo +where + T: io::Read, +{ + fn poll_read( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.read(buf)))) } - fn poll_read_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.read_vectored(bufs)))) } @@ -142,21 +162,32 @@ impl AsyncRead for AllowStdIo where T: io::Read { } } -impl io::Seek for AllowStdIo where T: io::Seek { +impl io::Seek for AllowStdIo +where + T: io::Seek, +{ fn seek(&mut self, pos: SeekFrom) -> io::Result { self.0.seek(pos) } } -impl AsyncSeek for AllowStdIo where T: io::Seek { - fn poll_seek(mut self: Pin<&mut Self>, _: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { +impl AsyncSeek for AllowStdIo +where + T: io::Seek, +{ + fn poll_seek( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.seek(pos)))) } } -impl io::BufRead for AllowStdIo where T: io::BufRead { +impl io::BufRead for AllowStdIo +where + T: io::BufRead, +{ fn fill_buf(&mut self) -> io::Result<&[u8]> { self.0.fill_buf() } @@ -165,10 +196,11 @@ impl io::BufRead for AllowStdIo where T: io::BufRead { } } -impl AsyncBufRead for AllowStdIo where T: io::BufRead { - fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { +impl AsyncBufRead for AllowStdIo +where + T: io::BufRead, +{ + fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { let this: *mut Self = &mut *self as *mut _; Poll::Ready(Ok(try_with_interrupt!(unsafe { &mut *this }.0.fill_buf()))) } diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 270a086cf8..5931edc1b2 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,3 +1,4 @@ +use super::DEFAULT_BUF_SIZE; use futures_core::ready; use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] @@ -7,7 +8,6 @@ use pin_project_lite::pin_project; use std::io::{self, Read}; use std::pin::Pin; use std::{cmp, fmt}; -use super::DEFAULT_BUF_SIZE; pin_project! { /// The `BufReader` struct adds buffering to any reader. @@ -51,12 +51,7 @@ impl BufReader { let mut buffer = Vec::with_capacity(capacity); buffer.set_len(capacity); super::initialize(&inner, &mut buffer); - Self { - inner, - buffer: buffer.into_boxed_slice(), - pos: 0, - cap: 0, - } + Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 } } } @@ -123,10 +118,7 @@ impl AsyncRead for BufReader { } impl AsyncBufRead for BufReader { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); // If we've reached the end of our internal buffer then we need to fetch @@ -192,7 +184,8 @@ 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().project().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().project().inner.poll_seek(cx, SeekFrom::Current(-remainder)))?; diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index 991a365a1c..f292b871f1 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -1,3 +1,4 @@ +use super::DEFAULT_BUF_SIZE; use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, SeekFrom}; @@ -5,7 +6,6 @@ use pin_project_lite::pin_project; use std::fmt; use std::io::{self, Write}; use std::pin::Pin; -use super::DEFAULT_BUF_SIZE; pin_project! { /// Wraps a writer and buffers its output. @@ -46,11 +46,7 @@ impl BufWriter { /// Creates a new `BufWriter` with the specified buffer capacity. pub fn with_capacity(cap: usize, inner: W) -> Self { - Self { - inner, - buf: Vec::with_capacity(cap), - written: 0, - } + Self { inner, buf: Vec::with_capacity(cap), written: 0 } } fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 1b6a335569..a35c50de35 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -26,11 +26,7 @@ where U: AsyncRead, { pub(super) fn new(first: T, second: U) -> Self { - Self { - first, - second, - done_first: false, - } + Self { first, second, done_first: false } } /// Gets references to the underlying readers in this `Chain`. diff --git a/futures-util/src/io/copy.rs b/futures-util/src/io/copy.rs index bc592552e9..c80add271b 100644 --- a/futures-util/src/io/copy.rs +++ b/futures-util/src/io/copy.rs @@ -1,10 +1,10 @@ +use super::{copy_buf, BufReader, CopyBuf}; use futures_core::future::Future; use futures_core::task::{Context, Poll}; use futures_io::{AsyncRead, AsyncWrite}; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; -use super::{BufReader, copy_buf, CopyBuf}; -use pin_project_lite::pin_project; /// Creates a future which copies all the bytes from one object to another. /// @@ -36,9 +36,7 @@ where R: AsyncRead, W: AsyncWrite + Unpin + ?Sized, { - Copy { - inner: copy_buf(BufReader::new(reader), writer), - } + Copy { inner: copy_buf(BufReader::new(reader), writer) } } pin_project! { diff --git a/futures-util/src/io/copy_buf.rs b/futures-util/src/io/copy_buf.rs index 6adf594d54..50f7abdca9 100644 --- a/futures-util/src/io/copy_buf.rs +++ b/futures-util/src/io/copy_buf.rs @@ -2,9 +2,9 @@ use futures_core::future::Future; use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncWrite}; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; -use pin_project_lite::pin_project; /// Creates a future which copies all the bytes from one object to another. /// @@ -36,11 +36,7 @@ where R: AsyncBufRead, W: AsyncWrite + Unpin + ?Sized, { - CopyBuf { - reader, - writer, - amt: 0, - } + CopyBuf { reader, writer, amt: 0 } } pin_project! { @@ -56,8 +52,9 @@ pin_project! { } impl Future for CopyBuf<'_, R, W> - where R: AsyncBufRead, - W: AsyncWrite + Unpin + ?Sized, +where + R: AsyncBufRead, + W: AsyncWrite + Unpin + ?Sized, { type Output = io::Result; @@ -72,7 +69,7 @@ impl Future for CopyBuf<'_, R, W> let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?; if i == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } *this.amt += i as u64; this.reader.as_mut().consume(i); diff --git a/futures-util/src/io/cursor.rs b/futures-util/src/io/cursor.rs index 084fb0863b..b6fb3724c7 100644 --- a/futures-util/src/io/cursor.rs +++ b/futures-util/src/io/cursor.rs @@ -43,9 +43,7 @@ impl Cursor { /// # force_inference(&buff); /// ``` pub fn new(inner: T) -> Self { - Self { - inner: io::Cursor::new(inner), - } + Self { inner: io::Cursor::new(inner) } } /// Consumes this cursor, returning the underlying value. @@ -199,15 +197,19 @@ where macro_rules! delegate_async_write_to_stdio { () => { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(io::Write::write(&mut self.inner, buf)) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(io::Write::write_vectored(&mut self.inner, bufs)) } @@ -218,7 +220,7 @@ macro_rules! delegate_async_write_to_stdio { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } - } + }; } impl AsyncWrite for Cursor<&mut [u8]> { diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs index 6fb3ec73aa..19b0d2044c 100644 --- a/futures-util/src/io/fill_buf.rs +++ b/futures-util/src/io/fill_buf.rs @@ -20,7 +20,8 @@ impl<'a, R: AsyncBufRead + ?Sized + Unpin> FillBuf<'a, R> { } impl<'a, R> Future for FillBuf<'a, R> - where R: AsyncBufRead + ?Sized + Unpin, +where + R: AsyncBufRead + ?Sized + Unpin, { type Output = io::Result<&'a [u8]>; diff --git a/futures-util/src/io/flush.rs b/futures-util/src/io/flush.rs index ece0a7cdc0..b75d14c5d3 100644 --- a/futures-util/src/io/flush.rs +++ b/futures-util/src/io/flush.rs @@ -20,7 +20,8 @@ impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> { } impl Future for Flush<'_, W> - where W: AsyncWrite + ?Sized + Unpin, +where + W: AsyncWrite + ?Sized + Unpin, { type Output = io::Result<()>; diff --git a/futures-util/src/io/into_sink.rs b/futures-util/src/io/into_sink.rs index 885ba2fb8d..384b8e3b92 100644 --- a/futures-util/src/io/into_sink.rs +++ b/futures-util/src/io/into_sink.rs @@ -2,9 +2,9 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncWrite; use futures_sink::Sink; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; -use pin_project_lite::pin_project; #[derive(Debug)] struct Block { @@ -36,8 +36,7 @@ impl> IntoSink { fn poll_flush_buffer( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> - { + ) -> Poll> { let mut this = self.project(); if let Some(buffer) = this.buffer { @@ -53,47 +52,30 @@ impl> IntoSink { *this.buffer = None; Poll::Ready(Ok(())) } - } impl> Sink for IntoSink { type Error = io::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.poll_flush_buffer(cx))?; Poll::Ready(Ok(())) } #[allow(clippy::debug_assert_with_mut_call)] - fn start_send( - self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> - { + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { debug_assert!(self.buffer.is_none()); *self.project().buffer = Some(Block { offset: 0, bytes: item }); Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; ready!(self.project().writer.poll_flush(cx))?; Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buffer(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 6ae7392245..13e70df238 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -1,12 +1,12 @@ +use super::read_line::read_line_internal; use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; +use pin_project_lite::pin_project; use std::io; use std::mem; use std::pin::Pin; -use super::read_line::read_line_internal; -use pin_project_lite::pin_project; pin_project! { /// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. @@ -23,12 +23,7 @@ pin_project! { impl Lines { pub(super) fn new(reader: R) -> Self { - Self { - reader, - buf: String::new(), - bytes: Vec::new(), - read: 0, - } + Self { reader, buf: String::new(), bytes: Vec::new(), read: 0 } } } @@ -39,7 +34,7 @@ impl Stream for Lines { let this = self.project(); let n = ready!(read_line_internal(this.reader, cx, this.buf, this.bytes, this.read))?; if n == 0 && this.buf.is_empty() { - return Poll::Ready(None) + return Poll::Ready(None); } if this.buf.ends_with('\n') { this.buf.pop(); diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 437aedf0c4..b96223d1c1 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -21,18 +21,18 @@ use crate::compat::Compat; use crate::future::assert_future; use crate::stream::assert_stream; -use std::{ptr, pin::Pin}; +use std::{pin::Pin, ptr}; // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. #[doc(no_inline)] -pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -#[doc(no_inline)] #[cfg(feature = "read-initializer")] #[cfg_attr(docsrs, doc(cfg(feature = "read-initializer")))] pub use std::io::Initializer; +#[doc(no_inline)] +pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -pub use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead}; +pub use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; // used by `BufReader` and `BufWriter` // https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/io.rs#L1 @@ -126,7 +126,7 @@ mod sink; pub use self::sink::{sink, Sink}; mod split; -pub use self::split::{ReadHalf, WriteHalf, ReuniteError}; +pub use self::split::{ReadHalf, ReuniteError, WriteHalf}; mod take; pub use self::take::Take; @@ -206,7 +206,8 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(Read::new(self, buf)) } @@ -217,7 +218,8 @@ pub trait AsyncReadExt: AsyncRead { /// The returned future will resolve to the number of bytes read once the read /// operation is completed. fn read_vectored<'a>(&'a mut self, bufs: &'a mut [IoSliceMut<'a>]) -> ReadVectored<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(ReadVectored::new(self, bufs)) } @@ -259,11 +261,9 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); /// # }); /// ``` - fn read_exact<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> ReadExact<'a, Self> - where Self: Unpin, + fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> + where + Self: Unpin, { assert_future::, _>(ReadExact::new(self, buf)) } @@ -287,11 +287,9 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_to_end<'a>( - &'a mut self, - buf: &'a mut Vec, - ) -> ReadToEnd<'a, Self> - where Self: Unpin, + fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec) -> ReadToEnd<'a, Self> + where + Self: Unpin, { assert_future::, _>(ReadToEnd::new(self, buf)) } @@ -315,11 +313,9 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(buffer, String::from("1234")); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_to_string<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ReadToString<'a, Self> - where Self: Unpin, + fn read_to_string<'a>(&'a mut self, buf: &'a mut String) -> ReadToString<'a, Self> + where + Self: Unpin, { assert_future::, _>(ReadToString::new(self, buf)) } @@ -354,7 +350,8 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn split(self) -> (ReadHalf, WriteHalf) - where Self: AsyncWrite + Sized, + where + Self: AsyncWrite + Sized, { let (r, w) = split::split(self); (assert_read(r), assert_write(w)) @@ -380,7 +377,8 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn take(self, limit: u64) -> Take - where Self: Sized + where + Self: Sized, { assert_read(Take::new(self, limit)) } @@ -394,7 +392,8 @@ pub trait AsyncReadExt: AsyncRead { #[cfg(feature = "io-compat")] #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] fn compat(self) -> Compat - where Self: Sized + Unpin, + where + Self: Sized + Unpin, { Compat::new(self) } @@ -427,14 +426,16 @@ pub trait AsyncWriteExt: AsyncWrite { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn flush(&mut self) -> Flush<'_, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(Flush::new(self)) } /// Creates a future which will entirely close this `AsyncWrite`. fn close(&mut self) -> Close<'_, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(Close::new(self)) } @@ -444,7 +445,8 @@ pub trait AsyncWriteExt: AsyncWrite { /// The returned future will resolve to the number of bytes written once the write /// operation is completed. fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(Write::new(self, buf)) } @@ -455,7 +457,8 @@ pub trait AsyncWriteExt: AsyncWrite { /// The returned future will resolve to the number of bytes written once the write /// operation is completed. fn write_vectored<'a>(&'a mut self, bufs: &'a [IoSlice<'a>]) -> WriteVectored<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(WriteVectored::new(self, bufs)) } @@ -481,7 +484,8 @@ pub trait AsyncWriteExt: AsyncWrite { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(WriteAll::new(self, buf)) } @@ -547,7 +551,8 @@ pub trait AsyncWriteExt: AsyncWrite { #[cfg(feature = "io-compat")] #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] fn compat_write(self) -> Compat - where Self: Sized + Unpin, + where + Self: Sized + Unpin, { Compat::new(self) } @@ -581,7 +586,8 @@ pub trait AsyncWriteExt: AsyncWrite { #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] fn into_sink>(self) -> IntoSink - where Self: Sized, + where + Self: Sized, { crate::sink::assert_sink::(IntoSink::new(self)) } @@ -597,7 +603,8 @@ pub trait AsyncSeekExt: AsyncSeek { /// In the case of an error the buffer and the object will be discarded, with /// the error yielded. fn seek(&mut self, pos: SeekFrom) -> Seek<'_, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(Seek::new(self, pos)) } @@ -642,7 +649,8 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn fill_buf(&mut self) -> FillBuf<'_, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(FillBuf::new(self)) } @@ -665,7 +673,8 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn consume_unpin(&mut self, amt: usize) - where Self: Unpin, + where + Self: Unpin, { Pin::new(self).consume(amt) } @@ -711,12 +720,9 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// assert_eq!(buf, b""); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_until<'a>( - &'a mut self, - byte: u8, - buf: &'a mut Vec, - ) -> ReadUntil<'a, Self> - where Self: Unpin, + fn read_until<'a>(&'a mut self, byte: u8, buf: &'a mut Vec) -> ReadUntil<'a, Self> + where + Self: Unpin, { assert_future::, _>(ReadUntil::new(self, byte, buf)) } @@ -773,7 +779,8 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn read_line<'a>(&'a mut self, buf: &'a mut String) -> ReadLine<'a, Self> - where Self: Unpin, + where + Self: Unpin, { assert_future::, _>(ReadLine::new(self, buf)) } @@ -811,7 +818,8 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn lines(self) -> Lines - where Self: Sized, + where + Self: Sized, { assert_stream::, _>(Lines::new(self)) } diff --git a/futures-util/src/io/read_exact.rs b/futures-util/src/io/read_exact.rs index f2e0440890..02e38c35be 100644 --- a/futures-util/src/io/read_exact.rs +++ b/futures-util/src/io/read_exact.rs @@ -1,6 +1,6 @@ use crate::io::AsyncRead; -use futures_core::ready; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use std::io; use std::mem; @@ -34,7 +34,7 @@ impl Future for ReadExact<'_, R> { this.buf = rest; } if n == 0 { - return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())) + return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())); } } Poll::Ready(Ok(())) diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index d402c96605..c75af9471f 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -1,12 +1,12 @@ -use futures_core::ready; +use super::read_until::read_until_internal; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; use std::io; use std::mem; use std::pin::Pin; use std::str; -use super::read_until::read_until_internal; /// Future for the [`read_line`](super::AsyncBufReadExt::read_line) method. #[derive(Debug)] @@ -22,12 +22,7 @@ impl Unpin for ReadLine<'_, R> {} 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: mem::replace(buf, String::new()).into_bytes(), - buf, - read: 0, - } + Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, read: 0 } } } diff --git a/futures-util/src/io/read_to_end.rs b/futures-util/src/io/read_to_end.rs index 7bd2c89914..919d7d13c7 100644 --- a/futures-util/src/io/read_to_end.rs +++ b/futures-util/src/io/read_to_end.rs @@ -20,11 +20,7 @@ impl Unpin for ReadToEnd<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToEnd<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut Vec) -> Self { let start_len = buf.len(); - Self { - reader, - buf, - start_len, - } + Self { reader, buf, start_len } } } @@ -56,10 +52,7 @@ pub(super) fn read_to_end_internal( buf: &mut Vec, start_len: usize, ) -> Poll> { - let mut g = Guard { - len: buf.len(), - buf, - }; + let mut g = Guard { len: buf.len(), buf }; loop { if g.len == g.buf.len() { unsafe { diff --git a/futures-util/src/io/read_to_string.rs b/futures-util/src/io/read_to_string.rs index 9242654ff1..457af59e4f 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -1,6 +1,6 @@ use super::read_to_end::read_to_end_internal; -use futures_core::ready; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncRead; use std::pin::Pin; @@ -22,12 +22,7 @@ impl Unpin for ReadToString<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToString<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { let start_len = buf.len(); - Self { - reader, - bytes: mem::replace(buf, String::new()).into_bytes(), - buf, - start_len, - } + Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len } } } @@ -41,10 +36,7 @@ fn read_to_string_internal( let ret = ready!(read_to_end_internal(reader, cx, bytes, start_len)); if str::from_utf8(bytes).is_err() { Poll::Ready(ret.and_then(|_| { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - )) + Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")) })) } else { debug_assert!(buf.is_empty()); diff --git a/futures-util/src/io/split.rs b/futures-util/src/io/split.rs index 185c21c7d4..3f1b9af456 100644 --- a/futures-util/src/io/split.rs +++ b/futures-util/src/io/split.rs @@ -1,8 +1,8 @@ use crate::lock::BiLock; +use core::fmt; use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut}; -use core::fmt; use std::io; use std::pin::Pin; @@ -18,12 +18,9 @@ pub struct WriteHalf { handle: BiLock, } -fn lock_and_then( - lock: &BiLock, - cx: &mut Context<'_>, - f: F -) -> Poll> - where F: FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll> +fn lock_and_then(lock: &BiLock, cx: &mut Context<'_>, f: F) -> Poll> +where + F: FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll>, { let mut l = ready!(lock.poll_lock(cx)); f(l.as_pin_mut(), cx) @@ -39,9 +36,9 @@ impl ReadHalf { /// 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 }) - }) + self.handle + .reunite(other.handle) + .map_err(|err| ReuniteError(ReadHalf { handle: err.0 }, WriteHalf { handle: err.1 })) } } @@ -55,29 +52,37 @@ impl WriteHalf { } impl AsyncRead for ReadHalf { - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_read(cx, buf)) } - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_read_vectored(cx, bufs)) } } impl AsyncWrite for WriteHalf { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_write(cx, buf)) } - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs)) } @@ -96,9 +101,7 @@ 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() + f.debug_tuple("ReuniteError").field(&"...").finish() } } diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index 687a69744f..680d2702dc 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -2,10 +2,10 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; -use futures_io::{AsyncRead, AsyncBufRead}; +use futures_io::{AsyncBufRead, AsyncRead}; use pin_project_lite::pin_project; -use std::{cmp, io}; use std::pin::Pin; +use std::{cmp, io}; pin_project! { /// Reader for the [`take`](super::AsyncReadExt::take) method. diff --git a/futures-util/src/io/window.rs b/futures-util/src/io/window.rs index 3424197d75..77b7267c69 100644 --- a/futures-util/src/io/window.rs +++ b/futures-util/src/io/window.rs @@ -30,10 +30,7 @@ impl> Window { /// Further methods can be called on the returned `Window` to alter the /// window into the data provided. pub fn new(t: T) -> Self { - Self { - range: 0..t.as_ref().len(), - inner: t, - } + Self { range: 0..t.as_ref().len(), inner: t } } /// Gets a shared reference to the underlying buffer inside of this diff --git a/futures-util/src/io/write_all_vectored.rs b/futures-util/src/io/write_all_vectored.rs index 380604df98..f465209fe2 100644 --- a/futures-util/src/io/write_all_vectored.rs +++ b/futures-util/src/io/write_all_vectored.rs @@ -1,5 +1,5 @@ -use futures_core::ready; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncWrite; use futures_io::IoSlice; @@ -56,11 +56,7 @@ mod tests { /// 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(), - } + TestWriter { n_bufs, per_call, written: Vec::new() } } // TODO: maybe move this the future-test crate? @@ -110,10 +106,9 @@ mod tests { let expected = $expected; match $e { Poll::Ready(Ok(ok)) if ok == expected => {} - got => panic!( - "unexpected result, got: {:?}, wanted: Ready(Ok({:?}))", - got, expected - ), + got => { + panic!("unexpected result, got: {:?}, wanted: Ready(Ok({:?}))", got, expected) + } } }; } @@ -154,11 +149,7 @@ mod tests { 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]), - ]; + 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]); diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 512d793653..6a22cf144d 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -5,12 +5,7 @@ #![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 -)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] diff --git a/futures-util/src/sink/buffer.rs b/futures-util/src/sink/buffer.rs index 8c58f4f569..c6ea548d65 100644 --- a/futures-util/src/sink/buffer.rs +++ b/futures-util/src/sink/buffer.rs @@ -1,10 +1,10 @@ +use alloc::collections::VecDeque; +use core::pin::Pin; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; use pin_project_lite::pin_project; -use core::pin::Pin; -use alloc::collections::VecDeque; pin_project! { /// Sink for the [`buffer`](super::SinkExt::buffer) method. @@ -22,19 +22,12 @@ pin_project! { impl, Item> Buffer { pub(super) fn new(sink: Si, capacity: usize) -> Self { - Self { - sink, - buf: VecDeque::with_capacity(capacity), - capacity, - } + Self { sink, buf: VecDeque::with_capacity(capacity), capacity } } delegate_access_inner!(sink, Si, ()); - fn try_empty_buffer( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn try_empty_buffer(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); ready!(this.sink.as_mut().poll_ready(cx))?; while let Some(item) = this.buf.pop_front() { @@ -48,7 +41,10 @@ impl, Item> Buffer { } // Forwarding impl of Stream from the underlying sink -impl Stream for Buffer where S: Sink + Stream { +impl Stream for Buffer +where + S: Sink + Stream, +{ type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -60,7 +56,10 @@ impl Stream for Buffer where S: Sink + Stream { } } -impl FusedStream for Buffer where S: Sink + FusedStream { +impl FusedStream for Buffer +where + S: Sink + FusedStream, +{ fn is_terminated(&self) -> bool { self.sink.is_terminated() } @@ -69,10 +68,7 @@ impl FusedStream for Buffer where S: Sink + FusedStream impl, Item> Sink for Buffer { type Error = Si::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.capacity == 0 { return self.project().sink.poll_ready(cx); } @@ -86,10 +82,7 @@ impl, Item> Sink for Buffer { } } - fn start_send( - self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { if self.capacity == 0 { self.project().sink.start_send(item) } else { @@ -99,20 +92,14 @@ impl, Item> Sink for Buffer { } #[allow(clippy::debug_assert_with_mut_call)] - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; debug_assert!(self.buf.is_empty()); self.project().sink.poll_flush(cx) } #[allow(clippy::debug_assert_with_mut_call)] - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; debug_assert!(self.buf.is_empty()); self.project().sink.poll_close(cx) diff --git a/futures-util/src/sink/close.rs b/futures-util/src/sink/close.rs index 4fc99f5d5b..43eea74b0f 100644 --- a/futures-util/src/sink/close.rs +++ b/futures-util/src/sink/close.rs @@ -19,20 +19,14 @@ impl Unpin for Close<'_, Si, Item> {} /// The sink itself is returned after closing is complete. impl<'a, Si: Sink + Unpin + ?Sized, Item> Close<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si) -> Self { - Self { - sink, - _phantom: PhantomData, - } + Self { sink, _phantom: PhantomData } } } impl + Unpin + ?Sized, Item> Future for Close<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.sink).poll_close(cx) } } diff --git a/futures-util/src/sink/drain.rs b/futures-util/src/sink/drain.rs index 33c5b3167c..5295115b66 100644 --- a/futures-util/src/sink/drain.rs +++ b/futures-util/src/sink/drain.rs @@ -35,31 +35,19 @@ impl Unpin for Drain {} impl Sink for Drain { type Error = Never; - fn poll_ready( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn start_send( - self: Pin<&mut Self>, - _item: T, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, _item: T) -> Result<(), Self::Error> { Ok(()) } - fn poll_flush( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll> { + 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> { + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } diff --git a/futures-util/src/sink/err_into.rs b/futures-util/src/sink/err_into.rs index 3eb994041f..a64d1337ba 100644 --- a/futures-util/src/sink/err_into.rs +++ b/futures-util/src/sink/err_into.rs @@ -1,6 +1,6 @@ use crate::sink::{SinkExt, SinkMapErr}; -use futures_core::stream::{Stream, FusedStream}; -use futures_sink::{Sink}; +use futures_core::stream::{FusedStream, Stream}; +use futures_sink::Sink; use pin_project_lite::pin_project; pin_project! { @@ -14,21 +14,21 @@ pin_project! { } impl SinkErrInto - where Si: Sink, - Si::Error: Into, +where + Si: Sink, + Si::Error: Into, { pub(super) fn new(sink: Si) -> Self { - Self { - sink: SinkExt::sink_map_err(sink, Into::into), - } + Self { sink: SinkExt::sink_map_err(sink, Into::into) } } delegate_access_inner!(sink, Si, (.)); } impl Sink for SinkErrInto - where Si: Sink, - Si::Error: Into, +where + Si: Sink, + Si::Error: Into, { type Error = E; @@ -37,8 +37,9 @@ impl Sink for SinkErrInto // Forwarding impl of Stream from the underlying sink impl Stream for SinkErrInto - where S: Sink + Stream, - S::Error: Into +where + S: Sink + Stream, + S::Error: Into, { type Item = S::Item; @@ -46,8 +47,9 @@ impl Stream for SinkErrInto } impl FusedStream for SinkErrInto - where S: Sink + FusedStream, - S::Error: Into +where + S: Sink + FusedStream, + S::Error: Into, { fn is_terminated(&self) -> bool { self.sink.is_terminated() diff --git a/futures-util/src/sink/fanout.rs b/futures-util/src/sink/fanout.rs index f351e867d4..fe2038f27f 100644 --- a/futures-util/src/sink/fanout.rs +++ b/futures-util/src/sink/fanout.rs @@ -50,36 +50,32 @@ impl Fanout { impl Debug for Fanout { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.debug_struct("Fanout") - .field("sink1", &self.sink1) - .field("sink2", &self.sink2) - .finish() + f.debug_struct("Fanout").field("sink1", &self.sink1).field("sink2", &self.sink2).finish() } } impl Sink for Fanout - where Si1: Sink, - Item: Clone, - Si2: Sink +where + Si1: Sink, + Item: Clone, + Si2: Sink, { type Error = Si1::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); let sink1_ready = this.sink1.poll_ready(cx)?.is_ready(); let sink2_ready = this.sink2.poll_ready(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } - fn start_send( - self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { let this = self.project(); this.sink1.start_send(item.clone())?; @@ -87,27 +83,29 @@ impl Sink for Fanout Ok(()) } - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); let sink1_ready = this.sink1.poll_flush(cx)?.is_ready(); let sink2_ready = this.sink2.poll_flush(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); let sink1_ready = this.sink1.poll_close(cx)?.is_ready(); let sink2_ready = this.sink2.poll_close(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } } diff --git a/futures-util/src/sink/feed.rs b/futures-util/src/sink/feed.rs index 06df9a91a8..6701f7a1b4 100644 --- a/futures-util/src/sink/feed.rs +++ b/futures-util/src/sink/feed.rs @@ -17,10 +17,7 @@ impl Unpin for Feed<'_, Si, Item> {} impl<'a, Si: Sink + Unpin + ?Sized, Item> Feed<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si, item: Item) -> Self { - Feed { - sink, - item: Some(item), - } + Feed { sink, item: Some(item) } } pub(super) fn sink_pin_mut(&mut self) -> Pin<&mut Si> { @@ -35,10 +32,7 @@ impl<'a, Si: Sink + Unpin + ?Sized, Item> Feed<'a, Si, Item> { impl + Unpin + ?Sized, Item> Future for Feed<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); let mut sink = Pin::new(&mut this.sink); ready!(sink.as_mut().poll_ready(cx))?; diff --git a/futures-util/src/sink/flush.rs b/futures-util/src/sink/flush.rs index c06a22185e..35a8372de7 100644 --- a/futures-util/src/sink/flush.rs +++ b/futures-util/src/sink/flush.rs @@ -23,20 +23,14 @@ impl Unpin for Flush<'_, Si, Item> {} /// all current requests are processed. impl<'a, Si: Sink + Unpin + ?Sized, Item> Flush<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si) -> Self { - Self { - sink, - _phantom: PhantomData, - } + Self { sink, _phantom: PhantomData } } } impl + Unpin + ?Sized, Item> Future for Flush<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.sink).poll_flush(cx) } } diff --git a/futures-util/src/sink/map_err.rs b/futures-util/src/sink/map_err.rs index 282934465e..9d2ab7b24b 100644 --- a/futures-util/src/sink/map_err.rs +++ b/futures-util/src/sink/map_err.rs @@ -1,7 +1,7 @@ use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use futures_sink::{Sink}; +use futures_sink::Sink; use pin_project_lite::pin_project; pin_project! { @@ -28,36 +28,25 @@ impl SinkMapErr { } impl Sink for SinkMapErr - where Si: Sink, - F: FnOnce(Si::Error) -> E, +where + Si: Sink, + F: FnOnce(Si::Error) -> E, { type Error = E; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 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> { + fn start_send(mut self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { 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> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 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> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.as_mut().project().sink.poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) } } diff --git a/futures-util/src/sink/send.rs b/futures-util/src/sink/send.rs index 384c22c56c..6d21f33fe4 100644 --- a/futures-util/src/sink/send.rs +++ b/futures-util/src/sink/send.rs @@ -17,19 +17,14 @@ impl Unpin for Send<'_, Si, Item> {} impl<'a, Si: Sink + Unpin + ?Sized, Item> Send<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si, item: Item) -> Self { - Self { - feed: Feed::new(sink, item), - } + Self { feed: Feed::new(sink, item) } } } impl + Unpin + ?Sized, Item> Future for Send<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; if this.feed.is_item_pending() { diff --git a/futures-util/src/sink/send_all.rs b/futures-util/src/sink/send_all.rs index 6a33459be0..1302dd2148 100644 --- a/futures-util/src/sink/send_all.rs +++ b/futures-util/src/sink/send_all.rs @@ -1,9 +1,9 @@ -use crate::stream::{StreamExt, TryStreamExt, Fuse}; +use crate::stream::{Fuse, StreamExt, TryStreamExt}; use core::fmt; use core::pin::Pin; use futures_core::future::Future; use futures_core::ready; -use futures_core::stream::{TryStream, Stream}; +use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; @@ -40,22 +40,16 @@ impl Unpin for SendAll<'_, Si, St> where Si: Unpin + ?Sized, St: TryStream + Unpin + ?Sized, -{} +{ +} impl<'a, Si, St, Ok, Error> SendAll<'a, Si, St> where Si: Sink + Unpin + ?Sized, St: TryStream + Stream + Unpin + ?Sized, { - pub(super) fn new( - sink: &'a mut Si, - stream: &'a mut St, - ) -> Self { - Self { - sink, - stream: stream.fuse(), - buffered: None, - } + pub(super) fn new(sink: &'a mut Si, stream: &'a mut St) -> Self { + Self { sink, stream: stream.fuse(), buffered: None } } fn try_start_send( @@ -65,9 +59,7 @@ where ) -> Poll> { debug_assert!(self.buffered.is_none()); match Pin::new(&mut self.sink).poll_ready(cx)? { - Poll::Ready(()) => { - Poll::Ready(Pin::new(&mut self.sink).start_send(item)) - } + Poll::Ready(()) => Poll::Ready(Pin::new(&mut self.sink).start_send(item)), Poll::Pending => { self.buffered = Some(item); Poll::Pending @@ -83,10 +75,7 @@ where { type Output = Result<(), Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; // If we've got an item buffered already, we need to write it to the // sink before we can do anything else @@ -96,16 +85,14 @@ where loop { match this.stream.try_poll_next_unpin(cx)? { - Poll::Ready(Some(item)) => { - ready!(this.try_start_send(cx, item))? - } + Poll::Ready(Some(item)) => ready!(this.try_start_send(cx, item))?, Poll::Ready(None) => { ready!(Pin::new(&mut this.sink).poll_flush(cx))?; - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); } Poll::Pending => { ready!(Pin::new(&mut this.sink).poll_flush(cx))?; - return Poll::Pending + return Poll::Pending; } } } diff --git a/futures-util/src/sink/unfold.rs b/futures-util/src/sink/unfold.rs index 3903716837..330a068c31 100644 --- a/futures-util/src/sink/unfold.rs +++ b/futures-util/src/sink/unfold.rs @@ -41,10 +41,7 @@ where F: FnMut(T, Item) -> R, R: Future>, { - assert_sink::(Unfold { - function, - state: UnfoldState::Value { value: init }, - }) + assert_sink::(Unfold { function, state: UnfoldState::Value { value: init } }) } impl Sink for Unfold diff --git a/futures-util/src/sink/with.rs b/futures-util/src/sink/with.rs index 73b87b72fc..86d3dcc7b8 100644 --- a/futures-util/src/sink/with.rs +++ b/futures-util/src/sink/with.rs @@ -27,29 +27,22 @@ where Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("With") - .field("sink", &self.sink) - .field("state", &self.state) - .finish() + f.debug_struct("With").field("sink", &self.sink).field("state", &self.state).finish() } } impl With -where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future, { pub(super) fn new(sink: Si, f: F) -> Self - where - Fut: Future>, - E: From, + where + Fut: Future>, + E: From, { - Self { - state: None, - sink, - f, - _phantom: PhantomData, - } + Self { state: None, sink, f, _phantom: PhantomData } } } @@ -71,9 +64,10 @@ where // Forwarding impl of Stream from the underlying sink impl Stream for With - where S: Stream + Sink, - F: FnMut(U) -> Fut, - Fut: Future +where + S: Stream + Sink, + F: FnMut(U) -> Fut, + Fut: Future, { type Item = S::Item; @@ -81,18 +75,16 @@ impl Stream for With } impl With - where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future>, - E: From, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future>, + E: From, { delegate_access_inner!(sink, Si, ()); /// Completes the processing of previous item if any. - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); let item = match this.state.as_mut().as_pin_mut() { @@ -106,26 +98,21 @@ impl With } impl Sink for With - where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future>, - E: From, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future>, + E: From, { type Error = E; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(cx))?; ready!(self.project().sink.poll_ready(cx)?); Poll::Ready(Ok(())) } - fn start_send( - self: Pin<&mut Self>, - item: U, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, item: U) -> Result<(), Self::Error> { let mut this = self.project(); assert!(this.state.is_none()); @@ -133,19 +120,13 @@ impl Sink for With Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(cx))?; ready!(self.project().sink.poll_flush(cx)?); Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(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 4b8d3a275c..2ae877a24b 100644 --- a/futures-util/src/sink/with_flat_map.rs +++ b/futures-util/src/sink/with_flat_map.rs @@ -2,7 +2,7 @@ use core::fmt; use core::marker::PhantomData; use core::pin::Pin; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; use pin_project_lite::pin_project; @@ -43,21 +43,12 @@ where St: Stream>, { pub(super) fn new(sink: Si, f: F) -> Self { - Self { - sink, - f, - stream: None, - buffer: None, - _marker: PhantomData, - } + Self { sink, f, stream: None, buffer: None, _marker: PhantomData } } delegate_access_inner!(sink, Si, ()); - fn try_empty_stream( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn try_empty_stream(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if this.buffer.is_some() { @@ -112,17 +103,11 @@ where { type Error = Si::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.try_empty_stream(cx) } - fn start_send( - self: Pin<&mut Self>, - item: U, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, item: U) -> Result<(), Self::Error> { let mut this = self.project(); assert!(this.stream.is_none()); @@ -130,18 +115,12 @@ where Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); self.project().sink.poll_flush(cx) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); self.project().sink.poll_close(cx) } diff --git a/futures-util/src/stream/empty.rs b/futures-util/src/stream/empty.rs index c629a4b7fc..e4fd87326b 100644 --- a/futures-util/src/stream/empty.rs +++ b/futures-util/src/stream/empty.rs @@ -8,16 +8,14 @@ use futures_core::task::{Context, Poll}; #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Empty { - _phantom: PhantomData + _phantom: PhantomData, } /// Creates a stream which contains no elements. /// /// The returned stream will always return `Ready(None)` when polled. pub fn empty() -> Empty { - assert_stream::(Empty { - _phantom: PhantomData - }) + assert_stream::(Empty { _phantom: PhantomData }) } impl Unpin for Empty {} diff --git a/futures-util/src/stream/iter.rs b/futures-util/src/stream/iter.rs index 033dae1924..20471c2ed0 100644 --- a/futures-util/src/stream/iter.rs +++ b/futures-util/src/stream/iter.rs @@ -27,15 +27,15 @@ impl Unpin for Iter {} /// # }); /// ``` pub fn iter(i: I) -> Iter - where I: IntoIterator, +where + I: IntoIterator, { - assert_stream::(Iter { - iter: i.into_iter(), - }) + assert_stream::(Iter { iter: i.into_iter() }) } impl Stream for Iter - where I: Iterator, +where + I: Iterator, { type Item = I::Item; diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index ecffdc7e41..f9c2e78fc8 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -19,8 +19,8 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; mod stream; pub use self::stream::{ Chain, Collect, Concat, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, - Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, - StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, + Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, Peekable, Scan, SelectNextSome, Skip, + SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, }; #[cfg(feature = "std")] diff --git a/futures-util/src/stream/once.rs b/futures-util/src/stream/once.rs index e16fe00148..ee21c8b594 100644 --- a/futures-util/src/stream/once.rs +++ b/futures-util/src/stream/once.rs @@ -2,7 +2,7 @@ use super::assert_stream; use core::pin::Pin; use futures_core::future::Future; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; diff --git a/futures-util/src/stream/repeat.rs b/futures-util/src/stream/repeat.rs index cf9f21bcf2..3f9aa87d5c 100644 --- a/futures-util/src/stream/repeat.rs +++ b/futures-util/src/stream/repeat.rs @@ -1,6 +1,6 @@ use super::assert_stream; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; /// Stream for the [`repeat`] function. @@ -25,7 +25,8 @@ pub struct Repeat { /// # }); /// ``` pub fn repeat(item: T) -> Repeat - where T: Clone +where + T: Clone, { assert_stream::(Repeat { item }) } @@ -33,7 +34,8 @@ pub fn repeat(item: T) -> Repeat impl Unpin for Repeat {} impl Stream for Repeat - where T: Clone +where + T: Clone, { type Item = T; @@ -47,7 +49,8 @@ impl Stream for Repeat } impl FusedStream for Repeat - where T: Clone, +where + T: Clone, { fn is_terminated(&self) -> bool { false diff --git a/futures-util/src/stream/repeat_with.rs b/futures-util/src/stream/repeat_with.rs index 0255643d57..f5a81b4ed4 100644 --- a/futures-util/src/stream/repeat_with.rs +++ b/futures-util/src/stream/repeat_with.rs @@ -1,6 +1,6 @@ use super::assert_stream; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; /// An stream that repeats elements of type `A` endlessly by @@ -28,8 +28,7 @@ impl A> Stream for RepeatWith { } } -impl A> FusedStream for RepeatWith -{ +impl A> FusedStream for RepeatWith { fn is_terminated(&self) -> bool { false } diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index 2942494678..133ac6c7ac 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -1,5 +1,5 @@ use super::assert_stream; -use crate::stream::{StreamExt, Fuse}; +use crate::stream::{Fuse, StreamExt}; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; @@ -29,8 +29,9 @@ pin_project! { /// Note that this function consumes both streams and returns a wrapped /// version of them. pub fn select(stream1: St1, stream2: St2) -> Select - where St1: Stream, - St2: Stream +where + St1: Stream, + St2: Stream, { assert_stream::(Select { stream1: stream1.fuse(), @@ -75,8 +76,9 @@ impl Select { } impl FusedStream for Select - where St1: Stream, - St2: Stream +where + St1: Stream, + St2: Stream, { fn is_terminated(&self) -> bool { self.stream1.is_terminated() && self.stream2.is_terminated() @@ -84,15 +86,13 @@ impl FusedStream for Select } impl Stream for Select - where St1: Stream, - St2: Stream +where + St1: Stream, + St2: Stream, { type Item = St1::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if !*this.flag { poll_inner(this.flag, this.stream1, this.stream2, cx) @@ -106,24 +106,24 @@ fn poll_inner( flag: &mut bool, a: Pin<&mut St1>, b: Pin<&mut St2>, - cx: &mut Context<'_> + cx: &mut Context<'_>, ) -> Poll> - where St1: Stream, St2: Stream +where + St1: Stream, + St2: Stream, { let a_done = match a.poll_next(cx) { Poll::Ready(Some(item)) => { // give the other stream a chance to go first next time *flag = !*flag; - return Poll::Ready(Some(item)) - }, + return Poll::Ready(Some(item)); + } Poll::Ready(None) => true, Poll::Pending => false, }; match b.poll_next(cx) { - Poll::Ready(Some(item)) => { - Poll::Ready(Some(item)) - } + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), Poll::Ready(None) if a_done => Poll::Ready(None), Poll::Ready(None) | Poll::Pending => Poll::Pending, } diff --git a/futures-util/src/stream/stream/catch_unwind.rs b/futures-util/src/stream/stream/catch_unwind.rs index d87a40a2e3..09a6dc1b76 100644 --- a/futures-util/src/stream/stream/catch_unwind.rs +++ b/futures-util/src/stream/stream/catch_unwind.rs @@ -1,9 +1,9 @@ -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; use std::any::Any; +use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use std::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; pin_project! { /// Stream for the [`catch_unwind`](super::StreamExt::catch_unwind) method. @@ -27,25 +27,20 @@ impl CatchUnwind { impl Stream for CatchUnwind { type Item = Result>; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if *this.caught_unwind { Poll::Ready(None) } else { - let res = catch_unwind(AssertUnwindSafe(|| { - this.stream.as_mut().poll_next(cx) - })); + let res = catch_unwind(AssertUnwindSafe(|| this.stream.as_mut().poll_next(cx))); match res { Ok(poll) => poll.map(|opt| opt.map(Ok)), Err(e) => { *this.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 2be710462a..c5da35e25e 100644 --- a/futures-util/src/stream/stream/chain.rs +++ b/futures-util/src/stream/stream/chain.rs @@ -18,20 +18,19 @@ pin_project! { // All interactions with `Pin<&mut Chain<..>>` happen through these methods impl Chain -where St1: Stream, - St2: Stream, +where + St1: Stream, + St2: Stream, { pub(super) fn new(stream1: St1, stream2: St2) -> Self { - Self { - first: Some(stream1), - second: stream2, - } + Self { first: Some(stream1), second: stream2 } } } impl FusedStream for Chain -where St1: Stream, - St2: FusedStream, +where + St1: Stream, + St2: FusedStream, { fn is_terminated(&self) -> bool { self.first.is_none() && self.second.is_terminated() @@ -39,19 +38,17 @@ where St1: Stream, } impl Stream for Chain -where St1: Stream, - St2: Stream, +where + St1: Stream, + St2: Stream, { type Item = St1::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if let Some(first) = this.first.as_mut().as_pin_mut() { if let Some(item) = ready!(first.poll_next(cx)) { - return Poll::Ready(Some(item)) + return Poll::Ready(Some(item)); } } this.first.set(None); @@ -67,7 +64,7 @@ where St1: Stream, let upper = match (first_upper, second_upper) { (Some(x), Some(y)) => x.checked_add(y), - _ => None + _ => None, }; (lower, upper) diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index 45a3212582..8457869999 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -1,13 +1,13 @@ use crate::stream::Fuse; +use alloc::vec::Vec; +use core::mem; +use core::pin::Pin; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use core::mem; -use core::pin::Pin; -use alloc::vec::Vec; pin_project! { /// Stream for the [`chunks`](super::StreamExt::chunks) method. @@ -21,7 +21,10 @@ pin_project! { } } -impl Chunks where St: Stream { +impl Chunks +where + St: Stream, +{ pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); @@ -43,10 +46,7 @@ impl Chunks where St: Stream { impl Stream for Chunks { type Item = Vec; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.as_mut().project(); loop { match ready!(this.stream.as_mut().poll_next(cx)) { @@ -56,7 +56,7 @@ impl Stream for Chunks { Some(item) => { this.items.push(item); if this.items.len() >= *this.cap { - return Poll::Ready(Some(self.take())) + return Poll::Ready(Some(self.take())); } } diff --git a/futures-util/src/stream/stream/collect.rs b/futures-util/src/stream/stream/collect.rs index 774b34b39a..b0e81b9ce0 100644 --- a/futures-util/src/stream/stream/collect.rs +++ b/futures-util/src/stream/stream/collect.rs @@ -23,16 +23,14 @@ impl Collect { } pub(super) fn new(stream: St) -> Self { - Self { - stream, - collection: Default::default(), - } + Self { stream, collection: Default::default() } } } impl FusedFuture for Collect -where St: FusedStream, - C: Default + Extend +where + St: FusedStream, + C: Default + Extend, { fn is_terminated(&self) -> bool { self.stream.is_terminated() @@ -40,8 +38,9 @@ where St: FusedStream, } impl Future for Collect -where St: Stream, - C: Default + Extend +where + St: Stream, + C: Default + Extend, { type Output = C; diff --git a/futures-util/src/stream/stream/concat.rs b/futures-util/src/stream/stream/concat.rs index ee1349f86d..7e058b2315 100644 --- a/futures-util/src/stream/stream/concat.rs +++ b/futures-util/src/stream/stream/concat.rs @@ -1,7 +1,7 @@ use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -17,35 +17,28 @@ pin_project! { } impl Concat -where St: Stream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: Stream, + St::Item: Extend<::Item> + IntoIterator + Default, { pub(super) fn new(stream: St) -> Self { - Self { - stream, - accum: None, - } + Self { stream, accum: None } } } impl Future for Concat -where St: Stream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: Stream, + St::Item: Extend<::Item> + IntoIterator + Default, { type Output = St::Item; - fn poll( - self: Pin<&mut Self>, cx: &mut Context<'_> - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); loop { match ready!(this.stream.as_mut().poll_next(cx)) { - None => { - return Poll::Ready(this.accum.take().unwrap_or_default()) - } + None => return Poll::Ready(this.accum.take().unwrap_or_default()), Some(e) => { if let Some(a) = this.accum { a.extend(e) @@ -59,9 +52,9 @@ where St: Stream, } impl FusedFuture for Concat -where St: FusedStream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: FusedStream, + St::Item: Extend<::Item> + IntoIterator + Default, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.stream.is_terminated() diff --git a/futures-util/src/stream/stream/cycle.rs b/futures-util/src/stream/stream/cycle.rs index a5b7dc08c5..507431d24f 100644 --- a/futures-util/src/stream/stream/cycle.rs +++ b/futures-util/src/stream/stream/cycle.rs @@ -21,10 +21,7 @@ where St: Clone + Stream, { pub(super) fn new(stream: St) -> Self { - Self { - orig: stream.clone(), - stream, - } + Self { orig: stream.clone(), stream } } } diff --git a/futures-util/src/stream/stream/enumerate.rs b/futures-util/src/stream/stream/enumerate.rs index 7d4c9cbe68..1cf9d49aaa 100644 --- a/futures-util/src/stream/stream/enumerate.rs +++ b/futures-util/src/stream/stream/enumerate.rs @@ -19,10 +19,7 @@ pin_project! { impl Enumerate { pub(super) fn new(stream: St) -> Self { - Self { - stream, - count: 0, - } + Self { stream, count: 0 } } delegate_access_inner!(stream, St, ()); @@ -37,10 +34,7 @@ impl FusedStream for Enumerate { impl Stream for Enumerate { type Item = (usize, St::Item); - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); match ready!(this.stream.poll_next(cx)) { diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index 57de0253a4..ccf1a5122f 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -1,3 +1,4 @@ +use crate::fns::FnMut1; use core::fmt; use core::pin::Pin; use futures_core::future::Future; @@ -7,7 +8,6 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use crate::fns::FnMut1; pin_project! { /// Stream for the [`filter`](super::StreamExt::filter) method. @@ -41,26 +41,23 @@ where #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Filter -where St: Stream, - F: for<'a> FnMut1<&'a St::Item, Output=Fut>, - Fut: Future, +where + St: Stream, + F: for<'a> FnMut1<&'a St::Item, Output = Fut>, + Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - } + Self { stream, f, pending_fut: None, pending_item: None } } delegate_access_inner!(stream, St, ()); } impl FusedStream for Filter - where St: Stream + FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream + FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_fut.is_none() && self.stream.is_terminated() @@ -69,16 +66,14 @@ impl FusedStream for Filter #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Stream for Filter - where St: Stream, - F: for<'a> FnMut1<&'a St::Item, Output=Fut>, - Fut: Future, +where + St: Stream, + F: for<'a> FnMut1<&'a St::Item, Output = Fut>, + Fut: Future, { type Item = St::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { @@ -111,9 +106,10 @@ impl Stream for Filter // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Filter - where S: Stream + Sink, - F: FnMut(&S::Item) -> Fut, - Fut: Future, +where + S: Stream + Sink, + F: FnMut(&S::Item) -> Fut, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/filter_map.rs b/futures-util/src/stream/stream/filter_map.rs index b762face28..02a0a4386e 100644 --- a/futures-util/src/stream/stream/filter_map.rs +++ b/futures-util/src/stream/stream/filter_map.rs @@ -1,3 +1,4 @@ +use crate::fns::FnMut1; use core::fmt; use core::pin::Pin; use futures_core::future::Future; @@ -7,7 +8,6 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use crate::fns::FnMut1; pin_project! { /// Stream for the [`filter_map`](super::StreamExt::filter_map) method. @@ -35,9 +35,10 @@ where } impl FilterMap - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { Self { stream, f, pending: None } @@ -47,9 +48,10 @@ impl FilterMap } impl FusedStream for FilterMap - where St: Stream + FusedStream, - F: FnMut1, - Fut: Future>, +where + St: Stream + FusedStream, + F: FnMut1, + Fut: Future>, { fn is_terminated(&self) -> bool { self.pending.is_none() && self.stream.is_terminated() @@ -57,16 +59,14 @@ impl FusedStream for FilterMap } impl Stream for FilterMap - where St: Stream, - F: FnMut1, - Fut: Future>, +where + St: Stream, + F: FnMut1, + Fut: Future>, { type Item = T; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { if let Some(p) = this.pending.as_mut().as_pin_mut() { @@ -100,9 +100,10 @@ impl Stream for FilterMap // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for FilterMap - where S: Stream + Sink, - F: FnMut1, - Fut: Future, +where + S: Stream + Sink, + F: FnMut1, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/fold.rs b/futures-util/src/stream/stream/fold.rs index e109c3bdcf..b8b55ecb67 100644 --- a/futures-util/src/stream/stream/fold.rs +++ b/futures-util/src/stream/stream/fold.rs @@ -35,24 +35,21 @@ where } impl Fold -where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, f: F, t: T) -> Self { - Self { - stream, - f, - accum: Some(t), - future: None, - } + Self { stream, f, accum: Some(t), future: None } } } impl FusedFuture for Fold - where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.future.is_none() @@ -60,9 +57,10 @@ impl FusedFuture for Fold } impl Future for Fold - where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { type Output = T; diff --git a/futures-util/src/stream/stream/for_each.rs b/futures-util/src/stream/stream/for_each.rs index ee90e66610..5302b0e034 100644 --- a/futures-util/src/stream/stream/for_each.rs +++ b/futures-util/src/stream/stream/for_each.rs @@ -32,23 +32,21 @@ where } impl ForEach -where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - future: None, - } + Self { stream, f, future: None } } } impl FusedFuture for ForEach - where St: FusedStream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -56,9 +54,10 @@ impl FusedFuture for ForEach } impl Future for ForEach - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Output = (); diff --git a/futures-util/src/stream/stream/forward.rs b/futures-util/src/stream/stream/forward.rs index 2247b21e97..1fe24273aa 100644 --- a/futures-util/src/stream/stream/forward.rs +++ b/futures-util/src/stream/stream/forward.rs @@ -23,11 +23,7 @@ pin_project! { impl Forward { pub(crate) fn new(stream: St, sink: Si) -> Self { - Self { - sink: Some(sink), - stream: Fuse::new(stream), - buffered_item: None, - } + Self { sink: Some(sink), stream: Fuse::new(stream), buffered_item: None } } } @@ -48,10 +44,7 @@ where { type Output = Result<(), E>; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let ForwardProj { mut sink, mut stream, buffered_item } = self.project(); let mut si = sink.as_mut().as_pin_mut().expect("polled `Forward` after completion"); @@ -70,11 +63,11 @@ where Poll::Ready(None) => { ready!(si.poll_close(cx))?; sink.set(None); - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); } Poll::Pending => { ready!(si.poll_flush(cx))?; - return Poll::Pending + return Poll::Pending; } } } diff --git a/futures-util/src/stream/stream/fuse.rs b/futures-util/src/stream/stream/fuse.rs index e1d8c122b3..fe67813e81 100644 --- a/futures-util/src/stream/stream/fuse.rs +++ b/futures-util/src/stream/stream/fuse.rs @@ -43,10 +43,7 @@ impl FusedStream for Fuse { impl Stream for Fuse { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); if *this.done { diff --git a/futures-util/src/stream/stream/into_future.rs b/futures-util/src/stream/stream/into_future.rs index a9a1e2374a..8abfddcccd 100644 --- a/futures-util/src/stream/stream/into_future.rs +++ b/futures-util/src/stream/stream/into_future.rs @@ -79,10 +79,7 @@ impl FusedFuture for StreamFuture { impl Future for StreamFuture { type Output = (Option, St); - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let item = { let s = self.stream.as_mut().expect("polling StreamFuture twice"); ready!(s.poll_next_unpin(cx)) diff --git a/futures-util/src/stream/stream/map.rs b/futures-util/src/stream/stream/map.rs index 1a269f0c30..88bb6129d4 100644 --- a/futures-util/src/stream/stream/map.rs +++ b/futures-util/src/stream/stream/map.rs @@ -24,9 +24,7 @@ where St: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Map") - .field("stream", &self.stream) - .finish() + f.debug_struct("Map").field("stream", &self.stream).finish() } } @@ -39,8 +37,9 @@ impl Map { } impl FusedStream for Map - where St: FusedStream, - F: FnMut1, +where + St: FusedStream, + F: FnMut1, { fn is_terminated(&self) -> bool { self.stream.is_terminated() @@ -48,15 +47,13 @@ impl FusedStream for Map } impl Stream for Map - where St: Stream, - F: FnMut1, +where + St: Stream, + F: FnMut1, { type Item = F::Output; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); let res = ready!(this.stream.as_mut().poll_next(cx)); Poll::Ready(res.map(|x| this.f.call_mut(x))) @@ -70,8 +67,9 @@ impl Stream for Map // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Map - where St: Stream + Sink, - F: FnMut1, +where + St: Stream + Sink, + F: FnMut1, { type Error = St::Error; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index b0f5b4b74c..fd3a103491 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -123,7 +123,7 @@ pub use self::select_next_some::SelectNextSome; mod peek; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::peek::{Peek, Peekable, NextIf, NextIfEq}; +pub use self::peek::{NextIf, NextIfEq, Peek, Peekable}; mod skip; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 diff --git a/futures-util/src/stream/stream/next.rs b/futures-util/src/stream/stream/next.rs index 6949878bef..8d8347aa03 100644 --- a/futures-util/src/stream/stream/next.rs +++ b/futures-util/src/stream/stream/next.rs @@ -28,10 +28,7 @@ impl FusedFuture for Next<'_, St> { impl Future for Next<'_, St> { type Output = Option; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.stream.poll_next_unpin(cx) } } diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index 21e84c110a..217fabaa6e 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -28,10 +28,7 @@ pin_project! { impl Peekable { pub(super) fn new(stream: St) -> Self { - Self { - stream: stream.fuse(), - peeked: None, - } + Self { stream: stream.fuse(), peeked: None } } delegate_access_inner!(stream, St, (.)); @@ -105,9 +102,7 @@ impl Peekable { where F: FnOnce(&St::Item) -> bool, { - NextIf { - inner: Some((self, func)), - } + NextIf { inner: Some((self, func)) } } /// Creates a future which will consume and return the next item if it is @@ -138,15 +133,7 @@ impl Peekable { St::Item: PartialEq, { NextIfEq { - inner: NextIf { - inner: Some(( - self, - NextIfEqFn { - expected, - _next: PhantomData, - }, - )), - }, + inner: NextIf { inner: Some((self, NextIfEqFn { expected, _next: PhantomData })) }, } } } @@ -247,9 +234,7 @@ where St::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("NextIf") - .field("inner", &self.inner.as_ref().map(|(s, _f)| s)) - .finish() + f.debug_struct("NextIf").field("inner", &self.inner.as_ref().map(|(s, _f)| s)).finish() } } diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index b6e3e5c2de..5ebc9582db 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -1,12 +1,12 @@ use crate::stream::Fuse; -use futures_core::stream::{Stream, FusedStream}; +use alloc::vec::Vec; +use core::mem; +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_project_lite::pin_project; -use core::mem; -use core::pin::Pin; -use alloc::vec::Vec; pin_project! { /// Stream for the [`ready_chunks`](super::StreamExt::ready_chunks) method. @@ -20,7 +20,10 @@ pin_project! { } } -impl ReadyChunks where St: Stream { +impl ReadyChunks +where + St: Stream, +{ pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); @@ -37,10 +40,7 @@ impl ReadyChunks where St: Stream { impl Stream for ReadyChunks { type Item = Vec; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); loop { @@ -61,7 +61,10 @@ impl Stream for ReadyChunks { Poll::Ready(Some(item)) => { this.items.push(item); if this.items.len() >= *this.cap { - return Poll::Ready(Some(mem::replace(this.items, Vec::with_capacity(*this.cap)))) + return Poll::Ready(Some(mem::replace( + this.items, + Vec::with_capacity(*this.cap), + ))); } } diff --git a/futures-util/src/stream/stream/scan.rs b/futures-util/src/stream/stream/scan.rs index 20972807ce..8724145ef3 100644 --- a/futures-util/src/stream/stream/scan.rs +++ b/futures-util/src/stream/stream/scan.rs @@ -56,14 +56,7 @@ where Fut: Future>, { pub(super) fn new(stream: St, initial_state: S, f: F) -> Self { - Self { - stream, - state_f: Some(StateFn { - state: initial_state, - f, - }), - future: None, - } + Self { stream, state_f: Some(StateFn { state: initial_state, f }), future: None } } delegate_access_inner!(stream, St, ()); diff --git a/futures-util/src/stream/stream/select_next_some.rs b/futures-util/src/stream/stream/select_next_some.rs index fe7a089899..3115e14d9a 100644 --- a/futures-util/src/stream/stream/select_next_some.rs +++ b/futures-util/src/stream/stream/select_next_some.rs @@ -1,9 +1,9 @@ +use crate::stream::StreamExt; use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; use futures_core::ready; use futures_core::stream::FusedStream; -use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use crate::stream::StreamExt; /// Future for the [`select_next_some`](super::StreamExt::select_next_some) /// method. diff --git a/futures-util/src/stream/stream/skip.rs b/futures-util/src/stream/stream/skip.rs index 631ae40ed3..f495779521 100644 --- a/futures-util/src/stream/stream/skip.rs +++ b/futures-util/src/stream/stream/skip.rs @@ -19,10 +19,7 @@ pin_project! { impl Skip { pub(super) fn new(stream: St, n: usize) -> Self { - Self { - stream, - remaining: n, - } + Self { stream, remaining: n } } delegate_access_inner!(stream, St, ()); @@ -37,10 +34,7 @@ impl FusedStream for Skip { impl Stream for Skip { type Item = St::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); while *this.remaining > 0 { diff --git a/futures-util/src/stream/stream/skip_while.rs b/futures-util/src/stream/stream/skip_while.rs index e1aa3f904b..50a21a21ae 100644 --- a/futures-util/src/stream/stream/skip_while.rs +++ b/futures-util/src/stream/stream/skip_while.rs @@ -39,27 +39,23 @@ where } impl SkipWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - done_skipping: false, - } + Self { stream, f, pending_fut: None, pending_item: None, done_skipping: false } } delegate_access_inner!(stream, St, ()); } impl FusedStream for SkipWhile - where St: FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_item.is_none() && self.stream.is_terminated() @@ -67,16 +63,14 @@ impl FusedStream for SkipWhile } impl Stream for SkipWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { type Item = St::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if *this.done_skipping { @@ -119,9 +113,10 @@ impl Stream for SkipWhile // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for SkipWhile - where S: Stream + Sink, - F: FnMut(&S::Item) -> Fut, - Fut: Future, +where + S: Stream + Sink, + F: FnMut(&S::Item) -> Fut, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/take.rs b/futures-util/src/stream/stream/take.rs index 124d397c46..b1c728e333 100644 --- a/futures-util/src/stream/stream/take.rs +++ b/futures-util/src/stream/stream/take.rs @@ -1,7 +1,7 @@ use core::cmp; use core::pin::Pin; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -20,24 +20,19 @@ pin_project! { impl Take { pub(super) fn new(stream: St, n: usize) -> Self { - Self { - stream, - remaining: n, - } + Self { stream, remaining: n } } delegate_access_inner!(stream, St, ()); } impl Stream for Take - where St: Stream, +where + St: Stream, { type Item = St::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.remaining == 0 { Poll::Ready(None) } else { @@ -63,7 +58,7 @@ impl Stream for Take let upper = match upper { Some(x) if x < self.remaining as usize => Some(x), - _ => Some(self.remaining as usize) + _ => Some(self.remaining as usize), }; (lower, upper) @@ -71,7 +66,8 @@ impl Stream for Take } impl FusedStream for Take - where St: FusedStream, +where + St: FusedStream, { fn is_terminated(&self) -> bool { self.remaining == 0 || self.stream.is_terminated() @@ -81,7 +77,8 @@ impl FusedStream for Take // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Take - where S: Stream + Sink, +where + S: Stream + Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/take_until.rs b/futures-util/src/stream/stream/take_until.rs index 4dea01aae9..d14f9ce100 100644 --- a/futures-util/src/stream/stream/take_until.rs +++ b/futures-util/src/stream/stream/take_until.rs @@ -34,10 +34,7 @@ where 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() + f.debug_struct("TakeUntil").field("stream", &self.stream).field("fut", &self.fut).finish() } } @@ -47,12 +44,7 @@ where Fut: Future, { pub(super) fn new(stream: St, fut: Fut) -> Self { - Self { - stream, - fut: Some(fut), - fut_result: None, - free: false, - } + Self { stream, fut: Some(fut), fut_result: None, free: false } } delegate_access_inner!(stream, St, ()); diff --git a/futures-util/src/stream/stream/take_while.rs b/futures-util/src/stream/stream/take_while.rs index 4cdba83564..01b27654b8 100644 --- a/futures-util/src/stream/stream/take_while.rs +++ b/futures-util/src/stream/stream/take_while.rs @@ -2,7 +2,7 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -39,34 +39,27 @@ where } impl TakeWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - done_taking: false, - } + Self { stream, f, pending_fut: None, pending_item: None, done_taking: false } } delegate_access_inner!(stream, St, ()); } impl Stream for TakeWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { type Item = St::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.done_taking { return Poll::Ready(None); } @@ -109,9 +102,10 @@ impl Stream for TakeWhile } impl FusedStream for TakeWhile - where St: FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.done_taking || self.pending_item.is_none() && self.stream.is_terminated() @@ -121,7 +115,8 @@ impl FusedStream for TakeWhile // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TakeWhile - where S: Stream + Sink, +where + S: Stream + Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/then.rs b/futures-util/src/stream/stream/then.rs index 3d42bdd5c2..d4531d4b94 100644 --- a/futures-util/src/stream/stream/then.rs +++ b/futures-util/src/stream/stream/then.rs @@ -26,32 +26,27 @@ where Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Then") - .field("stream", &self.stream) - .field("future", &self.future) - .finish() + f.debug_struct("Then").field("stream", &self.stream).field("future", &self.future).finish() } } impl Then - where St: Stream, - F: FnMut(St::Item) -> Fut, +where + St: Stream, + F: FnMut(St::Item) -> Fut, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - future: None, - f, - } + Self { stream, future: None, f } } delegate_access_inner!(stream, St, ()); } impl FusedStream for Then - where St: FusedStream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -59,16 +54,14 @@ impl FusedStream for Then } impl Stream for Then - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Item = Fut::Output; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -99,7 +92,8 @@ impl Stream for Then // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Then - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/unzip.rs b/futures-util/src/stream/stream/unzip.rs index 5024770769..15f22e80b0 100644 --- a/futures-util/src/stream/stream/unzip.rs +++ b/futures-util/src/stream/stream/unzip.rs @@ -21,25 +21,19 @@ pin_project! { impl Unzip { fn finish(self: Pin<&mut Self>) -> (FromA, FromB) { let this = self.project(); - ( - mem::replace(this.left, Default::default()), - mem::replace(this.right, Default::default()), - ) + (mem::replace(this.left, Default::default()), mem::replace(this.right, Default::default())) } pub(super) fn new(stream: St) -> Self { - Self { - stream, - left: Default::default(), - right: Default::default(), - } + Self { stream, left: Default::default(), right: Default::default() } } } impl FusedFuture for Unzip -where St: FusedStream, - FromA: Default + Extend, - FromB: Default + Extend, +where + St: FusedStream, + FromA: Default + Extend, + FromB: Default + Extend, { fn is_terminated(&self) -> bool { self.stream.is_terminated() @@ -47,9 +41,10 @@ where St: FusedStream, } impl Future for Unzip -where St: Stream, - FromA: Default + Extend, - FromB: Default + Extend, +where + St: Stream, + FromA: Default + Extend, + FromB: Default + Extend, { type Output = (FromA, FromB); @@ -60,7 +55,7 @@ where St: Stream, Some(e) => { this.left.extend(Some(e.0)); this.right.extend(Some(e.1)); - }, + } None => return Poll::Ready(self.finish()), } } diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index 588531a468..360a8b63bb 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -1,4 +1,4 @@ -use crate::stream::{StreamExt, Fuse}; +use crate::stream::{Fuse, StreamExt}; use core::cmp; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; @@ -21,12 +21,7 @@ pin_project! { impl Zip { pub(super) fn new(stream1: St1, stream2: St2) -> Self { - Self { - stream1: stream1.fuse(), - stream2: stream2.fuse(), - queued1: None, - queued2: None, - } + Self { stream1: stream1.fuse(), stream2: stream2.fuse(), queued1: None, queued2: None } } /// Acquires a reference to the underlying streams that this combinator is @@ -64,7 +59,9 @@ impl Zip { } impl FusedStream for Zip - where St1: Stream, St2: Stream, +where + St1: Stream, + St2: Stream, { fn is_terminated(&self) -> bool { self.stream1.is_terminated() && self.stream2.is_terminated() @@ -72,14 +69,13 @@ impl FusedStream for Zip } impl Stream for Zip - where St1: Stream, St2: Stream +where + St1: Stream, + St2: Stream, { type Item = (St1::Item, St2::Item); - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if this.queued1.is_none() { @@ -124,7 +120,7 @@ impl Stream for Zip } (Some(x), None) => x.checked_add(queued1_len), (None, Some(y)) => y.checked_add(queued2_len), - (None, None) => None + (None, None) => None, }; (lower, upper) diff --git a/futures-util/src/stream/try_stream/and_then.rs b/futures-util/src/stream/try_stream/and_then.rs index b18564649a..a7b50db0b1 100644 --- a/futures-util/src/stream/try_stream/and_then.rs +++ b/futures-util/src/stream/try_stream/and_then.rs @@ -2,7 +2,7 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::ready; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -34,9 +34,10 @@ where } impl AndThen - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { Self { stream, future: None, f } @@ -46,16 +47,14 @@ impl AndThen } impl Stream for AndThen - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -84,9 +83,10 @@ impl Stream for AndThen } impl FusedStream for AndThen - where St: TryStream + FusedStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -96,7 +96,8 @@ impl FusedStream for AndThen // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for AndThen - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/into_async_read.rs b/futures-util/src/stream/try_stream/into_async_read.rs index 197c10502d..914b277a02 100644 --- a/futures-util/src/stream/try_stream/into_async_read.rs +++ b/futures-util/src/stream/try_stream/into_async_read.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use futures_io::{AsyncRead, AsyncWrite, AsyncBufRead}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncWrite}; use std::cmp; use std::io::{Error, Result}; @@ -40,10 +40,7 @@ where St::Ok: AsRef<[u8]>, { pub(super) fn new(stream: St) -> Self { - Self { - stream, - state: ReadState::PendingChunk, - } + Self { stream, state: ReadState::PendingChunk } } } @@ -63,9 +60,7 @@ where let chunk = chunk.as_ref(); let len = cmp::min(buf.len(), chunk.len() - *chunk_start); - buf[..len].copy_from_slice( - &chunk[*chunk_start..*chunk_start + len], - ); + buf[..len].copy_from_slice(&chunk[*chunk_start..*chunk_start + len]); *chunk_start += len; if chunk.len() == *chunk_start { @@ -74,26 +69,21 @@ where return Poll::Ready(Ok(len)); } - ReadState::PendingChunk => { - match ready!(self.stream.try_poll_next_unpin(cx)) { - Some(Ok(chunk)) => { - if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { - chunk, - chunk_start: 0, - }; - } - } - Some(Err(err)) => { - self.state = ReadState::Eof; - return Poll::Ready(Err(err)); - } - None => { - self.state = ReadState::Eof; - return Poll::Ready(Ok(0)); + ReadState::PendingChunk => match ready!(self.stream.try_poll_next_unpin(cx)) { + Some(Ok(chunk)) => { + if !chunk.as_ref().is_empty() { + self.state = ReadState::Ready { chunk, chunk_start: 0 }; } } - } + Some(Err(err)) => { + self.state = ReadState::Eof; + return Poll::Ready(Err(err)); + } + None => { + self.state = ReadState::Eof; + return Poll::Ready(Ok(0)); + } + }, ReadState::Eof => { return Poll::Ready(Ok(0)); } @@ -110,23 +100,17 @@ where fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &[u8] + buf: &[u8], ) -> Poll> { - Pin::new( &mut self.stream ).poll_write( cx, buf ) + Pin::new(&mut self.stream).poll_write(cx, buf) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_> - ) -> Poll> { - Pin::new( &mut self.stream ).poll_flush( cx ) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_flush(cx) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_> - ) -> Poll> { - Pin::new( &mut self.stream ).poll_close( cx ) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_close(cx) } } @@ -135,18 +119,12 @@ where St: TryStream + Unpin, St::Ok: AsRef<[u8]>, { - fn poll_fill_buf( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { while let ReadState::PendingChunk = self.state { match ready!(self.stream.try_poll_next_unpin(cx)) { Some(Ok(chunk)) => { if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { - chunk, - chunk_start: 0, - }; + self.state = ReadState::Ready { chunk, chunk_start: 0 }; } } Some(Err(err)) => { @@ -169,12 +147,11 @@ where Poll::Ready(Ok(&[])) } - fn consume( - mut self: Pin<&mut Self>, - amount: usize, - ) { - // https://github.com/rust-lang/futures-rs/pull/1556#discussion_r281644295 - if amount == 0 { return } + fn consume(mut self: Pin<&mut Self>, amount: usize) { + // https://github.com/rust-lang/futures-rs/pull/1556#discussion_r281644295 + if amount == 0 { + return; + } if let ReadState::Ready { chunk, chunk_start } = &mut self.state { *chunk_start += amount; debug_assert!(*chunk_start <= chunk.as_ref().len()); diff --git a/futures-util/src/stream/try_stream/into_stream.rs b/futures-util/src/stream/try_stream/into_stream.rs index 89bc3ef177..2126258af7 100644 --- a/futures-util/src/stream/try_stream/into_stream.rs +++ b/futures-util/src/stream/try_stream/into_stream.rs @@ -34,10 +34,7 @@ impl Stream for IntoStream { type Item = Result; #[inline] - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().stream.try_poll_next(cx) } diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index b7353d908a..12f2de0d31 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -5,18 +5,19 @@ #[cfg(feature = "compat")] use crate::compat::Compat; +use crate::fns::{ + inspect_err_fn, inspect_ok_fn, into_fn, map_err_fn, map_ok_fn, InspectErrFn, InspectOkFn, + IntoFn, MapErrFn, MapOkFn, +}; +use crate::future::assert_future; +use crate::stream::assert_stream; +use crate::stream::{Inspect, Map}; use core::pin::Pin; use futures_core::{ future::{Future, TryFuture}, 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::future::assert_future; -use crate::stream::{Map, Inspect}; -use crate::stream::assert_stream; mod and_then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -919,7 +920,9 @@ pub trait TryStreamExt: TryStream { Self::Ok: TryFuture, Self: Sized, { - assert_stream::::Ok, Self::Error>, _>(TryBuffered::new(self, n)) + assert_stream::::Ok, Self::Error>, _>(TryBuffered::new( + self, n, + )) } // TODO: false positive warning from rustdoc. Verify once #43466 settles diff --git a/futures-util/src/stream/try_stream/or_else.rs b/futures-util/src/stream/try_stream/or_else.rs index 999123a437..cb69e81323 100644 --- a/futures-util/src/stream/try_stream/or_else.rs +++ b/futures-util/src/stream/try_stream/or_else.rs @@ -2,7 +2,7 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::ready; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -34,9 +34,10 @@ where } impl OrElse - where St: TryStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { Self { stream, future: None, f } @@ -46,16 +47,14 @@ impl OrElse } impl Stream for OrElse - where St: TryStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -68,7 +67,7 @@ impl Stream for OrElse Some(Ok(item)) => break Some(Ok(item)), Some(Err(e)) => { this.future.set(Some((this.f)(e))); - }, + } None => break None, } } @@ -88,9 +87,10 @@ impl Stream for OrElse } impl FusedStream for OrElse - where St: TryStream + FusedStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -100,7 +100,8 @@ impl FusedStream for OrElse // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for OrElse - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/try_collect.rs b/futures-util/src/stream/try_stream/try_collect.rs index 387de9703b..5d3b3d7668 100644 --- a/futures-util/src/stream/try_stream/try_collect.rs +++ b/futures-util/src/stream/try_stream/try_collect.rs @@ -19,10 +19,7 @@ pin_project! { impl TryCollect { pub(super) fn new(s: St) -> Self { - Self { - stream: s, - items: Default::default(), - } + Self { stream: s, items: Default::default() } } } @@ -43,10 +40,7 @@ where { type Output = Result; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); Poll::Ready(Ok(loop { match ready!(this.stream.as_mut().try_poll_next(cx)?) { diff --git a/futures-util/src/stream/try_stream/try_concat.rs b/futures-util/src/stream/try_stream/try_concat.rs index 2451332448..58fb6a5413 100644 --- a/futures-util/src/stream/try_stream/try_concat.rs +++ b/futures-util/src/stream/try_stream/try_concat.rs @@ -22,10 +22,7 @@ where St::Ok: Extend<::Item> + IntoIterator + Default, { pub(super) fn new(stream: St) -> Self { - Self { - stream, - accum: None, - } + Self { stream, accum: None } } } diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index eacefd20db..61e6105c37 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -2,7 +2,7 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; use futures_core::ready; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -40,24 +40,21 @@ where } impl TryFilter - where St: TryStream +where + St: TryStream, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - } + Self { stream, f, pending_fut: None, pending_item: None } } delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilter - where St: TryStream + FusedStream, - F: FnMut(&St::Ok) -> Fut, - Fut: Future, +where + St: TryStream + FusedStream, + F: FnMut(&St::Ok) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_fut.is_none() && self.stream.is_terminated() @@ -65,16 +62,14 @@ impl FusedStream for TryFilter } impl Stream for TryFilter - where St: TryStream, - Fut: Future, - F: FnMut(&St::Ok) -> Fut, +where + St: TryStream, + Fut: Future, + F: FnMut(&St::Ok) -> Fut, { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -108,7 +103,8 @@ impl Stream for TryFilter // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryFilter - where S: TryStream + Sink, +where + S: TryStream + Sink, { type Error = E; 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 335649bfc9..bb1b5b9db6 100644 --- a/futures-util/src/stream/try_stream/try_filter_map.rs +++ b/futures-util/src/stream/try_stream/try_filter_map.rs @@ -1,8 +1,8 @@ use core::fmt; use core::pin::Pin; -use futures_core::future::{TryFuture}; +use futures_core::future::TryFuture; use futures_core::ready; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -43,9 +43,10 @@ impl TryFilterMap { } impl FusedStream for TryFilterMap - where St: TryStream + FusedStream, - Fut: TryFuture, Error = St::Error>, - F: FnMut(St::Ok) -> Fut, +where + St: TryStream + FusedStream, + Fut: TryFuture, Error = St::Error>, + F: FnMut(St::Ok) -> Fut, { fn is_terminated(&self) -> bool { self.pending.is_none() && self.stream.is_terminated() @@ -53,16 +54,14 @@ impl FusedStream for TryFilterMap } impl Stream for TryFilterMap - where St: TryStream, - Fut: TryFuture, Error = St::Error>, - F: FnMut(St::Ok) -> Fut, +where + St: TryStream, + Fut: TryFuture, Error = St::Error>, + F: FnMut(St::Ok) -> Fut, { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); Poll::Ready(loop { @@ -98,7 +97,8 @@ impl Stream for TryFilterMap // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryFilterMap - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/try_fold.rs b/futures-util/src/stream/try_stream/try_fold.rs index 1d41e4bc2b..d344d96e7d 100644 --- a/futures-util/src/stream/try_stream/try_fold.rs +++ b/futures-util/src/stream/try_stream/try_fold.rs @@ -35,24 +35,21 @@ where } impl TryFold -where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F, t: T) -> Self { - Self { - stream, - f, - accum: Some(t), - future: None, - } + Self { stream, f, accum: Some(t), future: None } } } impl FusedFuture for TryFold - where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.future.is_none() @@ -60,9 +57,10 @@ impl FusedFuture for TryFold } impl Future for TryFold - where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { type Output = Result; 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 0a814ae86c..6a081d84e7 100644 --- a/futures-util/src/stream/try_stream/try_for_each.rs +++ b/futures-util/src/stream/try_stream/try_for_each.rs @@ -32,23 +32,21 @@ where } impl TryForEach -where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - future: None, - } + Self { stream, f, future: None } } } impl Future for TryForEach - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { type Output = Result<(), St::Error>; diff --git a/futures-util/src/stream/try_stream/try_next.rs b/futures-util/src/stream/try_stream/try_next.rs index 1bc00fbc2d..13fcf80cae 100644 --- a/futures-util/src/stream/try_stream/try_next.rs +++ b/futures-util/src/stream/try_stream/try_next.rs @@ -28,10 +28,7 @@ impl FusedFuture for TryNext<'_, S impl Future for TryNext<'_, St> { type Output = Result, St::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.stream.try_poll_next_unpin(cx)?.map(Ok) } } 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 0603b10f85..a424b6c5b1 100644 --- a/futures-util/src/stream/try_stream/try_skip_while.rs +++ b/futures-util/src/stream/try_stream/try_skip_while.rs @@ -2,7 +2,7 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::ready; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; @@ -40,34 +40,27 @@ where } impl TrySkipWhile - where St: TryStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - done_skipping: false, - } + Self { stream, f, pending_fut: None, pending_item: None, done_skipping: false } } delegate_access_inner!(stream, St, ()); } impl Stream for TrySkipWhile - where St: TryStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if *this.done_skipping { @@ -105,9 +98,10 @@ impl Stream for TrySkipWhile } impl FusedStream for TrySkipWhile - where St: TryStream + FusedStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.pending_item.is_none() && self.stream.is_terminated() @@ -117,7 +111,8 @@ impl FusedStream for TrySkipWhile // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TrySkipWhile - where S: TryStream + Sink, +where + S: TryStream + Sink, { type Error = E; diff --git a/futures-util/src/stream/try_stream/try_take_while.rs b/futures-util/src/stream/try_stream/try_take_while.rs index 624157290c..3375960ef4 100644 --- a/futures-util/src/stream/try_stream/try_take_while.rs +++ b/futures-util/src/stream/try_stream/try_take_while.rs @@ -49,13 +49,7 @@ where Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { - Self { - stream, - f, - pending_fut: None, - pending_item: None, - done_taking: false, - } + Self { stream, f, pending_fut: None, pending_item: None, done_taking: false } } delegate_access_inner!(stream, St, ()); diff --git a/futures-util/src/stream/try_stream/try_unfold.rs b/futures-util/src/stream/try_stream/try_unfold.rs index 258c18e461..fd9cdf1d8c 100644 --- a/futures-util/src/stream/try_stream/try_unfold.rs +++ b/futures-util/src/stream/try_stream/try_unfold.rs @@ -61,11 +61,7 @@ where F: FnMut(T) -> Fut, Fut: TryFuture>, { - assert_stream::, _>(TryUnfold { - f, - state: Some(init), - fut: None, - }) + assert_stream::, _>(TryUnfold { f, state: Some(init), fut: None }) } pin_project! { @@ -85,10 +81,7 @@ where Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryUnfold") - .field("state", &self.state) - .field("fut", &self.fut) - .finish() + f.debug_struct("TryUnfold").field("state", &self.state).field("fut", &self.fut).finish() } } @@ -99,10 +92,7 @@ where { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); if let Some(state) = this.state.take() { diff --git a/futures-util/src/stream/unfold.rs b/futures-util/src/stream/unfold.rs index e17d46515c..7d8ef6babc 100644 --- a/futures-util/src/stream/unfold.rs +++ b/futures-util/src/stream/unfold.rs @@ -52,10 +52,7 @@ where F: FnMut(T) -> Fut, Fut: Future>, { - assert_stream::(Unfold { - f, - state: UnfoldState::Value { value: init }, - }) + assert_stream::(Unfold { f, state: UnfoldState::Value { value: init } }) } pin_project! { @@ -74,9 +71,7 @@ where Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Unfold") - .field("state", &self.state) - .finish() + f.debug_struct("Unfold").field("state", &self.state).finish() } } @@ -105,9 +100,7 @@ where let mut this = self.project(); if let Some(state) = this.state.as_mut().take_value() { - this.state.set(UnfoldState::Future { - future: (this.f)(state), - }); + this.state.set(UnfoldState::Future { future: (this.f)(state) }); } let step = match this.state.as_mut().project_future() { diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index dd1515c7fa..c4afe308cd 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -11,12 +11,9 @@ //! executors or dealing with synchronization issues around task wakeup. #[doc(no_inline)] -pub use core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -pub use futures_task::{ - Spawn, LocalSpawn, SpawnError, - FutureObj, LocalFutureObj, UnsafeFutureObj, -}; +pub use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, UnsafeFutureObj}; pub use futures_task::noop_waker; #[cfg(feature = "std")] @@ -36,4 +33,4 @@ cfg_target_has_atomic! { } mod spawn; -pub use self::spawn::{SpawnExt, LocalSpawnExt}; +pub use self::spawn::{LocalSpawnExt, SpawnExt}; diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 58885c9cc9..e40ad16b7f 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -80,15 +80,12 @@ #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(feature = "read-initializer", feature(read_initializer))] - #![cfg_attr(not(feature = "std"), no_std)] - #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] - #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] diff --git a/futures/tests/_require_features.rs b/futures/tests/_require_features.rs index da76dcd1e9..8046cc99a4 100644 --- a/futures/tests/_require_features.rs +++ b/futures/tests/_require_features.rs @@ -1,8 +1,13 @@ #[cfg(not(all( - feature = "std", feature = "alloc", feature = "async-await", - feature = "compat", feature = "io-compat", - feature = "executor", feature = "thread-pool", + feature = "std", + feature = "alloc", + feature = "async-await", + feature = "compat", + feature = "io-compat", + feature = "executor", + feature = "thread-pool", )))] -compile_error!("`futures` tests must have all stable features activated: \ +compile_error!( + "`futures` tests must have all stable features activated: \ use `--all-features` or `--features default,thread-pool,io-compat`" ); diff --git a/futures/tests/compat.rs b/futures/tests/compat.rs index 39adc7cba4..c4125d895b 100644 --- a/futures/tests/compat.rs +++ b/futures/tests/compat.rs @@ -1,16 +1,14 @@ #![cfg(feature = "compat")] -use tokio::timer::Delay; -use tokio::runtime::Runtime; -use std::time::Instant; -use futures::prelude::*; use futures::compat::Future01CompatExt; +use futures::prelude::*; +use std::time::Instant; +use tokio::runtime::Runtime; +use tokio::timer::Delay; #[test] fn can_use_01_futures_in_a_03_future_running_on_a_01_executor() { - let f = async { - Delay::new(Instant::now()).compat().await - }; + let f = async { Delay::new(Instant::now()).compat().await }; let mut runtime = Runtime::new().unwrap(); runtime.block_on(f.boxed().compat()).unwrap(); diff --git a/futures/tests/eager_drop.rs b/futures/tests/eager_drop.rs index 45cf191d20..992507774c 100644 --- a/futures/tests/eager_drop.rs +++ b/futures/tests/eager_drop.rs @@ -69,16 +69,13 @@ fn then_drops_eagerly() { 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(); + 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(); @@ -91,16 +88,13 @@ fn and_then_drops_eagerly() { 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(); + 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(); @@ -113,16 +107,13 @@ fn or_else_drops_eagerly() { 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(); + 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(); diff --git a/futures/tests/future_basic_combinators.rs b/futures/tests/future_basic_combinators.rs index fa65b6f5f1..372ab48b79 100644 --- a/futures/tests/future_basic_combinators.rs +++ b/futures/tests/future_basic_combinators.rs @@ -13,17 +13,21 @@ fn basic_future_combinators() { tx1.send(x).unwrap(); // Send 1 tx1.send(2).unwrap(); // Send 2 future::ready(3) - }).map(move |x| { + }) + .map(move |x| { tx2.send(x).unwrap(); // Send 3 tx2.send(4).unwrap(); // Send 4 5 - }).map(move |x| { + }) + .map(move |x| { tx3.send(x).unwrap(); // Send 5 }); assert!(rx.try_recv().is_err()); // Not started yet fut.run_in_background(); // Start it - for i in 1..=5 { assert_eq!(rx.recv(), Ok(i)); } // Check it + for i in 1..=5 { + assert_eq!(rx.recv(), Ok(i)); + } // Check it assert!(rx.recv().is_err()); // Should be done } @@ -93,6 +97,8 @@ fn basic_try_future_combinators() { assert!(rx.try_recv().is_err()); // Not started yet fut.run_in_background(); // Start it - for i in 1..=12 { assert_eq!(rx.recv(), Ok(i)); } // Check it + for i in 1..=12 { + assert_eq!(rx.recv(), Ok(i)); + } // Check it assert!(rx.recv().is_err()); // Should be done } diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs index 662798bff4..718d6c41b0 100644 --- a/futures/tests/future_shared.rs +++ b/futures/tests/future_shared.rs @@ -119,9 +119,7 @@ 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)); @@ -193,8 +191,5 @@ fn shared_future_that_wakes_itself_until_pending_is_returned() { // 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) })), - ((), ()) - ); + assert_eq!(block_on(futures::future::join(fut, async { proceed.set(true) })), ((), ())); } diff --git a/futures/tests/future_try_join_all.rs b/futures/tests/future_try_join_all.rs index d80a2c634a..a4b3bb76a9 100644 --- a/futures/tests/future_try_join_all.rs +++ b/futures/tests/future_try_join_all.rs @@ -14,15 +14,9 @@ where #[test] fn collect_collects() { - 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), 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]), - ); + assert_done(|| Box::new(try_join_all(vec![ok(1)])), Ok::<_, usize>(vec![1])); // REVIEW: should this be implemented? // assert_done(|| Box::new(try_join_all(Vec::::new())), Ok(vec![])); @@ -38,10 +32,7 @@ fn try_join_all_iter_lifetime() { Box::new(try_join_all(iter)) } - assert_done( - || sizes(vec![&[1, 2, 3], &[], &[0]]), - Ok(vec![3_usize, 0, 1]), - ); + assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1])); } #[test] diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index f7d6de1cf9..d60df879c2 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -38,11 +38,7 @@ struct MaybePending<'a> { impl<'a> MaybePending<'a> { fn new(inner: &'a [u8]) -> Self { - Self { - inner, - ready_read: false, - ready_fill_buf: false, - } + Self { inner, ready_read: false, ready_fill_buf: false } } } @@ -126,10 +122,7 @@ fn test_buffered_reader_seek() { assert_eq!(block_on(reader.seek(SeekFrom::Start(3))).ok(), Some(3)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!( - run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), - None - ); + assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); assert_eq!(block_on(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); @@ -172,23 +165,14 @@ fn test_buffered_reader_seek_underflow() { let mut reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!( - block_on(reader.seek(SeekFrom::End(-5))).ok(), - Some(u64::max_value() - 5) - ); + assert_eq!(block_on(reader.seek(SeekFrom::End(-5))).ok(), Some(u64::max_value() - 5)); assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); // the following seek will require two underlying seeks let expected = 9_223_372_036_854_775_802; - assert_eq!( - block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), - Some(expected) - ); + assert_eq!(block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), Some(expected)); assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); // seeking to 0 should empty the buffer. - assert_eq!( - block_on(reader.seek(SeekFrom::Current(0))).ok(), - Some(expected) - ); + assert_eq!(block_on(reader.seek(SeekFrom::Current(0))).ok(), Some(expected)); assert_eq!(reader.get_ref().get_ref().pos, expected); } @@ -209,9 +193,7 @@ fn test_short_reads() { } } - let inner = ShortReader { - lengths: vec![0, 1, 2, 0, 1, 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]; assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); @@ -288,10 +270,7 @@ fn maybe_pending_seek() { impl<'a> MaybePendingSeek<'a> { fn new(inner: &'a [u8]) -> Self { - Self { - inner: Cursor::new(inner), - ready: true, - } + Self { inner: Cursor::new(inner), ready: true } } } @@ -340,10 +319,7 @@ fn maybe_pending_seek() { assert_eq!(run(reader.seek(SeekFrom::Current(3))).ok(), Some(3)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!( - run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), - None - ); + assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); assert_eq!(run(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); diff --git a/futures/tests/io_buf_writer.rs b/futures/tests/io_buf_writer.rs index dec4026fae..b264cd54c2 100644 --- a/futures/tests/io_buf_writer.rs +++ b/futures/tests/io_buf_writer.rs @@ -15,10 +15,7 @@ struct MaybePending { impl MaybePending { fn new(inner: Vec) -> Self { - Self { - inner, - ready: false, - } + Self { inner, ready: false } } } @@ -157,17 +154,11 @@ fn maybe_pending_buf_writer() { run(writer.write(&[9, 10, 11])).unwrap(); assert_eq!(writer.buffer(), []); - assert_eq!( - writer.get_ref().inner, - &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); + assert_eq!(writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); run(writer.flush()).unwrap(); assert_eq!(writer.buffer(), []); - assert_eq!( - &writer.get_ref().inner, - &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); + assert_eq!(&writer.get_ref().inner, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); } #[test] @@ -190,11 +181,7 @@ fn maybe_pending_buf_writer_seek() { impl MaybePendingSeek { fn new(inner: Vec) -> Self { - Self { - inner: Cursor::new(inner), - ready_write: false, - ready_seek: false, - } + Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } } } @@ -244,15 +231,9 @@ fn maybe_pending_buf_writer_seek() { run(w.write_all(&[0, 1, 2, 3, 4, 5])).unwrap(); run(w.write_all(&[6, 7])).unwrap(); assert_eq!(run(w.seek(SeekFrom::Current(0))).ok(), Some(8)); - assert_eq!( - &w.get_ref().inner.get_ref()[..], - &[0, 1, 2, 3, 4, 5, 6, 7][..] - ); + assert_eq!(&w.get_ref().inner.get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); assert_eq!(run(w.seek(SeekFrom::Start(2))).ok(), Some(2)); run(w.write_all(&[8, 9])).unwrap(); run(w.flush()).unwrap(); - assert_eq!( - &w.into_inner().inner.into_inner()[..], - &[0, 1, 8, 9, 4, 5, 6, 7] - ); + assert_eq!(&w.into_inner().inner.into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); } diff --git a/futures/tests/io_cursor.rs b/futures/tests/io_cursor.rs index 906e00d1be..435ea5a155 100644 --- a/futures/tests/io_cursor.rs +++ b/futures/tests/io_cursor.rs @@ -9,22 +9,10 @@ use std::pin::Pin; fn cursor_asyncwrite_vec() { let mut cursor = Cursor::new(vec![0; 5]); block_on(lazy(|cx| { - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[1, 2]), - Poll::Ready(Ok(2)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[3, 4]), - Poll::Ready(Ok(2)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[5, 6]), - Poll::Ready(Ok(2)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[6, 7]), - Poll::Ready(Ok(2)) - ); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(2))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[6, 7]), Poll::Ready(Ok(2))); })); assert_eq!(cursor.into_inner(), [1, 2, 3, 4, 5, 6, 6, 7]); } @@ -33,22 +21,10 @@ fn cursor_asyncwrite_vec() { fn cursor_asyncwrite_box() { let mut cursor = Cursor::new(vec![0; 5].into_boxed_slice()); block_on(lazy(|cx| { - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[1, 2]), - Poll::Ready(Ok(2)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[3, 4]), - Poll::Ready(Ok(2)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[5, 6]), - Poll::Ready(Ok(1)) - ); - assert_matches!( - Pin::new(&mut cursor).poll_write(cx, &[6, 7]), - Poll::Ready(Ok(0)) - ); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(1))); + assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[6, 7]), Poll::Ready(Ok(0))); })); assert_eq!(&*cursor.into_inner(), [1, 2, 3, 4, 5]); } diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs index 06e1ac9b14..5ce01a6945 100644 --- a/futures/tests/io_lines.rs +++ b/futures/tests/io_lines.rs @@ -43,10 +43,8 @@ fn lines() { #[test] fn maybe_pending() { - let buf = stream::iter(vec![&b"12"[..], &b"\r"[..]]) - .map(Ok) - .into_async_read() - .interleave_pending(); + let buf = + stream::iter(vec![&b"12"[..], &b"\r"[..]]).map(Ok).into_async_read().interleave_pending(); let mut s = buf.lines(); assert_eq!(run_next!(s), "12\r".to_string()); assert!(run(s.next()).is_none()); diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs index 6d82497495..88a877928a 100644 --- a/futures/tests/io_read_line.rs +++ b/futures/tests/io_read_line.rs @@ -41,10 +41,8 @@ fn maybe_pending() { assert_eq!(run(buf.read_line(&mut v)).unwrap(), 2); assert_eq!(v, "12"); - let mut buf = stream::iter(vec![&b"12"[..], &b"\n\n"[..]]) - .map(Ok) - .into_async_read() - .interleave_pending(); + let mut buf = + stream::iter(vec![&b"12"[..], &b"\n\n"[..]]).map(Ok).into_async_read().interleave_pending(); let mut v = String::new(); assert_eq!(run(buf.read_line(&mut v)).unwrap(), 3); assert_eq!(v, "12\n"); diff --git a/futures/tests/io_write.rs b/futures/tests/io_write.rs index 227b4f9670..6af27553cb 100644 --- a/futures/tests/io_write.rs +++ b/futures/tests/io_write.rs @@ -57,11 +57,7 @@ fn write_vectored_first_non_empty() { Poll::Ready(Ok(4)) }); let cx = &mut panic_context(); - let bufs = &mut [ - io::IoSlice::new(&[]), - io::IoSlice::new(&[]), - io::IoSlice::new(b"four"), - ]; + let bufs = &mut [io::IoSlice::new(&[]), io::IoSlice::new(&[]), io::IoSlice::new(b"four")]; let res = Pin::new(&mut writer).poll_write_vectored(cx, bufs); let res = res.map_err(|e| e.kind()); diff --git a/futures/tests/macro-reexport/src/lib.rs b/futures/tests/macro-reexport/src/lib.rs index 49691c9e07..82f939b343 100644 --- a/futures/tests/macro-reexport/src/lib.rs +++ b/futures/tests/macro-reexport/src/lib.rs @@ -1,8 +1,7 @@ // normal reexport -pub use futures03::{join, try_join, select, select_biased}; +pub use futures03::{join, select, select_biased, try_join}; // reexport + rename pub use futures03::{ - join as join2, try_join as try_join2, - select as select2, select_biased as select_biased2, + join as join2, select as select2, select_biased as select_biased2, try_join as try_join2, }; diff --git a/futures/tests/macro-tests/src/main.rs b/futures/tests/macro-tests/src/main.rs index 5d11f60834..c654e0c92c 100644 --- a/futures/tests/macro-tests/src/main.rs +++ b/futures/tests/macro-tests/src/main.rs @@ -65,5 +65,4 @@ fn main() { _ = b => unreachable!(), }; }); - } diff --git a/futures/tests/oneshot.rs b/futures/tests/oneshot.rs index 867cb45ff1..58951ec581 100644 --- a/futures/tests/oneshot.rs +++ b/futures/tests/oneshot.rs @@ -21,8 +21,7 @@ fn oneshot_send2() { let (tx2, rx2) = mpsc::channel(); thread::spawn(|| tx1.send(1).unwrap()).join().unwrap(); - rx1.map_ok(move |x| tx2.send(x).unwrap()) - .run_in_background(); + rx1.map_ok(move |x| tx2.send(x).unwrap()).run_in_background(); assert_eq!(1, rx2.recv().unwrap()); } @@ -31,8 +30,7 @@ fn oneshot_send3() { let (tx1, rx1) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); - rx1.map_ok(move |x| tx2.send(x).unwrap()) - .run_in_background(); + rx1.map_ok(move |x| tx2.send(x).unwrap()).run_in_background(); thread::spawn(|| tx1.send(1).unwrap()).join().unwrap(); assert_eq!(1, rx2.recv().unwrap()); } @@ -43,8 +41,7 @@ fn oneshot_drop_tx1() { let (tx2, rx2) = mpsc::channel(); drop(tx1); - rx1.map(move |result| tx2.send(result).unwrap()) - .run_in_background(); + rx1.map(move |result| tx2.send(result).unwrap()).run_in_background(); assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); } @@ -55,8 +52,7 @@ fn oneshot_drop_tx2() { let (tx2, rx2) = mpsc::channel(); let t = thread::spawn(|| drop(tx1)); - rx1.map(move |result| tx2.send(result).unwrap()) - .run_in_background(); + rx1.map(move |result| tx2.send(result).unwrap()).run_in_background(); t.join().unwrap(); assert_eq!(Err(oneshot::Canceled), rx2.recv().unwrap()); diff --git a/futures/tests/ready_queue.rs b/futures/tests/ready_queue.rs index b2b9bec461..82901327fb 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -52,19 +52,13 @@ fn resolving_errors() { drop(tx2); - assert_eq!( - Poll::Ready(Some(Err(oneshot::Canceled))), - queue.poll_next_unpin(cx) - ); + assert_eq!(Poll::Ready(Some(Err(oneshot::Canceled))), queue.poll_next_unpin(cx)); assert!(!queue.poll_next_unpin(cx).is_ready()); drop(tx1); tx3.send("world2").unwrap(); - assert_eq!( - Poll::Ready(Some(Err(oneshot::Canceled))), - queue.poll_next_unpin(cx) - ); + assert_eq!(Poll::Ready(Some(Err(oneshot::Canceled))), queue.poll_next_unpin(cx)); assert_eq!(Poll::Ready(Some(Ok("world2"))), queue.poll_next_unpin(cx)); assert_eq!(Poll::Ready(None), queue.poll_next_unpin(cx)); })); diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index 6c27e9ae1f..f3cf11b931 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -131,10 +131,7 @@ impl Sink> for ManualFlush { impl ManualFlush { fn new() -> Self { - Self { - data: Vec::new(), - waiting_tasks: Vec::new(), - } + Self { data: Vec::new(), waiting_tasks: Vec::new() } } fn force_flush(&mut self) -> Vec { @@ -157,10 +154,7 @@ struct Allow { impl Allow { fn new() -> Self { - Self { - flag: Cell::new(false), - tasks: RefCell::new(Vec::new()), - } + Self { flag: Cell::new(false), tasks: RefCell::new(Vec::new()) } } fn check(&self, cx: &mut Context<'_>) -> bool { @@ -208,20 +202,14 @@ impl Sink for ManualAllow { fn manual_allow() -> (ManualAllow, Rc) { let allow = Rc::new(Allow::new()); - let manual_allow = ManualAllow { - data: Vec::new(), - allow: allow.clone(), - }; + let manual_allow = ManualAllow { data: Vec::new(), allow: allow.clone() }; (manual_allow, allow) } #[test] fn either_sink() { - let mut s = if true { - Vec::::new().left_sink() - } else { - VecDeque::::new().right_sink() - }; + let mut s = + if true { Vec::::new().left_sink() } else { VecDeque::::new().right_sink() }; Pin::new(&mut s).start_send(0).unwrap(); } @@ -400,13 +388,10 @@ fn with_implements_clone() { let (mut tx, rx) = mpsc::channel(5); { - let mut is_positive = tx - .clone() - .with(|item| future::ok::(item > 0)); + let mut is_positive = tx.clone().with(|item| future::ok::(item > 0)); - let mut is_long = tx - .clone() - .with(|item: &str| future::ok::(item.len() > 5)); + let mut is_long = + tx.clone().with(|item: &str| future::ok::(item.len() > 5)); block_on(is_positive.clone().send(-1)).unwrap(); block_on(is_long.clone().send("123456")).unwrap(); @@ -418,10 +403,7 @@ fn with_implements_clone() { block_on(tx.close()).unwrap(); - assert_eq!( - block_on(rx.collect::>()), - vec![false, true, false, true, false] - ); + assert_eq!(block_on(rx.collect::>()), vec![false, true, false, true, false]); } // test that a buffer is a no-nop around a sink that always accepts sends @@ -513,10 +495,7 @@ fn sink_map_err() { } let tx = mpsc::channel(0).0; - assert_eq!( - Pin::new(&mut tx.sink_map_err(|_| ())).start_send(()), - Err(()) - ); + assert_eq!(Pin::new(&mut tx.sink_map_err(|_| ())).start_send(()), Err(())); } #[test] @@ -571,8 +550,5 @@ fn err_into() { } let tx = mpsc::channel(0).0; - assert_eq!( - Pin::new(&mut tx.sink_err_into()).start_send(()), - Err(ErrIntoTest) - ); + assert_eq!(Pin::new(&mut tx.sink_err_into()).start_send(()), Err(ErrIntoTest)); } diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index f2027b5d58..0d453d1752 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -24,16 +24,11 @@ fn select() { #[test] fn flat_map() { 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; + 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]); }); diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 1cdb197da4..7506c65a63 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -11,9 +11,7 @@ fn works_1() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![a_rx, b_rx, c_rx] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); b_tx.send(99).unwrap(); assert!(stream.poll_next_unpin(&mut noop_context()).is_pending()); @@ -34,12 +32,9 @@ fn works_2() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![ - a_rx.boxed(), - join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed(), - ] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx.boxed(), join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed()] + .into_iter() + .collect::>(); let mut cx = noop_context(); a_tx.send(33).unwrap(); @@ -52,13 +47,9 @@ fn works_2() { #[test] fn from_iterator() { - let stream = vec![ - future::ready::(1), - future::ready::(2), - future::ready::(3), - ] - .into_iter() - .collect::>(); + let stream = vec![future::ready::(1), future::ready::(2), future::ready::(3)] + .into_iter() + .collect::>(); assert_eq!(stream.len(), 3); assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index e55a91bdd4..93b9e293e3 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -43,11 +43,8 @@ fn works_1() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut iter = block_on_stream( - vec![a_rx, b_rx, c_rx] - .into_iter() - .collect::>(), - ); + let mut iter = + block_on_stream(vec![a_rx, b_rx, c_rx].into_iter().collect::>()); b_tx.send(99).unwrap(); assert_eq!(Some(Ok(99)), iter.next()); @@ -65,12 +62,9 @@ fn works_2() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![ - a_rx.boxed(), - join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed(), - ] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx.boxed(), join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed()] + .into_iter() + .collect::>(); a_tx.send(9).unwrap(); b_tx.send(10).unwrap(); @@ -84,13 +78,9 @@ fn works_2() { #[test] fn from_iterator() { - let stream = vec![ - future::ready::(1), - future::ready::(2), - future::ready::(3), - ] - .into_iter() - .collect::>(); + let stream = vec![future::ready::(1), future::ready::(2), future::ready::(3)] + .into_iter() + .collect::>(); assert_eq!(stream.len(), 3); assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } @@ -126,9 +116,7 @@ fn iter_mut_cancel() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![a_rx, b_rx, c_rx] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); for rx in stream.iter_mut() { rx.close(); @@ -148,13 +136,10 @@ fn iter_mut_cancel() { #[test] fn iter_mut_len() { - let mut stream = vec![ - future::pending::<()>(), - future::pending::<()>(), - future::pending::<()>(), - ] - .into_iter() - .collect::>(); + let mut stream = + vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); let mut iter_mut = stream.iter_mut(); assert_eq!(iter_mut.len(), 3); @@ -188,10 +173,7 @@ fn iter_cancel() { impl AtomicCancel { fn new(future: F) -> Self { - Self { - future, - cancel: AtomicBool::new(false), - } + Self { future, cancel: AtomicBool::new(false) } } } @@ -217,13 +199,9 @@ fn iter_cancel() { #[test] fn iter_len() { - let stream = vec![ - future::pending::<()>(), - future::pending::<()>(), - future::pending::<()>(), - ] - .into_iter() - .collect::>(); + let stream = vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); let mut iter = stream.iter(); assert_eq!(iter.len(), 3); diff --git a/futures/tests/stream_split.rs b/futures/tests/stream_split.rs index 7cee9ba6a2..694c151807 100644 --- a/futures/tests/stream_split.rs +++ b/futures/tests/stream_split.rs @@ -45,10 +45,7 @@ fn test_split() { let mut dest: Vec = Vec::new(); { - let join = Join { - stream: stream::iter(vec![10, 20, 30]), - sink: &mut dest, - }; + let join = Join { stream: stream::iter(vec![10, 20, 30]), sink: &mut dest }; let (sink, stream) = join.split(); let join = sink.reunite(stream).expect("test_split: reunite error"); diff --git a/futures/tests/task_arc_wake.rs b/futures/tests/task_arc_wake.rs index 3c5724a239..aedc15bcb8 100644 --- a/futures/tests/task_arc_wake.rs +++ b/futures/tests/task_arc_wake.rs @@ -8,9 +8,7 @@ struct CountingWaker { impl CountingWaker { fn new() -> Self { - Self { - nr_wake: Mutex::new(0), - } + Self { nr_wake: Mutex::new(0) } } fn wakes(&self) -> i32 { @@ -73,10 +71,7 @@ fn proper_refcount_on_wake_panic() { let w1: Waker = task::waker(some_w.clone()); assert_eq!( "WAKE UP", - *panic::catch_unwind(|| w1.wake_by_ref()) - .unwrap_err() - .downcast::<&str>() - .unwrap() + *panic::catch_unwind(|| w1.wake_by_ref()).unwrap_err().downcast::<&str>().unwrap() ); assert_eq!(2, Arc::strong_count(&some_w)); // some_w + w1 drop(w1); diff --git a/futures/tests/task_atomic_waker.rs b/futures/tests/task_atomic_waker.rs index 299270e63d..cec3db2876 100644 --- a/futures/tests/task_atomic_waker.rs +++ b/futures/tests/task_atomic_waker.rs @@ -1,4 +1,3 @@ - use futures::executor::block_on; use futures::future::poll_fn; use futures::task::{AtomicWaker, Poll}; From fb2a2d30f244caeee6571c3cfddaa74cfb66bbe7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 11 Apr 2021 03:12:10 +0900 Subject: [PATCH 016/283] Use stable compiler to installing cargo-hack --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb62875f83..663072f763 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. # Refs: cargo#3620, cargo#4106, cargo#4463, cargo#4753, cargo#5015, cargo#5364, cargo#6195 - - run: cargo install cargo-hack + - run: cargo +stable install cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace # Check no-default-features @@ -81,7 +81,7 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - run: cargo install cargo-hack + - run: cargo +stable install cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace # Check no-default-features @@ -108,7 +108,7 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - run: cargo install cargo-hack + - run: cargo +stable install cargo-hack - run: cargo hack build --workspace --no-dev-deps build: From 21a4d4eb9a6eed5ebac42ac122c2b4968b79c213 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Sat, 17 Apr 2021 14:11:11 +0100 Subject: [PATCH 017/283] Add Clone to WeakShared --- futures-util/src/future/future/shared.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 1f139d0af0..9b31932fe3 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -29,6 +29,12 @@ struct Notifier { /// A weak reference to a [`Shared`] that can be upgraded much like an `Arc`. pub struct WeakShared(Weak>); +impl Clone for WeakShared { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + // The future itself is polled behind the `Arc`, so it won't be moved // when `Shared` is moved. impl Unpin for Shared {} From 9ddf0ada20bdf314d28c7cc3cd01d044f9731232 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 1 May 2021 13:31:17 +0900 Subject: [PATCH 018/283] Bump MSRV of futures-util to 1.41 --- .github/workflows/ci.yml | 24 +++++------------------- README.md | 6 +++--- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 663072f763..129ba6bfe0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task, futures-channel. - # When updating this, the reminder to update the minimum required version of `async-await` feature in README.md. + # When updating this, the reminder to update the minimum required version in .clippy.toml. - 1.36.0 runs-on: ubuntu-latest steps: @@ -74,8 +74,8 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-test. - # When updating this, the reminder to update the minimum required version of `async-await` feature in README.md. - - 1.37.0 + # When updating this, the reminder to update the minimum required version in README.md. + - 1.41.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -84,6 +84,8 @@ jobs: - run: cargo +stable install cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace + # Check default features + - run: cargo hack build --workspace --ignore-private # Check no-default-features - run: cargo hack build --workspace --exclude futures-test --ignore-private --no-default-features # Check alloc feature @@ -95,22 +97,6 @@ jobs: # Check thread-pool feature (futures, futures-executor) - run: cargo hack build -p futures -p futures-executor --no-default-features --features std,thread-pool - async-await-msrv: - name: cargo +${{ matrix.rust }} build - strategy: - matrix: - rust: - # This is the minimum Rust version supported by `async-await` feature. - # When updating this, the reminder to update the minimum required version of `async-await` feature in README.md. - - 1.39.0 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Rust - run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - run: cargo +stable install cargo-hack - - run: cargo hack build --workspace --no-dev-deps - build: name: cargo +${{ matrix.rust }} build strategy: diff --git a/README.md b/README.md index 8c8df06b67..b97693fb83 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Crates.io - - Rustc Version + + Rustc Version

@@ -48,7 +48,7 @@ Now, you can use futures-rs: use futures::future::Future; ``` -The current futures-rs requires Rust 1.39 or later. +The current futures-rs requires Rust 1.41 or later. ### Feature `std` From a94c4b8eeebbb0468667556ded082bde52494940 Mon Sep 17 00:00:00 2001 From: Kruno Tomola Fabro Date: Sat, 1 May 2021 07:24:54 +0200 Subject: [PATCH 019/283] Document flush behavior on error for send_all & forward (#2404) Signed-off-by: ktf --- futures-util/src/sink/mod.rs | 3 ++- futures-util/src/stream/stream/mod.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/futures-util/src/sink/mod.rs b/futures-util/src/sink/mod.rs index e5b515b64a..78e9bedc54 100644 --- a/futures-util/src/sink/mod.rs +++ b/futures-util/src/sink/mod.rs @@ -243,7 +243,8 @@ pub trait SinkExt: Sink { /// This future will drive the stream to keep producing items until it is /// exhausted, sending each item to the sink. It will complete once both the /// stream is exhausted, the sink has received all items, and the sink has - /// been flushed. Note that the sink is **not** closed. + /// been flushed. Note that the sink is **not** closed. If the stream produces + /// an error, that error will be returned by this future without flushing the sink. /// /// Doing `sink.send_all(stream)` is roughly equivalent to /// `stream.forward(sink)`. The returned future will exhaust all items from diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index fd3a103491..e1b139fbaf 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -1329,7 +1329,8 @@ pub trait StreamExt: Stream { /// the sink is closed. Note that neither the original stream nor provided /// sink will be output by this future. Pass the sink by `Pin<&mut S>` /// (for example, via `forward(&mut sink)` inside an `async` fn/block) in - /// order to preserve access to the `Sink`. + /// order to preserve access to the `Sink`. If the stream produces an error, + /// that error will be returned by this future without flushing/closing the sink. #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] fn forward(self, sink: S) -> Forward From 53021a66967c4c300448267e3faa5fcbf57ebec7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 5 May 2021 18:07:01 +0900 Subject: [PATCH 020/283] Use #[proc_macro] at Rust 1.45+ --- futures-macro/Cargo.toml | 3 +++ futures-macro/build.rs | 26 ++++++++++++++++++++++ futures-macro/src/lib.rs | 13 ++++++----- futures-util/Cargo.toml | 3 +++ futures-util/build.rs | 26 ++++++++++++++++++++++ futures-util/src/async_await/join_mod.rs | 8 +++---- futures-util/src/async_await/select_mod.rs | 8 +++---- 7 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 futures-macro/build.rs create mode 100644 futures-util/build.rs diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 4ab6860bb3..9002a9f391 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -16,6 +16,9 @@ proc-macro = true [features] +[build-dependencies] +autocfg = "1" + [dependencies] proc-macro2 = "1.0" proc-macro-hack = "0.5.19" diff --git a/futures-macro/build.rs b/futures-macro/build.rs new file mode 100644 index 0000000000..fec7b029eb --- /dev/null +++ b/futures-macro/build.rs @@ -0,0 +1,26 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use autocfg::AutoCfg; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let cfg = match AutoCfg::new() { + Ok(cfg) => cfg, + Err(e) => { + println!( + "cargo:warning={}: unable to determine rustc version: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Function like procedural macros in expressions patterns statements stabilized in Rust 1.45: + // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements + if cfg.probe_rustc_version(1, 45) { + println!("cargo:rustc-cfg=fn_like_proc_macro"); + } +} diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 5f0c47ca89..98408ebfe6 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -13,31 +13,34 @@ extern crate proc_macro; use proc_macro::TokenStream; -use proc_macro_hack::proc_macro_hack; mod join; mod select; /// The `join!` macro. -#[proc_macro_hack] +#[cfg_attr(fn_like_proc_macro, proc_macro)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] pub fn join_internal(input: TokenStream) -> TokenStream { crate::join::join(input) } /// The `try_join!` macro. -#[proc_macro_hack] +#[cfg_attr(fn_like_proc_macro, proc_macro)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] pub fn try_join_internal(input: TokenStream) -> TokenStream { crate::join::try_join(input) } /// The `select!` macro. -#[proc_macro_hack] +#[cfg_attr(fn_like_proc_macro, proc_macro)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] pub fn select_internal(input: TokenStream) -> TokenStream { crate::select::select(input) } /// The `select_biased!` macro. -#[proc_macro_hack] +#[cfg_attr(fn_like_proc_macro, proc_macro)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 424e372630..4890c3f362 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -32,6 +32,9 @@ bilock = [] read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] write-all-vectored = ["io"] +[build-dependencies] +autocfg = "1" + [dependencies] futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } diff --git a/futures-util/build.rs b/futures-util/build.rs new file mode 100644 index 0000000000..fec7b029eb --- /dev/null +++ b/futures-util/build.rs @@ -0,0 +1,26 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use autocfg::AutoCfg; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let cfg = match AutoCfg::new() { + Ok(cfg) => cfg, + Err(e) => { + println!( + "cargo:warning={}: unable to determine rustc version: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Function like procedural macros in expressions patterns statements stabilized in Rust 1.45: + // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements + if cfg.probe_rustc_version(1, 45) { + println!("cargo:rustc-cfg=fn_like_proc_macro"); + } +} diff --git a/futures-util/src/async_await/join_mod.rs b/futures-util/src/async_await/join_mod.rs index 965d9fb236..c5cdd9babc 100644 --- a/futures-util/src/async_await/join_mod.rs +++ b/futures-util/src/async_await/join_mod.rs @@ -1,7 +1,5 @@ //! The `join` macro. -use proc_macro_hack::proc_macro_hack; - macro_rules! document_join_macro { ($join:item $try_join:item) => { /// Polls multiple futures simultaneously, returning a tuple @@ -81,12 +79,14 @@ macro_rules! document_join_macro { } } +#[allow(unreachable_pub)] #[doc(hidden)] -#[proc_macro_hack(support_nested, only_hack_old_rustc)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::join_internal; +#[allow(unreachable_pub)] #[doc(hidden)] -#[proc_macro_hack(support_nested, only_hack_old_rustc)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::try_join_internal; document_join_macro! { diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 59bca0840a..37e938da55 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -1,7 +1,5 @@ //! The `select` macro. -use proc_macro_hack::proc_macro_hack; - macro_rules! document_select_macro { // This branch is required for `futures 0.3.1`, from before select_biased was introduced ($select:item) => { @@ -309,12 +307,14 @@ macro_rules! document_select_macro { } #[cfg(feature = "std")] +#[allow(unreachable_pub)] #[doc(hidden)] -#[proc_macro_hack(support_nested, only_hack_old_rustc)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::select_internal; +#[allow(unreachable_pub)] #[doc(hidden)] -#[proc_macro_hack(support_nested, only_hack_old_rustc)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::select_biased_internal; document_select_macro! { From 8613bddd176fc58c5d79f531d9502f1f30a31e42 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 6 May 2021 01:23:54 -0400 Subject: [PATCH 021/283] improve must_use comment on RemoteHandle --- futures-util/src/future/future/remote_handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index 861e4c1cd8..1358902cab 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -36,7 +36,7 @@ use { /// must be careful with regard to unwind safety because the thread in which the future /// is polled will keep running after the panic and the thread running the [RemoteHandle] /// will unwind. -#[must_use = "futures do nothing unless you `.await` or poll them"] +#[must_use = "dropping a remote handle cancels the underlying future"] #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] pub struct RemoteHandle { From 18b977d57edb9168cdfe6ae905b1212c5059a8be Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 5 May 2021 19:10:26 +0900 Subject: [PATCH 022/283] Remove uses of unstable feature(cfg_target_has_atomic) --- .github/workflows/ci.yml | 75 +++++++++++------------ Cargo.toml | 1 + ci/no_atomic_cas.sh | 23 +++++++ futures-channel/Cargo.toml | 9 ++- futures-channel/build.rs | 42 +++++++++++++ futures-channel/no_atomic_cas.rs | 1 + futures-channel/src/lib.rs | 6 +- futures-core/Cargo.toml | 5 +- futures-core/build.rs | 42 +++++++++++++ futures-core/no_atomic_cas.rs | 1 + futures-core/src/lib.rs | 4 -- futures-core/src/task/__internal/mod.rs | 4 +- futures-macro/build.rs | 2 + futures-task/Cargo.toml | 5 +- futures-task/build.rs | 42 +++++++++++++ futures-task/no_atomic_cas.rs | 1 + futures-task/src/lib.rs | 6 +- futures-util/Cargo.toml | 5 +- futures-util/build.rs | 41 ++++++++++++- futures-util/no_atomic_cas.rs | 1 + futures-util/src/lib.rs | 6 +- futures-util/src/stream/mod.rs | 6 +- futures-util/src/stream/stream/mod.rs | 8 +-- futures-util/src/stream/try_stream/mod.rs | 6 +- futures/Cargo.toml | 5 +- futures/src/lib.rs | 4 -- futures/tests/no-std/Cargo.toml | 22 +++++++ futures/tests/no-std/build.rs | 14 +++++ futures/tests/no-std/src/lib.rs | 31 ++++++++++ no_atomic_cas.rs | 11 ++++ 30 files changed, 338 insertions(+), 91 deletions(-) create mode 100755 ci/no_atomic_cas.sh create mode 100644 futures-channel/build.rs create mode 120000 futures-channel/no_atomic_cas.rs create mode 100644 futures-core/build.rs create mode 120000 futures-core/no_atomic_cas.rs create mode 100644 futures-task/build.rs create mode 120000 futures-task/no_atomic_cas.rs create mode 120000 futures-util/no_atomic_cas.rs create mode 100644 futures/tests/no-std/Cargo.toml create mode 100644 futures/tests/no-std/build.rs create mode 100644 futures/tests/no-std/src/lib.rs create mode 100644 no_atomic_cas.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 129ba6bfe0..28a8f3663b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,59 +127,41 @@ jobs: - run: cargo update -Z minimal-versions - run: cargo build --workspace --all-features - thumbv6m: - name: cargo build --target thumbv6m-none-eabi + no-std: + name: cargo build --target ${{ matrix.target }} + strategy: + matrix: + target: + - thumbv6m-none-eabi + - thumbv7m-none-eabi runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - run: rustup target add thumbv6m-none-eabi + - run: rustup target add ${{ matrix.target }} - run: cargo install cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv6m-none-eabi \ - --no-default-features \ - --features unstable,cfg-target-has-atomic - - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv6m-none-eabi \ - --no-default-features \ - --features alloc,unstable,cfg-target-has-atomic - - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv6m-none-eabi \ - --no-default-features \ - --features async-await,unstable,cfg-target-has-atomic - - thumbv7m: - name: cargo build --target thumbv7m-none-eabi - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Rust - run: rustup update nightly && rustup default nightly - - run: rustup target add thumbv7m-none-eabi - - run: cargo install cargo-hack - # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - - run: cargo hack --remove-dev-deps --workspace + cargo hack build --manifest-path futures/tests/no-std/Cargo.toml \ + --each-feature --optional-deps \ + --target ${{ matrix.target }} - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv7m-none-eabi \ + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ --no-default-features \ - --features unstable,cfg-target-has-atomic + --target ${{ matrix.target }} - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv7m-none-eabi \ - --no-default-features \ - --features alloc + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ + --no-default-features --features alloc --ignore-unknown-features \ + --target ${{ matrix.target }} - run: | - cargo build --manifest-path futures/Cargo.toml \ - --target thumbv7m-none-eabi \ - --no-default-features \ - --features async-await + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ + --no-default-features --features async-await,alloc --ignore-unknown-features \ + --target ${{ matrix.target }} bench: name: cargo bench @@ -212,6 +194,19 @@ jobs: --workspace --exclude futures-test \ --features unstable --ignore-unknown-features + # When this job failed, run ci/no_atomic_cas.sh and commit result changes. + # TODO(taiki-e): Ideally, this should be automated using a bot that creates + # PR when failed, but there is no bandwidth to implement it + # right now... + codegen: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: ci/no_atomic_cas.sh + - run: git diff --exit-code + san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} strategy: diff --git a/Cargo.toml b/Cargo.toml index f972a73d2c..d27a9f2885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "futures/tests/macro-tests", "futures/tests/macro-reexport", + "futures/tests/no-std", "examples/functional", "examples/imperative", diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh new file mode 100755 index 0000000000..24faa70487 --- /dev/null +++ b/ci/no_atomic_cas.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -euo pipefail +IFS=$'\n\t' + +cd "$(cd "$(dirname "$0")" && pwd)"/.. + +file="no_atomic_cas.rs" + +{ + echo "// This file is @generated by $(basename "$0")." + echo "// It is not intended for manual editing." + echo "" + echo "const NO_ATOMIC_CAS_TARGETS: &[&str] = &[" +} >"$file" + +for target in $(rustc --print target-list); do + res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ + | jq -r "select(.\"atomic-cas\" == false)") + [[ -z "$res" ]] || echo " \"$target\"," >>"$file" +done + +echo "];" >>"$file" diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index d7843e7d5e..6d32d56cd2 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -17,11 +17,10 @@ std = ["alloc", "futures-core/std"] alloc = ["futures-core/alloc"] sink = ["futures-sink"] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. -unstable = ["futures-core/unstable"] -cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic"] +# These features are no longer used. +# TODO: remove in the next major version. +unstable = [] +cfg-target-has-atomic = [] [dependencies] futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } diff --git a/futures-channel/build.rs b/futures-channel/build.rs new file mode 100644 index 0000000000..c4f341d480 --- /dev/null +++ b/futures-channel/build.rs @@ -0,0 +1,42 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does not have atomic CAS (compare-and-swap). +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg strings below are +// *not* public API. Please let us know by opening a GitHub issue if your build +// environment requires some way to enable these cfgs other than by executing +// our build script. +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS_TARGETS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-channel/no_atomic_cas.rs b/futures-channel/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-channel/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 41a4a19af7..9377a3e2c2 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -11,7 +11,6 @@ //! All items are only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. @@ -19,12 +18,9 @@ #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#[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"); - macro_rules! cfg_target_has_atomic { ($($item:item)*) => {$( - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] $item )*}; } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index e4c42f717e..bcc0b60f91 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -16,9 +16,8 @@ default = ["std"] std = ["alloc"] alloc = [] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. +# These features are no longer used. +# TODO: remove in the next major version. unstable = [] cfg-target-has-atomic = [] diff --git a/futures-core/build.rs b/futures-core/build.rs new file mode 100644 index 0000000000..c4f341d480 --- /dev/null +++ b/futures-core/build.rs @@ -0,0 +1,42 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does not have atomic CAS (compare-and-swap). +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg strings below are +// *not* public API. Please let us know by opening a GitHub issue if your build +// environment requires some way to enable these cfgs other than by executing +// our build script. +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS_TARGETS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-core/no_atomic_cas.rs b/futures-core/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-core/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index f3bd9ab928..e363ff777d 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,6 +1,5 @@ //! Core traits and types for asynchronous operations in Rust. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. @@ -8,9 +7,6 @@ #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#[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"); - #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures-core/src/task/__internal/mod.rs b/futures-core/src/task/__internal/mod.rs index 77e3678075..c902eb4bfb 100644 --- a/futures-core/src/task/__internal/mod.rs +++ b/futures-core/src/task/__internal/mod.rs @@ -1,4 +1,4 @@ -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] mod atomic_waker; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] pub use self::atomic_waker::AtomicWaker; diff --git a/futures-macro/build.rs b/futures-macro/build.rs index fec7b029eb..ff8630ce69 100644 --- a/futures-macro/build.rs +++ b/futures-macro/build.rs @@ -23,4 +23,6 @@ fn main() { if cfg.probe_rustc_version(1, 45) { println!("cargo:rustc-cfg=fn_like_proc_macro"); } + + println!("cargo:rerun-if-changed=build.rs"); } diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 68abccd6a9..721c1ee4c4 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -16,9 +16,8 @@ default = ["std"] std = ["alloc"] alloc = [] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. +# These features are no longer used. +# TODO: remove in the next major version. unstable = [] cfg-target-has-atomic = [] diff --git a/futures-task/build.rs b/futures-task/build.rs new file mode 100644 index 0000000000..c4f341d480 --- /dev/null +++ b/futures-task/build.rs @@ -0,0 +1,42 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does not have atomic CAS (compare-and-swap). +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg strings below are +// *not* public API. Please let us know by opening a GitHub issue if your build +// environment requires some way to enable these cfgs other than by executing +// our build script. +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS_TARGETS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-task/no_atomic_cas.rs b/futures-task/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-task/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 6693c53af0..439af135af 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,6 +1,5 @@ //! Tools for working with tasks. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. @@ -8,15 +7,12 @@ #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#[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"); - #[cfg(feature = "alloc")] extern crate alloc; macro_rules! cfg_target_has_atomic { ($($item:item)*) => {$( - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] $item )*}; } diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 4890c3f362..57e3f77884 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -27,11 +27,14 @@ channel = ["std", "futures-channel"] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. 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"] +# These features are no longer used. +# TODO: remove in the next major version. +cfg-target-has-atomic = [] + [build-dependencies] autocfg = "1" diff --git a/futures-util/build.rs b/futures-util/build.rs index fec7b029eb..ffe9711414 100644 --- a/futures-util/build.rs +++ b/futures-util/build.rs @@ -1,11 +1,44 @@ #![warn(rust_2018_idioms, single_use_lifetimes)] use autocfg::AutoCfg; +use std::env; -// The rustc-cfg strings below are *not* public API. Please let us know by -// opening a GitHub issue if your build environment requires some way to enable -// these cfgs other than by executing our build script. +include!("no_atomic_cas.rs"); + +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does not have atomic CAS (compare-and-swap). +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg strings below are +// *not* public API. Please let us know by opening a GitHub issue if your build +// environment requires some way to enable these cfgs other than by executing +// our build script. fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS_TARGETS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + let cfg = match AutoCfg::new() { Ok(cfg) => cfg, Err(e) => { @@ -23,4 +56,6 @@ fn main() { if cfg.probe_rustc_version(1, 45) { println!("cargo:rustc-cfg=fn_like_proc_macro"); } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); } diff --git a/futures-util/no_atomic_cas.rs b/futures-util/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-util/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 6a22cf144d..76d6ca7666 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -1,7 +1,6 @@ //! Combinators and utilities for working with `Future`s, `Stream`s, `Sink`s, //! and the `AsyncRead` and `AsyncWrite` traits. -#![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)] @@ -12,9 +11,6 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] #![cfg_attr(docsrs, feature(doc_cfg))] -#[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"); - #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); @@ -53,7 +49,7 @@ pub mod __private { macro_rules! cfg_target_has_atomic { ($($item:item)*) => {$( - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] $item )*}; } diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index f9c2e78fc8..ec6c68bbf3 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -36,11 +36,11 @@ pub use self::stream::ReadyChunks; #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::stream::Forward; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] pub use self::stream::{BufferUnordered, Buffered, ForEachConcurrent}; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[cfg(feature = "alloc")] @@ -58,7 +58,7 @@ pub use self::try_stream::{ #[cfg(feature = "std")] pub use self::try_stream::IntoAsyncRead; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] pub use self::try_stream::{TryBufferUnordered, TryBuffered, TryForEachConcurrent}; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index e1b139fbaf..9089e6e3df 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -919,7 +919,7 @@ pub trait StreamExt: Stream { /// fut.await; /// # }) /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn for_each_concurrent( self, @@ -1142,7 +1142,7 @@ pub trait StreamExt: Stream { /// /// This method is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn buffered(self, n: usize) -> Buffered where @@ -1187,7 +1187,7 @@ pub trait StreamExt: Stream { /// assert_eq!(buffered.next().await, None); /// # Ok::<(), i32>(()) }).unwrap(); /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn buffer_unordered(self, n: usize) -> BufferUnordered where @@ -1355,7 +1355,7 @@ pub trait StreamExt: Stream { /// library is activated, and it is activated by default. #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn split(self) -> (SplitSink, SplitStream) where diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 12f2de0d31..11cd9c0d31 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -516,7 +516,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(Err(oneshot::Canceled), fut.await); /// # }) /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn try_for_each_concurrent( self, @@ -837,7 +837,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn try_buffer_unordered(self, n: usize) -> TryBufferUnordered where @@ -913,7 +913,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn try_buffered(self, n: usize) -> TryBuffered where diff --git a/futures/Cargo.toml b/futures/Cargo.toml index a0a64bfece..51c710d6e6 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -47,11 +47,14 @@ thread-pool = ["executor", "futures-executor/thread-pool"] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. unstable = ["futures-core/unstable", "futures-task/unstable", "futures-channel/unstable", "futures-io/unstable", "futures-util/unstable"] -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"] +# These features are no longer used. +# TODO: remove in the next major version. +cfg-target-has-atomic = [] + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures/src/lib.rs b/futures/src/lib.rs index e40ad16b7f..d15c16c377 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -78,7 +78,6 @@ //! The majority of examples and code snippets in this crate assume that they are //! inside an async block as written above. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(feature = "read-initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] @@ -88,9 +87,6 @@ #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] #![cfg_attr(docsrs, feature(doc_cfg))] -#[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"); - #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml new file mode 100644 index 0000000000..9526732e34 --- /dev/null +++ b/futures/tests/no-std/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "no-std" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +publish = false + +[features] +futures-core-alloc = ["futures-core/alloc"] +futures-task-alloc = ["futures-task/alloc"] +futures-channel-alloc = ["futures-channel/alloc"] +futures-util-alloc = ["futures-util/alloc"] +futures-util-async-await = ["futures-util/async-await"] +futures-alloc = ["futures/alloc"] +futures-async-await = ["futures/async-await"] + +[dependencies] +futures-core = { path = "../../../futures-core", optional = true, default-features = false } +futures-task = { path = "../../../futures-task", optional = true, default-features = false } +futures-channel = { path = "../../../futures-channel", optional = true, default-features = false } +futures-util = { path = "../../../futures-util", optional = true, default-features = false } +futures = { path = "../..", optional = true, default-features = false } diff --git a/futures/tests/no-std/build.rs b/futures/tests/no-std/build.rs new file mode 100644 index 0000000000..a96a68274b --- /dev/null +++ b/futures/tests/no-std/build.rs @@ -0,0 +1,14 @@ +use std::{env, process::Command}; + +fn main() { + if is_nightly() { + println!("cargo:rustc-cfg=nightly"); + } +} + +fn is_nightly() -> bool { + env::var_os("RUSTC") + .and_then(|rustc| Command::new(rustc).arg("--version").output().ok()) + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map_or(false, |version| version.contains("nightly") || version.contains("dev")) +} diff --git a/futures/tests/no-std/src/lib.rs b/futures/tests/no-std/src/lib.rs new file mode 100644 index 0000000000..308218d6b7 --- /dev/null +++ b/futures/tests/no-std/src/lib.rs @@ -0,0 +1,31 @@ +#![cfg(nightly)] +#![no_std] +#![feature(cfg_target_has_atomic)] + +#[cfg(feature = "futures-core-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_core::task::__internal::AtomicWaker as _; + +#[cfg(feature = "futures-task-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_task::ArcWake as _; + +#[cfg(feature = "futures-channel-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_channel::oneshot as _; + +#[cfg(any(feature = "futures", feature = "futures-alloc"))] +#[cfg(target_has_atomic = "ptr")] +pub use futures::task::AtomicWaker as _; + +#[cfg(feature = "futures-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures::stream::FuturesOrdered as _; + +#[cfg(any(feature = "futures-util", feature = "futures-util-alloc"))] +#[cfg(target_has_atomic = "ptr")] +pub use futures_util::task::AtomicWaker as _; + +#[cfg(feature = "futures-util-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_util::stream::FuturesOrdered as _; diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs new file mode 100644 index 0000000000..0819af1a45 --- /dev/null +++ b/no_atomic_cas.rs @@ -0,0 +1,11 @@ +// This file is @generated by no_atomic_cas.sh. +// It is not intended for manual editing. + +const NO_ATOMIC_CAS_TARGETS: &[&str] = &[ + "avr-unknown-gnu-atmega328", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", + "thumbv4t-none-eabi", + "thumbv6m-none-eabi", +]; From b54f9a98ed8ad2597df3d99ffd12958365c79b7a Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 13:02:38 -0400 Subject: [PATCH 023/283] add either example --- futures-util/src/future/either.rs | 21 +++++++++++++++++++-- futures-util/src/sink/mod.rs | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 5f5b614763..35650daa99 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -5,8 +5,25 @@ use futures_core::stream::{FusedStream, Stream}; #[cfg(feature = "sink")] use futures_sink::Sink; -/// Combines two different futures, streams, or sinks having the same associated types into a single -/// type. +/// Combines two different futures, streams, or sinks having the same associated types into a single type. +/// +/// This is useful when conditionally choosing between two distinct future types: +/// +/// ```rust +/// use futures::future::Either; +/// +/// # futures::executor::block_on(async { +/// let cond = true; +/// +/// let fut = if cond { +/// Either::Left(async move { 12 }) +/// } else { +/// Either::Right(async move { 44 }) +/// }; +/// +/// assert_eq!(fut.await, 12); +/// # }) +/// ``` #[derive(Debug, Clone)] pub enum Either { /// First branch of the type diff --git a/futures-util/src/sink/mod.rs b/futures-util/src/sink/mod.rs index 78e9bedc54..147e9adc93 100644 --- a/futures-util/src/sink/mod.rs +++ b/futures-util/src/sink/mod.rs @@ -243,7 +243,7 @@ pub trait SinkExt: Sink { /// This future will drive the stream to keep producing items until it is /// exhausted, sending each item to the sink. It will complete once both the /// stream is exhausted, the sink has received all items, and the sink has - /// been flushed. Note that the sink is **not** closed. If the stream produces + /// been flushed. Note that the sink is **not** closed. If the stream produces /// an error, that error will be returned by this future without flushing the sink. /// /// Doing `sink.send_all(stream)` is roughly equivalent to From 890d52c1e5405030ff990d5aed9db08d8c24861b Mon Sep 17 00:00:00 2001 From: Geoffry Song Date: Thu, 6 May 2021 16:27:01 -0700 Subject: [PATCH 024/283] Override Debug for oneshot::{Sender,Receiver}. Prior to this diff, the Debug impl for oneshot channels printed something like: ``` Sender { inner: Inner { complete: false, data: Lock { locked: false, data: UnsafeCell }, rx_task: Lock { locked: false, data: UnsafeCell }, tx_task: Lock { locked: false, data: UnsafeCell } } } ``` which isn't very helpful. Instead, just print `Sender { complete: false }` (or `true`, if the other end has dropped). Note that the `T: Debug` bound is retained to allow for the possibility of allowing slightly more interesting debug output in the future. --- futures-channel/src/oneshot.rs | 15 ++++++++++++--- futures/tests/oneshot.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index dbbce8112b..1c393c5a85 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -16,7 +16,6 @@ use crate::lock::Lock; /// /// This is created by the [`channel`](channel) function. #[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] pub struct Receiver { inner: Arc>, } @@ -24,7 +23,6 @@ pub struct Receiver { /// A means of transmitting a single value to another task. /// /// This is created by the [`channel`](channel) function. -#[derive(Debug)] pub struct Sender { inner: Arc>, } @@ -35,7 +33,6 @@ impl Unpin for Sender {} /// Internal state of the `Receiver`/`Sender` pair above. This is all used as /// the internal synchronization between the two for send/recv operations. -#[derive(Debug)] struct Inner { /// Indicates whether this oneshot is complete yet. This is filled in both /// by `Sender::drop` and by `Receiver::drop`, and both sides interpret it @@ -394,6 +391,12 @@ impl Drop for Sender { } } +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").field("complete", &self.inner.complete).finish() + } +} + /// 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). @@ -481,3 +484,9 @@ impl Drop for Receiver { self.inner.drop_rx() } } + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Receiver").field("complete", &self.inner.complete).finish() + } +} diff --git a/futures/tests/oneshot.rs b/futures/tests/oneshot.rs index 58951ec581..34b78a33fb 100644 --- a/futures/tests/oneshot.rs +++ b/futures/tests/oneshot.rs @@ -64,3 +64,15 @@ fn oneshot_drop_rx() { drop(rx); assert_eq!(Err(2), tx.send(2)); } + +#[test] +fn oneshot_debug() { + let (tx, rx) = oneshot::channel::(); + assert_eq!(format!("{:?}", tx), "Sender { complete: false }"); + assert_eq!(format!("{:?}", rx), "Receiver { complete: false }"); + drop(rx); + assert_eq!(format!("{:?}", tx), "Sender { complete: true }"); + let (tx, rx) = oneshot::channel::(); + drop(tx); + assert_eq!(format!("{:?}", rx), "Receiver { complete: true }"); +} From 440e2f042af5a1b5024bb022654ae5b25249a5c0 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Fri, 7 May 2021 04:18:55 -0400 Subject: [PATCH 025/283] impl Send + Sync for FuturesUnordered iterators --- .../src/stream/futures_unordered/iter.rs | 23 +++++++++++-------- futures/tests/auto_traits.rs | 16 ++++++------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/iter.rs b/futures-util/src/stream/futures_unordered/iter.rs index ef7b15aed8..17cde4fac4 100644 --- a/futures-util/src/stream/futures_unordered/iter.rs +++ b/futures-util/src/stream/futures_unordered/iter.rs @@ -1,5 +1,5 @@ -use super::FuturesUnordered; use super::task::Task; +use super::FuturesUnordered; use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::Ordering::Relaxed; @@ -9,12 +9,12 @@ use core::sync::atomic::Ordering::Relaxed; pub struct IterPinMut<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, - pub(super) _marker: PhantomData<&'a mut FuturesUnordered> + pub(super) _marker: PhantomData<&'a mut FuturesUnordered>, } #[derive(Debug)] /// Mutable iterator over all futures in the unordered set. -pub struct IterMut<'a, Fut: Unpin> (pub(super) IterPinMut<'a, Fut>); +pub struct IterMut<'a, Fut: Unpin>(pub(super) IterPinMut<'a, Fut>); #[derive(Debug)] /// Immutable iterator over all futures in the unordered set. @@ -22,12 +22,12 @@ pub struct IterPinRef<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, pub(super) pending_next_all: *mut Task, - pub(super) _marker: PhantomData<&'a FuturesUnordered> + pub(super) _marker: PhantomData<&'a FuturesUnordered>, } #[derive(Debug)] /// Immutable iterator over all the futures in the unordered set. -pub struct Iter<'a, Fut: Unpin> (pub(super) IterPinRef<'a, Fut>); +pub struct Iter<'a, Fut: Unpin>(pub(super) IterPinRef<'a, Fut>); impl<'a, Fut> Iterator for IterPinMut<'a, Fut> { type Item = Pin<&'a mut Fut>; @@ -85,10 +85,7 @@ impl<'a, Fut> Iterator for IterPinRef<'a, Fut> { // `head_all` was initially read for this iterator implies acquire // ordering for all previously inserted nodes (and we don't need to // read `len_all` again for any other nodes). - let next = (*self.task).spin_next_all( - self.pending_next_all, - Relaxed, - ); + let next = (*self.task).spin_next_all(self.pending_next_all, Relaxed); self.task = next; self.len -= 1; Some(Pin::new_unchecked(future)) @@ -115,3 +112,11 @@ impl<'a, Fut: Unpin> Iterator for Iter<'a, Fut> { } impl ExactSizeIterator for Iter<'_, Fut> {} + +// SAFETY: we do nothing thread-local and there is no interior mutability, +// so the usual structural `Send`/`Sync` apply. +unsafe impl Send for IterPinRef<'_, Fut> {} +unsafe impl Sync for IterPinRef<'_, Fut> {} + +unsafe impl Send for IterPinMut<'_, Fut> {} +unsafe impl Sync for IterPinMut<'_, Fut> {} diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index 111fdf6388..7a8076a79b 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -1780,24 +1780,24 @@ pub mod stream { assert_not_impl!(Zip: Unpin); assert_not_impl!(Zip: Unpin); - assert_not_impl!(futures_unordered::Iter<()>: Send); - assert_not_impl!(futures_unordered::Iter<()>: Sync); + assert_impl!(futures_unordered::Iter<()>: Send); + assert_impl!(futures_unordered::Iter<()>: Sync); assert_impl!(futures_unordered::Iter<()>: Unpin); // futures_unordered::Iter requires `Fut: Unpin` // assert_not_impl!(futures_unordered::Iter: Unpin); - assert_not_impl!(futures_unordered::IterMut<()>: Send); - assert_not_impl!(futures_unordered::IterMut<()>: Sync); + assert_impl!(futures_unordered::IterMut<()>: Send); + assert_impl!(futures_unordered::IterMut<()>: Sync); assert_impl!(futures_unordered::IterMut<()>: Unpin); // futures_unordered::IterMut requires `Fut: Unpin` // assert_not_impl!(futures_unordered::IterMut: Unpin); - assert_not_impl!(futures_unordered::IterPinMut<()>: Send); - assert_not_impl!(futures_unordered::IterPinMut<()>: Sync); + assert_impl!(futures_unordered::IterPinMut<()>: Send); + assert_impl!(futures_unordered::IterPinMut<()>: Sync); assert_impl!(futures_unordered::IterPinMut: Unpin); - assert_not_impl!(futures_unordered::IterPinRef<()>: Send); - assert_not_impl!(futures_unordered::IterPinRef<()>: Sync); + assert_impl!(futures_unordered::IterPinRef<()>: Send); + assert_impl!(futures_unordered::IterPinRef<()>: Sync); assert_impl!(futures_unordered::IterPinRef: Unpin); } From d9a09e7765be905b17e9ebf342a24b23de08060f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 7 May 2021 22:37:15 +0900 Subject: [PATCH 026/283] Do not run clippy on 0.3 branch --- .github/workflows/ci.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28a8f3663b..08e63486e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,18 +228,20 @@ jobs: # `--cfg futures_sanitizer`. RUSTFLAGS: -D warnings -Z sanitizer=${{ matrix.sanitizer }} --cfg futures_sanitizer - clippy: - name: cargo clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Rust and Clippy - run: | - toolchain=nightly-$(curl -sSf https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy) - rustup set profile minimal - rustup default "$toolchain" - rustup component add clippy - - run: cargo clippy --workspace --all-features --all-targets + # This branch no longer actively developed. Most commits to this + # branch are backporting and should not be blocked by clippy. + # clippy: + # name: cargo clippy + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - name: Install Rust and Clippy + # run: | + # toolchain=nightly-$(curl -sSf https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy) + # rustup set profile minimal + # rustup default "$toolchain" + # rustup component add clippy + # - run: cargo clippy --workspace --all-features --all-targets docs: name: cargo doc From 8e074e285c2a0ec797d8ffb00b17b87bd2f807c5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 7 May 2021 22:55:49 +0900 Subject: [PATCH 027/283] Add missing assertions to auto_traits test --- futures/tests/auto_traits.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index 7a8076a79b..e934595c2a 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -1383,6 +1383,26 @@ pub mod stream { assert_impl!(Next<'_, ()>: Unpin); assert_not_impl!(Next<'_, PhantomPinned>: Unpin); + assert_impl!(NextIf<'_, SendStream<()>, ()>: Send); + assert_not_impl!(NextIf<'_, SendStream<()>, *const ()>: Send); + assert_not_impl!(NextIf<'_, SendStream, ()>: Send); + assert_not_impl!(NextIf<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIf<'_, SyncStream<()>, ()>: Sync); + assert_not_impl!(NextIf<'_, SyncStream<()>, *const ()>: Sync); + assert_not_impl!(NextIf<'_, SyncStream, ()>: Sync); + assert_not_impl!(NextIf<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIf<'_, PinnedStream, PhantomPinned>: Unpin); + + assert_impl!(NextIfEq<'_, SendStream<()>, ()>: Send); + assert_not_impl!(NextIfEq<'_, SendStream<()>, *const ()>: Send); + assert_not_impl!(NextIfEq<'_, SendStream, ()>: Send); + assert_not_impl!(NextIfEq<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIfEq<'_, SyncStream<()>, ()>: Sync); + assert_not_impl!(NextIfEq<'_, SyncStream<()>, *const ()>: Sync); + assert_not_impl!(NextIfEq<'_, SyncStream, ()>: Sync); + assert_not_impl!(NextIfEq<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIfEq<'_, PinnedStream, PhantomPinned>: Unpin); + assert_impl!(Once<()>: Send); assert_not_impl!(Once<*const ()>: Send); assert_impl!(Once<()>: Sync); @@ -1781,23 +1801,31 @@ pub mod stream { assert_not_impl!(Zip: Unpin); assert_impl!(futures_unordered::Iter<()>: Send); + assert_not_impl!(futures_unordered::Iter<*const ()>: Send); assert_impl!(futures_unordered::Iter<()>: Sync); + assert_not_impl!(futures_unordered::Iter<*const ()>: Sync); assert_impl!(futures_unordered::Iter<()>: Unpin); - // futures_unordered::Iter requires `Fut: Unpin` + // The definition of futures_unordered::Iter has `Fut: Unpin` bounds. // assert_not_impl!(futures_unordered::Iter: Unpin); assert_impl!(futures_unordered::IterMut<()>: Send); + assert_not_impl!(futures_unordered::IterMut<*const ()>: Send); assert_impl!(futures_unordered::IterMut<()>: Sync); + assert_not_impl!(futures_unordered::IterMut<*const ()>: Sync); assert_impl!(futures_unordered::IterMut<()>: Unpin); - // futures_unordered::IterMut requires `Fut: Unpin` + // The definition of futures_unordered::IterMut has `Fut: Unpin` bounds. // assert_not_impl!(futures_unordered::IterMut: Unpin); assert_impl!(futures_unordered::IterPinMut<()>: Send); + assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Send); assert_impl!(futures_unordered::IterPinMut<()>: Sync); + assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Sync); assert_impl!(futures_unordered::IterPinMut: Unpin); assert_impl!(futures_unordered::IterPinRef<()>: Send); + assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Send); assert_impl!(futures_unordered::IterPinRef<()>: Sync); + assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Sync); assert_impl!(futures_unordered::IterPinRef: Unpin); } From 692fc79f17365f79b91fa517244d32375fc9a652 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 7 May 2021 23:14:23 +0900 Subject: [PATCH 028/283] Add rustfmt check to CI --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08e63486e9..b71d9d0cc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -243,6 +243,14 @@ jobs: # rustup component add clippy # - run: cargo clippy --workspace --all-features --all-targets + rustfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable && rustup default stable + - run: cargo fmt --all -- --check + docs: name: cargo doc runs-on: ubuntu-latest From 97795727be2e275af18f96b734e833e24ca6e9b7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 7 May 2021 23:49:46 +0900 Subject: [PATCH 029/283] Format modules defined inside macros `cargo fmt` cannot recognize modules defined inside macros. --- .github/workflows/ci.yml | 4 +- .rustfmt.toml | 1 + futures-channel/src/lock.rs | 7 +- futures-channel/src/mpsc/mod.rs | 171 +++++--------- futures-channel/src/mpsc/queue.rs | 22 +- futures-channel/src/mpsc/sink_impl.rs | 58 +---- futures-channel/src/oneshot.rs | 24 +- futures-task/src/waker.rs | 8 +- futures-task/src/waker_ref.rs | 23 +- futures-util/benches_disabled/bilock.rs | 208 +++++++++--------- futures-util/src/future/abortable.rs | 52 ++--- futures-util/src/lock/bilock.rs | 29 +-- futures-util/src/lock/mutex.rs | 31 +-- futures-util/src/stream/futures_ordered.rs | 10 +- .../src/stream/futures_unordered/mod.rs | 81 +++---- .../futures_unordered/ready_to_run_queue.rs | 4 +- .../src/stream/futures_unordered/task.rs | 8 +- futures-util/src/stream/select_all.rs | 14 +- .../src/stream/stream/buffer_unordered.rs | 11 +- futures-util/src/stream/stream/buffered.rs | 17 +- .../src/stream/stream/for_each_concurrent.rs | 29 +-- futures-util/src/stream/stream/split.rs | 31 +-- .../stream/try_stream/try_buffer_unordered.rs | 24 +- .../src/stream/try_stream/try_buffered.rs | 9 +- .../try_stream/try_for_each_concurrent.rs | 29 +-- futures/tests_disabled/all.rs | 149 ++++++++----- futures/tests_disabled/bilock.rs | 21 +- futures/tests_disabled/stream.rs | 112 ++++------ tools/fmt.sh | 26 +++ 29 files changed, 545 insertions(+), 668 deletions(-) create mode 100755 tools/fmt.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b71d9d0cc7..2c516e3138 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -243,13 +243,13 @@ jobs: # rustup component add clippy # - run: cargo clippy --workspace --all-features --all-targets - rustfmt: + fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update stable && rustup default stable - - run: cargo fmt --all -- --check + - run: tools/fmt.sh docs: name: cargo doc diff --git a/.rustfmt.toml b/.rustfmt.toml index 2a35f0230c..2a79d9274a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1 +1,2 @@ use_small_heuristics = "Max" +edition = "2018" diff --git a/futures-channel/src/lock.rs b/futures-channel/src/lock.rs index 5eecdd9aa2..b328d0f7dd 100644 --- a/futures-channel/src/lock.rs +++ b/futures-channel/src/lock.rs @@ -6,8 +6,8 @@ use core::cell::UnsafeCell; use core::ops::{Deref, DerefMut}; -use core::sync::atomic::Ordering::SeqCst; use core::sync::atomic::AtomicBool; +use core::sync::atomic::Ordering::SeqCst; /// A "mutex" around a value, similar to `std::sync::Mutex`. /// @@ -37,10 +37,7 @@ unsafe impl Sync for Lock {} impl Lock { /// Creates a new lock around the given value. pub(crate) fn new(t: T) -> Self { - Self { - locked: AtomicBool::new(false), - data: UnsafeCell::new(t), - } + Self { locked: AtomicBool::new(false), data: UnsafeCell::new(t) } } /// Attempts to acquire this lock, returning whether the lock was acquired or diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index c32ad4b683..28612da84d 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -79,13 +79,13 @@ // by the queue structure. use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll, Waker}; use futures_core::task::__internal::AtomicWaker; +use futures_core::task::{Context, Poll, Waker}; use std::fmt; use std::pin::Pin; -use std::sync::{Arc, Mutex}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; +use std::sync::{Arc, Mutex}; use std::thread; use crate::mpsc::queue::Queue; @@ -209,9 +209,7 @@ impl SendError { impl fmt::Debug for TrySendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TrySendError") - .field("kind", &self.err.kind) - .finish() + f.debug_struct("TrySendError").field("kind", &self.err.kind).finish() } } @@ -251,8 +249,7 @@ impl TrySendError { impl fmt::Debug for TryRecvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TryRecvError") - .finish() + f.debug_tuple("TryRecvError").finish() } } @@ -335,10 +332,7 @@ struct SenderTask { impl SenderTask { fn new() -> Self { - Self { - task: None, - is_parked: false, - } + Self { task: None, is_parked: false } } fn notify(&mut self) { @@ -381,9 +375,7 @@ pub fn channel(buffer: usize) -> (Sender, Receiver) { maybe_parked: false, }; - let rx = Receiver { - inner: Some(inner), - }; + let rx = Receiver { inner: Some(inner) }; (Sender(Some(tx)), rx) } @@ -399,7 +391,6 @@ pub fn channel(buffer: usize) -> (Sender, Receiver) { /// the channel. Using an `unbounded` channel has the ability of causing the /// process to run out of memory. In this case, the process will be aborted. pub fn unbounded() -> (UnboundedSender, UnboundedReceiver) { - let inner = Arc::new(UnboundedInner { state: AtomicUsize::new(INIT_STATE), message_queue: Queue::new(), @@ -407,13 +398,9 @@ pub fn unbounded() -> (UnboundedSender, UnboundedReceiver) { recv_task: AtomicWaker::new(), }); - let tx = UnboundedSenderInner { - inner: inner.clone(), - }; + let tx = UnboundedSenderInner { inner: inner.clone() }; - let rx = UnboundedReceiver { - inner: Some(inner), - }; + let rx = UnboundedReceiver { inner: Some(inner) }; (UnboundedSender(Some(tx)), rx) } @@ -430,13 +417,10 @@ impl UnboundedSenderInner { if state.is_open { Poll::Ready(Ok(())) } else { - Poll::Ready(Err(SendError { - kind: SendErrorKind::Disconnected, - })) + Poll::Ready(Err(SendError { kind: SendErrorKind::Disconnected })) } } - // Push message to the queue and signal to the receiver fn queue_push_and_signal(&self, msg: T) { // Push the message onto the message queue @@ -462,16 +446,17 @@ impl UnboundedSenderInner { // This probably is never hit? Odds are the process will run out of // memory first. It may be worth to return something else in this // case? - assert!(state.num_messages < MAX_CAPACITY, "buffer space \ - exhausted; sending this messages would overflow the state"); + assert!( + state.num_messages < MAX_CAPACITY, + "buffer space \ + exhausted; sending this messages would overflow the state" + ); state.num_messages += 1; let next = encode_state(&state); match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) { - Ok(_) => { - return Some(state.num_messages) - } + Ok(_) => return Some(state.num_messages), Err(actual) => curr = actual, } } @@ -516,12 +501,7 @@ impl BoundedSenderInner { fn try_send(&mut self, msg: T) -> Result<(), TrySendError> { // If the sender is currently blocked, reject the message if !self.poll_unparked(None).is_ready() { - return Err(TrySendError { - err: SendError { - kind: SendErrorKind::Full, - }, - val: msg, - }); + return Err(TrySendError { err: SendError { kind: SendErrorKind::Full }, val: msg }); } // The channel has capacity to accept the message, so send it @@ -531,9 +511,7 @@ impl BoundedSenderInner { // Do the send without failing. // Can be called only by bounded sender. #[allow(clippy::debug_assert_with_mut_call)] - fn do_send_b(&mut self, msg: T) - -> Result<(), TrySendError> - { + fn do_send_b(&mut self, msg: T) -> Result<(), TrySendError> { // Anyone callig do_send *should* make sure there is room first, // but assert here for tests as a sanity check. debug_assert!(self.poll_unparked(None).is_ready()); @@ -551,12 +529,12 @@ impl BoundedSenderInner { // the configured buffer size num_messages > self.inner.buffer } - None => return Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }), + None => { + return Err(TrySendError { + err: SendError { kind: SendErrorKind::Disconnected }, + val: msg, + }) + } }; // If the channel has reached capacity, then the sender task needs to @@ -600,16 +578,17 @@ impl BoundedSenderInner { // This probably is never hit? Odds are the process will run out of // memory first. It may be worth to return something else in this // case? - assert!(state.num_messages < MAX_CAPACITY, "buffer space \ - exhausted; sending this messages would overflow the state"); + assert!( + state.num_messages < MAX_CAPACITY, + "buffer space \ + exhausted; sending this messages would overflow the state" + ); state.num_messages += 1; let next = encode_state(&state); match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) { - Ok(_) => { - return Some(state.num_messages) - } + Ok(_) => return Some(state.num_messages), Err(actual) => curr = actual, } } @@ -644,15 +623,10 @@ impl BoundedSenderInner { /// capacity, in which case the current task is queued to be notified once /// capacity is available; /// - `Poll::Ready(Err(SendError))` if the receiver has been dropped. - fn poll_ready( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let state = decode_state(self.inner.state.load(SeqCst)); if !state.is_open { - return Poll::Ready(Err(SendError { - kind: SendErrorKind::Disconnected, - })); + return Poll::Ready(Err(SendError { kind: SendErrorKind::Disconnected })); } self.poll_unparked(Some(cx)).map(Ok) @@ -699,7 +673,7 @@ impl BoundedSenderInner { if !task.is_parked { self.maybe_parked = false; - return Poll::Ready(()) + return Poll::Ready(()); } // At this point, an unpark request is pending, so there will be an @@ -724,12 +698,7 @@ impl Sender { if let Some(inner) = &mut self.0 { inner.try_send(msg) } else { - Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }) + Err(TrySendError { err: SendError { kind: SendErrorKind::Disconnected }, val: msg }) } } @@ -739,8 +708,7 @@ impl Sender { /// [`poll_ready`](Sender::poll_ready) has reported that the channel is /// ready to receive a message. pub fn start_send(&mut self, msg: T) -> Result<(), SendError> { - self.try_send(msg) - .map_err(|e| e.err) + self.try_send(msg).map_err(|e| e.err) } /// Polls the channel to determine if there is guaranteed capacity to send @@ -755,13 +723,8 @@ impl Sender { /// capacity, in which case the current task is queued to be notified once /// capacity is available; /// - `Poll::Ready(Err(SendError))` if the receiver has been dropped. - pub fn poll_ready( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { - let inner = self.0.as_mut().ok_or(SendError { - kind: SendErrorKind::Disconnected, - })?; + pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let inner = self.0.as_mut().ok_or(SendError { kind: SendErrorKind::Disconnected })?; inner.poll_ready(cx) } @@ -799,7 +762,10 @@ impl Sender { } /// Hashes the receiver into the provided hasher - pub fn hash_receiver(&self, hasher: &mut H) where H: std::hash::Hasher { + pub fn hash_receiver(&self, hasher: &mut H) + where + H: std::hash::Hasher, + { use std::hash::Hash; let ptr = self.0.as_ref().map(|inner| inner.ptr()); @@ -809,13 +775,8 @@ impl Sender { impl UnboundedSender { /// Check if the channel is ready to receive a message. - pub fn poll_ready( - &self, - _: &mut Context<'_>, - ) -> Poll> { - let inner = self.0.as_ref().ok_or(SendError { - kind: SendErrorKind::Disconnected, - })?; + pub fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { + let inner = self.0.as_ref().ok_or(SendError { kind: SendErrorKind::Disconnected })?; inner.poll_ready_nb() } @@ -845,12 +806,7 @@ impl UnboundedSender { } } - Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }) + Err(TrySendError { err: SendError { kind: SendErrorKind::Disconnected }, val: msg }) } /// Send a message on the channel. @@ -858,8 +814,7 @@ impl UnboundedSender { /// This method should only be called after `poll_ready` has been used to /// verify that the channel is ready to receive a message. pub fn start_send(&mut self, msg: T) -> Result<(), SendError> { - self.do_send_nb(msg) - .map_err(|e| e.err) + self.do_send_nb(msg).map_err(|e| e.err) } /// Sends a message along this channel. @@ -888,7 +843,10 @@ impl UnboundedSender { } /// Hashes the receiver into the provided hasher - pub fn hash_receiver(&self, hasher: &mut H) where H: std::hash::Hasher { + pub fn hash_receiver(&self, hasher: &mut H) + where + H: std::hash::Hasher, + { use std::hash::Hash; let ptr = self.0.as_ref().map(|inner| inner.ptr()); @@ -928,9 +886,7 @@ impl Clone for UnboundedSenderInner { Ok(_) => { // The ABA problem doesn't matter here. We only care that the // number of senders never exceeds the maximum. - return Self { - inner: self.inner.clone(), - }; + return Self { inner: self.inner.clone() }; } Err(actual) => curr = actual, } @@ -1027,9 +983,7 @@ impl Receiver { /// * `Err(e)` when there are no messages available, but channel is not yet closed pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { - Poll::Ready(msg) => { - Ok(msg) - }, + Poll::Ready(msg) => Ok(msg), Poll::Pending => Err(TryRecvError { _priv: () }), } } @@ -1103,18 +1057,15 @@ impl FusedStream for Receiver { impl Stream for Receiver { type Item = T; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - // Try to read a message off of the message queue. + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Try to read a message off of the message queue. match self.next_message() { Poll::Ready(msg) => { if msg.is_none() { self.inner = None; } Poll::Ready(msg) - }, + } Poll::Pending => { // There are no messages to read, in this case, park. self.inner.as_ref().unwrap().recv_task.register(cx.waker()); @@ -1180,9 +1131,7 @@ impl UnboundedReceiver { /// * `Err(e)` when there are no messages available, but channel is not yet closed pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { - Poll::Ready(msg) => { - Ok(msg) - }, + Poll::Ready(msg) => Ok(msg), Poll::Pending => Err(TryRecvError { _priv: () }), } } @@ -1240,10 +1189,7 @@ impl FusedStream for UnboundedReceiver { impl Stream for UnboundedReceiver { type Item = T; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Try to read a message off of the message queue. match self.next_message() { Poll::Ready(msg) => { @@ -1251,7 +1197,7 @@ impl Stream for UnboundedReceiver { self.inner = None; } Poll::Ready(msg) - }, + } Poll::Pending => { // There are no messages to read, in this case, park. self.inner.as_ref().unwrap().recv_task.register(cx.waker()); @@ -1349,10 +1295,7 @@ impl State { */ fn decode_state(num: usize) -> State { - State { - is_open: num & OPEN_MASK == OPEN_MASK, - num_messages: num & MAX_CAPACITY, - } + State { is_open: num & OPEN_MASK == OPEN_MASK, num_messages: num & MAX_CAPACITY } } fn encode_state(state: &State) -> usize { diff --git a/futures-channel/src/mpsc/queue.rs b/futures-channel/src/mpsc/queue.rs index b00e1b1755..57dc7f5654 100644 --- a/futures-channel/src/mpsc/queue.rs +++ b/futures-channel/src/mpsc/queue.rs @@ -43,10 +43,10 @@ pub(super) use self::PopResult::*; -use std::thread; use std::cell::UnsafeCell; use std::ptr; use std::sync::atomic::{AtomicPtr, Ordering}; +use std::thread; /// A result of the `pop` function. pub(super) enum PopResult { @@ -76,15 +76,12 @@ pub(super) struct Queue { tail: UnsafeCell<*mut Node>, } -unsafe impl Send for Queue { } -unsafe impl Sync for Queue { } +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} impl Node { unsafe fn new(v: Option) -> *mut Self { - Box::into_raw(Box::new(Self { - next: AtomicPtr::new(ptr::null_mut()), - value: v, - })) + Box::into_raw(Box::new(Self { next: AtomicPtr::new(ptr::null_mut()), value: v })) } } @@ -93,10 +90,7 @@ impl Queue { /// one consumer. pub(super) fn new() -> Self { let stub = unsafe { Node::new(None) }; - Self { - head: AtomicPtr::new(stub), - tail: UnsafeCell::new(stub), - } + Self { head: AtomicPtr::new(stub), tail: UnsafeCell::new(stub) } } /// Pushes a new value onto this queue. @@ -133,7 +127,11 @@ impl Queue { return Data(ret); } - if self.head.load(Ordering::Acquire) == tail {Empty} else {Inconsistent} + if self.head.load(Ordering::Acquire) == tail { + Empty + } else { + Inconsistent + } } /// Pop an element similarly to `pop` function, but spin-wait on inconsistent diff --git a/futures-channel/src/mpsc/sink_impl.rs b/futures-channel/src/mpsc/sink_impl.rs index 4ce66b4e59..1be20162c2 100644 --- a/futures-channel/src/mpsc/sink_impl.rs +++ b/futures-channel/src/mpsc/sink_impl.rs @@ -6,24 +6,15 @@ use std::pin::Pin; impl Sink for Sender { type Error = SendError; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { (*self).poll_ready(cx) } - fn start_send( - mut self: Pin<&mut Self>, - msg: T, - ) -> Result<(), Self::Error> { + fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { (*self).start_send(msg) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match (*self).poll_ready(cx) { Poll::Ready(Err(ref e)) if e.is_disconnected() => { // If the receiver disconnected, we consider the sink to be flushed. @@ -33,10 +24,7 @@ impl Sink for Sender { } } - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.disconnect(); Poll::Ready(Ok(())) } @@ -45,31 +33,19 @@ impl Sink for Sender { impl Sink for UnboundedSender { type Error = SendError; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Self::poll_ready(&*self, cx) } - fn start_send( - mut self: Pin<&mut Self>, - msg: T, - ) -> Result<(), Self::Error> { + fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { Self::start_send(&mut *self, msg) } - fn poll_flush( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.disconnect(); Poll::Ready(Ok(())) } @@ -78,29 +54,19 @@ impl Sink for UnboundedSender { impl Sink for &UnboundedSender { type Error = SendError; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { UnboundedSender::poll_ready(*self, cx) } fn start_send(self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { - self.unbounded_send(msg) - .map_err(TrySendError::into_send_error) + self.unbounded_send(msg).map_err(TrySendError::into_send_error) } - fn poll_flush( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn poll_close( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.close_channel(); Poll::Ready(Ok(())) } diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index 1c393c5a85..5af651b913 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -7,7 +7,7 @@ use core::fmt; use core::pin::Pin; use core::sync::atomic::AtomicBool; use core::sync::atomic::Ordering::SeqCst; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use crate::lock::Lock; @@ -103,12 +103,8 @@ struct Inner { /// ``` pub fn channel() -> (Sender, Receiver) { let inner = Arc::new(Inner::new()); - let receiver = Receiver { - inner: inner.clone(), - }; - let sender = Sender { - inner, - }; + let receiver = Receiver { inner: inner.clone() }; + let sender = Sender { inner }; (sender, receiver) } @@ -124,7 +120,7 @@ impl Inner { fn send(&self, t: T) -> Result<(), T> { if self.complete.load(SeqCst) { - return Err(t) + return Err(t); } // Note that this lock acquisition may fail if the receiver @@ -161,7 +157,7 @@ impl Inner { // destructor, but our destructor hasn't run yet so if it's set then the // oneshot is gone. if self.complete.load(SeqCst) { - return Poll::Ready(()) + return Poll::Ready(()); } // If our other half is not gone then we need to park our current task @@ -270,7 +266,10 @@ impl Inner { } else { let task = cx.waker().clone(); match self.rx_task.try_lock() { - Some(mut slot) => { *slot = Some(task); false }, + Some(mut slot) => { + *slot = Some(task); + false + } None => true, } }; @@ -456,10 +455,7 @@ impl Receiver { impl Future for Receiver { type Output = Result; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.inner.recv(cx) } } diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs index 265a445d91..a7310a07af 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -1,7 +1,7 @@ use super::arc_wake::ArcWake; -use core::mem; -use core::task::{Waker, RawWaker, RawWakerVTable}; use alloc::sync::Arc; +use core::mem; +use core::task::{RawWaker, RawWakerVTable, Waker}; pub(super) fn waker_vtable() -> &'static RawWakerVTable { &RawWakerVTable::new( @@ -22,9 +22,7 @@ where { let ptr = Arc::into_raw(wake) as *const (); - unsafe { - Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) - } + unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) } } // FIXME: panics on Arc::clone / refcount changes could wreak havoc on the diff --git a/futures-task/src/waker_ref.rs b/futures-task/src/waker_ref.rs index 76d849ac1b..791c690120 100644 --- a/futures-task/src/waker_ref.rs +++ b/futures-task/src/waker_ref.rs @@ -1,10 +1,10 @@ -use super::arc_wake::{ArcWake}; +use super::arc_wake::ArcWake; use super::waker::waker_vtable; use alloc::sync::Arc; -use core::mem::ManuallyDrop; use core::marker::PhantomData; +use core::mem::ManuallyDrop; use core::ops::Deref; -use core::task::{Waker, RawWaker}; +use core::task::{RawWaker, Waker}; /// A [`Waker`] that is only valid for a given lifetime. /// @@ -22,10 +22,7 @@ impl<'a> WakerRef<'a> { // copy the underlying (raw) waker without calling a clone, // as we won't call Waker::drop either. let waker = ManuallyDrop::new(unsafe { core::ptr::read(waker) }); - Self { - waker, - _marker: PhantomData, - } + Self { waker, _marker: PhantomData } } /// Create a new [`WakerRef`] from a [`Waker`] that must not be dropped. @@ -35,10 +32,7 @@ impl<'a> WakerRef<'a> { /// by the caller), and the [`Waker`] doesn't need to or must not be /// destroyed. pub fn new_unowned(waker: ManuallyDrop) -> Self { - Self { - waker, - _marker: PhantomData, - } + Self { waker, _marker: PhantomData } } } @@ -57,14 +51,13 @@ impl Deref for WakerRef<'_> { #[inline] pub fn waker_ref(wake: &Arc) -> WakerRef<'_> where - W: ArcWake + W: ArcWake, { // simply copy the pointer instead of using Arc::into_raw, // as we don't actually keep a refcount by using ManuallyDrop.< let ptr = (&**wake as *const W) as *const (); - let waker = ManuallyDrop::new(unsafe { - Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) - }); + let waker = + ManuallyDrop::new(unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) }); WakerRef::new_unowned(waker) } diff --git a/futures-util/benches_disabled/bilock.rs b/futures-util/benches_disabled/bilock.rs index 48afe3c551..417f75d31e 100644 --- a/futures-util/benches_disabled/bilock.rs +++ b/futures-util/benches_disabled/bilock.rs @@ -2,125 +2,121 @@ #[cfg(feature = "bilock")] mod bench { -use futures::task::{Context, Waker}; -use futures::executor::LocalPool; -use futures_util::lock::BiLock; -use futures_util::lock::BiLockAcquire; -use futures_util::lock::BiLockAcquired; -use futures_util::task::ArcWake; + use futures::executor::LocalPool; + use futures::task::{Context, Waker}; + use futures_util::lock::BiLock; + use futures_util::lock::BiLockAcquire; + use futures_util::lock::BiLockAcquired; + use futures_util::task::ArcWake; -use std::sync::Arc; -use test::Bencher; + use std::sync::Arc; + use test::Bencher; -fn notify_noop() -> Waker { - struct Noop; + fn notify_noop() -> Waker { + struct Noop; - impl ArcWake for Noop { - fn wake(_: &Arc) {} - } - - ArcWake::into_waker(Arc::new(Noop)) -} - - -/// Pseudo-stream which simply calls `lock.poll()` on `poll` -struct LockStream { - lock: BiLockAcquire, -} - -impl LockStream { - fn new(lock: BiLock) -> Self { - Self { - lock: lock.lock() + impl ArcWake for Noop { + fn wake(_: &Arc) {} } - } - /// Release a lock after it was acquired in `poll`, - /// so `poll` could be called again. - fn release_lock(&mut self, guard: BiLockAcquired) { - self.lock = guard.unlock().lock() + ArcWake::into_waker(Arc::new(Noop)) } -} - -impl Stream for LockStream { - type Item = BiLockAcquired; - type Error = (); - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll, Self::Error> { - self.lock.poll(cx).map(|a| a.map(Some)) + /// Pseudo-stream which simply calls `lock.poll()` on `poll` + struct LockStream { + lock: BiLockAcquire, } -} - - -#[bench] -fn contended(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - // Try poll second lock while first lock still holds the lock - match y.poll_next(&mut waker) { - Ok(Poll::Pending) => (), - _ => panic!(), - }; - - x.release_lock(x_guard); - - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - y.release_lock(y_guard); + impl LockStream { + fn new(lock: BiLock) -> Self { + Self { lock: lock.lock() } } - (x, y) - }); -} - -#[bench] -fn lock_unlock(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); + /// Release a lock after it was acquired in `poll`, + /// so `poll` could be called again. + fn release_lock(&mut self, guard: BiLockAcquired) { + self.lock = guard.unlock().lock() + } + } - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; + impl Stream for LockStream { + type Item = BiLockAcquired; + type Error = (); - x.release_lock(x_guard); + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll, Self::Error> { + self.lock.poll(cx).map(|a| a.map(Some)) + } + } - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; + #[bench] + fn contended(b: &mut Bencher) { + let pool = LocalPool::new(); + let mut exec = pool.executor(); + let waker = notify_noop(); + let mut map = task::LocalMap::new(); + let mut waker = task::Context::new(&mut map, &waker, &mut exec); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + let mut x = LockStream::new(x); + let mut y = LockStream::new(y); + + for _ in 0..1000 { + let x_guard = match x.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + // Try poll second lock while first lock still holds the lock + match y.poll_next(&mut waker) { + Ok(Poll::Pending) => (), + _ => panic!(), + }; + + x.release_lock(x_guard); + + let y_guard = match y.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + y.release_lock(y_guard); + } + (x, y) + }); + } - y.release_lock(y_guard); - } - (x, y) - }) -} + #[bench] + fn lock_unlock(b: &mut Bencher) { + let pool = LocalPool::new(); + let mut exec = pool.executor(); + let waker = notify_noop(); + let mut map = task::LocalMap::new(); + let mut waker = task::Context::new(&mut map, &waker, &mut exec); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + let mut x = LockStream::new(x); + let mut y = LockStream::new(y); + + for _ in 0..1000 { + let x_guard = match x.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + x.release_lock(x_guard); + + let y_guard = match y.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + y.release_lock(y_guard); + } + (x, y) + }) + } } diff --git a/futures-util/src/future/abortable.rs b/futures-util/src/future/abortable.rs index 3f2e5a064d..198cc8e668 100644 --- a/futures-util/src/future/abortable.rs +++ b/futures-util/src/future/abortable.rs @@ -1,11 +1,11 @@ use super::assert_future; use crate::task::AtomicWaker; -use futures_core::future::Future; -use futures_core::task::{Context, Poll}; +use alloc::sync::Arc; use core::fmt; use core::pin::Pin; use core::sync::atomic::{AtomicBool, Ordering}; -use alloc::sync::Arc; +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; pin_project! { @@ -19,7 +19,10 @@ pin_project! { } } -impl Abortable where Fut: Future { +impl Abortable +where + Fut: Future, +{ /// Creates a new `Abortable` future using an existing `AbortRegistration`. /// `AbortRegistration`s can be acquired through `AbortHandle::new`. /// @@ -40,10 +43,7 @@ impl Abortable where Fut: Future { /// # }); /// ``` pub fn new(future: Fut, reg: AbortRegistration) -> Self { - assert_future::, _>(Self { - future, - inner: reg.inner, - }) + assert_future::, _>(Self { future, inner: reg.inner }) } } @@ -80,19 +80,10 @@ impl AbortHandle { /// # }); /// ``` pub fn new_pair() -> (Self, AbortRegistration) { - let inner = Arc::new(AbortInner { - waker: AtomicWaker::new(), - cancel: AtomicBool::new(false), - }); - - ( - Self { - inner: inner.clone(), - }, - AbortRegistration { - inner, - }, - ) + let inner = + Arc::new(AbortInner { waker: AtomicWaker::new(), cancel: AtomicBool::new(false) }); + + (Self { inner: inner.clone() }, AbortRegistration { inner }) } } @@ -112,13 +103,11 @@ struct AbortInner { /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. pub fn abortable(future: Fut) -> (Abortable, AbortHandle) - where Fut: Future +where + Fut: Future, { let (handle, reg) = AbortHandle::new_pair(); - ( - Abortable::new(future, reg), - handle, - ) + (Abortable::new(future, reg), handle) } /// Indicator that the `Abortable` future was aborted. @@ -134,18 +123,21 @@ impl fmt::Display for Aborted { #[cfg(feature = "std")] impl std::error::Error for Aborted {} -impl Future for Abortable where Fut: Future { +impl Future for Abortable +where + Fut: Future, +{ type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // Check if the future has been aborted if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)) + return Poll::Ready(Err(Aborted)); } // attempt to complete the future if let Poll::Ready(x) = self.as_mut().project().future.poll(cx) { - return Poll::Ready(Ok(x)) + return Poll::Ready(Ok(x)); } // Register to receive a wakeup if the future is aborted in the... future @@ -156,7 +148,7 @@ impl Future for Abortable where Fut: Future { // Checking with `Relaxed` is sufficient because `register` introduces an // `AcqRel` barrier. if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)) + return Poll::Ready(Err(Aborted)); } Poll::Pending diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 600e16e421..2f51ae7c98 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -1,16 +1,16 @@ //! Futures-powered synchronization primitives. -#[cfg(feature = "bilock")] -use futures_core::future::Future; -use futures_core::task::{Context, Poll, Waker}; +use alloc::boxed::Box; +use alloc::sync::Arc; use core::cell::UnsafeCell; use core::fmt; use core::ops::{Deref, DerefMut}; use core::pin::Pin; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering::SeqCst; -use alloc::boxed::Box; -use alloc::sync::Arc; +#[cfg(feature = "bilock")] +use futures_core::future::Future; +use futures_core::task::{Context, Poll, Waker}; /// A type of futures-powered synchronization primitive which is a mutex between /// two possible owners. @@ -61,10 +61,7 @@ impl BiLock { /// Similarly, reuniting the lock and extracting the inner value is only /// possible when `T` is `Unpin`. pub fn new(t: T) -> (Self, Self) { - let arc = Arc::new(Inner { - state: AtomicUsize::new(0), - value: Some(UnsafeCell::new(t)), - }); + let arc = Arc::new(Inner { state: AtomicUsize::new(0), value: Some(UnsafeCell::new(t)) }); (Self { arc: arc.clone() }, Self { arc }) } @@ -103,11 +100,11 @@ impl BiLock { 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 = waker.take().unwrap_or_else(||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) { @@ -145,9 +142,7 @@ impl BiLock { #[cfg(feature = "bilock")] #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub fn lock(&self) -> BiLockAcquire<'_, T> { - BiLockAcquire { - bilock: self, - } + BiLockAcquire { bilock: self } } /// Attempts to put the two "halves" of a `BiLock` back together and @@ -181,7 +176,7 @@ impl BiLock { // up as its now their turn. n => unsafe { Box::from_raw(n as *mut Waker).wake(); - } + }, } } } @@ -205,9 +200,7 @@ pub struct ReuniteError(pub BiLock, pub BiLock); impl fmt::Debug for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ReuniteError") - .field(&"...") - .finish() + f.debug_tuple("ReuniteError").field(&"...").finish() } } diff --git a/futures-util/src/lock/mutex.rs b/futures-util/src/lock/mutex.rs index a78de6283c..a849aeeb38 100644 --- a/futures-util/src/lock/mutex.rs +++ b/futures-util/src/lock/mutex.rs @@ -1,13 +1,13 @@ use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use slab::Slab; -use std::{fmt, mem}; use std::cell::UnsafeCell; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::pin::Pin; -use std::sync::Mutex as StdMutex; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex as StdMutex; +use std::{fmt, mem}; /// A futures-aware mutex. /// @@ -53,7 +53,7 @@ enum Waiter { impl Waiter { fn register(&mut self, waker: &Waker) { match self { - Self::Waiting(w) if waker.will_wake(w) => {}, + Self::Waiting(w) if waker.will_wake(w) => {} _ => *self = Self::Waiting(waker.clone()), } } @@ -61,7 +61,7 @@ impl Waiter { fn wake(&mut self) { match mem::replace(self, Self::Woken) { Self::Waiting(waker) => waker.wake(), - Self::Woken => {}, + Self::Woken => {} } } } @@ -113,10 +113,7 @@ impl Mutex { /// This method returns a future that will resolve once the lock has been /// successfully acquired. pub fn lock(&self) -> MutexLockFuture<'_, T> { - MutexLockFuture { - mutex: Some(self), - wait_key: WAIT_KEY_NONE, - } + MutexLockFuture { mutex: Some(self), wait_key: WAIT_KEY_NONE } } /// Returns a mutable reference to the underlying data. @@ -145,7 +142,7 @@ impl Mutex { if wait_key != WAIT_KEY_NONE { let mut waiters = self.waiters.lock().unwrap(); match waiters.remove(wait_key) { - Waiter::Waiting(_) => {}, + Waiter::Waiting(_) => {} Waiter::Woken => { // We were awoken, but then dropped before we could // wake up to acquire the lock. Wake up another @@ -191,13 +188,10 @@ impl fmt::Debug for MutexLockFuture<'_, T> { f.debug_struct("MutexLockFuture") .field("was_acquired", &self.mutex.is_none()) .field("mutex", &self.mutex) - .field("wait_key", &( - if self.wait_key == WAIT_KEY_NONE { - None - } else { - Some(self.wait_key) - } - )) + .field( + "wait_key", + &(if self.wait_key == WAIT_KEY_NONE { None } else { Some(self.wait_key) }), + ) .finish() } } @@ -295,10 +289,7 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { impl fmt::Debug for MutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MutexGuard") - .field("value", &&**self) - .field("mutex", &self.mutex) - .finish() + f.debug_struct("MutexGuard").field("value", &&**self).field("mutex", &self.mutex).finish() } } diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index eda3b27038..f596b3b0e3 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -52,10 +52,7 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let index = self.index; - self.project().data.poll(cx).map(|output| OrderWrapper { - data: output, - index, - }) + self.project().data.poll(cx).map(|output| OrderWrapper { data: output, index }) } } @@ -139,10 +136,7 @@ impl FuturesOrdered { /// must ensure that `FuturesOrdered::poll` is called in order to receive /// task notifications. pub fn push(&mut self, future: Fut) { - let wrapped = OrderWrapper { - data: future, - index: self.next_incoming_index, - }; + let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; self.next_incoming_index += 1; self.in_progress_queue.push(wrapped); } diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 8dcc551e9f..d1377ff327 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -3,11 +3,8 @@ //! This module is only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -use futures_core::future::Future; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -use futures_task::{FutureObj, LocalFutureObj, Spawn, LocalSpawn, SpawnError}; use crate::task::AtomicWaker; +use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; use core::fmt::{self, Debug}; use core::iter::FromIterator; @@ -16,8 +13,11 @@ use core::mem; use core::pin::Pin; use core::ptr; use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst}; -use core::sync::atomic::{AtomicPtr, AtomicBool}; -use alloc::sync::{Arc, Weak}; +use core::sync::atomic::{AtomicBool, AtomicPtr}; +use futures_core::future::Future; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError}; mod abort; @@ -28,8 +28,7 @@ mod task; use self::task::Task; mod ready_to_run_queue; -use self::ready_to_run_queue::{ReadyToRunQueue, Dequeue}; - +use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue}; /// A set of futures which may complete in any order. /// @@ -63,18 +62,14 @@ unsafe impl Sync for FuturesUnordered {} impl Unpin for FuturesUnordered {} impl Spawn for FuturesUnordered> { - fn spawn_obj(&self, future_obj: FutureObj<'static, ()>) - -> Result<(), SpawnError> - { + fn spawn_obj(&self, future_obj: FutureObj<'static, ()>) -> Result<(), SpawnError> { self.push(future_obj); Ok(()) } } impl LocalSpawn for FuturesUnordered> { - fn spawn_local_obj(&self, future_obj: LocalFutureObj<'static, ()>) - -> Result<(), SpawnError> - { + fn spawn_local_obj(&self, future_obj: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { self.push(future_obj); Ok(()) } @@ -191,7 +186,10 @@ impl FuturesUnordered { } /// Returns an iterator that allows inspecting each future in the set. - pub fn iter(&self) -> Iter<'_, Fut> where Fut: Unpin { + pub fn iter(&self) -> Iter<'_, Fut> + where + Fut: Unpin, + { Iter(Pin::new(self).iter_pin_ref()) } @@ -199,16 +197,14 @@ impl FuturesUnordered { fn iter_pin_ref(self: Pin<&Self>) -> IterPinRef<'_, Fut> { let (task, len) = self.atomic_load_head_and_len_all(); - IterPinRef { - task, - len, - pending_next_all: self.pending_next_all(), - _marker: PhantomData, - } + IterPinRef { task, len, pending_next_all: self.pending_next_all(), _marker: PhantomData } } /// Returns an iterator that allows modifying each future in the set. - pub fn iter_mut(&mut self) -> IterMut<'_, Fut> where Fut: Unpin { + pub fn iter_mut(&mut self) -> IterMut<'_, Fut> + where + Fut: Unpin, + { IterMut(Pin::new(self).iter_pin_mut()) } @@ -217,19 +213,9 @@ impl FuturesUnordered { // `head_all` can be accessed directly and we don't need to spin on // `Task::next_all` since we have exclusive access to the set. let task = *self.head_all.get_mut(); - let len = if task.is_null() { - 0 - } else { - unsafe { - *(*task).len_all.get() - } - }; + let len = if task.is_null() { 0 } else { unsafe { *(*task).len_all.get() } }; - IterPinMut { - task, - len, - _marker: PhantomData - } + IterPinMut { task, len, _marker: PhantomData } } /// Returns the current head node and number of futures in the list of all @@ -395,9 +381,7 @@ impl FuturesUnordered { impl Stream for FuturesUnordered { type Item = Fut::Output; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Variable to determine how many times it is allowed to poll underlying // futures without yielding. // @@ -469,14 +453,11 @@ impl Stream for FuturesUnordered { // Double check that the call to `release_task` really // happened. Calling it required the task to be unlinked. - debug_assert_eq!( - task.next_all.load(Relaxed), - self.pending_next_all() - ); + debug_assert_eq!(task.next_all.load(Relaxed), self.pending_next_all()); unsafe { debug_assert!((*task.prev_all.get()).is_null()); } - continue + continue; } }; @@ -516,10 +497,7 @@ impl Stream for FuturesUnordered { } } - let mut bomb = Bomb { - task: Some(task), - queue: &mut *self, - }; + let mut bomb = Bomb { task: Some(task), queue: &mut *self }; // Poll the underlying future with the appropriate waker // implementation. This is where a large bit of the unsafety @@ -555,11 +533,9 @@ impl Stream for FuturesUnordered { cx.waker().wake_by_ref(); return Poll::Pending; } - continue - } - Poll::Ready(output) => { - return Poll::Ready(Some(output)) + continue; } + Poll::Ready(output) => return Poll::Ready(Some(output)), } } } @@ -611,7 +587,10 @@ impl FromIterator for FuturesUnordered { I: IntoIterator, { let acc = Self::new(); - iter.into_iter().fold(acc, |acc, item| { acc.push(item); acc }) + iter.into_iter().fold(acc, |acc, item| { + acc.push(item); + acc + }) } } diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 210519585e..3b34dc6e27 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -1,9 +1,9 @@ use crate::task::AtomicWaker; +use alloc::sync::Arc; use core::cell::UnsafeCell; use core::ptr; use core::sync::atomic::AtomicPtr; -use core::sync::atomic::Ordering::{Relaxed, Acquire, Release, AcqRel}; -use alloc::sync::Arc; +use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use super::abort::abort; use super::task::Task; diff --git a/futures-util/src/stream/futures_unordered/task.rs b/futures-util/src/stream/futures_unordered/task.rs index 261408f01c..da2cd67d97 100644 --- a/futures-util/src/stream/futures_unordered/task.rs +++ b/futures-util/src/stream/futures_unordered/task.rs @@ -1,11 +1,11 @@ +use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicPtr, AtomicBool}; use core::sync::atomic::Ordering::{self, SeqCst}; -use alloc::sync::{Arc, Weak}; +use core::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::task::{ArcWake, WakerRef, waker_ref}; -use super::ReadyToRunQueue; use super::abort::abort; +use super::ReadyToRunQueue; +use crate::task::{waker_ref, ArcWake, WakerRef}; pub(super) struct Task { // The future diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index c0b92faabe..6b17bad125 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -5,11 +5,11 @@ use core::iter::FromIterator; use core::pin::Pin; use futures_core::ready; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use super::assert_stream; -use crate::stream::{StreamExt, StreamFuture, FuturesUnordered}; +use crate::stream::{FuturesUnordered, StreamExt, StreamFuture}; /// An unbounded set of streams /// @@ -75,10 +75,7 @@ impl Default for SelectAll { impl Stream for SelectAll { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match ready!(self.inner.poll_next_unpin(cx)) { Some((Some(item), remaining)) => { @@ -116,8 +113,9 @@ impl FusedStream for SelectAll { /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. pub fn select_all(streams: I) -> SelectAll - where I: IntoIterator, - I::Item: Stream + Unpin +where + I: IntoIterator, + I::Item: Stream + Unpin, { let mut set = SelectAll::new(); diff --git a/futures-util/src/stream/stream/buffer_unordered.rs b/futures-util/src/stream/stream/buffer_unordered.rs index de42cfd330..d64c142b41 100644 --- a/futures-util/src/stream/stream/buffer_unordered.rs +++ b/futures-util/src/stream/stream/buffer_unordered.rs @@ -1,12 +1,12 @@ use crate::stream::{Fuse, FuturesUnordered, StreamExt}; +use core::fmt; +use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use core::fmt; -use core::pin::Pin; pin_project! { /// Stream for the [`buffer_unordered`](super::StreamExt::buffer_unordered) @@ -63,10 +63,7 @@ where { type Item = ::Output; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); // First up, try to spawn off as many futures as possible by filling up diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 1af9f492b9..6052a737ba 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -1,4 +1,6 @@ use crate::stream::{Fuse, FuturesOrdered, StreamExt}; +use core::fmt; +use core::pin::Pin; use futures_core::future::Future; use futures_core::ready; use futures_core::stream::Stream; @@ -6,8 +8,6 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use core::fmt; -use core::pin::Pin; pin_project! { /// Stream for the [`buffered`](super::StreamExt::buffered) method. @@ -44,11 +44,7 @@ where St::Item: Future, { pub(super) fn new(stream: St, n: usize) -> Self { - Self { - stream: super::Fuse::new(stream), - in_progress_queue: FuturesOrdered::new(), - max: n, - } + Self { stream: super::Fuse::new(stream), in_progress_queue: FuturesOrdered::new(), max: n } } delegate_access_inner!(stream, St, (.)); @@ -61,10 +57,7 @@ where { type Item = ::Output; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); // First up, try to spawn off as many futures as possible by filling up @@ -79,7 +72,7 @@ where // Attempt to pull the next value from the in_progress_queue let res = this.in_progress_queue.poll_next_unpin(cx); if let Some(val) = ready!(res) { - return Poll::Ready(Some(val)) + return Poll::Ready(Some(val)); } // If more values are still coming from the stream, we're not done yet diff --git a/futures-util/src/stream/stream/for_each_concurrent.rs b/futures-util/src/stream/stream/for_each_concurrent.rs index cee0ba197e..6c18753eb9 100644 --- a/futures-util/src/stream/stream/for_each_concurrent.rs +++ b/futures-util/src/stream/stream/for_each_concurrent.rs @@ -1,7 +1,7 @@ use crate::stream::{FuturesUnordered, StreamExt}; use core::fmt; -use core::pin::Pin; use core::num::NonZeroUsize; +use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; @@ -35,9 +35,10 @@ where } impl ForEachConcurrent -where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { pub(super) fn new(stream: St, limit: Option, f: F) -> Self { Self { @@ -51,9 +52,10 @@ where St: Stream, } impl FusedFuture for ForEachConcurrent - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.stream.is_none() && self.futures.is_empty() @@ -61,9 +63,10 @@ impl FusedFuture for ForEachConcurrent } impl Future for ForEachConcurrent - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Output = (); @@ -80,7 +83,7 @@ impl Future for ForEachConcurrent Poll::Ready(Some(elem)) => { made_progress_this_iter = true; Some(elem) - }, + } Poll::Ready(None) => { stream_completed = true; None @@ -102,9 +105,9 @@ impl Future for ForEachConcurrent Poll::Ready(Some(())) => made_progress_this_iter = true, Poll::Ready(None) => { if this.stream.is_none() { - return Poll::Ready(()) + return Poll::Ready(()); } - }, + } Poll::Pending => {} } diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index 997b9747a2..3a72fee30b 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -1,9 +1,9 @@ +use core::fmt; +use core::pin::Pin; use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use core::fmt; -use core::pin::Pin; use crate::lock::BiLock; @@ -20,7 +20,8 @@ impl SplitStream { /// together. Succeeds only if the `SplitStream` and `SplitSink` are /// a matching pair originating from the same call to `StreamExt::split`. pub fn reunite(self, other: SplitSink) -> Result> - where S: Sink, + where + S: Sink, { other.reunite(self) } @@ -36,10 +37,7 @@ impl Stream for SplitStream { #[allow(bad_style)] fn SplitSink, Item>(lock: BiLock) -> SplitSink { - SplitSink { - lock, - slot: None, - } + SplitSink { lock, slot: None } } /// A `Sink` part of the split pair @@ -58,14 +56,16 @@ impl + Unpin, Item> SplitSink { /// together. Succeeds only if the `SplitStream` and `SplitSink` are /// 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)) - }) + self.lock.reunite(other.0).map_err(|err| ReuniteError(SplitSink(err.0), SplitStream(err.1))) } } impl, Item> SplitSink { - fn poll_flush_slot(mut inner: Pin<&mut S>, slot: &mut Option, cx: &mut Context<'_>) -> Poll> { + fn poll_flush_slot( + mut inner: Pin<&mut S>, + slot: &mut Option, + cx: &mut Context<'_>, + ) -> Poll> { if slot.is_some() { ready!(inner.as_mut().poll_ready(cx))?; Poll::Ready(inner.start_send(slot.take().unwrap())) @@ -74,7 +74,10 @@ impl, Item> SplitSink { } } - fn poll_lock_and_flush_slot(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_lock_and_flush_slot( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = &mut *self; let mut inner = ready!(this.lock.poll_lock(cx)); Self::poll_flush_slot(inner.as_pin_mut(), &mut this.slot, cx) @@ -127,9 +130,7 @@ pub struct ReuniteError(pub SplitSink, pub SplitStream); impl fmt::Debug for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ReuniteError") - .field(&"...") - .finish() + f.debug_tuple("ReuniteError").field(&"...").finish() } } 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 71c6fc7e26..9a899d4ea6 100644 --- a/futures-util/src/stream/try_stream/try_buffer_unordered.rs +++ b/futures-util/src/stream/try_stream/try_buffer_unordered.rs @@ -1,12 +1,12 @@ -use crate::stream::{Fuse, FuturesUnordered, StreamExt, IntoStream}; use crate::future::{IntoFuture, TryFutureExt}; +use crate::stream::{Fuse, FuturesUnordered, IntoStream, StreamExt}; +use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use core::pin::Pin; pin_project! { /// Stream for the @@ -24,8 +24,9 @@ pin_project! { } impl TryBufferUnordered - where St: TryStream, - St::Ok: TryFuture, +where + St: TryStream, + St::Ok: TryFuture, { pub(super) fn new(stream: St, n: usize) -> Self { Self { @@ -39,15 +40,13 @@ impl TryBufferUnordered } impl Stream for TryBufferUnordered - where St: TryStream, - St::Ok: TryFuture, +where + St: TryStream, + St::Ok: TryFuture, { type Item = Result<::Ok, St::Error>; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); // First up, try to spawn off as many futures as possible by filling up @@ -77,8 +76,9 @@ impl Stream for TryBufferUnordered // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryBufferUnordered - where S: TryStream + Sink, - S::Ok: TryFuture, +where + S: TryStream + Sink, + S::Ok: TryFuture, { type Error = E; diff --git a/futures-util/src/stream/try_stream/try_buffered.rs b/futures-util/src/stream/try_stream/try_buffered.rs index ff7e8447f9..45bd3f8c7a 100644 --- a/futures-util/src/stream/try_stream/try_buffered.rs +++ b/futures-util/src/stream/try_stream/try_buffered.rs @@ -1,12 +1,12 @@ -use crate::stream::{Fuse, FuturesOrdered, StreamExt, IntoStream}; use crate::future::{IntoFuture, TryFutureExt}; +use crate::stream::{Fuse, FuturesOrdered, IntoStream, StreamExt}; +use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; use pin_project_lite::pin_project; -use core::pin::Pin; pin_project! { /// Stream for the [`try_buffered`](super::TryStreamExt::try_buffered) method. @@ -47,10 +47,7 @@ where { type Item = Result<::Ok, St::Error>; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); // First up, try to spawn off as many futures as possible by filling up 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 d2f4b0fed2..62734c746b 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 @@ -1,8 +1,8 @@ use crate::stream::{FuturesUnordered, StreamExt}; use core::fmt; use core::mem; -use core::pin::Pin; use core::num::NonZeroUsize; +use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; @@ -37,9 +37,10 @@ where } impl FusedFuture for TryForEachConcurrent - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { fn is_terminated(&self) -> bool { self.stream.is_none() && self.futures.is_empty() @@ -47,9 +48,10 @@ impl FusedFuture for TryForEachConcurrent } impl TryForEachConcurrent -where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { pub(super) fn new(stream: St, limit: Option, f: F) -> Self { Self { @@ -63,9 +65,10 @@ where St: TryStream, } impl Future for TryForEachConcurrent - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { type Output = Result<(), St::Error>; @@ -85,7 +88,7 @@ impl Future for TryForEachConcurrent Poll::Ready(Some(Ok(elem))) => { made_progress_this_iter = true; Some(elem) - }, + } Poll::Ready(None) => { this.stream.set(None); None @@ -109,9 +112,9 @@ impl Future for TryForEachConcurrent Poll::Ready(Some(Ok(()))) => made_progress_this_iter = true, Poll::Ready(None) => { if this.stream.is_none() { - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); } - }, + } Poll::Pending => {} Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know diff --git a/futures/tests_disabled/all.rs b/futures/tests_disabled/all.rs index 6c7e11cf7b..a7a571040a 100644 --- a/futures/tests_disabled/all.rs +++ b/futures/tests_disabled/all.rs @@ -1,27 +1,27 @@ -use futures::future; -use futures::executor::block_on; use futures::channel::oneshot::{self, Canceled}; +use futures::executor::block_on; +use futures::future; use std::sync::mpsc::{channel, TryRecvError}; -mod support; -use support::*; +// mod support; +// use support::*; fn unselect(r: Result, Either<(E, B), (E, A)>>) -> Result { match r { - Ok(Either::Left((t, _))) | - Ok(Either::Right((t, _))) => Ok(t), - Err(Either::Left((e, _))) | - Err(Either::Right((e, _))) => Err(e), + Ok(Either::Left((t, _))) | Ok(Either::Right((t, _))) => Ok(t), + Err(Either::Left((e, _))) | Err(Either::Right((e, _))) => Err(e), } } #[test] fn result_smoke() { fn is_future_v(_: C) - where A: Send + 'static, - B: Send + 'static, - C: Future - {} + where + A: Send + 'static, + B: Send + 'static, + C: Future, + { + } is_future_v::(f_ok(1).map(|a| a + 1)); is_future_v::(f_ok(1).map_err(|a| a + 1)); @@ -64,7 +64,9 @@ fn result_smoke() { #[test] fn test_empty() { - fn empty() -> Empty { future::empty() } + fn empty() -> Empty { + future::empty() + } assert_empty(|| empty()); assert_empty(|| empty().select(empty())); @@ -105,16 +107,22 @@ fn flatten() { #[test] fn smoke_oneshot() { - assert_done(|| { - let (c, p) = oneshot::channel(); - c.send(1).unwrap(); - p - }, Ok(1)); - assert_done(|| { - let (c, p) = oneshot::channel::(); - drop(c); - p - }, Err(Canceled)); + assert_done( + || { + let (c, p) = oneshot::channel(); + c.send(1).unwrap(); + p + }, + Ok(1), + ); + assert_done( + || { + let (c, p) = oneshot::channel::(); + drop(c); + p + }, + Err(Canceled), + ); let mut completes = Vec::new(); assert_empty(|| { let (a, b) = oneshot::channel::(); @@ -129,9 +137,7 @@ fn smoke_oneshot() { let (c, p) = oneshot::channel::(); drop(c); let (tx, rx) = channel(); - p.then(move |_| { - tx.send(()) - }).forget(); + p.then(move |_| tx.send(())).forget(); rx.recv().unwrap(); } @@ -139,8 +145,14 @@ fn smoke_oneshot() { fn select_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.select(d).then(unselect); // assert!(f.poll(&mut Task::new()).is_pending()); @@ -156,8 +168,14 @@ fn select_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.select(d).then(unselect); assert!(f.poll(lw).ok().unwrap().is_pending()); @@ -173,8 +191,14 @@ fn select_cancels() { fn join_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.join(d); drop(a); @@ -185,8 +209,14 @@ fn join_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let (tx, rx) = channel(); let f = b.join(d); @@ -194,7 +224,8 @@ fn join_cancels() { tx.send(()).unwrap(); let res: Result<(), ()> = Ok(()); res - }).forget(); + }) + .forget(); assert!(rx.try_recv().is_err()); drop(a); rx.recv().unwrap(); @@ -243,7 +274,6 @@ fn join_incomplete() { }) } - #[test] fn select2() { assert_done(|| f_ok(2).select(empty()).then(unselect), Ok(2)); @@ -251,14 +281,15 @@ fn select2() { assert_done(|| f_err(2).select(empty()).then(unselect), Err(2)); assert_done(|| empty().select(f_err(2)).then(unselect), Err(2)); - assert_done(|| { - f_ok(1).select(f_ok(2)) - .map_err(|_| 0) - .and_then(|either_tup| { - let (a, b) = either_tup.into_inner(); - b.map(move |b| a + b) - }) - }, Ok(3)); + assert_done( + || { + f_ok(1).select(f_ok(2)).map_err(|_| 0).and_then(|either_tup| { + let (a, b) = either_tup.into_inner(); + b.map(move |b| a + b) + }) + }, + Ok(3), + ); // Finish one half of a select and then fail the second, ensuring that we // get the notification of the second one. @@ -297,8 +328,14 @@ fn select2() { { let ((_a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let f = b.select(d); drop(f); assert!(drx.recv().is_err()); @@ -309,8 +346,14 @@ fn select2() { { let ((_a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let mut f = b.select(d); let _res = noop_waker_lw(|lw| f.poll(lw)); drop(f); @@ -322,8 +365,14 @@ fn select2() { { let ((a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let (tx, rx) = channel(); b.select(d).map(move |_| tx.send(()).unwrap()).forget(); drop(a); diff --git a/futures/tests_disabled/bilock.rs b/futures/tests_disabled/bilock.rs index c1bc33f507..0166ca48ba 100644 --- a/futures/tests_disabled/bilock.rs +++ b/futures/tests_disabled/bilock.rs @@ -1,11 +1,11 @@ -use futures::task; -use futures::stream; use futures::future; +use futures::stream; +use futures::task; use futures_util::lock::BiLock; use std::thread; -mod support; -use support::*; +// mod support; +// use support::*; #[test] fn smoke() { @@ -41,9 +41,9 @@ fn smoke() { }); assert!(task::spawn(future) - .poll_future_notify(¬ify_noop(), 0) - .expect("failure in poll") - .is_ready()); + .poll_future_notify(¬ify_noop(), 0) + .expect("failure in poll") + .is_ready()); } #[test] @@ -51,10 +51,7 @@ fn concurrent() { const N: usize = 10000; let (a, b) = BiLock::new(0); - let a = Increment { - a: Some(a), - remaining: N, - }; + let a = Increment { a: Some(a), remaining: N }; let b = stream::iter_ok(0..N).fold(b, |b, _n| { b.lock().map(|mut b| { *b += 1; @@ -89,7 +86,7 @@ fn concurrent() { fn poll(&mut self) -> Poll, ()> { loop { if self.remaining == 0 { - return Ok(self.a.take().unwrap().into()) + return Ok(self.a.take().unwrap().into()); } let a = self.a.as_ref().unwrap(); diff --git a/futures/tests_disabled/stream.rs b/futures/tests_disabled/stream.rs index ef0676db35..854dbad829 100644 --- a/futures/tests_disabled/stream.rs +++ b/futures/tests_disabled/stream.rs @@ -1,26 +1,26 @@ +use futures::channel::mpsc; +use futures::channel::oneshot; use futures::executor::{block_on, block_on_stream}; use futures::future::{err, ok}; use futures::stream::{empty, iter_ok, poll_fn, Peekable}; -use futures::channel::oneshot; -use futures::channel::mpsc; -mod support; -use support::*; +// mod support; +// use support::*; pub struct Iter { iter: I, } pub fn iter(i: J) -> Iter - where J: IntoIterator>, +where + J: IntoIterator>, { - Iter { - iter: i.into_iter(), - } + Iter { iter: i.into_iter() } } impl Stream for Iter - where I: Iterator>, +where + I: Iterator>, { type Item = T; type Error = E; @@ -34,21 +34,15 @@ impl Stream for Iter } } -fn list() -> Box + Send> { +fn list() -> Box + Send> { let (tx, rx) = mpsc::channel(1); - tx.send(Ok(1)) - .and_then(|tx| tx.send(Ok(2))) - .and_then(|tx| tx.send(Ok(3))) - .forget(); + tx.send(Ok(1)).and_then(|tx| tx.send(Ok(2))).and_then(|tx| tx.send(Ok(3))).forget(); Box::new(rx.then(|r| r.unwrap())) } -fn err_list() -> Box + Send> { +fn err_list() -> Box + Send> { let (tx, rx) = mpsc::channel(1); - tx.send(Ok(1)) - .and_then(|tx| tx.send(Ok(2))) - .and_then(|tx| tx.send(Err(3))) - .forget(); + tx.send(Ok(1)).and_then(|tx| tx.send(Ok(2))).and_then(|tx| tx.send(Err(3))).forget(); Box::new(rx.then(|r| r.unwrap())) } @@ -89,40 +83,31 @@ fn filter() { #[test] fn filter_map() { - assert_done(|| list().filter_map(|x| { - ok(if x % 2 == 0 { - Some(x + 10) - } else { - None - }) - }).collect(), Ok(vec![12])); + assert_done( + || list().filter_map(|x| ok(if x % 2 == 0 { Some(x + 10) } else { None })).collect(), + Ok(vec![12]), + ); } #[test] fn and_then() { assert_done(|| list().and_then(|a| Ok(a + 1)).collect(), Ok(vec![2, 3, 4])); - assert_done(|| list().and_then(|a| err::(a as u32)).collect::>(), - Err(1)); + assert_done(|| list().and_then(|a| err::(a as u32)).collect::>(), Err(1)); } #[test] fn then() { assert_done(|| list().then(|a| a.map(|e| e + 1)).collect(), Ok(vec![2, 3, 4])); - } #[test] fn or_else() { - assert_done(|| err_list().or_else(|a| { - ok::(a as i32) - }).collect(), Ok(vec![1, 2, 3])); + assert_done(|| err_list().or_else(|a| ok::(a as i32)).collect(), Ok(vec![1, 2, 3])); } #[test] fn flatten() { - assert_done(|| list().map(|_| list()).flatten().collect(), - Ok(vec![1, 2, 3, 1, 2, 3, 1, 2, 3])); - + assert_done(|| list().map(|_| list()).flatten().collect(), Ok(vec![1, 2, 3, 1, 2, 3, 1, 2, 3])); } #[test] @@ -132,9 +117,7 @@ fn skip() { #[test] fn skip_passes_errors_through() { - let mut s = block_on_stream( - iter(vec![Err(1), Err(2), Ok(3), Ok(4), Ok(5)]).skip(1) - ); + let mut s = block_on_stream(iter(vec![Err(1), Err(2), Ok(3), Ok(4), Ok(5)]).skip(1)); assert_eq!(s.next(), Some(Err(1))); assert_eq!(s.next(), Some(Err(2))); assert_eq!(s.next(), Some(Ok(4))); @@ -144,8 +127,7 @@ fn skip_passes_errors_through() { #[test] fn skip_while() { - assert_done(|| list().skip_while(|e| Ok(*e % 2 == 1)).collect(), - Ok(vec![2, 3])); + assert_done(|| list().skip_while(|e| Ok(*e % 2 == 1)).collect(), Ok(vec![2, 3])); } #[test] fn take() { @@ -154,8 +136,7 @@ fn take() { #[test] fn take_while() { - assert_done(|| list().take_while(|e| Ok(*e < 3)).collect(), - Ok(vec![1, 2])); + assert_done(|| list().take_while(|e| Ok(*e < 3)).collect(), Ok(vec![1, 2])); } #[test] @@ -193,9 +174,9 @@ fn buffered() { let (a, b) = oneshot::channel::(); let (c, d) = oneshot::channel::(); - tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) - .forget(); + tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) + .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) + .forget(); let mut rx = rx.buffered(2); sassert_empty(&mut rx); @@ -211,9 +192,9 @@ fn buffered() { let (a, b) = oneshot::channel::(); let (c, d) = oneshot::channel::(); - tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) - .forget(); + tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) + .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) + .forget(); let mut rx = rx.buffered(1); sassert_empty(&mut rx); @@ -233,8 +214,8 @@ fn unordered() { let (c, d) = oneshot::channel::(); tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) - .forget(); + .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) + .forget(); let mut rx = rx.buffer_unordered(2); sassert_empty(&mut rx); @@ -250,8 +231,8 @@ fn unordered() { let (c, d) = oneshot::channel::(); tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) - .forget(); + .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) + .forget(); // We don't even get to see `c` until `a` completes. let mut rx = rx.buffer_unordered(1); @@ -267,21 +248,17 @@ fn unordered() { #[test] fn zip() { - assert_done(|| list().zip(list()).collect(), - Ok(vec![(1, 1), (2, 2), (3, 3)])); - assert_done(|| list().zip(list().take(2)).collect(), - Ok(vec![(1, 1), (2, 2)])); - assert_done(|| list().take(2).zip(list()).collect(), - Ok(vec![(1, 1), (2, 2)])); + assert_done(|| list().zip(list()).collect(), Ok(vec![(1, 1), (2, 2), (3, 3)])); + assert_done(|| list().zip(list().take(2)).collect(), Ok(vec![(1, 1), (2, 2)])); + assert_done(|| list().take(2).zip(list()).collect(), Ok(vec![(1, 1), (2, 2)])); assert_done(|| err_list().zip(list()).collect::>(), Err(3)); - assert_done(|| list().zip(list().map(|x| x + 1)).collect(), - Ok(vec![(1, 2), (2, 3), (3, 4)])); + assert_done(|| list().zip(list().map(|x| x + 1)).collect(), Ok(vec![(1, 2), (2, 3), (3, 4)])); } #[test] fn peek() { struct Peek { - inner: Peekable + Send>> + inner: Peekable + Send>>, } impl Future for Peek { @@ -299,15 +276,12 @@ fn peek() { } } - block_on(Peek { - inner: list().peekable(), - }).unwrap() + block_on(Peek { inner: list().peekable() }).unwrap() } #[test] fn wait() { - assert_eq!(block_on_stream(list()).collect::, _>>(), - Ok(vec![1, 2, 3])); + assert_eq!(block_on_stream(list()).collect::, _>>(), Ok(vec![1, 2, 3])); } #[test] @@ -337,8 +311,10 @@ fn forward() { let v = block_on(iter_ok::<_, Never>(vec![2, 3]).forward(v)).unwrap().1; assert_eq!(v, vec![0, 1, 2, 3]); - assert_done(move || iter_ok::<_, Never>(vec![4, 5]).forward(v).map(|(_, s)| s), - Ok(vec![0, 1, 2, 3, 4, 5])); + assert_done( + move || iter_ok::<_, Never>(vec![4, 5]).forward(v).map(|(_, s)| s), + Ok(vec![0, 1, 2, 3, 4, 5]), + ); } #[test] diff --git a/tools/fmt.sh b/tools/fmt.sh new file mode 100755 index 0000000000..5438030aee --- /dev/null +++ b/tools/fmt.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Format all rust code. +# +# Usage: +# ./tools/fmt.sh +# +# This script is needed because `cargo fmt` cannot recognize modules defined inside macros. +# Refs: https://github.com/rust-lang/rustfmt/issues/4078 + +set -euo pipefail +IFS=$'\n\t' + +cd "$(cd "$(dirname "${0}")" && pwd)"/.. + +# shellcheck disable=SC2046 +if [[ -z "${CI:-}" ]]; then + ( + # `cargo fmt` cannot recognize modules defined inside macros so run rustfmt directly. + rustfmt $(git ls-files "*.rs") + ) +else + ( + rustfmt --check $(git ls-files "*.rs") + ) +fi From 2c8fc27f76156f0daf228d3d03299c996941b4a3 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 03:28:06 -0400 Subject: [PATCH 030/283] async test function attr --- futures-macro/src/executor.rs | 27 +++++++++++++++++++++++++++ futures-macro/src/lib.rs | 7 +++++++ futures-test/Cargo.toml | 1 + futures-test/src/lib.rs | 14 ++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 futures-macro/src/executor.rs diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs new file mode 100644 index 0000000000..ac008acc7f --- /dev/null +++ b/futures-macro/src/executor.rs @@ -0,0 +1,27 @@ +use proc_macro::TokenStream; +use quote::quote; + +pub(crate) fn test(_: TokenStream, item: TokenStream) -> TokenStream { + let mut input = syn::parse_macro_input!(item as syn::ItemFn); + let attrs = &input.attrs; + let vis = &input.vis; + let sig = &mut input.sig; + let body = &input.block; + + if sig.asyncness.is_none() { + return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") + .to_compile_error() + .into(); + } + + sig.asyncness = None; + + let gen = quote! { + #(#attrs)* + #vis #sig { + ::futures_test::__private::block_on(async move { #body }) + } + }; + + gen.into() +} diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 98408ebfe6..f3cc774142 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -14,6 +14,7 @@ extern crate proc_macro; use proc_macro::TokenStream; +mod executor; mod join; mod select; @@ -44,3 +45,9 @@ pub fn select_internal(input: TokenStream) -> TokenStream { pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } + +/// The `test` attribute. +#[proc_macro_attribute] +pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { + crate::executor::test(input, item) +} diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 4835f447b0..0e725f873f 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -18,6 +18,7 @@ futures-io = { version = "0.3.14", path = "../futures-io", default-features = fa futures-util = { version = "0.3.14", path = "../futures-util", default-features = false } futures-executor = { version = "0.3.14", path = "../futures-executor", default-features = false } futures-sink = { version = "0.3.14", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.14", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 4c26a56987..109cc95434 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -16,6 +16,7 @@ compile_error!( #[cfg(feature = "std")] pub mod __private { pub use futures_core::{future, stream, task}; + pub use futures_executor::block_on; pub use std::{ option::Option::{None, Some}, pin::Pin, @@ -49,3 +50,16 @@ pub mod io; mod assert_unmoved; mod interleave_pending; mod track_closed; + +/// Enables an `async` test function. The generated future will be run to completion with +/// [`futures_executor::block_on`](futures_executor::block_on). +/// +/// ```no_run +/// #[futures_test::test] +/// async fn my_test() { +/// let fut = async { true }; +/// assert!(fut.await); +/// } +/// ``` +#[cfg(feature = "std")] +pub use futures_macro::test_internal as test; From 57cfc2f8d10132cde750c38f4d42e9e37df3d322 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 03:39:52 -0400 Subject: [PATCH 031/283] don't skip async-test macro doc tests --- futures-test/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 109cc95434..00cd980903 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -54,7 +54,7 @@ mod track_closed; /// Enables an `async` test function. The generated future will be run to completion with /// [`futures_executor::block_on`](futures_executor::block_on). /// -/// ```no_run +/// ``` /// #[futures_test::test] /// async fn my_test() { /// let fut = async { true }; From 5ddb0dea07945a86a38581a022f4eff8a0ad8c5b Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 6 May 2021 11:38:38 -0400 Subject: [PATCH 032/283] Apply suggestions from code review Co-authored-by: Taiki Endo --- futures-macro/src/executor.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index ac008acc7f..48f510ffaa 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -1,25 +1,29 @@ use proc_macro::TokenStream; use quote::quote; -pub(crate) fn test(_: TokenStream, item: TokenStream) -> TokenStream { +pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { + if !args.is_empty() { + return syn::Error::new_spanned(args, "invalid argument")) + .to_compile_error() + .into(); + } let mut input = syn::parse_macro_input!(item as syn::ItemFn); let attrs = &input.attrs; let vis = &input.vis; let sig = &mut input.sig; let body = &input.block; - if sig.asyncness.is_none() { + if sig.asyncness.take().is_none() { return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") .to_compile_error() .into(); } - sig.asyncness = None; - let gen = quote! { + #[::core::prelude::v1::test] #(#attrs)* #vis #sig { - ::futures_test::__private::block_on(async move { #body }) + ::futures_test::__private::block_on(async move #body) } }; From ea7a587599ca5254faf97e7cd0b46b75793e9809 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 11:57:42 -0400 Subject: [PATCH 033/283] add tests for async-test macro --- futures-macro/src/executor.rs | 3 ++- futures/tests/test_macro.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 futures/tests/test_macro.rs diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index 48f510ffaa..1efb48c7c7 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -3,10 +3,11 @@ use quote::quote; pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { if !args.is_empty() { - return syn::Error::new_spanned(args, "invalid argument")) + return syn::Error::new_spanned(proc_macro2::TokenStream::from(args), "invalid argument") .to_compile_error() .into(); } + let mut input = syn::parse_macro_input!(item as syn::ItemFn); let attrs = &input.attrs; let vis = &input.vis; diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs new file mode 100644 index 0000000000..4b3b44634e --- /dev/null +++ b/futures/tests/test_macro.rs @@ -0,0 +1,18 @@ +#[cfg(test)] +mod tests { + #[futures_test::test] + async fn it_works() { + let fut = async { true }; + assert!(fut.await); + + let fut = async { false }; + assert!(!fut.await); + } + + #[futures_test::test] + #[should_panic] + async fn it_is_being_run() { + let fut = async { false }; + assert!(fut.await); + } +} From f6d3eb02e47a98018a76c75d4fe4328ff8b27ec0 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Fri, 7 May 2021 11:07:23 -0400 Subject: [PATCH 034/283] apply suggestions from code review --- futures-test/src/lib.rs | 12 ++++++++++++ futures/tests/test_macro.rs | 27 ++++++++++++--------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 00cd980903..1117bb36cf 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -61,5 +61,17 @@ mod track_closed; /// assert!(fut.await); /// } /// ``` +/// +/// This is equivalent to the following code: +/// +/// ``` +/// #[test] +/// fn my_test() { +/// futures::executor::block_on(async move { +/// let fut = async { true }; +/// assert!(fut.await); +/// }) +/// } +/// ``` #[cfg(feature = "std")] pub use futures_macro::test_internal as test; diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs index 4b3b44634e..2f391997ea 100644 --- a/futures/tests/test_macro.rs +++ b/futures/tests/test_macro.rs @@ -1,18 +1,15 @@ -#[cfg(test)] -mod tests { - #[futures_test::test] - async fn it_works() { - let fut = async { true }; - assert!(fut.await); +#[futures_test::test] +async fn it_works() { + let fut = async { true }; + assert!(fut.await); - let fut = async { false }; - assert!(!fut.await); - } + let fut = async { false }; + assert!(!fut.await); +} - #[futures_test::test] - #[should_panic] - async fn it_is_being_run() { - let fut = async { false }; - assert!(fut.await); - } +#[should_panic] +#[futures_test::test] +async fn it_is_being_run() { + let fut = async { false }; + assert!(fut.await); } From 61dae0d10541430c4401400d4926b923236ba8e0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 8 May 2021 16:34:17 +0900 Subject: [PATCH 035/283] Remove extra parentheses from tools/fmt.sh --- tools/fmt.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/fmt.sh b/tools/fmt.sh index 5438030aee..32f4f00748 100755 --- a/tools/fmt.sh +++ b/tools/fmt.sh @@ -5,7 +5,8 @@ # Usage: # ./tools/fmt.sh # -# This script is needed because `cargo fmt` cannot recognize modules defined inside macros. +# This is similar to `cargo fmt`, but unlike `cargo fmt`, it can recognize +# modules defined inside macros. # Refs: https://github.com/rust-lang/rustfmt/issues/4078 set -euo pipefail @@ -15,12 +16,13 @@ cd "$(cd "$(dirname "${0}")" && pwd)"/.. # shellcheck disable=SC2046 if [[ -z "${CI:-}" ]]; then - ( - # `cargo fmt` cannot recognize modules defined inside macros so run rustfmt directly. - rustfmt $(git ls-files "*.rs") - ) + # `cargo fmt` cannot recognize modules defined inside macros, so run + # rustfmt directly. + # Refs: https://github.com/rust-lang/rustfmt/issues/4078 + rustfmt $(git ls-files "*.rs") else - ( - rustfmt --check $(git ls-files "*.rs") - ) + # `cargo fmt` cannot recognize modules defined inside macros, so run + # rustfmt directly. + # Refs: https://github.com/rust-lang/rustfmt/issues/4078 + rustfmt --check $(git ls-files "*.rs") fi From aa39d144fdc018aaf38f996addff4aed4931cfce Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 8 May 2021 17:16:08 +0900 Subject: [PATCH 036/283] Rename Take::limit_ field to limit This workaround is no longer needed. --- futures-util/src/io/take.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index 680d2702dc..05830203d0 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -14,14 +14,13 @@ pin_project! { pub struct Take { #[pin] inner: R, - // Add '_' to avoid conflicts with `limit` method. - limit_: u64, + limit: u64, } } impl Take { pub(super) fn new(inner: R, limit: u64) -> Self { - Self { inner, limit_: limit } + Self { inner, limit } } /// Returns the remaining number of bytes that can be @@ -48,7 +47,7 @@ impl Take { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` pub fn limit(&self) -> u64 { - self.limit_ + self.limit } /// Sets the number of bytes that can be read before this instance will @@ -78,7 +77,7 @@ impl Take { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` pub fn set_limit(&mut self, limit: u64) { - self.limit_ = limit + self.limit = limit } delegate_access_inner!(inner, R, ()); @@ -92,13 +91,13 @@ impl AsyncRead for Take { ) -> Poll> { let this = self.project(); - if *this.limit_ == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(0)); } - let max = cmp::min(buf.len() as u64, *this.limit_) as usize; + let max = cmp::min(buf.len() as u64, *this.limit) as usize; let n = ready!(this.inner.poll_read(cx, &mut buf[..max]))?; - *this.limit_ -= n as u64; + *this.limit -= n as u64; Poll::Ready(Ok(n)) } @@ -113,12 +112,12 @@ impl AsyncBufRead for Take { let this = self.project(); // Don't call into inner reader at all at EOF because it may still block - if *this.limit_ == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(&[])); } let buf = ready!(this.inner.poll_fill_buf(cx)?); - let cap = cmp::min(buf.len() as u64, *this.limit_) as usize; + let cap = cmp::min(buf.len() as u64, *this.limit) as usize; Poll::Ready(Ok(&buf[..cap])) } @@ -126,8 +125,8 @@ impl AsyncBufRead for Take { let this = self.project(); // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, *this.limit_) as usize; - *this.limit_ -= amt as u64; + let amt = cmp::min(amt as u64, *this.limit) as usize; + *this.limit -= amt as u64; this.inner.consume(amt); } } From 48d65c33e7cc75a951933cb14a3d54708051744a Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Mon, 10 May 2021 08:30:30 -0400 Subject: [PATCH 037/283] add FuturesUnordered::into_iter, make iter_pin_ref public (#2423) --- .../src/stream/futures_unordered/iter.rs | 46 +++++++++++++++++++ .../src/stream/futures_unordered/mod.rs | 39 ++++++++++++++-- futures/tests/auto_traits.rs | 7 +++ futures/tests/stream_futures_unordered.rs | 45 ++++++++++++++++++ 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/iter.rs b/futures-util/src/stream/futures_unordered/iter.rs index 17cde4fac4..59ea28efa6 100644 --- a/futures-util/src/stream/futures_unordered/iter.rs +++ b/futures-util/src/stream/futures_unordered/iter.rs @@ -29,6 +29,47 @@ pub struct IterPinRef<'a, Fut> { /// Immutable iterator over all the futures in the unordered set. pub struct Iter<'a, Fut: Unpin>(pub(super) IterPinRef<'a, Fut>); +#[derive(Debug)] +/// Owned iterator over all futures in the unordered set. +pub struct IntoIter { + pub(super) len: usize, + pub(super) inner: FuturesUnordered, +} + +impl Iterator for IntoIter { + type Item = Fut; + + fn next(&mut self) -> Option { + // `head_all` can be accessed directly and we don't need to spin on + // `Task::next_all` since we have exclusive access to the set. + let task = self.inner.head_all.get_mut(); + + if (*task).is_null() { + return None; + } + + unsafe { + // Moving out of the future is safe because it is `Unpin` + let future = (*(**task).future.get()).take().unwrap(); + + // Mutable access to a previously shared `FuturesUnordered` implies + // that the other threads already released the object before the + // current thread acquired it, so relaxed ordering can be used and + // valid `next_all` checks can be skipped. + let next = (**task).next_all.load(Relaxed); + *task = next; + self.len -= 1; + Some(future) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl ExactSizeIterator for IntoIter {} + impl<'a, Fut> Iterator for IterPinMut<'a, Fut> { type Item = Pin<&'a mut Fut>; @@ -36,6 +77,7 @@ impl<'a, Fut> Iterator for IterPinMut<'a, Fut> { if self.task.is_null() { return None; } + unsafe { let future = (*(*self.task).future.get()).as_mut().unwrap(); @@ -78,6 +120,7 @@ impl<'a, Fut> Iterator for IterPinRef<'a, Fut> { if self.task.is_null() { return None; } + unsafe { let future = (*(*self.task).future.get()).as_ref().unwrap(); @@ -120,3 +163,6 @@ unsafe impl Sync for IterPinRef<'_, Fut> {} unsafe impl Send for IterPinMut<'_, Fut> {} unsafe impl Sync for IterPinMut<'_, Fut> {} + +unsafe impl Send for IntoIter {} +unsafe impl Sync for IntoIter {} diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index d1377ff327..89ce113d64 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -22,7 +22,7 @@ use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError}; mod abort; mod iter; -pub use self::iter::{Iter, IterMut, IterPinMut, IterPinRef}; +pub use self::iter::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; mod task; use self::task::Task; @@ -194,10 +194,11 @@ impl FuturesUnordered { } /// Returns an iterator that allows inspecting each future in the set. - fn iter_pin_ref(self: Pin<&Self>) -> IterPinRef<'_, Fut> { + pub fn iter_pin_ref(self: Pin<&Self>) -> IterPinRef<'_, Fut> { let (task, len) = self.atomic_load_head_and_len_all(); + let pending_next_all = self.pending_next_all(); - IterPinRef { task, len, pending_next_all: self.pending_next_all(), _marker: PhantomData } + IterPinRef { task, len, pending_next_all, _marker: PhantomData } } /// Returns an iterator that allows modifying each future in the set. @@ -581,6 +582,38 @@ impl Drop for FuturesUnordered { } } +impl<'a, Fut: Unpin> IntoIterator for &'a FuturesUnordered { + type Item = &'a Fut; + type IntoIter = Iter<'a, Fut>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, Fut: Unpin> IntoIterator for &'a mut FuturesUnordered { + type Item = &'a mut Fut; + type IntoIter = IterMut<'a, Fut>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl IntoIterator for FuturesUnordered { + type Item = Fut; + type IntoIter = IntoIter; + + fn into_iter(mut self) -> Self::IntoIter { + // `head_all` can be accessed directly and we don't need to spin on + // `Task::next_all` since we have exclusive access to the set. + let task = *self.head_all.get_mut(); + let len = if task.is_null() { 0 } else { unsafe { *(*task).len_all.get() } }; + + IntoIter { len, inner: self } + } +} + impl FromIterator for FuturesUnordered { fn from_iter(iter: I) -> Self where diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index e934595c2a..e0192a118b 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -1827,6 +1827,13 @@ pub mod stream { assert_impl!(futures_unordered::IterPinRef<()>: Sync); assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Sync); assert_impl!(futures_unordered::IterPinRef: Unpin); + + assert_impl!(futures_unordered::IntoIter<()>: Send); + assert_not_impl!(futures_unordered::IntoIter<*const ()>: Send); + assert_impl!(futures_unordered::IntoIter<()>: Sync); + assert_not_impl!(futures_unordered::IntoIter<*const ()>: Sync); + // The definition of futures_unordered::IntoIter has `Fut: Unpin` bounds. + // assert_not_impl!(futures_unordered::IntoIter: Unpin); } /// Assert Send/Sync/Unpin for all public types in `futures::task`. diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 93b9e293e3..3a5d41853d 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -214,6 +214,51 @@ fn iter_len() { assert!(iter.next().is_none()); } +#[test] +fn into_iter_cancel() { + let (a_tx, a_rx) = oneshot::channel::(); + let (b_tx, b_rx) = oneshot::channel::(); + let (c_tx, c_rx) = oneshot::channel::(); + + let stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); + + let stream = stream + .into_iter() + .map(|mut rx| { + rx.close(); + rx + }) + .collect::>(); + + let mut iter = block_on_stream(stream); + + assert!(a_tx.is_canceled()); + assert!(b_tx.is_canceled()); + assert!(c_tx.is_canceled()); + + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), None); +} + +#[test] +fn into_iter_len() { + let stream = vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); + + let mut into_iter = stream.into_iter(); + assert_eq!(into_iter.len(), 3); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 2); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 1); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 0); + assert!(into_iter.next().is_none()); +} + #[test] fn futures_not_moved_after_poll() { // Future that will be ready after being polled twice, From 5912e2e8b1abd0e6ce416385cf0904a9c8cb66ab Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Mon, 10 May 2021 09:13:20 -0400 Subject: [PATCH 038/283] Add FuturesUnordered::clear (#2415) --- .../src/stream/futures_unordered/mod.rs | 30 +++++++++++---- .../futures_unordered/ready_to_run_queue.rs | 37 +++++++++++++------ futures/tests/stream_futures_unordered.rs | 22 +++++++++++ 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 89ce113d64..a25fbe03ef 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -236,7 +236,7 @@ impl FuturesUnordered { (task, len) } - /// Releases the task. It destorys the future inside and either drops + /// Releases the task. It destroys the future inside and either drops /// the `Arc` or transfers ownership to the ready to run queue. /// The task this method is called on must have been unlinked before. fn release_task(&mut self, task: Arc>) { @@ -553,19 +553,33 @@ impl Debug for FuturesUnordered { } } +impl FuturesUnordered { + /// Clears the set, removing all futures. + pub fn clear(&mut self) { + self.clear_head_all(); + + // we just cleared all the tasks, and we have &mut self, so this is safe. + unsafe { self.ready_to_run_queue.clear() }; + + self.is_terminated.store(false, Relaxed); + } + + fn clear_head_all(&mut self) { + while !self.head_all.get_mut().is_null() { + let head = *self.head_all.get_mut(); + let task = unsafe { self.unlink(head) }; + self.release_task(task); + } + } +} + impl Drop for FuturesUnordered { fn drop(&mut self) { // When a `FuturesUnordered` is dropped we want to drop all futures // associated with it. At the same time though there may be tons of // wakers flying around which contain `Task` references // inside them. We'll let those naturally get deallocated. - unsafe { - while !self.head_all.get_mut().is_null() { - let head = *self.head_all.get_mut(); - let task = self.unlink(head); - self.release_task(task); - } - } + self.clear_head_all(); // Note that at this point we could still have a bunch of tasks in the // ready to run queue. None of those tasks, however, have futures diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 3b34dc6e27..5ef6cde83d 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -85,25 +85,38 @@ impl ReadyToRunQueue { pub(super) fn stub(&self) -> *const Task { &*self.stub } + + // Clear the queue of tasks. + // + // Note that each task has a strong reference count associated with it + // which is owned by the ready to run queue. This method just pulls out + // tasks and drops their refcounts. + // + // # Safety + // + // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear) + // - The caller **must** guarantee unique access to `self` + pub(crate) unsafe fn clear(&self) { + loop { + // SAFETY: We have the guarantee of mutual exclusion required by `dequeue`. + match self.dequeue() { + Dequeue::Empty => break, + Dequeue::Inconsistent => abort("inconsistent in drop"), + Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), + } + } + } } impl Drop for ReadyToRunQueue { fn drop(&mut self) { // Once we're in the destructor for `Inner` we need to clear out // the ready to run queue of tasks if there's anything left in there. - // - // Note that each task has a strong reference count associated with it - // which is owned by the ready to run queue. All tasks should have had - // their futures dropped already by the `FuturesUnordered` destructor - // above, so we're just pulling out tasks and dropping their refcounts. + + // All tasks have had their futures dropped already by the `FuturesUnordered` + // destructor above, and we have &mut self, so this is safe. unsafe { - loop { - match self.dequeue() { - Dequeue::Empty => break, - Dequeue::Inconsistent => abort("inconsistent in drop"), - Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), - } - } + self.clear(); } } } diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 3a5d41853d..4b9afccaf9 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -345,3 +345,25 @@ fn polled_only_once_at_most_per_iteration() { let mut tasks = FuturesUnordered::::new(); assert_eq!(Poll::Ready(None), tasks.poll_next_unpin(cx)); } + +#[test] +fn clear() { + let mut tasks = FuturesUnordered::from_iter(vec![future::ready(1), future::ready(2)]); + + assert_eq!(block_on(tasks.next()), Some(1)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + tasks.push(future::ready(3)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + assert_eq!(block_on(tasks.next()), None); + assert!(tasks.is_terminated()); + tasks.clear(); + assert!(!tasks.is_terminated()); +} From 37d15b7528881b3af6dca693f49dd1cd98b11f53 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Mon, 10 May 2021 09:18:13 -0400 Subject: [PATCH 039/283] Abortable streams (#2410) --- futures-util/src/abortable.rs | 185 +++++++++++++++++++++++++++ futures-util/src/future/abortable.rs | 158 +---------------------- futures-util/src/future/mod.rs | 4 +- futures-util/src/lib.rs | 5 + futures-util/src/stream/abortable.rs | 19 +++ futures-util/src/stream/mod.rs | 7 + futures/tests/future_abortable.rs | 5 + futures/tests/stream_abortable.rs | 46 +++++++ 8 files changed, 274 insertions(+), 155 deletions(-) create mode 100644 futures-util/src/abortable.rs create mode 100644 futures-util/src/stream/abortable.rs create mode 100644 futures/tests/stream_abortable.rs diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs new file mode 100644 index 0000000000..bb82dd0db8 --- /dev/null +++ b/futures-util/src/abortable.rs @@ -0,0 +1,185 @@ +use crate::task::AtomicWaker; +use alloc::sync::Arc; +use core::fmt; +use core::pin::Pin; +use core::sync::atomic::{AtomicBool, Ordering}; +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; +use futures_core::Stream; +use pin_project_lite::pin_project; + +pin_project! { + /// A future/stream which can be remotely short-circuited using an `AbortHandle`. + #[derive(Debug, Clone)] + #[must_use = "futures/streams do nothing unless you poll them"] + pub struct Abortable { + #[pin] + task: T, + inner: Arc, + } +} + +impl Abortable { + /// Creates a new `Abortable` future/stream using an existing `AbortRegistration`. + /// `AbortRegistration`s can be acquired through `AbortHandle::new`. + /// + /// When `abort` is called on the handle tied to `reg` or if `abort` has + /// already been called, the future/stream will complete immediately without making + /// any further progress. + /// + /// # Examples: + /// + /// Usage with futures: + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future::{Abortable, AbortHandle, Aborted}; + /// + /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); + /// let future = Abortable::new(async { 2 }, abort_registration); + /// abort_handle.abort(); + /// assert_eq!(future.await, Err(Aborted)); + /// # }); + /// ``` + /// + /// Usage with streams: + /// + /// ``` + /// # futures::executor::block_on(async { + /// # use futures::future::{Abortable, AbortHandle}; + /// # use futures::stream::{self, StreamExt}; + /// + /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); + /// let mut stream = Abortable::new(stream::iter(vec![1, 2, 3]), abort_registration); + /// abort_handle.abort(); + /// assert_eq!(stream.next().await, None); + /// # }); + /// ``` + pub fn new(task: T, reg: AbortRegistration) -> Self { + Self { task, inner: reg.inner } + } + + /// Checks whether the task has been aborted. Note that all this + /// method indicates is whether [`AbortHandle::abort`] was *called*. + /// This means that it will return `true` even if: + /// * `abort` was called after the task had completed. + /// * `abort` was called while the task was being polled - the task may still be running and + /// will not be stopped until `poll` returns. + pub fn is_aborted(&self) -> bool { + self.inner.aborted.load(Ordering::Relaxed) + } +} + +/// A registration handle for an `Abortable` task. +/// Values of this type can be acquired from `AbortHandle::new` and are used +/// in calls to `Abortable::new`. +#[derive(Debug)] +pub struct AbortRegistration { + inner: Arc, +} + +/// A handle to an `Abortable` task. +#[derive(Debug, Clone)] +pub struct AbortHandle { + inner: Arc, +} + +impl AbortHandle { + /// Creates an (`AbortHandle`, `AbortRegistration`) pair which can be used + /// to abort a running future or stream. + /// + /// This function is usually paired with a call to [`Abortable::new`]. + pub fn new_pair() -> (Self, AbortRegistration) { + let inner = + Arc::new(AbortInner { waker: AtomicWaker::new(), aborted: AtomicBool::new(false) }); + + (Self { inner: inner.clone() }, AbortRegistration { inner }) + } +} + +// Inner type storing the waker to awaken and a bool indicating that it +// should be aborted. +#[derive(Debug)] +struct AbortInner { + waker: AtomicWaker, + aborted: AtomicBool, +} + +/// Indicator that the `Abortable` task was aborted. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Aborted; + +impl fmt::Display for Aborted { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "`Abortable` future has been aborted") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Aborted {} + +impl Abortable { + fn try_poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + poll: impl Fn(Pin<&mut T>, &mut Context<'_>) -> Poll, + ) -> Poll> { + // Check if the task has been aborted + if self.is_aborted() { + return Poll::Ready(Err(Aborted)); + } + + // attempt to complete the task + if let Poll::Ready(x) = poll(self.as_mut().project().task, cx) { + return Poll::Ready(Ok(x)); + } + + // Register to receive a wakeup if the task is aborted in the future + self.inner.waker.register(cx.waker()); + + // Check to see if the task was aborted between the first check and + // registration. + // Checking with `is_aborted` which uses `Relaxed` is sufficient because + // `register` introduces an `AcqRel` barrier. + if self.is_aborted() { + return Poll::Ready(Err(Aborted)); + } + + Poll::Pending + } +} + +impl Future for Abortable +where + Fut: Future, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.try_poll(cx, |fut, cx| fut.poll(cx)) + } +} + +impl Stream for Abortable +where + St: Stream, +{ + type Item = St::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.try_poll(cx, |stream, cx| stream.poll_next(cx)).map(Result::ok).map(Option::flatten) + } +} + +impl AbortHandle { + /// Abort the `Abortable` stream/future associated with this handle. + /// + /// Notifies the Abortable task associated with this handle that it + /// should abort. Note that if the task is currently being polled on + /// another thread, it will not immediately stop running. Instead, it will + /// continue to run until its poll method returns. + pub fn abort(&self) { + self.inner.aborted.store(true, Ordering::Relaxed); + self.inner.waker.wake(); + } +} diff --git a/futures-util/src/future/abortable.rs b/futures-util/src/future/abortable.rs index 198cc8e668..d017ab7340 100644 --- a/futures-util/src/future/abortable.rs +++ b/futures-util/src/future/abortable.rs @@ -1,101 +1,8 @@ use super::assert_future; -use crate::task::AtomicWaker; -use alloc::sync::Arc; -use core::fmt; -use core::pin::Pin; -use core::sync::atomic::{AtomicBool, Ordering}; +use crate::future::{AbortHandle, Abortable, Aborted}; use futures_core::future::Future; -use futures_core::task::{Context, Poll}; -use pin_project_lite::pin_project; -pin_project! { - /// A future which can be remotely short-circuited using an `AbortHandle`. - #[derive(Debug, Clone)] - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub struct Abortable { - #[pin] - future: Fut, - inner: Arc, - } -} - -impl Abortable -where - Fut: Future, -{ - /// Creates a new `Abortable` future using an existing `AbortRegistration`. - /// `AbortRegistration`s can be acquired through `AbortHandle::new`. - /// - /// When `abort` is called on the handle tied to `reg` or if `abort` has - /// already been called, the future will complete immediately without making - /// any further progress. - /// - /// Example: - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::future::{Abortable, AbortHandle, Aborted}; - /// - /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); - /// let future = Abortable::new(async { 2 }, abort_registration); - /// abort_handle.abort(); - /// assert_eq!(future.await, Err(Aborted)); - /// # }); - /// ``` - pub fn new(future: Fut, reg: AbortRegistration) -> Self { - assert_future::, _>(Self { future, inner: reg.inner }) - } -} - -/// A registration handle for a `Abortable` future. -/// Values of this type can be acquired from `AbortHandle::new` and are used -/// in calls to `Abortable::new`. -#[derive(Debug)] -pub struct AbortRegistration { - inner: Arc, -} - -/// A handle to a `Abortable` future. -#[derive(Debug, Clone)] -pub struct AbortHandle { - inner: Arc, -} - -impl AbortHandle { - /// Creates an (`AbortHandle`, `AbortRegistration`) pair which can be used - /// to abort a running future. - /// - /// This function is usually paired with a call to `Abortable::new`. - /// - /// Example: - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::future::{Abortable, AbortHandle, Aborted}; - /// - /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); - /// let future = Abortable::new(async { 2 }, abort_registration); - /// abort_handle.abort(); - /// assert_eq!(future.await, Err(Aborted)); - /// # }); - /// ``` - pub fn new_pair() -> (Self, AbortRegistration) { - let inner = - Arc::new(AbortInner { waker: AtomicWaker::new(), cancel: AtomicBool::new(false) }); - - (Self { inner: inner.clone() }, AbortRegistration { inner }) - } -} - -// Inner type storing the waker to awaken and a bool indicating that it -// should be cancelled. -#[derive(Debug)] -struct AbortInner { - waker: AtomicWaker, - cancel: AtomicBool, -} - -/// Creates a new `Abortable` future and a `AbortHandle` which can be used to stop it. +/// Creates a new `Abortable` future and an `AbortHandle` which can be used to stop it. /// /// This function is a convenient (but less flexible) alternative to calling /// `AbortHandle::new` and `Abortable::new` manually. @@ -107,63 +14,6 @@ where Fut: Future, { let (handle, reg) = AbortHandle::new_pair(); - (Abortable::new(future, reg), handle) -} - -/// Indicator that the `Abortable` future was aborted. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Aborted; - -impl fmt::Display for Aborted { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "`Abortable` future has been aborted") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Aborted {} - -impl Future for Abortable -where - Fut: Future, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Check if the future has been aborted - if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)); - } - - // attempt to complete the future - if let Poll::Ready(x) = self.as_mut().project().future.poll(cx) { - return Poll::Ready(Ok(x)); - } - - // Register to receive a wakeup if the future is aborted in the... future - self.inner.waker.register(cx.waker()); - - // Check to see if the future was aborted between the first check and - // registration. - // Checking with `Relaxed` is sufficient because `register` introduces an - // `AcqRel` barrier. - if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)); - } - - Poll::Pending - } -} - -impl AbortHandle { - /// Abort the `Abortable` future associated with this handle. - /// - /// Notifies the Abortable future associated with this handle that it - /// should abort. Note that if the future is currently being polled on - /// another thread, it will not immediately stop running. Instead, it will - /// continue to run until its poll method returns. - pub fn abort(&self) { - self.inner.cancel.store(true, Ordering::Relaxed); - self.inner.waker.wake(); - } + let abortable = assert_future::, _>(Abortable::new(future, reg)); + (abortable, handle) } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 8d9152b6c9..7a63e5ff85 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -112,7 +112,9 @@ cfg_target_has_atomic! { #[cfg(feature = "alloc")] mod abortable; #[cfg(feature = "alloc")] - pub use self::abortable::{abortable, Abortable, AbortHandle, AbortRegistration, Aborted}; + pub use crate::abortable::{Abortable, AbortHandle, AbortRegistration, Aborted}; + #[cfg(feature = "alloc")] + pub use abortable::abortable; } // Just a helper function to ensure the futures we're returning all have the diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 76d6ca7666..16871cb4b5 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -336,5 +336,10 @@ pub use crate::io::{ #[cfg(feature = "alloc")] pub mod lock; +cfg_target_has_atomic! { + #[cfg(feature = "alloc")] + mod abortable; +} + mod fns; mod unfold_state; diff --git a/futures-util/src/stream/abortable.rs b/futures-util/src/stream/abortable.rs new file mode 100644 index 0000000000..1fea895822 --- /dev/null +++ b/futures-util/src/stream/abortable.rs @@ -0,0 +1,19 @@ +use super::assert_stream; +use crate::stream::{AbortHandle, Abortable}; +use crate::Stream; + +/// Creates a new `Abortable` stream and an `AbortHandle` which can be used to stop it. +/// +/// This function is a convenient (but less flexible) alternative to calling +/// `AbortHandle::new` and `Abortable::new` manually. +/// +/// This function is only available when the `std` or `alloc` feature of this +/// library is activated, and it is activated by default. +pub fn abortable(stream: St) -> (Abortable, AbortHandle) +where + St: Stream, +{ + let (handle, reg) = AbortHandle::new_pair(); + let abortable = assert_stream::(Abortable::new(stream, reg)); + (abortable, handle) +} diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index ec6c68bbf3..2eac9711a1 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -107,6 +107,13 @@ cfg_target_has_atomic! { mod select_all; #[cfg(feature = "alloc")] pub use self::select_all::{select_all, SelectAll}; + + #[cfg(feature = "alloc")] + mod abortable; + #[cfg(feature = "alloc")] + pub use crate::abortable::{Abortable, AbortHandle, AbortRegistration, Aborted}; + #[cfg(feature = "alloc")] + pub use abortable::abortable; } // Just a helper function to ensure the streams we're returning all have the diff --git a/futures/tests/future_abortable.rs b/futures/tests/future_abortable.rs index 5925c9a27b..e119f0b719 100644 --- a/futures/tests/future_abortable.rs +++ b/futures/tests/future_abortable.rs @@ -10,6 +10,7 @@ fn abortable_works() { let (abortable_rx, abort_handle) = abortable(a_rx); abort_handle.abort(); + assert!(abortable_rx.is_aborted()); assert_eq!(Err(Aborted), block_on(abortable_rx)); } @@ -20,11 +21,14 @@ fn abortable_awakens() { let (waker, counter) = new_count_waker(); let mut cx = Context::from_waker(&waker); + assert_eq!(counter, 0); assert_eq!(Poll::Pending, abortable_rx.poll_unpin(&mut cx)); assert_eq!(counter, 0); + abort_handle.abort(); assert_eq!(counter, 1); + assert!(abortable_rx.is_aborted()); assert_eq!(Poll::Ready(Err(Aborted)), abortable_rx.poll_unpin(&mut cx)); } @@ -35,5 +39,6 @@ fn abortable_resolves() { tx.send(()).unwrap(); + assert!(!abortable_rx.is_aborted()); assert_eq!(Ok(Ok(())), block_on(abortable_rx)); } diff --git a/futures/tests/stream_abortable.rs b/futures/tests/stream_abortable.rs new file mode 100644 index 0000000000..2339dd0522 --- /dev/null +++ b/futures/tests/stream_abortable.rs @@ -0,0 +1,46 @@ +use futures::channel::mpsc; +use futures::executor::block_on; +use futures::stream::{abortable, Stream, StreamExt}; +use futures::task::{Context, Poll}; +use futures::SinkExt; +use futures_test::task::new_count_waker; +use std::pin::Pin; + +#[test] +fn abortable_works() { + let (_tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, abort_handle) = abortable(a_rx); + + abort_handle.abort(); + assert!(abortable_rx.is_aborted()); + assert_eq!(None, block_on(abortable_rx.next())); +} + +#[test] +fn abortable_awakens() { + let (_tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, abort_handle) = abortable(a_rx); + + let (waker, counter) = new_count_waker(); + let mut cx = Context::from_waker(&waker); + + assert_eq!(counter, 0); + assert_eq!(Poll::Pending, Pin::new(&mut abortable_rx).poll_next(&mut cx)); + assert_eq!(counter, 0); + + abort_handle.abort(); + assert_eq!(counter, 1); + assert!(abortable_rx.is_aborted()); + assert_eq!(Poll::Ready(None), Pin::new(&mut abortable_rx).poll_next(&mut cx)); +} + +#[test] +fn abortable_resolves() { + let (mut tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, _abort_handle) = abortable(a_rx); + + block_on(tx.send(())).unwrap(); + + assert!(!abortable_rx.is_aborted()); + assert_eq!(Some(()), block_on(abortable_rx.next())); +} From d663bc1517a428f206f34ed050724db44b2eee0b Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Mon, 10 May 2021 09:45:04 -0400 Subject: [PATCH 040/283] expose iterators from SelectAll (#2428) --- futures-util/src/stream/select_all.rs | 83 ++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index 6b17bad125..e9ab519e5e 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -8,24 +8,30 @@ use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + use super::assert_stream; +use crate::stream::futures_unordered::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; use crate::stream::{FuturesUnordered, StreamExt, StreamFuture}; -/// An unbounded set of streams -/// -/// This "combinator" provides the ability to maintain a set of streams -/// and drive them all to completion. -/// -/// Streams are pushed into this set and their realized values are -/// yielded as they become ready. Streams will only be polled when they -/// generate notifications. This allows to coordinate a large number of streams. -/// -/// Note that you can create a ready-made `SelectAll` via the -/// `select_all` function in the `stream` module, or you can start with an -/// empty set with the `SelectAll::new` constructor. -#[must_use = "streams do nothing unless polled"] -pub struct SelectAll { - inner: FuturesUnordered>, +pin_project! { + /// An unbounded set of streams + /// + /// This "combinator" provides the ability to maintain a set of streams + /// and drive them all to completion. + /// + /// Streams are pushed into this set and their realized values are + /// yielded as they become ready. Streams will only be polled when they + /// generate notifications. This allows to coordinate a large number of streams. + /// + /// Note that you can create a ready-made `SelectAll` via the + /// `select_all` function in the `stream` module, or you can start with an + /// empty set with the `SelectAll::new` constructor. + #[must_use = "streams do nothing unless polled"] + pub struct SelectAll { + #[pin] + inner: FuturesUnordered>, + } } impl Debug for SelectAll { @@ -64,6 +70,26 @@ impl SelectAll { pub fn push(&mut self, stream: St) { self.inner.push(stream.into_future()); } + + /// Returns an iterator that allows inspecting each future in the set. + pub fn iter(&self) -> Iter<'_, StreamFuture> { + self.inner.iter() + } + + /// Returns an iterator that allows inspecting each future in the set. + pub fn iter_pin_ref(self: Pin<&'_ Self>) -> IterPinRef<'_, StreamFuture> { + self.project_ref().inner.iter_pin_ref() + } + + /// Returns an iterator that allows modifying each future in the set. + pub fn iter_mut(&mut self) -> IterMut<'_, StreamFuture> { + self.inner.iter_mut() + } + + /// Returns an iterator that allows modifying each future in the set. + pub fn iter_pin_mut(self: Pin<&mut Self>) -> IterPinMut<'_, StreamFuture> { + self.project().inner.iter_pin_mut() + } } impl Default for SelectAll { @@ -139,3 +165,30 @@ impl Extend for SelectAll { } } } + +impl IntoIterator for SelectAll { + type Item = StreamFuture; + type IntoIter = IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} + +impl<'a, St: Stream + Unpin> IntoIterator for &'a SelectAll { + type Item = &'a StreamFuture; + type IntoIter = Iter<'a, StreamFuture>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, St: Stream + Unpin> IntoIterator for &'a mut SelectAll { + type Item = &'a mut StreamFuture; + type IntoIter = IterMut<'a, StreamFuture>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} From 579b98b73de0d2ea814435dd2fd94e67aead0f55 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Mon, 10 May 2021 10:43:50 -0400 Subject: [PATCH 041/283] Add SelectAll::clear (#2430) --- futures-util/src/stream/select_all.rs | 5 +++++ futures/tests/stream_select_all.rs | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index e9ab519e5e..145099cb29 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -90,6 +90,11 @@ impl SelectAll { pub fn iter_pin_mut(self: Pin<&mut Self>) -> IterPinMut<'_, StreamFuture> { self.project().inner.iter_pin_mut() } + + /// Clears the set, removing all futures. + pub fn clear(&mut self) { + self.inner.clear() + } } impl Default for SelectAll { diff --git a/futures/tests/stream_select_all.rs b/futures/tests/stream_select_all.rs index eb711dda0c..e7e3976398 100644 --- a/futures/tests/stream_select_all.rs +++ b/futures/tests/stream_select_all.rs @@ -1,5 +1,5 @@ use futures::channel::mpsc; -use futures::executor::block_on_stream; +use futures::executor::{block_on, block_on_stream}; use futures::future::{self, FutureExt}; use futures::stream::{self, select_all, FusedStream, SelectAll, StreamExt}; use futures::task::Poll; @@ -76,3 +76,26 @@ fn works_1() { drop((a_tx, b_tx, c_tx)); assert_eq!(None, stream.next()); } + +#[test] +fn clear() { + let mut tasks = + select_all(vec![stream::iter(vec![1].into_iter()), stream::iter(vec![2].into_iter())]); + + assert_eq!(block_on(tasks.next()), Some(1)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + tasks.push(stream::iter(vec![3].into_iter())); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + assert_eq!(block_on(tasks.next()), None); + assert!(tasks.is_terminated()); + tasks.clear(); + assert!(!tasks.is_terminated()); +} From 56bc87ef0949a9c2fed13520051578635ee32246 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 May 2021 00:25:15 +0900 Subject: [PATCH 042/283] Change SelectAll iterators to return stream instead of StreamFuture (#2431) --- .../src/stream/futures_unordered/iter.rs | 20 ++-- futures-util/src/stream/mod.rs | 2 +- futures-util/src/stream/select_all.rs | 109 +++++++++++++----- futures/tests/stream_select_all.rs | 96 +++++++++++++++ 4 files changed, 189 insertions(+), 38 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/iter.rs b/futures-util/src/stream/futures_unordered/iter.rs index 59ea28efa6..04db5ee753 100644 --- a/futures-util/src/stream/futures_unordered/iter.rs +++ b/futures-util/src/stream/futures_unordered/iter.rs @@ -4,20 +4,20 @@ use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::Ordering::Relaxed; -#[derive(Debug)] /// Mutable iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IterPinMut<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, pub(super) _marker: PhantomData<&'a mut FuturesUnordered>, } -#[derive(Debug)] /// Mutable iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IterMut<'a, Fut: Unpin>(pub(super) IterPinMut<'a, Fut>); -#[derive(Debug)] /// Immutable iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IterPinRef<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, @@ -25,12 +25,12 @@ pub struct IterPinRef<'a, Fut> { pub(super) _marker: PhantomData<&'a FuturesUnordered>, } -#[derive(Debug)] /// Immutable iterator over all the futures in the unordered set. +#[derive(Debug)] pub struct Iter<'a, Fut: Unpin>(pub(super) IterPinRef<'a, Fut>); -#[derive(Debug)] /// Owned iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IntoIter { pub(super) len: usize, pub(super) inner: FuturesUnordered, @@ -39,7 +39,7 @@ pub struct IntoIter { impl Iterator for IntoIter { type Item = Fut; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { // `head_all` can be accessed directly and we don't need to spin on // `Task::next_all` since we have exclusive access to the set. let task = self.inner.head_all.get_mut(); @@ -73,7 +73,7 @@ impl ExactSizeIterator for IntoIter {} impl<'a, Fut> Iterator for IterPinMut<'a, Fut> { type Item = Pin<&'a mut Fut>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.task.is_null() { return None; } @@ -102,7 +102,7 @@ impl ExactSizeIterator for IterPinMut<'_, Fut> {} impl<'a, Fut: Unpin> Iterator for IterMut<'a, Fut> { type Item = &'a mut Fut; - fn next(&mut self) -> Option<&'a mut Fut> { + fn next(&mut self) -> Option { self.0.next().map(Pin::get_mut) } @@ -116,7 +116,7 @@ impl ExactSizeIterator for IterMut<'_, Fut> {} impl<'a, Fut> Iterator for IterPinRef<'a, Fut> { type Item = Pin<&'a Fut>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.task.is_null() { return None; } @@ -145,7 +145,7 @@ impl ExactSizeIterator for IterPinRef<'_, Fut> {} impl<'a, Fut: Unpin> Iterator for Iter<'a, Fut> { type Item = &'a Fut; - fn next(&mut self) -> Option<&'a Fut> { + fn next(&mut self) -> Option { self.0.next().map(Pin::get_ref) } diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 2eac9711a1..0b2fc90532 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -104,7 +104,7 @@ cfg_target_has_atomic! { pub use self::futures_unordered::FuturesUnordered; #[cfg(feature = "alloc")] - mod select_all; + pub mod select_all; #[cfg(feature = "alloc")] pub use self::select_all::{select_all, SelectAll}; diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index 145099cb29..3474331adc 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -11,8 +11,7 @@ use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; use super::assert_stream; -use crate::stream::futures_unordered::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; -use crate::stream::{FuturesUnordered, StreamExt, StreamFuture}; +use crate::stream::{futures_unordered, FuturesUnordered, StreamExt, StreamFuture}; pin_project! { /// An unbounded set of streams @@ -71,27 +70,17 @@ impl SelectAll { self.inner.push(stream.into_future()); } - /// Returns an iterator that allows inspecting each future in the set. - pub fn iter(&self) -> Iter<'_, StreamFuture> { - self.inner.iter() + /// Returns an iterator that allows inspecting each stream in the set. + pub fn iter(&self) -> Iter<'_, St> { + Iter(self.inner.iter()) } - /// Returns an iterator that allows inspecting each future in the set. - pub fn iter_pin_ref(self: Pin<&'_ Self>) -> IterPinRef<'_, StreamFuture> { - self.project_ref().inner.iter_pin_ref() + /// Returns an iterator that allows modifying each stream in the set. + pub fn iter_mut(&mut self) -> IterMut<'_, St> { + IterMut(self.inner.iter_mut()) } - /// Returns an iterator that allows modifying each future in the set. - pub fn iter_mut(&mut self) -> IterMut<'_, StreamFuture> { - self.inner.iter_mut() - } - - /// Returns an iterator that allows modifying each future in the set. - pub fn iter_pin_mut(self: Pin<&mut Self>) -> IterPinMut<'_, StreamFuture> { - self.project().inner.iter_pin_mut() - } - - /// Clears the set, removing all futures. + /// Clears the set, removing all streams. pub fn clear(&mut self) { self.inner.clear() } @@ -139,7 +128,7 @@ impl FusedStream for SelectAll { /// streams internally, in the order they become available. /// /// Note that the returned set can also be used to dynamically push more -/// futures into the set as they become available. +/// streams into the set as they become available. /// /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. @@ -172,17 +161,17 @@ impl Extend for SelectAll { } impl IntoIterator for SelectAll { - type Item = StreamFuture; - type IntoIter = IntoIter>; + type Item = St; + type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { - self.inner.into_iter() + IntoIter(self.inner.into_iter()) } } impl<'a, St: Stream + Unpin> IntoIterator for &'a SelectAll { - type Item = &'a StreamFuture; - type IntoIter = Iter<'a, StreamFuture>; + type Item = &'a St; + type IntoIter = Iter<'a, St>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -190,10 +179,76 @@ impl<'a, St: Stream + Unpin> IntoIterator for &'a SelectAll { } impl<'a, St: Stream + Unpin> IntoIterator for &'a mut SelectAll { - type Item = &'a mut StreamFuture; - type IntoIter = IterMut<'a, StreamFuture>; + type Item = &'a mut St; + type IntoIter = IterMut<'a, St>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } + +/// Immutable iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct Iter<'a, St: Unpin>(futures_unordered::Iter<'a, StreamFuture>); + +/// Mutable iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct IterMut<'a, St: Unpin>(futures_unordered::IterMut<'a, StreamFuture>); + +/// Owned iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct IntoIter(futures_unordered::IntoIter>); + +impl<'a, St: Stream + Unpin> Iterator for Iter<'a, St> { + type Item = &'a St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.get_ref(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for Iter<'_, St> {} + +impl<'a, St: Stream + Unpin> Iterator for IterMut<'a, St> { + type Item = &'a mut St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.get_mut(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for IterMut<'_, St> {} + +impl Iterator for IntoIter { + type Item = St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.into_inner(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for IntoIter {} diff --git a/futures/tests/stream_select_all.rs b/futures/tests/stream_select_all.rs index e7e3976398..4ae0735762 100644 --- a/futures/tests/stream_select_all.rs +++ b/futures/tests/stream_select_all.rs @@ -99,3 +99,99 @@ fn clear() { tasks.clear(); assert!(!tasks.is_terminated()); } + +#[test] +fn iter_mut() { + let mut stream = + vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + let mut stream = vec![stream::iter(vec![]), stream::iter(vec![1]), stream::iter(vec![2])] + .into_iter() + .collect::>(); + + assert_eq!(stream.len(), 3); + assert_eq!(block_on(stream.next()), Some(1)); + assert_eq!(stream.len(), 2); + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + assert_eq!(block_on(stream.next()), Some(2)); + assert_eq!(stream.len(), 2); + assert_eq!(block_on(stream.next()), None); + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} + +#[test] +fn iter() { + let stream = vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.iter(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + let mut stream = vec![stream::iter(vec![]), stream::iter(vec![1]), stream::iter(vec![2])] + .into_iter() + .collect::>(); + + assert_eq!(stream.len(), 3); + assert_eq!(block_on(stream.next()), Some(1)); + assert_eq!(stream.len(), 2); + let mut iter = stream.iter(); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + assert_eq!(block_on(stream.next()), Some(2)); + assert_eq!(stream.len(), 2); + assert_eq!(block_on(stream.next()), None); + let mut iter = stream.iter(); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} + +#[test] +fn into_iter() { + let stream = vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.into_iter(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} From 5954cff771dc408127d35ee5de844635b72842c2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 May 2021 01:27:56 +0900 Subject: [PATCH 043/283] futures-macro: improve diagnostics on type mismatch (#2433) --- futures-macro/Cargo.toml | 2 +- futures-macro/src/executor.rs | 45 ++++++++++++++++++++++++++--------- futures/tests/test_macro.rs | 5 ++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 9002a9f391..bb40701665 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -23,4 +23,4 @@ autocfg = "1" proc-macro2 = "1.0" proc-macro-hack = "0.5.19" quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.56", features = ["full"] } diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index 1efb48c7c7..40a091f94c 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -1,5 +1,6 @@ use proc_macro::TokenStream; -use quote::quote; +use proc_macro2::Span; +use quote::{quote, quote_spanned, ToTokens}; pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { if !args.is_empty() { @@ -9,23 +10,45 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { } let mut input = syn::parse_macro_input!(item as syn::ItemFn); - let attrs = &input.attrs; - let vis = &input.vis; - let sig = &mut input.sig; - let body = &input.block; - if sig.asyncness.take().is_none() { - return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") + if input.sig.asyncness.take().is_none() { + return syn::Error::new_spanned(input.sig.fn_token, "Only async functions are supported") .to_compile_error() .into(); } + // If type mismatch occurs, the current rustc points to the last statement. + let (last_stmt_start_span, last_stmt_end_span) = { + let mut last_stmt = input + .block + .stmts + .last() + .map(ToTokens::into_token_stream) + .unwrap_or_default() + .into_iter(); + // `Span` on stable Rust has a limitation that only points to the first + // token, not the whole tokens. We can work around this limitation by + // using the first/last span of the tokens like + // `syn::Error::new_spanned` does. + let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span()); + let end = last_stmt.last().map_or(start, |t| t.span()); + (start, end) + }; + + let path = quote_spanned! {last_stmt_start_span=> + ::futures_test::__private + }; + let body = &input.block; + input.block.stmts = vec![syn::Stmt::Expr( + syn::parse2(quote_spanned! {last_stmt_end_span=> + #path::block_on(async #body) + }) + .unwrap(), + )]; + let gen = quote! { #[::core::prelude::v1::test] - #(#attrs)* - #vis #sig { - ::futures_test::__private::block_on(async move #body) - } + #input }; gen.into() diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs index 2f391997ea..6adf51d8bb 100644 --- a/futures/tests/test_macro.rs +++ b/futures/tests/test_macro.rs @@ -13,3 +13,8 @@ async fn it_is_being_run() { let fut = async { false }; assert!(fut.await); } + +#[futures_test::test] +async fn return_ty() -> Result<(), ()> { + Ok(()) +} From 3d3b6642dd64ea18c56891166ae184ab4a5a1af2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 May 2021 02:14:26 +0900 Subject: [PATCH 044/283] Remove extra text from test attribute docs (#2435) --- futures-macro/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index f3cc774142..8835de4fa8 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -46,7 +46,8 @@ pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } -/// The `test` attribute. +// TODO: Change this to doc comment once rustdoc bug fixed. +// The `test` attribute. #[proc_macro_attribute] pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { crate::executor::test(input, item) From fc080d153bc7bf00429ec5e2b91e2f21f2243846 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 May 2021 20:53:17 +0900 Subject: [PATCH 045/283] Release 0.3.15 (#2436) --- CHANGELOG.md | 34 ++++++++++++++++++++++++---------- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 59 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2aea765b..2209cb2847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,30 @@ +# 0.3.15 - 2021-05-11 +* Use `#[proc_macro]` at Rust 1.45+ to fix an issue where proc macros don't work with rust-analyzer (#2407) +* Support targets that do not have atomic CAS on stable Rust (#2400) +* futures-test: Add async `#[test]` function attribute (#2409) +* Add `stream::abortable` (#2410) +* Add `FuturesUnordered::clear` (#2415) +* Implement `IntoIterator` for `FuturesUnordered` (#2423) +* Implement `Send` and `Sync` for `FuturesUnordered` iterators (#2416) +* Make `FuturesUnordered::iter_pin_ref` public (#2423) +* Add `SelectAll::clear` (#2430) +* Add `SelectAll::{iter, iter_mut}` (#2428) +* Implement `IntoIterator` for `SelectAll` (#2428) +* Implement `Clone` for `WeakShared` (#2396) + # 0.3.14 - 2021-04-10 -- Add `future::SelectAll::into_inner` (#2363) -- Allow calling `UnboundedReceiver::try_next` after `None` (#2369) -- Reexport non-Ext traits from the root of `futures_util` (#2377) -- Add `AsyncSeekExt::stream_position` (#2380) -- Add `stream::Peekable::{next_if, next_if_eq}` (#2379) +* Add `future::SelectAll::into_inner` (#2363) +* Allow calling `UnboundedReceiver::try_next` after `None` (#2369) +* Reexport non-Ext traits from the root of `futures_util` (#2377) +* Add `AsyncSeekExt::stream_position` (#2380) +* Add `stream::Peekable::{next_if, next_if_eq}` (#2379) # 0.3.13 - 2021-02-23 -- Mitigated starvation issues in `FuturesUnordered` (#2333) -- Fixed race with dropping `mpsc::Receiver` (#2304) -- Added `Shared::{strong_count, weak_count}` (#2346) -- Added `no_std` support for `task::noop_waker_ref` (#2332) -- Implemented `Stream::size_hint` for `Either` (#2325) +* Mitigated starvation issues in `FuturesUnordered` (#2333) +* Fixed race with dropping `mpsc::Receiver` (#2304) +* Added `Shared::{strong_count, weak_count}` (#2346) +* Added `no_std` support for `task::noop_waker_ref` (#2332) +* Implemented `Stream::size_hint` for `Either` (#2325) # 0.3.12 - 2021-01-15 * Fixed `Unpin` impl of `future::{MaybeDone, TryMaybeDone}` where trait bounds were accidentally added in 0.3.9. (#2317) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 6d32d56cd2..fae78a0edf 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-channel" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -23,8 +23,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index bcc0b60f91..a2440eab74 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-core" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 225b7c0070..55c69e3a9c 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-executor" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -17,9 +17,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.14", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.15", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 329f09f3bf..b4f50547bf 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-io" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index bb40701665..082faf1547 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-macro" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 6cf7802660..02cb0d3063 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-sink" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 721c1ee4c4..b454722777 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-task" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0e725f873f..0f34875fd8 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-test" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -12,13 +12,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.14", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.14", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.14", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.14", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.14", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.14", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.14", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.15", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.15", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.15", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.15", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.15", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.15", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.15", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 57e3f77884..de77fdab86 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-util" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -39,12 +39,12 @@ cfg-target-has-atomic = [] autocfg = "1" [dependencies] -futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.14", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.14", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.14", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.15", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.15", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.15", default-features = false, optional = true } proc-macro-hack = { version = "0.5.19", optional = true } proc-macro-nested = { version = "0.1.2", optional = true } slab = { version = "0.4.2", optional = true } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 51c710d6e6..c62e99e72b 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures" edition = "2018" -version = "0.3.14" +version = "0.3.15" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" @@ -16,13 +16,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.14", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.14", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.14", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.14", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.14", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.14", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.14", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.15", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.15", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.15", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.15", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From ad2031678e8c43cb49a4a39b2cac41e4f50468df Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 May 2021 22:03:09 +0900 Subject: [PATCH 046/283] Remove cfg_target_has_atomic! macro (#2439) --- .github/workflows/ci.yml | 4 +- ci/no_atomic_cas.sh | 5 ++ futures-channel/build.rs | 2 +- futures-channel/src/lib.rs | 29 +++++------- futures-core/build.rs | 2 +- futures-task/build.rs | 2 +- futures-task/src/lib.rs | 43 ++++++++--------- futures-task/src/spawn.rs | 32 ++++++------- futures-util/build.rs | 2 +- futures-util/src/future/mod.rs | 17 +++---- futures-util/src/lib.rs | 14 ++---- futures-util/src/lock/mod.rs | 35 +++++++------- futures-util/src/stream/mod.rs | 55 +++++++++++---------- futures-util/src/stream/stream/mod.rs | 58 +++++++++++++---------- futures-util/src/stream/try_stream/mod.rs | 38 ++++++++------- futures-util/src/task/mod.rs | 20 ++++---- tools/fmt.sh | 28 ----------- 17 files changed, 184 insertions(+), 202 deletions(-) delete mode 100755 tools/fmt.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c516e3138..51fb1d967d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -248,8 +248,8 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Rust - run: rustup update stable && rustup default stable - - run: tools/fmt.sh + run: rustup update stable + - run: cargo fmt --all -- --check docs: name: cargo doc diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh index 24faa70487..40b2f1b90b 100755 --- a/ci/no_atomic_cas.sh +++ b/ci/no_atomic_cas.sh @@ -1,5 +1,10 @@ #!/bin/bash +# Update the list of targets that do not support atomic CAS operations. +# +# Usage: +# ./ci/no_atomic_cas.sh + set -euo pipefail IFS=$'\n\t' diff --git a/futures-channel/build.rs b/futures-channel/build.rs index c4f341d480..07b50bd552 100644 --- a/futures-channel/build.rs +++ b/futures-channel/build.rs @@ -8,7 +8,7 @@ include!("no_atomic_cas.rs"); // and outside of the normal semver guarantees: // // - `futures_no_atomic_cas` -// Assume the target does not have atomic CAS (compare-and-swap). +// Assume the target does *not* support atomic CAS operations. // This is usually detected automatically by the build script, but you may // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 9377a3e2c2..15e71f81a6 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -18,21 +18,16 @@ #![warn(clippy::all)] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -macro_rules! cfg_target_has_atomic { - ($($item:item)*) => {$( - #[cfg(not(futures_no_atomic_cas))] - $item - )*}; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +extern crate alloc; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - extern crate alloc; - - #[cfg(feature = "alloc")] - mod lock; - #[cfg(feature = "std")] - pub mod mpsc; - #[cfg(feature = "alloc")] - pub mod oneshot; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod lock; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "std")] +pub mod mpsc; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub mod oneshot; diff --git a/futures-core/build.rs b/futures-core/build.rs index c4f341d480..07b50bd552 100644 --- a/futures-core/build.rs +++ b/futures-core/build.rs @@ -8,7 +8,7 @@ include!("no_atomic_cas.rs"); // and outside of the normal semver guarantees: // // - `futures_no_atomic_cas` -// Assume the target does not have atomic CAS (compare-and-swap). +// Assume the target does *not* support atomic CAS operations. // This is usually detected automatically by the build script, but you may // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. diff --git a/futures-task/build.rs b/futures-task/build.rs index c4f341d480..07b50bd552 100644 --- a/futures-task/build.rs +++ b/futures-task/build.rs @@ -8,7 +8,7 @@ include!("no_atomic_cas.rs"); // and outside of the normal semver guarantees: // // - `futures_no_atomic_cas` -// Assume the target does not have atomic CAS (compare-and-swap). +// Assume the target does *not* support atomic CAS operations. // This is usually detected automatically by the build script, but you may // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 439af135af..1622dadbdd 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -10,32 +10,29 @@ #[cfg(feature = "alloc")] extern crate alloc; -macro_rules! cfg_target_has_atomic { - ($($item:item)*) => {$( - #[cfg(not(futures_no_atomic_cas))] - $item - )*}; -} - mod spawn; pub use crate::spawn::{LocalSpawn, Spawn, SpawnError}; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod arc_wake; - #[cfg(feature = "alloc")] - pub use crate::arc_wake::ArcWake; - - #[cfg(feature = "alloc")] - mod waker; - #[cfg(feature = "alloc")] - pub use crate::waker::waker; - - #[cfg(feature = "alloc")] - mod waker_ref; - #[cfg(feature = "alloc")] - pub use crate::waker_ref::{waker_ref, WakerRef}; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod arc_wake; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use crate::arc_wake::ArcWake; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod waker; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use crate::waker::waker; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod waker_ref; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use crate::waker_ref::{waker_ref, WakerRef}; mod future_obj; pub use crate::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj}; diff --git a/futures-task/src/spawn.rs b/futures-task/src/spawn.rs index 50f5d0d56a..f4e63397bd 100644 --- a/futures-task/src/spawn.rs +++ b/futures-task/src/spawn.rs @@ -168,27 +168,25 @@ mod if_alloc { } } - cfg_target_has_atomic! { - use alloc::{ sync::Arc }; - - impl Spawn for Arc { - fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { - (**self).spawn_obj(future) - } + #[cfg(not(futures_no_atomic_cas))] + impl Spawn for alloc::sync::Arc { + fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { + (**self).spawn_obj(future) + } - fn status(&self) -> Result<(), SpawnError> { - (**self).status() - } + fn status(&self) -> Result<(), SpawnError> { + (**self).status() } + } - impl LocalSpawn for Arc { - fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { - (**self).spawn_local_obj(future) - } + #[cfg(not(futures_no_atomic_cas))] + impl LocalSpawn for alloc::sync::Arc { + fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { + (**self).spawn_local_obj(future) + } - fn status_local(&self) -> Result<(), SpawnError> { - (**self).status_local() - } + fn status_local(&self) -> Result<(), SpawnError> { + (**self).status_local() } } } diff --git a/futures-util/build.rs b/futures-util/build.rs index ffe9711414..f8aa5fe4f4 100644 --- a/futures-util/build.rs +++ b/futures-util/build.rs @@ -9,7 +9,7 @@ include!("no_atomic_cas.rs"); // and outside of the normal semver guarantees: // // - `futures_no_atomic_cas` -// Assume the target does not have atomic CAS (compare-and-swap). +// Assume the target does *not* support atomic CAS operations. // This is usually detected automatically by the build script, but you may // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 7a63e5ff85..cd082642b7 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -108,14 +108,15 @@ pub use self::select_ok::{select_ok, SelectOk}; mod either; pub use self::either::Either; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod abortable; - #[cfg(feature = "alloc")] - pub use crate::abortable::{Abortable, AbortHandle, AbortRegistration, Aborted}; - #[cfg(feature = "alloc")] - pub use abortable::abortable; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod abortable; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use crate::abortable::{AbortHandle, AbortRegistration, Abortable, Aborted}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use abortable::abortable; // Just a helper function to ensure the futures we're returning all have the // right implementations. diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 16871cb4b5..c46dfb89a7 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -47,13 +47,6 @@ pub mod __private { } } -macro_rules! cfg_target_has_atomic { - ($($item:item)*) => {$( - #[cfg(not(futures_no_atomic_cas))] - $item - )*}; -} - #[cfg(feature = "sink")] macro_rules! delegate_sink { ($field:ident, $item:ty) => { @@ -336,10 +329,9 @@ pub use crate::io::{ #[cfg(feature = "alloc")] pub mod lock; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod abortable; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod abortable; mod fns; mod unfold_state; diff --git a/futures-util/src/lock/mod.rs b/futures-util/src/lock/mod.rs index 071eef6f62..cf374c016f 100644 --- a/futures-util/src/lock/mod.rs +++ b/futures-util/src/lock/mod.rs @@ -3,20 +3,23 @@ //! This module is only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -cfg_target_has_atomic! { - #[cfg(feature = "std")] - mod mutex; - #[cfg(feature = "std")] - pub use self::mutex::{MappedMutexGuard, Mutex, MutexLockFuture, MutexGuard}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "std")] +mod mutex; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "std")] +pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture}; - #[cfg(any(feature = "bilock", feature = "sink", feature = "io"))] - #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] - #[cfg_attr(not(feature = "bilock"), allow(unreachable_pub))] - mod bilock; - #[cfg(feature = "bilock")] - #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] - pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; - #[cfg(any(feature = "sink", feature = "io"))] - #[cfg(not(feature = "bilock"))] - pub(crate) use self::bilock::BiLock; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(any(feature = "bilock", feature = "sink", feature = "io"))] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] +#[cfg_attr(not(feature = "bilock"), allow(unreachable_pub))] +mod bilock; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(any(feature = "sink", feature = "io"))] +#[cfg(not(feature = "bilock"))] +pub(crate) use self::bilock::BiLock; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "bilock")] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] +pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 0b2fc90532..1fb282fb66 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -91,30 +91,37 @@ pub use self::select::{select, Select}; mod unfold; pub use self::unfold::{unfold, Unfold}; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod futures_ordered; - #[cfg(feature = "alloc")] - pub use self::futures_ordered::FuturesOrdered; - - #[cfg(feature = "alloc")] - pub mod futures_unordered; - #[cfg(feature = "alloc")] - #[doc(inline)] - pub use self::futures_unordered::FuturesUnordered; - - #[cfg(feature = "alloc")] - pub mod select_all; - #[cfg(feature = "alloc")] - pub use self::select_all::{select_all, SelectAll}; - - #[cfg(feature = "alloc")] - mod abortable; - #[cfg(feature = "alloc")] - pub use crate::abortable::{Abortable, AbortHandle, AbortRegistration, Aborted}; - #[cfg(feature = "alloc")] - pub use abortable::abortable; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod futures_ordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use self::futures_ordered::FuturesOrdered; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub mod futures_unordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[doc(inline)] +pub use self::futures_unordered::FuturesUnordered; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub mod select_all; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use self::select_all::{select_all, SelectAll}; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod abortable; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use crate::abortable::{AbortHandle, AbortRegistration, Abortable, Aborted}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use abortable::abortable; // Just a helper function to ensure the streams we're returning all have the // right implementations. diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 9089e6e3df..420ac82dea 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -169,35 +169,41 @@ mod scan; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::scan::Scan; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod buffer_unordered; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::buffer_unordered::BufferUnordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod buffer_unordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::buffer_unordered::BufferUnordered; - #[cfg(feature = "alloc")] - mod buffered; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::buffered::Buffered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod buffered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::buffered::Buffered; - #[cfg(feature = "alloc")] - mod for_each_concurrent; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::for_each_concurrent::ForEachConcurrent; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod for_each_concurrent; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::for_each_concurrent::ForEachConcurrent; - #[cfg(feature = "sink")] - #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] - #[cfg(feature = "alloc")] - mod split; - #[cfg(feature = "sink")] - #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::split::{SplitStream, SplitSink, ReuniteError}; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] +#[cfg(feature = "alloc")] +mod split; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::split::{ReuniteError, SplitSink, SplitStream}; #[cfg(feature = "std")] mod catch_unwind; diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 11cd9c0d31..b7d4d2c085 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -110,25 +110,29 @@ mod try_take_while; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_take_while::TryTakeWhile; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - mod try_buffer_unordered; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::try_buffer_unordered::TryBufferUnordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod try_buffer_unordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_buffer_unordered::TryBufferUnordered; - #[cfg(feature = "alloc")] - mod try_buffered; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::try_buffered::TryBuffered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod try_buffered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_buffered::TryBuffered; - #[cfg(feature = "alloc")] - mod try_for_each_concurrent; - #[cfg(feature = "alloc")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use self::try_for_each_concurrent::TryForEachConcurrent; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod try_for_each_concurrent; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_for_each_concurrent::TryForEachConcurrent; #[cfg(feature = "io")] #[cfg(feature = "std")] diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index c4afe308cd..eff6d48d2f 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -19,18 +19,20 @@ pub use futures_task::noop_waker; #[cfg(feature = "std")] pub use futures_task::noop_waker_ref; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - pub use futures_task::ArcWake; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::ArcWake; - #[cfg(feature = "alloc")] - pub use futures_task::waker; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::waker; - #[cfg(feature = "alloc")] - pub use futures_task::{waker_ref, WakerRef}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::{waker_ref, WakerRef}; - pub use futures_core::task::__internal::AtomicWaker; -} +#[cfg(not(futures_no_atomic_cas))] +pub use futures_core::task::__internal::AtomicWaker; mod spawn; pub use self::spawn::{LocalSpawnExt, SpawnExt}; diff --git a/tools/fmt.sh b/tools/fmt.sh deleted file mode 100755 index 32f4f00748..0000000000 --- a/tools/fmt.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Format all rust code. -# -# Usage: -# ./tools/fmt.sh -# -# This is similar to `cargo fmt`, but unlike `cargo fmt`, it can recognize -# modules defined inside macros. -# Refs: https://github.com/rust-lang/rustfmt/issues/4078 - -set -euo pipefail -IFS=$'\n\t' - -cd "$(cd "$(dirname "${0}")" && pwd)"/.. - -# shellcheck disable=SC2046 -if [[ -z "${CI:-}" ]]; then - # `cargo fmt` cannot recognize modules defined inside macros, so run - # rustfmt directly. - # Refs: https://github.com/rust-lang/rustfmt/issues/4078 - rustfmt $(git ls-files "*.rs") -else - # `cargo fmt` cannot recognize modules defined inside macros, so run - # rustfmt directly. - # Refs: https://github.com/rust-lang/rustfmt/issues/4078 - rustfmt --check $(git ls-files "*.rs") -fi From a43d90e1204ae36c7de3218d719954285de37f37 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 May 2021 22:07:48 +0900 Subject: [PATCH 047/283] futures-core: Remove __private module (#2440) --- futures-core/src/lib.rs | 6 ------ futures-core/src/task/poll.rs | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index e363ff777d..e4605befd9 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -20,9 +20,3 @@ pub use self::stream::{FusedStream, Stream, TryStream}; #[macro_use] pub mod task; - -// Not public API. -#[doc(hidden)] -pub mod __private { - pub use core::task::Poll; -} diff --git a/futures-core/src/task/poll.rs b/futures-core/src/task/poll.rs index 930d888d2d..607e78e060 100644 --- a/futures-core/src/task/poll.rs +++ b/futures-core/src/task/poll.rs @@ -5,8 +5,8 @@ macro_rules! ready { ($e:expr $(,)?) => { match $e { - $crate::__private::Poll::Ready(t) => t, - $crate::__private::Poll::Pending => return $crate::__private::Poll::Pending, + $crate::task::Poll::Ready(t) => t, + $crate::task::Poll::Pending => return $crate::task::Poll::Pending, } }; } From d17a10b74bfaffc82e270f9877f4d6303a4aa5c9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 May 2021 22:41:44 +0900 Subject: [PATCH 048/283] Add #[doc(inline)] to export of SelectAll (#2442) --- futures-util/src/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 1fb282fb66..50cd91fc23 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -111,6 +111,7 @@ pub use self::futures_unordered::FuturesUnordered; pub mod select_all; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] +#[doc(inline)] pub use self::select_all::{select_all, SelectAll}; #[cfg(not(futures_no_atomic_cas))] From 8abb21ea78989251314a965f6ae597ae5fc0419a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 May 2021 22:47:30 +0900 Subject: [PATCH 049/283] Adjust lints (#2441) --- .github/workflows/ci.yml | 12 ++++++------ futures-channel/src/lib.rs | 19 ++++++++++++++----- futures-channel/src/mpsc/mod.rs | 1 - futures-core/src/lib.rs | 11 ++++++++--- futures-executor/src/lib.rs | 19 ++++++++++++++----- futures-io/src/lib.rs | 11 ++++++++--- futures-macro/src/lib.rs | 14 ++++++++------ futures-sink/src/lib.rs | 11 ++++++++--- futures-task/src/lib.rs | 11 ++++++++--- futures-test/src/lib.rs | 19 ++++++++++++++----- futures-util/src/io/into_sink.rs | 1 - futures-util/src/lib.rs | 19 ++++++++++++++----- futures-util/src/lock/mutex.rs | 1 - futures-util/src/sink/buffer.rs | 2 -- futures/src/lib.rs | 19 ++++++++++++++----- 15 files changed, 116 insertions(+), 54 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51fb1d967d..64cff420ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,11 +38,11 @@ jobs: - run: cargo test --workspace --all-features --release core-msrv: - name: cargo +${{ matrix.rust }} build (futures-{core, io, sink, task, channel}) + name: cargo +${{ matrix.rust }} build (futures-{core, io, sink, task}) strategy: matrix: rust: - # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task, futures-channel. + # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task. # When updating this, the reminder to update the minimum required version in .clippy.toml. - 1.36.0 runs-on: ubuntu-latest @@ -58,22 +58,22 @@ jobs: # Check no-default-features - run: | cargo hack build --workspace --ignore-private --no-default-features \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test # Check alloc feature - run: | cargo hack build --workspace --ignore-private --no-default-features --features alloc --ignore-unknown-features \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test # Check std feature - run: | cargo hack build --workspace --ignore-private --no-default-features --features std \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test util-msrv: name: cargo +${{ matrix.rust }} build strategy: matrix: rust: - # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-test. + # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-channel, futures-test. # When updating this, the reminder to update the minimum required version in README.md. - 1.41.0 runs-on: ubuntu-latest diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 15e71f81a6..4cd936d552 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -12,11 +12,20 @@ //! library is activated, and it is activated by default. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 28612da84d..1ff7ae4d7f 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -510,7 +510,6 @@ impl BoundedSenderInner { // Do the send without failing. // Can be called only by bounded sender. - #[allow(clippy::debug_assert_with_mut_call)] fn do_send_b(&mut self, msg: T) -> Result<(), TrySendError> { // Anyone callig do_send *should* make sure there is room first, // but assert here for tests as a sanity check. diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index e4605befd9..35666065c2 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,11 +1,16 @@ //! Core traits and types for asynchronous operations in Rust. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index 873ac7a684..b1af87545f 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -37,11 +37,20 @@ //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(feature = "std")] diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index 998042d299..7e03b8e5a0 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -10,11 +10,16 @@ #![cfg_attr(all(feature = "read-initializer", feature = "std"), feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "read-initializer", not(feature = "unstable")))] diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 8835de4fa8..d1cbc3ce94 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -1,11 +1,13 @@ //! The futures-rs procedural macro implementations. -#![recursion_limit = "128"] -#![warn(rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] // Since https://github.com/rust-lang/cargo/pull/7700 `proc_macro` is part of the prelude for // proc-macro crates, but to support older compilers we still need this explicit `extern crate`. diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index 4dc2fb3d37..0328740efd 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -4,11 +4,16 @@ //! asynchronously. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 1622dadbdd..c72460744c 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,11 +1,16 @@ //! Tools for working with tasks. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. #![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 1117bb36cf..2eb4a1c4cd 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -1,10 +1,19 @@ //! Utilities to make testing [`Future`s](futures_core::future::Future) easier -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #[cfg(not(feature = "std"))] compile_error!( diff --git a/futures-util/src/io/into_sink.rs b/futures-util/src/io/into_sink.rs index 384b8e3b92..6a41ee2269 100644 --- a/futures-util/src/io/into_sink.rs +++ b/futures-util/src/io/into_sink.rs @@ -62,7 +62,6 @@ impl> Sink for IntoSink { Poll::Ready(Ok(())) } - #[allow(clippy::debug_assert_with_mut_call)] fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { debug_assert!(self.buffer.is_none()); *self.project().buffer = Some(Block { offset: 0, bytes: item }); diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index c46dfb89a7..5f803a7274 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -4,11 +4,20 @@ #![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)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "bilock", not(feature = "unstable")))] diff --git a/futures-util/src/lock/mutex.rs b/futures-util/src/lock/mutex.rs index a849aeeb38..85dcb1537b 100644 --- a/futures-util/src/lock/mutex.rs +++ b/futures-util/src/lock/mutex.rs @@ -66,7 +66,6 @@ impl Waiter { } } -#[allow(clippy::identity_op)] // https://github.com/rust-lang/rust-clippy/issues/3445 const IS_LOCKED: usize = 1 << 0; const HAS_WAITERS: usize = 1 << 1; diff --git a/futures-util/src/sink/buffer.rs b/futures-util/src/sink/buffer.rs index c6ea548d65..4aa6c36033 100644 --- a/futures-util/src/sink/buffer.rs +++ b/futures-util/src/sink/buffer.rs @@ -91,14 +91,12 @@ impl, Item> Sink for Buffer { } } - #[allow(clippy::debug_assert_with_mut_call)] fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; debug_assert!(self.buf.is_empty()); self.project().sink.poll_flush(cx) } - #[allow(clippy::debug_assert_with_mut_call)] fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; debug_assert!(self.buf.is_empty()); diff --git a/futures/src/lib.rs b/futures/src/lib.rs index d15c16c377..9aa1090db8 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -80,11 +80,20 @@ #![cfg_attr(feature = "read-initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "bilock", not(feature = "unstable")))] From b32fbc45da500999b4c6696016828a14aed45b13 Mon Sep 17 00:00:00 2001 From: Ivan Leshchenko Date: Mon, 17 May 2021 19:38:06 +0300 Subject: [PATCH 050/283] Implement try_chunks (#2438) --- futures-util/src/stream/mod.rs | 3 + futures-util/src/stream/try_stream/mod.rs | 55 ++++++++ .../src/stream/try_stream/try_chunks.rs | 131 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 futures-util/src/stream/try_stream/try_chunks.rs diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 50cd91fc23..40ba11310f 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -62,6 +62,9 @@ pub use self::try_stream::IntoAsyncRead; #[cfg(feature = "alloc")] pub use self::try_stream::{TryBufferUnordered, TryBuffered, TryForEachConcurrent}; +#[cfg(feature = "alloc")] +pub use self::try_stream::{TryChunks, TryChunksError}; + // Primitive streams mod iter; diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index b7d4d2c085..455ddca3f4 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -12,6 +12,8 @@ use crate::fns::{ use crate::future::assert_future; use crate::stream::assert_stream; use crate::stream::{Inspect, Map}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::pin::Pin; use futures_core::{ future::{Future, TryFuture}, @@ -94,6 +96,12 @@ mod try_concat; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_concat::TryConcat; +#[cfg(feature = "alloc")] +mod try_chunks; +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_chunks::{TryChunks, TryChunksError}; + mod try_fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_fold::TryFold; @@ -576,6 +584,53 @@ pub trait TryStreamExt: TryStream { assert_future::, _>(TryCollect::new(self)) } + /// An adaptor for chunking up successful items of the stream inside a vector. + /// + /// This combinator will attempt to pull successful 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. + /// + /// Note that the vectors returned from this iterator may not always have + /// `capacity` elements. 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. + /// + /// This function is similar to + /// [`StreamExt::chunks`](crate::stream::StreamExt::chunks) but exits + /// early if an error occurs. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, TryChunksError, TryStreamExt}; + /// + /// let stream = stream::iter(vec![Ok::(1), Ok(2), Ok(3), Err(4), Ok(5), Ok(6)]); + /// let mut stream = stream.try_chunks(2); + /// + /// assert_eq!(stream.try_next().await, Ok(Some(vec![1, 2]))); + /// assert_eq!(stream.try_next().await, Err(TryChunksError(vec![3], 4))); + /// assert_eq!(stream.try_next().await, Ok(Some(vec![5, 6]))); + /// # }) + /// ``` + /// + /// # Panics + /// + /// This method will panic if `capacity` is zero. + #[cfg(feature = "alloc")] + fn try_chunks(self, capacity: usize) -> TryChunks + where + Self: Sized, + { + assert_stream::, TryChunksError>, _>( + TryChunks::new(self, capacity), + ) + } + /// Attempt to filter the values produced by this stream according to the /// provided asynchronous closure. /// diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs new file mode 100644 index 0000000000..ac10df2114 --- /dev/null +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -0,0 +1,131 @@ +use crate::stream::{Fuse, IntoStream, StreamExt}; + +use alloc::vec::Vec; +use core::pin::Pin; +use core::{fmt, mem}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream, TryStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_chunks`](super::TryStreamExt::try_chunks) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryChunks { + #[pin] + stream: Fuse>, + items: Vec, + cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 + } +} + +impl TryChunks { + pub(super) fn new(stream: St, capacity: usize) -> Self { + assert!(capacity > 0); + + Self { + stream: IntoStream::new(stream).fuse(), + items: Vec::with_capacity(capacity), + cap: capacity, + } + } + + fn take(self: Pin<&mut Self>) -> Vec { + let cap = self.cap; + mem::replace(self.project().items, Vec::with_capacity(cap)) + } + + delegate_access_inner!(stream, St, (. .)); +} + +impl Stream for TryChunks { + #[allow(clippy::type_complexity)] + type Item = Result, TryChunksError>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.as_mut().project(); + loop { + match ready!(this.stream.as_mut().try_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) => match item { + Ok(item) => { + this.items.push(item); + if this.items.len() >= *this.cap { + return Poll::Ready(Some(Ok(self.take()))); + } + } + Err(e) => { + return Poll::Ready(Some(Err(TryChunksError(self.take(), e)))); + } + }, + + // Since the underlying stream ran out of values, return what we + // have buffered, if we have anything. + None => { + let last = if this.items.is_empty() { + None + } else { + let full_buf = mem::replace(this.items, Vec::new()); + Some(full_buf) + }; + + return Poll::Ready(last.map(Ok)); + } + } + } + } + + 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 TryChunks { + 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 TryChunks +where + S: TryStream + Sink, +{ + type Error = >::Error; + + delegate_sink!(stream, Item); +} + +/// Error indicating, that while chunk was collected inner stream produced an error. +/// +/// Contains all items that were collected before an error occured, and the stream error itself. +#[derive(PartialEq, Eq)] +pub struct TryChunksError(pub Vec, pub E); + +impl fmt::Debug for TryChunksError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.1.fmt(f) + } +} + +impl fmt::Display for TryChunksError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.1.fmt(f) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryChunksError {} From e5c15d482a4158154cac3216da6b5b235e030755 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 28 May 2021 02:16:40 +0900 Subject: [PATCH 051/283] Test i686-unknown-linux-gnu and aarch64-unknown-linux-gnu on CI (#2447) --- .github/workflows/ci.yml | 37 ++++++++++++++++++++++------- futures/tests/async_await_macros.rs | 2 ++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64cff420ab..3788885ecf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,24 +19,40 @@ defaults: jobs: test: - name: cargo +${{ matrix.rust }} test (${{ matrix.os }}) + name: cargo test (${{ matrix.os }}) strategy: + fail-fast: false matrix: - rust: - - nightly os: - - ubuntu-latest - - macos-latest - - windows-latest + - ubuntu-latest + - macos-latest + - windows-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. - run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + run: rustup update nightly --no-self-update && rustup default nightly - run: cargo test --workspace --all-features - run: cargo test --workspace --all-features --release + cross: + name: cross test --target ${{ matrix.target }} + strategy: + fail-fast: false + matrix: + target: + - i686-unknown-linux-gnu + - aarch64-unknown-linux-gnu + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: cargo install cross + - run: cross test --target ${{ matrix.target }} --workspace --all-features + - run: cross test --target ${{ matrix.target }} --workspace --all-features --release + core-msrv: name: cargo +${{ matrix.rust }} build (futures-{core, io, sink, task}) strategy: @@ -100,6 +116,7 @@ jobs: build: name: cargo +${{ matrix.rust }} build strategy: + fail-fast: false matrix: rust: - stable @@ -130,10 +147,13 @@ jobs: no-std: name: cargo build --target ${{ matrix.target }} strategy: + fail-fast: false matrix: + # thumbv7m-none-eabi supports atomic CAS. + # thumbv6m-none-eabi supports atomic, but not atomic CAS. target: - - thumbv6m-none-eabi - thumbv7m-none-eabi + - thumbv6m-none-eabi runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -210,6 +230,7 @@ jobs: san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} strategy: + fail-fast: false matrix: sanitizer: - address diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index 3e461b50eb..19833d01ca 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -158,6 +158,7 @@ fn select_nested() { assert_eq!(res, 3); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn select_size() { let fut = async { @@ -212,6 +213,7 @@ fn select_on_non_unpin_expressions_with_default() { assert_eq!(res, 5); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn select_on_non_unpin_size() { // The returned Future is !Unpin From 30470162bcf21dafcecd79be030bbd4ef037435b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 8 Jun 2021 01:46:36 +0900 Subject: [PATCH 052/283] Update list of targets that do not support atomic CAS (#2448) --- no_atomic_cas.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 0819af1a45..4708bf8534 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -3,6 +3,8 @@ const NO_ATOMIC_CAS_TARGETS: &[&str] = &[ "avr-unknown-gnu-atmega328", + "bpfeb-unknown-none", + "bpfel-unknown-none", "msp430-none-elf", "riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", From 64f0050e385679267bc30cbc01f0fbe06a070b19 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 9 Jun 2021 01:02:08 +0900 Subject: [PATCH 053/283] Fix typos (#2449) --- CHANGELOG.md | 12 ++++++------ examples/functional/src/main.rs | 2 +- examples/imperative/src/main.rs | 2 +- futures-channel/src/mpsc/mod.rs | 2 +- futures-executor/src/thread_pool.rs | 4 ++-- futures-executor/tests/local_pool.rs | 2 +- futures-test/src/task/mod.rs | 2 +- futures-util/src/io/fill_buf.rs | 2 +- futures-util/src/stream/futures_unordered/mod.rs | 2 +- futures-util/src/stream/try_stream/try_chunks.rs | 2 +- futures/src/lib.rs | 2 +- futures/tests/try_join.rs | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2209cb2847..86b614f4a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,7 +102,7 @@ * Fixed bug with zero-size buffers in vectored IO (#1998) * `AtomicWaker::new()` is now `const fn` (#2007) * Fixed bug between threadpool and user park/unparking (#2010) -* Added `stream::Peakable::peek` (#2021) +* Added `stream::Peekable::peek` (#2021) * Added `StreamExt::scan` (#2044) * Added impl of `AsyncRead`/`Write` for `BufReader`/`Writer` (#2033) * Added impl of `Spawn` and `LocalSpawn` for `Arc` (#2039) @@ -238,7 +238,7 @@ # 0.3.0-alpha.14 - 2019-4-15 * Updated to new nightly `futures_api`. * Changed `Forward` combinator to drop sink after completion, and allow `!Unpin` `Sink`s. -* Added 0.1 <-> 0.3 compatability shim for `Sink`s. +* Added 0.1 <-> 0.3 compatibility shim for `Sink`s. * Changed `Sink::Item` to a generic parameter `Sink`, allowing `Sink`s to accept multiple different types, including types containing references. * Changed `AsyncRead` and `AsyncWrite` to take `Pin<&mut Self>` rather than `&mut self`. @@ -313,8 +313,8 @@ * `StreamExt::boxed` combinator * Unsoundness fix for `FuturesUnordered` * `StreamObj` (similar to `FutureObj`) -* Code examples for compatiblity layer functions -* Use cargo create renaming feature to import `futures@0.1` for compatiblily layer +* Code examples for compatibility layer functions +* Use cargo create renaming feature to import `futures@0.1` for compatibility layer * Import pinning APIs from `core::pin` * Run Clippy in CI only when it is available @@ -338,7 +338,7 @@ # 0.3.0-alpha.2 - 2018-07-30 * The changelog is back! -* Compatiblity with futures API in latest nightly +* Compatibility with futures API in latest nightly * Code examples and doc improvements - IO: Methods of traits `AsyncReadExt`, `AsyncWriteExt` - Future: @@ -360,7 +360,7 @@ * We now use the unstable `use_extern_macros` feature for macro reexports * CI improvements: Named CI jobs, tests are now run on macOS and Linux, the docs are generated and Clippy needs to pass * `#[deny(warnings)]` was removed from all crates and is now only enforced in the CI -* We now have a naming convention for type paramters: `Fut` future, `F` function, `St` stream, `Si` sink, `S` sink & stream, `R` reader, `W` writer, `T` value, `E` error +* We now have a naming convention for type parameters: `Fut` future, `F` function, `St` stream, `Si` sink, `S` sink & stream, `R` reader, `W` writer, `T` value, `E` error * "Task" is now defined as our term for "lightweight thread". The code of the executors and `FuturesUnordered` was refactored to align with this definition. # 0.3.0-alpha.1 - 2018-07-19 diff --git a/examples/functional/src/main.rs b/examples/functional/src/main.rs index 7dea28d674..2ed8b37c58 100644 --- a/examples/functional/src/main.rs +++ b/examples/functional/src/main.rs @@ -38,7 +38,7 @@ fn main() { }; // Actually execute the above future, which will invoke Future::poll and - // subsequenty chain appropriate Future::poll and methods needing executors + // subsequently chain appropriate Future::poll and methods needing executors // to drive all futures. Eventually fut_values will be driven to completion. let values: Vec = executor::block_on(fut_values); diff --git a/examples/imperative/src/main.rs b/examples/imperative/src/main.rs index 10ef7f4df5..44f4153cd9 100644 --- a/examples/imperative/src/main.rs +++ b/examples/imperative/src/main.rs @@ -40,7 +40,7 @@ fn main() { }; // Actually execute the above future, which will invoke Future::poll and - // subsequenty chain appropriate Future::poll and methods needing executors + // subsequently chain appropriate Future::poll and methods needing executors // to drive all futures. Eventually fut_values will be driven to completion. let values: Vec = executor::block_on(fut_values); diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 1ff7ae4d7f..44834b7c95 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -511,7 +511,7 @@ impl BoundedSenderInner { // Do the send without failing. // Can be called only by bounded sender. fn do_send_b(&mut self, msg: T) -> Result<(), TrySendError> { - // Anyone callig do_send *should* make sure there is room first, + // Anyone calling do_send *should* make sure there is room first, // but assert here for tests as a sanity check. debug_assert!(self.poll_unparked(None).is_ready()); diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index f2347dbbdf..5e1f586eb8 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -245,7 +245,7 @@ impl ThreadPoolBuilder { /// Execute closure `f` just prior to shutting down each worker thread. /// /// This hook is intended for bookkeeping and monitoring. - /// The closure `f` will be dropped after the `builder` is droppped + /// The closure `f` will be dropped after the `builder` is dropped /// and all threads in the pool have executed it. /// /// The closure provided will receive an index corresponding to the worker @@ -367,7 +367,7 @@ mod tests { .create() .unwrap(); - // After ThreadPoolBuilder is deconstructed, the tx should be droped + // After ThreadPoolBuilder is deconstructed, the tx should be dropped // so that we can use rx as an iterator. let count = rx.into_iter().count(); assert_eq!(count, 2); diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index 56e6daa077..9b1316b998 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -404,7 +404,7 @@ fn tasks_are_scheduled_fairly() { } // Tests that the use of park/unpark in user-code has no -// effect on the expected behaviour of the executor. +// effect on the expected behavior of the executor. #[test] fn park_unpark_independence() { let mut done = false; diff --git a/futures-test/src/task/mod.rs b/futures-test/src/task/mod.rs index ef6dd1cea4..cec645d9f2 100644 --- a/futures-test/src/task/mod.rs +++ b/futures-test/src/task/mod.rs @@ -1,5 +1,5 @@ // TODO: note that paths like futures_core::task::Context actually get redirected to core::task::Context -// in the redered docs. Is this desirable? If so, should we change the paths here? +// in the rendered docs. Is this desirable? If so, should we change the paths here? // // Also, there is cross crate links in here. They are not going to work anytime soon. Do we put https links // in here? to here: https://rust-lang.github.io/futures-api-docs? The problem is these have a diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs index 19b0d2044c..a1484c0322 100644 --- a/futures-util/src/io/fill_buf.rs +++ b/futures-util/src/io/fill_buf.rs @@ -30,7 +30,7 @@ where let reader = this.reader.take().expect("Polled FillBuf after completion"); match Pin::new(&mut *reader).poll_fill_buf(cx) { - // With polinius it is possible to remove this inner match and just have the correct + // With polonius it is possible to remove this inner match and just have the correct // lifetime of the reference inferred based on which branch is taken Poll::Ready(Ok(_)) => match Pin::new(reader).poll_fill_buf(cx) { Poll::Ready(Ok(slice)) => Poll::Ready(Ok(slice)), diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index a25fbe03ef..4a05d8823e 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -97,7 +97,7 @@ impl LocalSpawn for FuturesUnordered> { // Each task is wrapped in an `Arc` and thereby atomically reference counted. // Also, each task contains an `AtomicBool` which acts as a flag that indicates // whether the task is currently inserted in the atomic queue. When a wake-up -// notifiaction is received, the task will only be inserted into the ready to +// notification is received, the task will only be inserted into the ready to // run queue if it isn't inserted already. impl Default for FuturesUnordered { diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs index ac10df2114..07d4425a81 100644 --- a/futures-util/src/stream/try_stream/try_chunks.rs +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -111,7 +111,7 @@ where /// Error indicating, that while chunk was collected inner stream produced an error. /// -/// Contains all items that were collected before an error occured, and the stream error itself. +/// Contains all items that were collected before an error occurred, and the stream error itself. #[derive(PartialEq, Eq)] pub struct TryChunksError(pub Vec, pub E); diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 9aa1090db8..287696f845 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -67,7 +67,7 @@ //! }; //! //! // Actually execute the above future, which will invoke Future::poll and -//! // subsequenty chain appropriate Future::poll and methods needing executors +//! // subsequently chain appropriate Future::poll and methods needing executors //! // to drive all futures. Eventually fut_values will be driven to completion. //! let values: Vec = executor::block_on(fut_values); //! diff --git a/futures/tests/try_join.rs b/futures/tests/try_join.rs index 8b0b38c1a3..0281ab897d 100644 --- a/futures/tests/try_join.rs +++ b/futures/tests/try_join.rs @@ -3,7 +3,7 @@ use futures::{executor::block_on, try_join}; // TODO: This abuses https://github.com/rust-lang/rust/issues/58733 in order to -// test behaviour of the `try_join!` macro with the never type before it is +// test behavior of the `try_join!` macro with the never type before it is // stabilized. Once `!` is again stabilized this can be removed and replaced // with direct use of `!` below where `Never` is used. trait MyTrait { From 0b29f22752b70cfc89d43a2d598a89bb20a25be8 Mon Sep 17 00:00:00 2001 From: Naja Melan Date: Thu, 17 Jun 2021 07:58:12 +0000 Subject: [PATCH 054/283] Introduce stream::select_with_strategy (#2450) --- futures-util/src/stream/mod.rs | 3 + futures-util/src/stream/select.rs | 79 +++--- .../src/stream/select_with_strategy.rs | 230 ++++++++++++++++++ 3 files changed, 266 insertions(+), 46 deletions(-) create mode 100644 futures-util/src/stream/select_with_strategy.rs diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 40ba11310f..8cf9f80a9d 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -91,6 +91,9 @@ pub use self::poll_fn::{poll_fn, PollFn}; mod select; pub use self::select::{select, Select}; +mod select_with_strategy; +pub use self::select_with_strategy::{select_with_strategy, PollNext, SelectWithStrategy}; + mod unfold; pub use self::unfold::{unfold, Unfold}; diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index 133ac6c7ac..0c1e3af782 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -1,5 +1,5 @@ use super::assert_stream; -use crate::stream::{Fuse, StreamExt}; +use crate::stream::{select_with_strategy, PollNext, SelectWithStrategy}; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; @@ -11,10 +11,7 @@ pin_project! { #[must_use = "streams do nothing unless polled"] pub struct Select { #[pin] - stream1: Fuse, - #[pin] - stream2: Fuse, - flag: bool, + inner: SelectWithStrategy PollNext, PollNext>, } } @@ -22,21 +19,42 @@ pin_project! { /// stream will be polled in a round-robin fashion, and whenever a stream is /// ready to yield an item that item is yielded. /// -/// After one of the two input stream completes, the remaining one will be +/// After one of the two input streams completes, the remaining one will be /// polled exclusively. The returned stream completes when both input /// streams have completed. /// /// Note that this function consumes both streams and returns a wrapped /// version of them. +/// +/// ## Examples +/// +/// ```rust +/// # futures::executor::block_on(async { +/// use futures::stream::{ repeat, select, StreamExt }; +/// +/// let left = repeat(1); +/// let right = repeat(2); +/// +/// let mut out = select(left, right); +/// +/// for _ in 0..100 { +/// // We should be alternating. +/// assert_eq!(1, out.select_next_some().await); +/// assert_eq!(2, out.select_next_some().await); +/// } +/// # }); +/// ``` pub fn select(stream1: St1, stream2: St2) -> Select where St1: Stream, St2: Stream, { + fn round_robin(last: &mut PollNext) -> PollNext { + last.toggle() + } + assert_stream::(Select { - stream1: stream1.fuse(), - stream2: stream2.fuse(), - flag: false, + inner: select_with_strategy(stream1, stream2, round_robin), }) } @@ -44,7 +62,7 @@ impl Select { /// Acquires a reference to the underlying streams that this combinator is /// pulling from. pub fn get_ref(&self) -> (&St1, &St2) { - (self.stream1.get_ref(), self.stream2.get_ref()) + self.inner.get_ref() } /// Acquires a mutable reference to the underlying streams that this @@ -53,7 +71,7 @@ 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_mut(&mut self) -> (&mut St1, &mut St2) { - (self.stream1.get_mut(), self.stream2.get_mut()) + self.inner.get_mut() } /// Acquires a pinned mutable reference to the underlying streams that this @@ -63,7 +81,7 @@ impl Select { /// stream which may otherwise confuse this combinator. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { let this = self.project(); - (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) + this.inner.get_pin_mut() } /// Consumes this combinator, returning the underlying streams. @@ -71,7 +89,7 @@ impl Select { /// 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) -> (St1, St2) { - (self.stream1.into_inner(), self.stream2.into_inner()) + self.inner.into_inner() } } @@ -81,7 +99,7 @@ where St2: Stream, { fn is_terminated(&self) -> bool { - self.stream1.is_terminated() && self.stream2.is_terminated() + self.inner.is_terminated() } } @@ -94,37 +112,6 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - if !*this.flag { - poll_inner(this.flag, this.stream1, this.stream2, cx) - } else { - poll_inner(this.flag, this.stream2, this.stream1, cx) - } - } -} - -fn poll_inner( - flag: &mut bool, - a: Pin<&mut St1>, - b: Pin<&mut St2>, - cx: &mut Context<'_>, -) -> Poll> -where - St1: Stream, - St2: Stream, -{ - let a_done = match a.poll_next(cx) { - Poll::Ready(Some(item)) => { - // give the other stream a chance to go first next time - *flag = !*flag; - return Poll::Ready(Some(item)); - } - Poll::Ready(None) => true, - Poll::Pending => false, - }; - - match b.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) if a_done => Poll::Ready(None), - Poll::Ready(None) | Poll::Pending => Poll::Pending, + this.inner.poll_next(cx) } } diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs new file mode 100644 index 0000000000..1b501132c5 --- /dev/null +++ b/futures-util/src/stream/select_with_strategy.rs @@ -0,0 +1,230 @@ +use super::assert_stream; +use crate::stream::{Fuse, StreamExt}; +use core::{fmt, pin::Pin}; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +/// Type to tell [`SelectWithStrategy`] which stream to poll next. +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub enum PollNext { + /// Poll the first stream. + Left, + /// Poll the second stream. + Right, +} + +impl PollNext { + /// Toggle the value and return the old one. + pub fn toggle(&mut self) -> Self { + let old = *self; + + match self { + PollNext::Left => *self = PollNext::Right, + PollNext::Right => *self = PollNext::Left, + } + + old + } +} + +impl Default for PollNext { + fn default() -> Self { + PollNext::Left + } +} + +pin_project! { + /// Stream for the [`select_with_strategy()`] function. See function docs for details. + #[must_use = "streams do nothing unless polled"] + pub struct SelectWithStrategy { + #[pin] + stream1: Fuse, + #[pin] + stream2: Fuse, + state: State, + clos: Clos, + } +} + +/// This function will attempt to pull items from both streams. You provide a +/// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can +/// store state on `SelectWithStrategy` to which it will receive a `&mut` on every +/// invocation. This allows basing the strategy on prior choices. +/// +/// After one of the two input streams completes, the remaining one will be +/// polled exclusively. The returned stream completes when both input +/// streams have completed. +/// +/// Note that this function consumes both streams and returns a wrapped +/// version of them. +/// +/// ## Examples +/// +/// ### Priority +/// This example shows how to always prioritize the left stream. +/// +/// ```rust +/// # futures::executor::block_on(async { +/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt }; +/// +/// let left = repeat(1); +/// let right = repeat(2); +/// +/// // We don't need any state, so let's make it an empty tuple. +/// // We must provide some type here, as there is no way for the compiler +/// // to infer it. As we don't need to capture variables, we can just +/// // use a function pointer instead of a closure. +/// fn prio_left(_: &mut ()) -> PollNext { PollNext::Left } +/// +/// let mut out = select_with_strategy(left, right, prio_left); +/// +/// for _ in 0..100 { +/// // Whenever we poll out, we will alwas get `1`. +/// assert_eq!(1, out.select_next_some().await); +/// } +/// # }); +/// ``` +/// +/// ### Round Robin +/// This example shows how to select from both streams round robin. +/// Note: this special case is provided by [`futures-util::stream::select`]. +/// +/// ```rust +/// # futures::executor::block_on(async { +/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt }; +/// +/// let left = repeat(1); +/// let right = repeat(2); +/// +/// // We don't need any state, so let's make it an empty tuple. +/// let rrobin = |last: &mut PollNext| last.toggle(); +/// +/// let mut out = select_with_strategy(left, right, rrobin); +/// +/// for _ in 0..100 { +/// // We should be alternating now. +/// assert_eq!(1, out.select_next_some().await); +/// assert_eq!(2, out.select_next_some().await); +/// } +/// # }); +/// ``` +pub fn select_with_strategy( + stream1: St1, + stream2: St2, + which: Clos, +) -> SelectWithStrategy +where + St1: Stream, + St2: Stream, + Clos: FnMut(&mut State) -> PollNext, + State: Default, +{ + assert_stream::(SelectWithStrategy { + stream1: stream1.fuse(), + stream2: stream2.fuse(), + state: Default::default(), + clos: which, + }) +} + +impl SelectWithStrategy { + /// Acquires a reference to the underlying streams that this combinator is + /// pulling from. + pub fn get_ref(&self) -> (&St1, &St2) { + (self.stream1.get_ref(), self.stream2.get_ref()) + } + + /// Acquires a mutable reference to the underlying streams 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 St1, &mut St2) { + (self.stream1.get_mut(), self.stream2.get_mut()) + } + + /// Acquires a pinned mutable reference to the underlying streams 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 St1>, Pin<&mut St2>) { + let this = self.project(); + (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) + } + + /// Consumes this combinator, returning the underlying streams. + /// + /// 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) -> (St1, St2) { + (self.stream1.into_inner(), self.stream2.into_inner()) + } +} + +impl FusedStream for SelectWithStrategy +where + St1: Stream, + St2: Stream, + Clos: FnMut(&mut State) -> PollNext, +{ + fn is_terminated(&self) -> bool { + self.stream1.is_terminated() && self.stream2.is_terminated() + } +} + +impl Stream for SelectWithStrategy +where + St1: Stream, + St2: Stream, + Clos: FnMut(&mut State) -> PollNext, +{ + type Item = St1::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + match (this.clos)(this.state) { + PollNext::Left => poll_inner(this.stream1, this.stream2, cx), + PollNext::Right => poll_inner(this.stream2, this.stream1, cx), + } + } +} + +fn poll_inner( + a: Pin<&mut St1>, + b: Pin<&mut St2>, + cx: &mut Context<'_>, +) -> Poll> +where + St1: Stream, + St2: Stream, +{ + let a_done = match a.poll_next(cx) { + Poll::Ready(Some(item)) => return Poll::Ready(Some(item)), + Poll::Ready(None) => true, + Poll::Pending => false, + }; + + match b.poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(item)), + Poll::Ready(None) if a_done => Poll::Ready(None), + Poll::Ready(None) | Poll::Pending => Poll::Pending, + } +} + +impl fmt::Debug for SelectWithStrategy +where + St1: fmt::Debug, + St2: fmt::Debug, + State: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SelectWithStrategy") + .field("stream1", &self.stream1) + .field("stream2", &self.stream2) + .field("state", &self.state) + .finish() + } +} From 6b56472f944997d2a39f35257542eecb7393805f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 18 Jun 2021 21:11:53 +0900 Subject: [PATCH 055/283] Update to new io_slice_advance interface --- futures-util/src/io/write_all_vectored.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/futures-util/src/io/write_all_vectored.rs b/futures-util/src/io/write_all_vectored.rs index f465209fe2..a8fc4c641c 100644 --- a/futures-util/src/io/write_all_vectored.rs +++ b/futures-util/src/io/write_all_vectored.rs @@ -4,7 +4,6 @@ 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 @@ -19,8 +18,9 @@ pub struct WriteAllVectored<'a, W: ?Sized + Unpin> { 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 { - Self { writer, bufs: IoSlice::advance(bufs, 0) } + pub(super) fn new(writer: &'a mut W, mut bufs: &'a mut [IoSlice<'a>]) -> Self { + IoSlice::advance_slices(&mut bufs, 0); + Self { writer, bufs } } } @@ -34,7 +34,7 @@ impl Future for WriteAllVectored<'_, W> { if n == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } else { - this.bufs = IoSlice::advance(mem::take(&mut this.bufs), n); + IoSlice::advance_slices(&mut this.bufs, n); } } From 464f903c59d1e2ef225ebb14c4157965c0688474 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 18 Jun 2021 21:42:02 +0900 Subject: [PATCH 056/283] Do not use rustup-components-history It sometimes stops: https://github.com/rust-lang/rustup-components-history/issues/34 --- .github/workflows/ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3788885ecf..c88384f599 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -256,12 +256,8 @@ jobs: # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v2 - # - name: Install Rust and Clippy - # run: | - # toolchain=nightly-$(curl -sSf https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy) - # rustup set profile minimal - # rustup default "$toolchain" - # rustup component add clippy + # - name: Install Rust + # run: rustup toolchain install nightly --component clippy && rustup default nightly # - run: cargo clippy --workspace --all-features --all-targets fmt: From e3cd164005738a2d6c3ff56677d3e9d33e961cb5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 18 Jun 2021 21:50:00 +0900 Subject: [PATCH 057/283] Temporarily disable test with release mode on aarch64-unknown-linux-gnu Refs: https://github.com/rust-lang/futures-rs/issues/2451 --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c88384f599..c204ef6670 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,8 @@ jobs: - run: cargo install cross - run: cross test --target ${{ matrix.target }} --workspace --all-features - run: cross test --target ${{ matrix.target }} --workspace --all-features --release + # TODO: https://github.com/rust-lang/futures-rs/issues/2451 + if: matrix.target != 'aarch64-unknown-linux-gnu' core-msrv: name: cargo +${{ matrix.rust }} build (futures-{core, io, sink, task}) From 0afa002e23a3ce49dc2bfbffe71c1fd76160f3e1 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 25 Jun 2021 00:29:51 +1000 Subject: [PATCH 058/283] Remove an incorrect comment (#2456) It looks like this comment was copy-pasted from the previous doc test. --- futures-util/src/stream/select_with_strategy.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs index 1b501132c5..bd86990cdb 100644 --- a/futures-util/src/stream/select_with_strategy.rs +++ b/futures-util/src/stream/select_with_strategy.rs @@ -97,7 +97,6 @@ pin_project! { /// let left = repeat(1); /// let right = repeat(2); /// -/// // We don't need any state, so let's make it an empty tuple. /// let rrobin = |last: &mut PollNext| last.toggle(); /// /// let mut out = select_with_strategy(left, right, rrobin); From 1a1e83c856f14da988c0ea93ced712050c69a185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Barv=C3=AD=C5=99?= Date: Wed, 7 Jul 2021 20:11:03 +0200 Subject: [PATCH 059/283] StreamExt::All / StreamExt::Any (#2460) Closes #2458 Co-authored-by: Marek Barvir --- futures-util/src/stream/stream/all.rs | 92 +++++++++++++++++++++++++++ futures-util/src/stream/stream/any.rs | 92 +++++++++++++++++++++++++++ futures-util/src/stream/stream/mod.rs | 52 +++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 futures-util/src/stream/stream/all.rs create mode 100644 futures-util/src/stream/stream/any.rs diff --git a/futures-util/src/stream/stream/all.rs b/futures-util/src/stream/stream/all.rs new file mode 100644 index 0000000000..ba2baa5cf1 --- /dev/null +++ b/futures-util/src/stream/stream/all.rs @@ -0,0 +1,92 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::Stream; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`all`](super::StreamExt::all) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct All { + #[pin] + stream: St, + f: F, + accum: Option, + #[pin] + future: Option, + } +} + +impl fmt::Debug for All +where + St: fmt::Debug, + Fut: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("All") + .field("stream", &self.stream) + .field("accum", &self.accum) + .field("future", &self.future) + .finish() + } +} + +impl All +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, accum: Some(true), future: None } + } +} + +impl FusedFuture for All +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.accum.is_none() && self.future.is_none() + } +} + +impl Future for All +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Poll::Ready(loop { + if let Some(fut) = this.future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + let acc = this.accum.unwrap() && ready!(fut.poll(cx)); + if !acc { + break false; + } // early exit + *this.accum = Some(acc); + this.future.set(None); + } else if this.accum.is_some() { + // we're waiting on a new item from the stream + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(item) => { + this.future.set(Some((this.f)(item))); + } + None => { + break this.accum.take().unwrap(); + } + } + } else { + panic!("All polled after completion") + } + }) + } +} diff --git a/futures-util/src/stream/stream/any.rs b/futures-util/src/stream/stream/any.rs new file mode 100644 index 0000000000..f023125c70 --- /dev/null +++ b/futures-util/src/stream/stream/any.rs @@ -0,0 +1,92 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::Stream; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`any`](super::StreamExt::any) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Any { + #[pin] + stream: St, + f: F, + accum: Option, + #[pin] + future: Option, + } +} + +impl fmt::Debug for Any +where + St: fmt::Debug, + Fut: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Any") + .field("stream", &self.stream) + .field("accum", &self.accum) + .field("future", &self.future) + .finish() + } +} + +impl Any +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, accum: Some(false), future: None } + } +} + +impl FusedFuture for Any +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.accum.is_none() && self.future.is_none() + } +} + +impl Future for Any +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Poll::Ready(loop { + if let Some(fut) = this.future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + let acc = this.accum.unwrap() || ready!(fut.poll(cx)); + if acc { + break true; + } // early exit + *this.accum = Some(acc); + this.future.set(None); + } else if this.accum.is_some() { + // we're waiting on a new item from the stream + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(item) => { + this.future.set(Some((this.f)(item))); + } + None => { + break this.accum.take().unwrap(); + } + } + } else { + panic!("Any polled after completion") + } + }) + } +} diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 420ac82dea..b3b0155f67 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -70,6 +70,14 @@ mod fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::fold::Fold; +mod any; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::any::Any; + +mod all; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::all::All; + #[cfg(feature = "sink")] mod forward; @@ -627,6 +635,50 @@ pub trait StreamExt: Stream { assert_future::(Fold::new(self, f, init)) } + /// Execute predicate over asynchronous stream, and return `true` if any element in stream satisfied a predicate. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let number_stream = stream::iter(0..10); + /// let contain_three = number_stream.any(|i| async move { i == 3 }); + /// assert_eq!(contain_three.await, true); + /// # }); + /// ``` + fn any(self, f: F) -> Any + where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized, + { + assert_future::(Any::new(self, f)) + } + + /// Execute predicate over asynchronous stream, and return `true` if all element in stream satisfied a predicate. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let number_stream = stream::iter(0..10); + /// let less_then_twenty = number_stream.all(|i| async move { i < 20 }); + /// assert_eq!(less_then_twenty.await, true); + /// # }); + /// ``` + fn all(self, f: F) -> All + where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized, + { + assert_future::(All::new(self, f)) + } + /// Flattens a stream of streams into just one continuous stream. /// /// # Examples From ac6a9cb49ab324dd1915306d2c51a5c2f562167c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 24 Jul 2021 03:32:26 +0900 Subject: [PATCH 060/283] Update changelog --- CHANGELOG.md | 80 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86b614f4a6..b6d4369d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +# 0.3.16 - 2021-07-23 + +* Add `TryStreamExt::try_chunks` (#2438) +* Add `StreamExt::{all, any}` (#2460) +* Add `stream::select_with_strategy` (#2450) +* Update to new `io_slice_advance` interface (#2454) + # 0.3.15 - 2021-05-11 + * Use `#[proc_macro]` at Rust 1.45+ to fix an issue where proc macros don't work with rust-analyzer (#2407) * Support targets that do not have atomic CAS on stable Rust (#2400) * futures-test: Add async `#[test]` function attribute (#2409) @@ -13,6 +21,7 @@ * Implement `Clone` for `WeakShared` (#2396) # 0.3.14 - 2021-04-10 + * Add `future::SelectAll::into_inner` (#2363) * Allow calling `UnboundedReceiver::try_next` after `None` (#2369) * Reexport non-Ext traits from the root of `futures_util` (#2377) @@ -20,6 +29,7 @@ * Add `stream::Peekable::{next_if, next_if_eq}` (#2379) # 0.3.13 - 2021-02-23 + * Mitigated starvation issues in `FuturesUnordered` (#2333) * Fixed race with dropping `mpsc::Receiver` (#2304) * Added `Shared::{strong_count, weak_count}` (#2346) @@ -27,15 +37,19 @@ * Implemented `Stream::size_hint` for `Either` (#2325) # 0.3.12 - 2021-01-15 + * Fixed `Unpin` impl of `future::{MaybeDone, TryMaybeDone}` where trait bounds were accidentally added in 0.3.9. (#2317) # 0.3.11 - 2021-01-14 + * Fixed heap buffer overflow in `AsyncReadExt::{read_to_end, read_to_string}` (#2314) # 0.3.10 - 2021-01-13 + * Fixed type-inference in `sink::unfold` by specifying more of its types (breaking change -- see #2311) # 0.3.9 - 2021-01-08 + * Significantly improved compile time when `async-await` crate feature is disabled (#2273) * Added `stream::repeat_with` (#2279) * Added `StreamExt::unzip` (#2263) @@ -46,6 +60,7 @@ * Re-exported `MapOkOrElse`, `MapInto`, `OkInto`, `TryFlatten`, `WriteAllVectored` (#2275) # 0.3.8 - 2020-11-04 + * Switched proc-macros to use native `#[proc_macro]` at Rust 1.45+ (#2243) * Added `WeakShared` (#2169) * Added `TryStreamExt::try_buffered` (#2245) @@ -54,11 +69,13 @@ * Fixed panic in some `TryStreamExt` combinators (#2250) # 0.3.7 - 2020-10-23 + * Fixed unsoundness in `MappedMutexGuard` (#2240) * Re-exported `TakeUntil` (#2235) * futures-test: Prevent double panic in `panic_waker` (#2236) # 0.3.6 - 2020-10-06 + * Fixed UB due to missing 'static on `task::waker` (#2206) * Added `AsyncBufReadExt::fill_buf` (#2225) * Added `TryStreamExt::try_take_while` (#2212) @@ -72,6 +89,7 @@ * futures-test: Implemented more traits for `AssertUnmoved` (#2208) # 0.3.5 - 2020-05-08 + * Added `StreamExt::flat_map`. * Added `StreamExt::ready_chunks`. * Added `*_unpin` methods to `SinkExt`. @@ -89,12 +107,15 @@ * Removed and replaced a large amount of internal `unsafe`. # 0.3.4 - 2020-02-06 + * Fixed missing `Drop` for `UnboundedReceiver` (#2064) # 0.3.3 - 2020-02-04 + * Fixed compatibility issue with pinned facade (#2062) # 0.3.2 - 2020-02-03 + * Improved buffering performance of `SplitSink` (#1969) * Added `select_biased!` macro (#1976) * Added `hash_receiver` method to mpsc channel (#1962) @@ -112,10 +133,12 @@ * Mitigated starvation issues in `FuturesUnordered` (#2049) * Added `TryFutureExt::map_ok_or_else` (#2058) -# 0.3.1 - 2019-11-7 +# 0.3.1 - 2019-11-07 + * Fix signature of `LocalSpawn` trait (breaking change -- see #1959) -# 0.3.0 - 2019-11-5 +# 0.3.0 - 2019-11-05 + * Stable release along with stable async/await! * Added async/await to default features (#1953) * Changed `Spawn` trait and `FuturesUnordered::push` to take `&self` (#1950) @@ -136,7 +159,8 @@ * Added some missing `Clone` implementations * Documentation fixes -# 0.3.0-alpha.19 - 2019-9-25 +# 0.3.0-alpha.19 - 2019-09-25 + * Stabilized the `async-await` feature (#1816) * Made `async-await` feature no longer require `std` feature (#1815) * Updated `proc-macro2`, `syn`, and `quote` to 1.0 (#1798) @@ -159,7 +183,8 @@ * Removed dependency on `rand` by using our own PRNG (#1837) * Removed `futures-core` dependency from `futures-sink` (#1832) -# 0.3.0-alpha.18 - 2019-8-9 +# 0.3.0-alpha.18 - 2019-08-09 + * Rewrote `join!` and `try_join!` as procedural macros to allow passing expressions (#1783) * Banned manual implementation of `TryFuture` and `TryStream` for forward compatibility. See #1776 for more details. (#1777) * Changed `AsyncReadExt::read_to_end` to return the total number of bytes read (#1721) @@ -178,7 +203,8 @@ * Added `TryStreamExt::try_flatten` (#1731) * Added `FutureExt::now_or_never` (#1747) -# 0.3.0-alpha.17 - 2019-7-3 +# 0.3.0-alpha.17 - 2019-07-03 + * Removed `try_ready!` macro in favor of `ready!(..)?`. (#1602) * Removed `io::Window::{set_start, set_end}` in favor of `io::Window::set`. (#1667) * Re-exported `pin_utils::pin_mut!` macro. (#1686) @@ -211,7 +237,8 @@ * Renamed `Sink::SinkError` to `Sink::Error`. * Made a number of dependencies of `futures-util` optional. -# 0.3.0-alpha.16 - 2019-5-10 +# 0.3.0-alpha.16 - 2019-05-10 + * Updated to new nightly `async_await`. * Changed `AsyncRead::poll_vectored_read` and `AsyncWrite::poll_vectored_write` to use stabilized `std::io::{IoSlice, IoSliceMut}` instead of `iovec::IoVec`, and renamed to @@ -222,7 +249,8 @@ * Added `AsyncBufReadExt::{read_line, lines}`. * Added `io::BufReader`. -# 0.3.0-alpha.15 - 2019-4-26 +# 0.3.0-alpha.15 - 2019-04-26 + * Updated to stabilized `futures_api`. * Removed `StreamObj`, cautioned against usage of `FutureObj`. * Changed `StreamExt::select` to a function. @@ -235,7 +263,8 @@ * Added functions to partially progress a local pool. * Changed to use our own `Either` type rather than the one from the `either` crate. -# 0.3.0-alpha.14 - 2019-4-15 +# 0.3.0-alpha.14 - 2019-04-15 + * Updated to new nightly `futures_api`. * Changed `Forward` combinator to drop sink after completion, and allow `!Unpin` `Sink`s. * Added 0.1 <-> 0.3 compatibility shim for `Sink`s. @@ -246,7 +275,8 @@ * Changed `join` and `try_join` combinators to functions. * Fixed propagation of `cfg-target-has-atomic` feature. -# 0.3.0-alpha.13 - 2019-2-20 +# 0.3.0-alpha.13 - 2019-02-20 + * Updated to new nightly with stabilization candidate API. * Removed `LocalWaker`. * Added `#[must_use]` to `Stream` and `Sink` traits. @@ -256,7 +286,8 @@ * Removed `TokioDefaultSpawner` and `tokio-compat`. * Moved intra-crate dependencies to exact versions. -# 0.3.0-alpha.12 - 2019-1-14 +# 0.3.0-alpha.12 - 2019-01-14 + * Updated to new nightly with a modification to `Pin::set`. * Expose `AssertUnmoved` and `PendingOnce`. * Prevent double-panic in `AssertUnmoved`. @@ -264,6 +295,7 @@ * Implement `Default` for `Mutex` and `SelectAll`. # 0.3.0-alpha.11 - 2018-12-27 + * Updated to newly stabilized versions of the `pin` and `arbitrary_self_types` features. * Re-added `select_all` for streams. * Added `TryStream::into_async_read` for converting from a stream of bytes into @@ -273,6 +305,7 @@ * Exposed `join_all` from the facade # 0.3.0-alpha.10 - 2018-11-27 + * Revamped `select!` macro * Added `select_next_some` method for getting only the `Some` elements of a stream from `select!` * Added `futures::lock::Mutex` for async-aware synchronization. @@ -285,27 +318,33 @@ * Added `try_concat` # 0.3.0-alpha.9 - 2018-10-18 + * Fixed in response to new nightly handling of 2018 edition + `#![no_std]` # 0.3.0-alpha.8 - 2018-10-16 + * Fixed stack overflow in 0.1 compatibility layer * Added AsyncRead / AsyncWrite compatibility layer * Added Spawn -> 0.1 Executor compatibility * Made 0.1 futures usable on 0.3 executors without an additional global `Task`, accomplished by wrapping 0.1 futures in an 0.1 `Spawn` when using them as 0.3 futures. -* Cleanups and improvments to the `AtomicWaker` implementation. +* Cleanups and improvements to the `AtomicWaker` implementation. # 0.3.0-alpha.7 - 2018-10-01 + * Update to new nightly which removes `Spawn` from `task::Context` and replaces `Context` with `LocalWaker`. * Add `Spawn` and `LocalSpawn` traits and `FutureObj` and `LocalFutureObj` types to `futures-core`. # 0.3.0-alpha.6 - 2018-09-10 + * Replace usage of `crate` visibility with `pub(crate)` now that `crate` visibility is no longer included in the 2018 edition * Remove newly-stabilized "edition" feature in Cargo.toml files # 0.3.0-alpha.5 - 2018-09-03 + * Revert usage of cargo crate renaming feature # 0.3.0-alpha.4 - 2018-09-02 + **Note: This release does not work, use `0.3.0-alpha.5` instead** * `future::ok` and `future:err` to create result wrapping futures (similar to `future::ready`) @@ -319,7 +358,8 @@ * Run Clippy in CI only when it is available # 0.3.0-alpha.3 - 2018-08-15 -* Compatibilty with newest nightly + +* Compatibility with newest nightly * Futures 0.1 compatibility layer including Tokio compatibility * Added `spawn!` and `spawn_with_handle!` macros * Added `SpawnExt` methods `spawn` and `spawn_with_handle` @@ -337,16 +377,17 @@ * Doc improvements to `StreamExt::select` # 0.3.0-alpha.2 - 2018-07-30 + * The changelog is back! * Compatibility with futures API in latest nightly * Code examples and doc improvements - - IO: Methods of traits `AsyncReadExt`, `AsyncWriteExt` - - Future: - - Methods of trait `TryFutureExt` - - Free functions `empty`, `lazy`, `maybe_done`, `poll_fn` and `ready` - - Type `FutureOption` - - Macros `join!`, `select!` and `pending!` - - Stream: Methods of trait `TryStreamExt` + * IO: Methods of traits `AsyncReadExt`, `AsyncWriteExt` + * Future: + * Methods of trait `TryFutureExt` + * Free functions `empty`, `lazy`, `maybe_done`, `poll_fn` and `ready` + * Type `FutureOption` + * Macros `join!`, `select!` and `pending!` + * Stream: Methods of trait `TryStreamExt` * Added `TryStreamExt` combinators `map_ok`, `map_err`, `err_into`, `try_next` and `try_for_each` * Added `Drain`, a sink that will discard all items given to it. Can be created using the `drain` function * Bugfix for the `write_all` combinator @@ -364,6 +405,7 @@ * "Task" is now defined as our term for "lightweight thread". The code of the executors and `FuturesUnordered` was refactored to align with this definition. # 0.3.0-alpha.1 - 2018-07-19 + * Major changes: See [the announcement](https://rust-lang-nursery.github.io/futures-rs/blog/2018/07/19/futures-0.3.0-alpha.1.html) on our new blog for details. The changes are too numerous to be covered in this changelog because nearly every line of code was modified. # 0.1.17 - 2017-10-31 From ab38fd29d3f84f8fc028fa7883e53dba423da0ee Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 24 Jul 2021 03:34:49 +0900 Subject: [PATCH 061/283] Release 0.3.16 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index fae78a0edf..1d3cb74f5e 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-channel" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -23,8 +23,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index a2440eab74..6c1b5e70ec 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-core" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 55c69e3a9c..286b253c65 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-executor" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -17,9 +17,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.15", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.16", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index b4f50547bf..518d4615a0 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-io" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 082faf1547..1acd4a22eb 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-macro" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 02cb0d3063..2144518ec4 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-sink" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index b454722777..3d246add7a 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-task" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0f34875fd8..ef138d4437 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-test" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -12,13 +12,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.15", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.15", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.15", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.15", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.15", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.15", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.15", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.16", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.16", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.16", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.16", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.16", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.16", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.16", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index de77fdab86..98cace6b4c 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-util" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -39,12 +39,12 @@ cfg-target-has-atomic = [] autocfg = "1" [dependencies] -futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.15", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.15", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.15", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.16", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.16", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.16", default-features = false, optional = true } proc-macro-hack = { version = "0.5.19", optional = true } proc-macro-nested = { version = "0.1.2", optional = true } slab = { version = "0.4.2", optional = true } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index c62e99e72b..04cea8a125 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures" edition = "2018" -version = "0.3.15" +version = "0.3.16" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" @@ -16,13 +16,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.15", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.15", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.15", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.15", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.15", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.15", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.15", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.16", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.16", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.16", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.16", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From d6e70466cfef7581e268d0715ca73d11347c9079 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 24 Jul 2021 04:17:25 +0900 Subject: [PATCH 062/283] Add publish script --- ci/publish.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 ci/publish.sh diff --git a/ci/publish.sh b/ci/publish.sh new file mode 100755 index 0000000000..7c8be7926a --- /dev/null +++ b/ci/publish.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -euo pipefail +IFS=$'\n\t' + +# A list of paths to the crate to be published. +# It will be published in the order listed. +MEMBERS=( + "futures-core" + "futures-io" + "futures-sink" + "futures-task" + "futures-channel" + "futures-macro" + "futures-util" + "futures-executor" + "futures" + "futures-test" +) + +cd "$(cd "$(dirname "$0")" && pwd)"/.. + +set -x + +for i in "${!MEMBERS[@]}"; do + ( + cd "${MEMBERS[${i}]}" + cargo publish + ) + if [[ $((i + 1)) != "${#MEMBERS[@]}" ]]; then + sleep 45 + fi +done From 660530fda97ec42abbf1507a1b38d478f4834d53 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 29 Jul 2021 06:38:56 -0400 Subject: [PATCH 063/283] Use FuturesOrdered in join_all (#2412) --- futures-util/src/future/join_all.rs | 95 ++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 427e71ce04..2e52ac17f4 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -12,6 +12,9 @@ use core::task::{Context, Poll}; use super::{assert_future, MaybeDone}; +#[cfg(not(futures_no_atomic_cas))] +use crate::stream::{Collect, FuturesOrdered, StreamExt}; + fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has @@ -19,13 +22,29 @@ fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) } -/// Future for the [`join_all`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] +/// Future for the [`join_all`] function. pub struct JoinAll where F: Future, { - elems: Pin]>>, + kind: JoinAllKind, +} + +#[cfg(not(futures_no_atomic_cas))] +const SMALL: usize = 30; + +pub(crate) enum JoinAllKind +where + F: Future, +{ + Small { + elems: Pin]>>, + }, + #[cfg(not(futures_no_atomic_cas))] + Big { + fut: Collect, Vec>, + }, } impl fmt::Debug for JoinAll @@ -34,7 +53,13 @@ where F::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinAll").field("elems", &self.elems).finish() + match self.kind { + JoinAllKind::Small { ref elems } => { + f.debug_struct("JoinAll").field("elems", elems).finish() + } + #[cfg(not(futures_no_atomic_cas))] + JoinAllKind::Big { ref fut, .. } => fmt::Debug::fmt(fut, f), + } } } @@ -50,10 +75,9 @@ where /// /// # See Also /// -/// This is purposefully a very simple API for basic use-cases. In a lot of -/// cases you will want to use the more powerful -/// [`FuturesOrdered`][crate::stream::FuturesOrdered] APIs, or, if order does -/// not matter, [`FuturesUnordered`][crate::stream::FuturesUnordered]. +/// `join_all` will switch to the more powerful [`FuturesOrdered`] for performance +/// reasons if the number of futures is large. You may want to look into using it or +/// it's counterpart [`FuturesUnordered`][crate::stream::FuturesUnordered] directly. /// /// Some examples for additional functionality provided by these are: /// @@ -75,13 +99,33 @@ where /// assert_eq!(join_all(futures).await, [1, 2, 3]); /// # }); /// ``` -pub fn join_all(i: I) -> JoinAll +pub fn join_all(iter: I) -> JoinAll where I: IntoIterator, I::Item: Future, { - let elems: Box<[_]> = i.into_iter().map(MaybeDone::Future).collect(); - assert_future::::Output>, _>(JoinAll { elems: elems.into() }) + #[cfg(futures_no_atomic_cas)] + { + let elems = iter.into_iter().map(MaybeDone::Future).collect::>().into(); + let kind = JoinAllKind::Small { elems }; + assert_future::::Output>, _>(JoinAll { kind }) + } + #[cfg(not(futures_no_atomic_cas))] + { + let iter = iter.into_iter(); + let kind = match iter.size_hint().1 { + None => JoinAllKind::Big { fut: iter.collect::>().collect() }, + Some(max) => { + if max <= SMALL { + let elems = iter.map(MaybeDone::Future).collect::>().into(); + JoinAllKind::Small { elems } + } else { + JoinAllKind::Big { fut: iter.collect::>().collect() } + } + } + }; + assert_future::::Output>, _>(JoinAll { kind }) + } } impl Future for JoinAll @@ -91,20 +135,27 @@ where type Output = Vec; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut all_done = true; + match &mut self.kind { + JoinAllKind::Small { elems } => { + let mut all_done = true; - for elem in iter_pin_mut(self.elems.as_mut()) { - if elem.poll(cx).is_pending() { - all_done = false; - } - } + for elem in iter_pin_mut(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_output().unwrap()).collect(); - Poll::Ready(result) - } else { - Poll::Pending + if all_done { + let mut elems = mem::replace(elems, Box::pin([])); + let result = + iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); + Poll::Ready(result) + } else { + Poll::Pending + } + } + #[cfg(not(futures_no_atomic_cas))] + JoinAllKind::Big { fut } => Pin::new(fut).poll(cx), } } } From 79965bd22ebfc2b196de094a14f7af59df661458 Mon Sep 17 00:00:00 2001 From: Tyler Hawkes Date: Thu, 29 Jul 2021 10:34:23 -0600 Subject: [PATCH 064/283] New poll_immediate functions to immediately return from a poll (#2452) --- futures-util/src/future/mod.rs | 3 + futures-util/src/future/poll_immediate.rs | 126 ++++++++++++++++++++++ futures-util/src/stream/mod.rs | 3 + futures-util/src/stream/poll_immediate.rs | 80 ++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 futures-util/src/future/poll_immediate.rs create mode 100644 futures-util/src/stream/poll_immediate.rs diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index cd082642b7..374e36512f 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -68,6 +68,9 @@ pub use self::option::OptionFuture; mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; +mod poll_immediate; +pub use self::poll_immediate::{poll_immediate, PollImmediate}; + mod ready; pub use self::ready::{err, ok, ready, Ready}; diff --git a/futures-util/src/future/poll_immediate.rs b/futures-util/src/future/poll_immediate.rs new file mode 100644 index 0000000000..5ae555c73e --- /dev/null +++ b/futures-util/src/future/poll_immediate.rs @@ -0,0 +1,126 @@ +use super::assert_future; +use core::pin::Pin; +use futures_core::task::{Context, Poll}; +use futures_core::{FusedFuture, Future, Stream}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`poll_immediate`](poll_immediate()) function. + /// + /// It will never return [Poll::Pending](core::task::Poll::Pending) + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct PollImmediate { + #[pin] + future: Option + } +} + +impl Future for PollImmediate +where + F: Future, +{ + type Output = Option; + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let inner = + this.future.as_mut().as_pin_mut().expect("PollImmediate polled after completion"); + match inner.poll(cx) { + Poll::Ready(t) => { + this.future.set(None); + Poll::Ready(Some(t)) + } + Poll::Pending => Poll::Ready(None), + } + } +} + +impl FusedFuture for PollImmediate { + fn is_terminated(&self) -> bool { + self.future.is_none() + } +} + +/// A [Stream](crate::stream::Stream) implementation that can be polled repeatedly until the future is done. +/// The stream will never return [Poll::Pending](core::task::Poll::Pending) +/// so polling it in a tight loop is worse than using a blocking synchronous function. +/// ``` +/// # futures::executor::block_on(async { +/// use futures::task::Poll; +/// use futures::{StreamExt, future, pin_mut}; +/// use future::FusedFuture; +/// +/// let f = async { 1_u32 }; +/// pin_mut!(f); +/// let mut r = future::poll_immediate(f); +/// assert_eq!(r.next().await, Some(Poll::Ready(1))); +/// +/// let f = async {futures::pending!(); 42_u8}; +/// pin_mut!(f); +/// let mut p = future::poll_immediate(f); +/// assert_eq!(p.next().await, Some(Poll::Pending)); +/// assert!(!p.is_terminated()); +/// assert_eq!(p.next().await, Some(Poll::Ready(42))); +/// assert!(p.is_terminated()); +/// assert_eq!(p.next().await, None); +/// # }); +/// ``` +impl Stream for PollImmediate +where + F: Future, +{ + type Item = Poll; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + match this.future.as_mut().as_pin_mut() { + // inner is gone, so we can signal that the stream is closed. + None => Poll::Ready(None), + Some(fut) => Poll::Ready(Some(fut.poll(cx).map(|t| { + this.future.set(None); + t + }))), + } + } +} + +/// Creates a future that is immediately ready with an Option of a value. +/// Specifically this means that [poll](core::future::Future::poll()) always returns [Poll::Ready](core::task::Poll::Ready). +/// +/// # Caution +/// +/// When consuming the future by this function, note the following: +/// +/// - This function does not guarantee that the future will run to completion, so it is generally incompatible with passing the non-cancellation-safe future by value. +/// - Even if the future is cancellation-safe, creating and dropping new futures frequently may lead to performance problems. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::future; +/// +/// let r = future::poll_immediate(async { 1_u32 }); +/// assert_eq!(r.await, Some(1)); +/// +/// let p = future::poll_immediate(future::pending::()); +/// assert_eq!(p.await, None); +/// # }); +/// ``` +/// +/// ### Reusing a future +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::{future, pin_mut}; +/// let f = async {futures::pending!(); 42_u8}; +/// pin_mut!(f); +/// assert_eq!(None, future::poll_immediate(&mut f).await); +/// assert_eq!(42, f.await); +/// # }); +/// ``` +pub fn poll_immediate(f: F) -> PollImmediate { + assert_future::, PollImmediate>(PollImmediate { future: Some(f) }) +} diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 8cf9f80a9d..a3740325f8 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -88,6 +88,9 @@ pub use self::pending::{pending, Pending}; mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; +mod poll_immediate; +pub use self::poll_immediate::{poll_immediate, PollImmediate}; + mod select; pub use self::select::{select, Select}; diff --git a/futures-util/src/stream/poll_immediate.rs b/futures-util/src/stream/poll_immediate.rs new file mode 100644 index 0000000000..c7e8a5b3c6 --- /dev/null +++ b/futures-util/src/stream/poll_immediate.rs @@ -0,0 +1,80 @@ +use core::pin::Pin; +use futures_core::task::{Context, Poll}; +use futures_core::Stream; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [poll_immediate](poll_immediate()) function. + /// + /// It will never return [Poll::Pending](core::task::Poll::Pending) + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct PollImmediate { + #[pin] + stream: Option + } +} + +impl Stream for PollImmediate +where + S: Stream, +{ + type Item = Poll; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let stream = match this.stream.as_mut().as_pin_mut() { + // inner is gone, so we can continue to signal that the stream is closed. + None => return Poll::Ready(None), + Some(inner) => inner, + }; + + match stream.poll_next(cx) { + Poll::Ready(Some(t)) => Poll::Ready(Some(Poll::Ready(t))), + Poll::Ready(None) => { + this.stream.set(None); + Poll::Ready(None) + } + Poll::Pending => Poll::Ready(Some(Poll::Pending)), + } + } + + fn size_hint(&self) -> (usize, Option) { + self.stream.as_ref().map_or((0, Some(0)), Stream::size_hint) + } +} + +impl super::FusedStream for PollImmediate { + fn is_terminated(&self) -> bool { + self.stream.is_none() + } +} + +/// Creates a new stream that always immediately returns [Poll::Ready](core::task::Poll::Ready) when awaiting it. +/// +/// This is useful when immediacy is more important than waiting for the next item to be ready. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::stream::{self, StreamExt}; +/// use futures::task::Poll; +/// +/// let mut r = stream::poll_immediate(Box::pin(stream::iter(1_u32..3))); +/// assert_eq!(r.next().await, Some(Poll::Ready(1))); +/// assert_eq!(r.next().await, Some(Poll::Ready(2))); +/// assert_eq!(r.next().await, None); +/// +/// let mut p = stream::poll_immediate(Box::pin(stream::once(async { +/// futures::pending!(); +/// 42_u8 +/// }))); +/// assert_eq!(p.next().await, Some(Poll::Pending)); +/// assert_eq!(p.next().await, Some(Poll::Ready(42))); +/// assert_eq!(p.next().await, None); +/// # }); +/// ``` +pub fn poll_immediate(s: S) -> PollImmediate { + super::assert_stream::, PollImmediate>(PollImmediate { stream: Some(s) }) +} From a74e4e54000b36a80be2352d0044b6530e88dcce Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Fri, 30 Jul 2021 18:31:41 -0600 Subject: [PATCH 065/283] Add stream_select macro (#2262) --- futures-macro/src/lib.rs | 10 ++ futures-macro/src/stream_select.rs | 113 ++++++++++++++++++ futures-util/src/async_await/mod.rs | 7 ++ .../src/async_await/stream_select_mod.rs | 45 +++++++ futures/src/lib.rs | 5 + futures/tests/async_await_macros.rs | 40 ++++++- 6 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 futures-macro/src/stream_select.rs create mode 100644 futures-util/src/async_await/stream_select_mod.rs diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index d1cbc3ce94..fa93e48f1f 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -19,6 +19,7 @@ use proc_macro::TokenStream; mod executor; mod join; mod select; +mod stream_select; /// The `join!` macro. #[cfg_attr(fn_like_proc_macro, proc_macro)] @@ -54,3 +55,12 @@ pub fn select_biased_internal(input: TokenStream) -> TokenStream { pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { crate::executor::test(input, item) } + +/// The `stream_select!` macro. +#[cfg_attr(fn_like_proc_macro, proc_macro)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +pub fn stream_select_internal(input: TokenStream) -> TokenStream { + crate::stream_select::stream_select(input.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/futures-macro/src/stream_select.rs b/futures-macro/src/stream_select.rs new file mode 100644 index 0000000000..9927b53073 --- /dev/null +++ b/futures-macro/src/stream_select.rs @@ -0,0 +1,113 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{parse::Parser, punctuated::Punctuated, Expr, Index, Token}; + +/// The `stream_select!` macro. +pub(crate) fn stream_select(input: TokenStream) -> Result { + let args = Punctuated::::parse_terminated.parse2(input)?; + if args.len() < 2 { + return Ok(quote! { + compile_error!("stream select macro needs at least two arguments.") + }); + } + let generic_idents = (0..args.len()).map(|i| format_ident!("_{}", i)).collect::>(); + let field_idents = (0..args.len()).map(|i| format_ident!("__{}", i)).collect::>(); + let field_idents_2 = (0..args.len()).map(|i| format_ident!("___{}", i)).collect::>(); + let field_indices = (0..args.len()).map(Index::from).collect::>(); + let args = args.iter().map(|e| e.to_token_stream()); + + Ok(quote! { + { + #[derive(Debug)] + struct StreamSelect<#(#generic_idents),*> (#(Option<#generic_idents>),*); + + enum StreamEnum<#(#generic_idents),*> { + #( + #generic_idents(#generic_idents) + ),*, + None, + } + + impl __futures_crate::stream::Stream for StreamEnum<#(#generic_idents),*> + where #(#generic_idents: __futures_crate::stream::Stream + ::std::marker::Unpin,)* + { + type Item = ITEM; + + fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll> { + match self.get_mut() { + #( + Self::#generic_idents(#generic_idents) => ::std::pin::Pin::new(#generic_idents).poll_next(cx) + ),*, + Self::None => panic!("StreamEnum::None should never be polled!"), + } + } + } + + impl __futures_crate::stream::Stream for StreamSelect<#(#generic_idents),*> + where #(#generic_idents: __futures_crate::stream::Stream + ::std::marker::Unpin,)* + { + type Item = ITEM; + + fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll> { + let Self(#(ref mut #field_idents),*) = self.get_mut(); + #( + let mut #field_idents_2 = false; + )* + let mut any_pending = false; + { + let mut stream_array = [#(#field_idents.as_mut().map(|f| StreamEnum::#generic_idents(f)).unwrap_or(StreamEnum::None)),*]; + __futures_crate::async_await::shuffle(&mut stream_array); + + for mut s in stream_array { + if let StreamEnum::None = s { + continue; + } else { + match __futures_crate::stream::Stream::poll_next(::std::pin::Pin::new(&mut s), cx) { + r @ __futures_crate::task::Poll::Ready(Some(_)) => { + return r; + }, + __futures_crate::task::Poll::Pending => { + any_pending = true; + }, + __futures_crate::task::Poll::Ready(None) => { + match s { + #( + StreamEnum::#generic_idents(_) => { #field_idents_2 = true; } + ),*, + StreamEnum::None => panic!("StreamEnum::None should never be polled!"), + } + }, + } + } + } + } + #( + if #field_idents_2 { + *#field_idents = None; + } + )* + if any_pending { + __futures_crate::task::Poll::Pending + } else { + __futures_crate::task::Poll::Ready(None) + } + } + + fn size_hint(&self) -> (usize, Option) { + let mut s = (0, Some(0)); + #( + if let Some(new_hint) = self.#field_indices.as_ref().map(|s| s.size_hint()) { + s.0 += new_hint.0; + // We can change this out for `.zip` when the MSRV is 1.46.0 or higher. + s.1 = s.1.and_then(|a| new_hint.1.map(|b| a + b)); + } + )* + s + } + } + + StreamSelect(#(Some(#args)),*) + + } + }) +} diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 5f5d4aca3f..7276da227a 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -30,6 +30,13 @@ mod select_mod; #[cfg(feature = "async-await-macro")] pub use self::select_mod::*; +// Primary export is a macro +#[cfg(feature = "async-await-macro")] +mod stream_select_mod; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 +#[cfg(feature = "async-await-macro")] +pub use self::stream_select_mod::*; + #[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] mod random; diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs new file mode 100644 index 0000000000..7743406dab --- /dev/null +++ b/futures-util/src/async_await/stream_select_mod.rs @@ -0,0 +1,45 @@ +//! The `stream_select` macro. + +#[cfg(feature = "std")] +#[allow(unreachable_pub)] +#[doc(hidden)] +#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] +pub use futures_macro::stream_select_internal; + +/// Combines several streams, all producing the same `Item` type, into one stream. +/// This is similar to `select_all` but does not require the streams to all be the same type. +/// It also keeps the streams inline, and does not require `Box`s to be allocated. +/// Streams passed to this macro must be `Unpin`. +/// +/// If multiple streams are ready, one will be pseudo randomly selected at runtime. +/// +/// This macro is gated behind the `async-await` feature of this library, which is activated by default. +/// Note that `stream_select!` relies on `proc-macro-hack`, and may require to set the compiler's recursion +/// limit very high, e.g. `#![recursion_limit="1024"]`. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::{stream, StreamExt, stream_select}; +/// let endless_ints = |i| stream::iter(vec![i].into_iter().cycle()).fuse(); +/// +/// let mut endless_numbers = stream_select!(endless_ints(1i32), endless_ints(2), endless_ints(3)); +/// match endless_numbers.next().await { +/// Some(1) => println!("Got a 1"), +/// Some(2) => println!("Got a 2"), +/// Some(3) => println!("Got a 3"), +/// _ => unreachable!(), +/// } +/// # }); +/// ``` +#[cfg(feature = "std")] +#[macro_export] +macro_rules! stream_select { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::stream_select_internal! { + $( $tokens )* + } + }} +} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 287696f845..37476615b9 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -137,6 +137,11 @@ pub use futures_util::{join, pending, poll, select_biased, try_join}; // Async-a #[doc(inline)] pub use futures_util::{future, never, sink, stream, task}; +#[cfg(feature = "std")] +#[cfg(feature = "async-await")] +pub use futures_util::stream_select; + +#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[doc(inline)] pub use futures_channel as channel; diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index 19833d01ca..ce1f3a3379 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -4,7 +4,9 @@ use futures::future::{self, poll_fn, FutureExt}; use futures::sink::SinkExt; use futures::stream::StreamExt; use futures::task::{Context, Poll}; -use futures::{join, pending, pin_mut, poll, select, select_biased, try_join}; +use futures::{ + join, pending, pin_mut, poll, select, select_biased, stream, stream_select, try_join, +}; use std::mem; #[test] @@ -308,6 +310,42 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { }); } +#[test] +#[allow(unused_assignments)] +fn stream_select() { + // stream_select! macro + block_on(async { + let endless_ints = |i| stream::iter(vec![i].into_iter().cycle()); + + let mut endless_ones = stream_select!(endless_ints(1i32), stream::pending()); + assert_eq!(endless_ones.next().await, Some(1)); + assert_eq!(endless_ones.next().await, Some(1)); + + let mut finite_list = + stream_select!(stream::iter(vec![1].into_iter()), stream::iter(vec![1].into_iter())); + assert_eq!(finite_list.next().await, Some(1)); + assert_eq!(finite_list.next().await, Some(1)); + assert_eq!(finite_list.next().await, None); + + let endless_mixed = stream_select!(endless_ints(1i32), endless_ints(2), endless_ints(3)); + // Take 1000, and assert a somewhat even distribution of values. + // The fairness is randomized, but over 1000 samples we should be pretty close to even. + // This test may be a bit flaky. Feel free to adjust the margins as you see fit. + let mut count = 0; + let results = endless_mixed + .take_while(move |_| { + count += 1; + let ret = count < 1000; + async move { ret } + }) + .collect::>() + .await; + assert!(results.iter().filter(|x| **x == 1).count() >= 299); + assert!(results.iter().filter(|x| **x == 2).count() >= 299); + assert!(results.iter().filter(|x| **x == 3).count() >= 299); + }); +} + #[test] fn join_size() { let fut = async { From ccb59ce5e14be1c233eabf73e0c058c088c55951 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Thu, 5 Aug 2021 18:39:28 +0800 Subject: [PATCH 066/283] Implement `Default` trait for `OptionFuture` (#2471) This provides an easy way to create empty `OptionFuture`s. --- futures-util/src/future/option.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 426fe50fea..0bc377758a 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -31,6 +31,12 @@ pin_project! { } } +impl Default for OptionFuture { + fn default() -> Self { + Self { inner: None } + } +} + impl Future for OptionFuture { type Output = Option; From 77c148fb0b6c3b32ecc19a01f6e22a679d12ca56 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Sun, 22 Aug 2021 04:07:06 -0700 Subject: [PATCH 067/283] Prefer doc(no_inline) to doc(hidden) for reexports (#2479) Fixes #2472 --- futures-core/src/lib.rs | 4 ++-- futures-util/src/lib.rs | 8 ++++---- futures/src/lib.rs | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 35666065c2..9c31d8d90b 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -16,11 +16,11 @@ extern crate alloc; pub mod future; -#[doc(hidden)] +#[doc(no_inline)] pub use self::future::{FusedFuture, Future, TryFuture}; pub mod stream; -#[doc(hidden)] +#[doc(no_inline)] pub use self::stream::{FusedStream, Stream, TryStream}; #[macro_use] diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 5f803a7274..76d37994ff 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -301,18 +301,18 @@ macro_rules! delegate_all { } pub mod future; -#[doc(hidden)] +#[doc(no_inline)] pub use crate::future::{Future, FutureExt, TryFuture, TryFutureExt}; pub mod stream; -#[doc(hidden)] +#[doc(no_inline)] pub use crate::stream::{Stream, StreamExt, TryStream, TryStreamExt}; #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub mod sink; #[cfg(feature = "sink")] -#[doc(hidden)] +#[doc(no_inline)] pub use crate::sink::{Sink, SinkExt}; pub mod task; @@ -329,7 +329,7 @@ pub mod compat; pub mod io; #[cfg(feature = "io")] #[cfg(feature = "std")] -#[doc(hidden)] +#[doc(no_inline)] pub use crate::io::{ AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 37476615b9..dcf0e65b97 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -102,26 +102,26 @@ compile_error!("The `bilock` feature requires the `unstable` feature as an expli #[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"); -#[doc(hidden)] +#[doc(no_inline)] pub use futures_core::future::{Future, TryFuture}; -#[doc(hidden)] +#[doc(no_inline)] pub use futures_util::future::{FutureExt, TryFutureExt}; -#[doc(hidden)] +#[doc(no_inline)] pub use futures_core::stream::{Stream, TryStream}; -#[doc(hidden)] +#[doc(no_inline)] pub use futures_util::stream::{StreamExt, TryStreamExt}; -#[doc(hidden)] +#[doc(no_inline)] pub use futures_sink::Sink; -#[doc(hidden)] +#[doc(no_inline)] pub use futures_util::sink::SinkExt; #[cfg(feature = "std")] -#[doc(hidden)] +#[doc(no_inline)] pub use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; #[cfg(feature = "std")] -#[doc(hidden)] +#[doc(no_inline)] pub use futures_util::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; // Macro reexports From 302444b2807d09fedcc0625d0ba65d7c22d2b25e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 20:17:54 +0900 Subject: [PATCH 068/283] Remove remaining cfg(target_has_atomic) (#2487) --- futures/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index dcf0e65b97..362aa3cd99 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -141,7 +141,6 @@ pub use futures_util::{future, never, sink, stream, task}; #[cfg(feature = "async-await")] pub use futures_util::stream_select; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[doc(inline)] pub use futures_channel as channel; From 8b39aa198800ec222c6fa197753e836581fea702 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 21:42:28 +0900 Subject: [PATCH 069/283] Add Peekable::{peek_mut, poll_peek_mut} (#2488) --- futures-util/src/stream/mod.rs | 4 +- futures-util/src/stream/stream/mod.rs | 2 +- futures-util/src/stream/stream/peek.rs | 92 +++++++++++++++++++++++++- futures/tests/auto_traits.rs | 22 ++++++ futures/tests/stream_peekable.rs | 14 ++++ 5 files changed, 130 insertions(+), 4 deletions(-) diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index a3740325f8..ec685b9848 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -19,8 +19,8 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; mod stream; pub use self::stream::{ Chain, Collect, Concat, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, - Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, Peekable, Scan, SelectNextSome, Skip, - SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, + Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, PeekMut, Peekable, Scan, SelectNextSome, + Skip, SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, }; #[cfg(feature = "std")] diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index b3b0155f67..86997f45c9 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -131,7 +131,7 @@ pub use self::select_next_some::SelectNextSome; mod peek; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::peek::{NextIf, NextIfEq, Peek, Peekable}; +pub use self::peek::{NextIf, NextIfEq, Peek, PeekMut, Peekable}; mod skip; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index 217fabaa6e..c72dfc3666 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -33,7 +33,7 @@ impl Peekable { delegate_access_inner!(stream, St, (.)); - /// Produces a `Peek` future which retrieves a reference to the next item + /// Produces a future which retrieves a reference to the next item /// in the stream, or `None` if the underlying stream terminates. pub fn peek(self: Pin<&mut Self>) -> Peek<'_, St> { Peek { inner: Some(self) } @@ -57,6 +57,54 @@ impl Peekable { }) } + /// Produces a future which retrieves a mutable reference to the next item + /// in the stream, or `None` if the underlying stream terminates. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(vec![1, 2, 3]).peekable(); + /// pin_mut!(stream); + /// + /// assert_eq!(stream.as_mut().peek_mut().await, Some(&mut 1)); + /// assert_eq!(stream.as_mut().next().await, Some(1)); + /// + /// // Peek into the stream and modify the value which will be returned next + /// if let Some(p) = stream.as_mut().peek_mut().await { + /// if *p == 2 { + /// *p = 5; + /// } + /// } + /// + /// assert_eq!(stream.collect::>().await, vec![5, 3]); + /// # }); + /// ``` + pub fn peek_mut(self: Pin<&mut Self>) -> PeekMut<'_, St> { + PeekMut { inner: Some(self) } + } + + /// Peek retrieves a mutable reference to the next item in the stream. + pub fn poll_peek_mut( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if this.peeked.is_some() { + break this.peeked.as_mut(); + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + *this.peeked = Some(item); + } else { + break None; + } + }) + } + /// Creates a future which will consume and return the next value of this /// stream if a condition is true. /// @@ -220,6 +268,48 @@ where } } +pin_project! { + /// Future for the [`Peekable::peek_mut`](self::Peekable::peek_mut) method. + #[must_use = "futures do nothing unless polled"] + pub struct PeekMut<'a, St: Stream> { + inner: Option>>, + } +} + +impl fmt::Debug for PeekMut<'_, St> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PeekMut").field("inner", &self.inner).finish() + } +} + +impl FusedFuture for PeekMut<'_, St> { + fn is_terminated(&self) -> bool { + self.inner.is_none() + } +} + +impl<'a, St> Future for PeekMut<'a, St> +where + St: Stream, +{ + type Output = Option<&'a mut St::Item>; + + 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_mut(cx)); + + inner.take().unwrap().poll_peek_mut(cx) + } else { + panic!("PeekMut polled after completion") + } + } +} + pin_project! { /// Future for the [`Peekable::next_if`](self::Peekable::next_if) method. #[must_use = "futures do nothing unless polled"] diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index e0192a118b..0877e45f4d 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -470,6 +470,13 @@ pub mod future { assert_not_impl!(PollFn<*const ()>: Sync); assert_impl!(PollFn: Unpin); + assert_impl!(PollImmediate: Send); + assert_not_impl!(PollImmediate>: Send); + assert_impl!(PollImmediate: Sync); + assert_not_impl!(PollImmediate>: Sync); + assert_impl!(PollImmediate: Unpin); + assert_not_impl!(PollImmediate: Unpin); + assert_impl!(Ready<()>: Send); assert_not_impl!(Ready<*const ()>: Send); assert_impl!(Ready<()>: Sync); @@ -1430,6 +1437,14 @@ pub mod stream { assert_not_impl!(Peek<'_, LocalStream<()>>: Sync); assert_impl!(Peek<'_, PinnedStream>: Unpin); + assert_impl!(PeekMut<'_, SendStream<()>>: Send); + assert_not_impl!(PeekMut<'_, SendStream>: Send); + assert_not_impl!(PeekMut<'_, LocalStream<()>>: Send); + assert_impl!(PeekMut<'_, SyncStream<()>>: Sync); + assert_not_impl!(PeekMut<'_, SyncStream>: Sync); + assert_not_impl!(PeekMut<'_, LocalStream<()>>: Sync); + assert_impl!(PeekMut<'_, PinnedStream>: Unpin); + assert_impl!(Peekable>: Send); assert_not_impl!(Peekable: Send); assert_not_impl!(Peekable: Send); @@ -1451,6 +1466,13 @@ pub mod stream { assert_not_impl!(PollFn<*const ()>: Sync); assert_impl!(PollFn: Unpin); + assert_impl!(PollImmediate: Send); + assert_not_impl!(PollImmediate>: Send); + assert_impl!(PollImmediate: Sync); + assert_not_impl!(PollImmediate>: Sync); + assert_impl!(PollImmediate: Unpin); + assert_not_impl!(PollImmediate: Unpin); + assert_impl!(ReadyChunks>: Send); assert_not_impl!(ReadyChunks: Send); assert_not_impl!(ReadyChunks: Send); diff --git a/futures/tests/stream_peekable.rs b/futures/tests/stream_peekable.rs index 2fa7f3a4f5..2af20f4fa9 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -12,6 +12,20 @@ fn peekable() { }); } +#[test] +fn peekable_mut() { + block_on(async { + let s = stream::iter(vec![1u8, 2, 3]).peekable(); + pin_mut!(s); + if let Some(p) = s.as_mut().peek_mut().await { + if *p == 1 { + *p = 5; + } + } + assert_eq!(s.collect::>().await, vec![5, 2, 3]); + }); +} + #[test] fn peekable_next_if_eq() { block_on(async { From efcdda8a57bc55c8780053e3fd7f61ed7363230c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 22:46:03 +0900 Subject: [PATCH 070/283] Add BufReader::seek_relative (#2489) --- futures-util/src/io/buf_reader.rs | 69 +++++++ futures-util/src/io/mod.rs | 2 +- futures/tests/auto_traits.rs | 6 + futures/tests/io_buf_reader.rs | 292 ++++++++++++++++++++---------- futures/tests/stream_peekable.rs | 5 + 5 files changed, 279 insertions(+), 95 deletions(-) diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 5931edc1b2..2d585a9eb6 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,4 +1,5 @@ use super::DEFAULT_BUF_SIZE; +use futures_core::future::Future; use futures_core::ready; use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] @@ -73,6 +74,40 @@ impl BufReader { } } +impl BufReader { + /// Seeks relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + pub fn seek_relative(self: Pin<&mut Self>, offset: i64) -> SeeKRelative<'_, R> { + SeeKRelative { inner: self, offset, first: true } + } + + /// Attempts to seek relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + pub fn poll_seek_relative( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + offset: i64, + ) -> Poll> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + *self.project().pos = new_pos as usize; + return Poll::Ready(Ok(())); + } + } else if let Some(new_pos) = pos.checked_add(offset as u64) { + if new_pos <= self.cap as u64 { + *self.project().pos = new_pos as usize; + return Poll::Ready(Ok(())); + } + } + self.poll_seek(cx, SeekFrom::Current(offset)).map(|res| res.map(|_| ())) + } +} + impl AsyncRead for BufReader { fn poll_read( mut self: Pin<&mut Self>, @@ -163,6 +198,10 @@ impl AsyncSeek for BufReader { /// `.into_inner()` immediately after a seek yields the underlying reader /// at the same position. /// + /// To seek without discarding the internal buffer, use + /// [`BufReader::seek_relative`](BufReader::seek_relative) or + /// [`BufReader::poll_seek_relative`](BufReader::poll_seek_relative). + /// /// See [`AsyncSeek`](futures_io::AsyncSeek) for more details. /// /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)` @@ -200,3 +239,33 @@ impl AsyncSeek for BufReader { Poll::Ready(Ok(result)) } } + +/// Future for the [`BufReader::seek_relative`](self::BufReader::seek_relative) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless polled"] +pub struct SeeKRelative<'a, R> { + inner: Pin<&'a mut BufReader>, + offset: i64, + first: bool, +} + +impl Future for SeeKRelative<'_, R> +where + R: AsyncRead + AsyncSeek, +{ + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let offset = self.offset; + if self.first { + self.first = false; + self.inner.as_mut().poll_seek_relative(cx, offset) + } else { + self.inner + .as_mut() + .as_mut() + .poll_seek(cx, SeekFrom::Current(offset)) + .map(|res| res.map(|_| ())) + } + } +} diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index b96223d1c1..16cf5a7bab 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -56,7 +56,7 @@ mod allow_std; pub use self::allow_std::AllowStdIo; mod buf_reader; -pub use self::buf_reader::BufReader; +pub use self::buf_reader::{BufReader, SeeKRelative}; mod buf_writer; pub use self::buf_writer::BufWriter; diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index 0877e45f4d..b3d8b00773 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -817,6 +817,12 @@ pub mod io { assert_impl!(Seek<'_, ()>: Unpin); assert_not_impl!(Seek<'_, PhantomPinned>: Unpin); + assert_impl!(SeeKRelative<'_, ()>: Send); + assert_not_impl!(SeeKRelative<'_, *const ()>: Send); + assert_impl!(SeeKRelative<'_, ()>: Sync); + assert_not_impl!(SeeKRelative<'_, *const ()>: Sync); + assert_impl!(SeeKRelative<'_, PhantomPinned>: Unpin); + assert_impl!(Sink: Send); assert_impl!(Sink: Sync); assert_impl!(Sink: Unpin); diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index d60df879c2..717297ccea 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -2,25 +2,17 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; use futures::io::{ AllowStdIo, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, - BufReader, Cursor, SeekFrom, + BufReader, SeekFrom, }; +use futures::pin_mut; use futures::task::{Context, Poll}; use futures_test::task::noop_context; +use pin_project::pin_project; use std::cmp; use std::io; use std::pin::Pin; -macro_rules! run_fill_buf { - ($reader:expr) => {{ - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = Pin::new(&mut $reader).poll_fill_buf(&mut cx) { - break x; - } - } - }}; -} - +// helper for maybe_pending_* tests fn run(mut f: F) -> F::Output { let mut cx = noop_context(); loop { @@ -30,6 +22,49 @@ fn run(mut f: F) -> F::Output { } } +// https://github.com/rust-lang/futures-rs/pull/2489#discussion_r697865719 +#[pin_project(!Unpin)] +struct Cursor { + #[pin] + inner: futures::io::Cursor, +} + +impl Cursor { + fn new(inner: T) -> Self { + Self { inner: futures::io::Cursor::new(inner) } + } +} + +impl AsyncRead for Cursor<&[u8]> { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + self.project().inner.poll_read(cx, buf) + } +} + +impl AsyncBufRead for Cursor<&[u8]> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_fill_buf(cx) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.project().inner.consume(amt) + } +} + +impl AsyncSeek for Cursor<&[u8]> { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + self.project().inner.poll_seek(cx, pos) + } +} + struct MaybePending<'a> { inner: &'a [u8], ready_read: bool, @@ -80,54 +115,119 @@ impl AsyncBufRead for MaybePending<'_> { #[test] fn test_buffered_reader() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 3); - assert_eq!(buf, [5, 6, 7]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 2); - assert_eq!(buf, [0, 1]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [2]); - assert_eq!(reader.buffer(), [3]); + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 3); + assert_eq!(buf, [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 2); + assert_eq!(buf, [0, 1]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [2]); + assert_eq!(reader.buffer(), [3]); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [3, 0, 0]); + assert_eq!(reader.buffer(), []); + + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [4, 0, 0]); + assert_eq!(reader.buffer(), []); + + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + }); +} - let mut buf = [0, 0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [3, 0, 0]); - assert_eq!(reader.buffer(), []); +#[test] +fn test_buffered_reader_seek() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(2, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.seek(SeekFrom::Start(3)).await.unwrap(), 3); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.seek(SeekFrom::Current(i64::MIN)).await.is_err()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert_eq!(reader.seek(SeekFrom::Current(1)).await.unwrap(), 4); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[1, 2][..]); + reader.as_mut().consume(1); + assert_eq!(reader.seek(SeekFrom::Current(-2)).await.unwrap(), 3); + }); +} - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [4, 0, 0]); - assert_eq!(reader.buffer(), []); +#[test] +fn test_buffered_reader_seek_relative() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(2, Cursor::new(inner)); + pin_mut!(reader); + + assert!(reader.as_mut().seek_relative(3).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(0).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(1).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[1][..]); + assert!(reader.as_mut().seek_relative(-1).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(2).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[2, 3][..]); + }); +} - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); +#[test] +fn test_buffered_reader_invalidated_after_read() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(3, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[5, 6, 7][..]); + reader.as_mut().consume(3); + + let mut buffer = [0, 0, 0, 0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 5); + assert_eq!(buffer, [0, 1, 2, 3, 4]); + + assert!(reader.as_mut().seek_relative(-2).await.is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 2); + assert_eq!(buffer, [3, 4]); + }); } #[test] -fn test_buffered_reader_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); - - assert_eq!(block_on(reader.seek(SeekFrom::Start(3))).ok(), Some(3)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(block_on(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); - Pin::new(&mut reader).consume(1); - assert_eq!(block_on(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); +fn test_buffered_reader_invalidated_after_seek() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(3, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[5, 6, 7][..]); + reader.as_mut().consume(3); + + assert!(reader.seek(SeekFrom::Current(5)).await.is_ok()); + + assert!(reader.as_mut().seek_relative(-2).await.is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 2); + assert_eq!(buffer, [3, 4]); + }); } #[test] @@ -156,24 +256,27 @@ fn test_buffered_reader_seek_underflow() { self.pos = self.pos.wrapping_add(n as u64); } SeekFrom::End(n) => { - self.pos = u64::max_value().wrapping_add(n as u64); + self.pos = u64::MAX.wrapping_add(n as u64); } } Ok(self.pos) } } - let mut reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(block_on(reader.seek(SeekFrom::End(-5))).ok(), Some(u64::max_value() - 5)); - assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); - // the following seek will require two underlying seeks - let expected = 9_223_372_036_854_775_802; - assert_eq!(block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), Some(expected)); - assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); - // seeking to 0 should empty the buffer. - assert_eq!(block_on(reader.seek(SeekFrom::Current(0))).ok(), Some(expected)); - assert_eq!(reader.get_ref().get_ref().pos, expected); + block_on(async { + let reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); + pin_mut!(reader); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1, 2, 3, 4][..]); + assert_eq!(reader.seek(SeekFrom::End(-5)).await.unwrap(), u64::MAX - 5); + assert_eq!(reader.as_mut().fill_buf().await.unwrap().len(), 5); + // the following seek will require two underlying seeks + let expected = 9_223_372_036_854_775_802; + assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).await.unwrap(), expected); + assert_eq!(reader.as_mut().fill_buf().await.unwrap().len(), 5); + // seeking to 0 should empty the buffer. + assert_eq!(reader.seek(SeekFrom::Current(0)).await.unwrap(), expected); + assert_eq!(reader.get_ref().get_ref().pos, expected); + }); } #[test] @@ -193,16 +296,18 @@ fn test_short_reads() { } } - let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; - let mut reader = BufReader::new(AllowStdIo::new(inner)); - let mut buf = [0, 0]; - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 1); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 2); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 1); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); + block_on(async { + let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; + let mut reader = BufReader::new(AllowStdIo::new(inner)); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 1); + assert_eq!(reader.read(&mut buf).await.unwrap(), 2); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 1); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + }); } #[test] @@ -263,7 +368,9 @@ fn maybe_pending_buf_read() { // https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 #[test] fn maybe_pending_seek() { + #[pin_project] struct MaybePendingSeek<'a> { + #[pin] inner: Cursor<&'a [u8]>, ready: bool, } @@ -276,25 +383,21 @@ fn maybe_pending_seek() { impl AsyncRead for MaybePendingSeek<'_> { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut self.inner).poll_read(cx, buf) + self.project().inner.poll_read(cx, buf) } } 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 poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_fill_buf(cx) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + fn consume(self: Pin<&mut Self>, amt: usize) { + self.project().inner.consume(amt) } } @@ -305,24 +408,25 @@ fn maybe_pending_seek() { pos: SeekFrom, ) -> Poll> { if self.ready { - self.ready = false; - Pin::new(&mut self.inner).poll_seek(cx, pos) + *self.as_mut().project().ready = false; + self.project().inner.poll_seek(cx, pos) } else { - self.ready = true; + *self.project().ready = true; Poll::Pending } } } let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, MaybePendingSeek::new(inner)); + let reader = BufReader::with_capacity(2, MaybePendingSeek::new(inner)); + pin_mut!(reader); assert_eq!(run(reader.seek(SeekFrom::Current(3))).ok(), Some(3)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[0, 1][..])); + assert_eq!(run(reader.seek(SeekFrom::Current(i64::MIN))).ok(), None); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[0, 1][..])); assert_eq!(run(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[1, 2][..])); Pin::new(&mut reader).consume(1); assert_eq!(run(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); } diff --git a/futures/tests/stream_peekable.rs b/futures/tests/stream_peekable.rs index 2af20f4fa9..153fcc25b4 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -9,6 +9,11 @@ fn peekable() { pin_mut!(peekable); assert_eq!(peekable.as_mut().peek().await, Some(&1u8)); assert_eq!(peekable.collect::>().await, vec![1, 2, 3]); + + let s = stream::once(async { 1 }).peekable(); + pin_mut!(s); + assert_eq!(s.as_mut().peek().await, Some(&1u8)); + assert_eq!(s.collect::>().await, vec![1]); }); } From 1c9b1c246b03555848097ebe18e75249b1691cd6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 30 Aug 2021 19:12:44 +0900 Subject: [PATCH 071/283] Update changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d4369d4c..1f9d944346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.3.17 - 2021-08-30 + +* Use `FuturesOrdered` in `join_all` (#2412) +* Add `{future, stream}::poll_immediate` (#2452) +* Add `stream_select!` macro (#2262) +* Implement `Default` for `OptionFuture` (#2471) +* Add `Peekable::{peek_mut, poll_peek_mut}` (#2488) +* Add `BufReader::seek_relative` (#2489) + # 0.3.16 - 2021-07-23 * Add `TryStreamExt::try_chunks` (#2438) From 3f064f4a7e0a55898f01f5563bdc2c22d5127180 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 30 Aug 2021 19:15:35 +0900 Subject: [PATCH 072/283] Create GitHub release automatically --- .github/workflows/release.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..4e6aa773a9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + push: + tags: + - '[0-9]+.*' + +env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + +jobs: + create-release: + if: github.repository_owner == 'rust-lang' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable + - run: cargo build --all + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 7caefa51304e78fd5018cd5d2a03f3b9089cc010 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 30 Aug 2021 19:15:55 +0900 Subject: [PATCH 073/283] Release 0.3.17 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 1d3cb74f5e..6bd240cacf 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-channel" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -23,8 +23,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 6c1b5e70ec..29134f2954 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-core" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 286b253c65..6d6f71bded 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-executor" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -17,9 +17,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.16", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.17", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 518d4615a0..2e5abba5f0 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-io" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 1acd4a22eb..3b2740c4fd 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-macro" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 2144518ec4..c5d5681dde 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-sink" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 3d246add7a..23e5aed8b3 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-task" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index ef138d4437..efd18db2e0 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-test" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -12,13 +12,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.16", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.16", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.16", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.16", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.16", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.16", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.16", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.17", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.17", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.17", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.17", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.17", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.17", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.17", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 98cace6b4c..a8e93620b3 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-util" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" @@ -39,12 +39,12 @@ cfg-target-has-atomic = [] autocfg = "1" [dependencies] -futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.16", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.16", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.16", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.17", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.17", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.17", default-features = false, optional = true } proc-macro-hack = { version = "0.5.19", optional = true } proc-macro-nested = { version = "0.1.2", optional = true } slab = { version = "0.4.2", optional = true } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 04cea8a125..b01b12e06d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures" edition = "2018" -version = "0.3.16" +version = "0.3.17" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" @@ -16,13 +16,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.16", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.16", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.16", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.16", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.16", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.16", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.16", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.17", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.17", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.17", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.17", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 6633d2306ca5a19e921ef9e509bfb2c05ff5d6fa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 30 Aug 2021 19:42:44 +0900 Subject: [PATCH 074/283] Fix release workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e6aa773a9..dc9b65bf6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,6 @@ jobs: - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md - branch: master + branch: 'master|[0-9]+\.[0-9]+' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9080b83b40d55a4268f2ae44aaeed2adb9fdfa04 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 2 Sep 2021 13:59:20 -0400 Subject: [PATCH 075/283] Remove implicit clear in `ReadyToRunQueue::drop` (#2493) --- .../src/stream/futures_unordered/mod.rs | 21 +++---------------- .../futures_unordered/ready_to_run_queue.rs | 15 +------------ 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 4a05d8823e..6918a26b91 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -558,7 +558,7 @@ impl FuturesUnordered { pub fn clear(&mut self) { self.clear_head_all(); - // we just cleared all the tasks, and we have &mut self, so this is safe. + // SAFETY: we just cleared all the tasks and we have &mut self unsafe { self.ready_to_run_queue.clear() }; self.is_terminated.store(false, Relaxed); @@ -575,24 +575,9 @@ impl FuturesUnordered { impl Drop for FuturesUnordered { fn drop(&mut self) { - // When a `FuturesUnordered` is dropped we want to drop all futures - // associated with it. At the same time though there may be tons of - // wakers flying around which contain `Task` references - // inside them. We'll let those naturally get deallocated. self.clear_head_all(); - - // Note that at this point we could still have a bunch of tasks in the - // ready to run queue. None of those tasks, however, have futures - // associated with them so they're safe to destroy on any thread. At - // this point the `FuturesUnordered` struct, the owner of the one strong - // reference to the ready to run queue will drop the strong reference. - // At that point whichever thread releases the strong refcount last (be - // it this thread or some other thread as part of an `upgrade`) will - // clear out the ready to run queue and free all remaining tasks. - // - // While that freeing operation isn't guaranteed to happen here, it's - // guaranteed to happen "promptly" as no more "blocking work" will - // happen while there's a strong refcount held. + // SAFETY: we just cleared all the tasks and we have &mut self + unsafe { self.ready_to_run_queue.clear() }; } } diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 5ef6cde83d..2bc208682a 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -94,7 +94,7 @@ impl ReadyToRunQueue { // // # Safety // - // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear) + // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear_head_all) // - The caller **must** guarantee unique access to `self` pub(crate) unsafe fn clear(&self) { loop { @@ -107,16 +107,3 @@ impl ReadyToRunQueue { } } } - -impl Drop for ReadyToRunQueue { - fn drop(&mut self) { - // Once we're in the destructor for `Inner` we need to clear out - // the ready to run queue of tasks if there's anything left in there. - - // All tasks have had their futures dropped already by the `FuturesUnordered` - // destructor above, and we have &mut self, so this is safe. - unsafe { - self.clear(); - } - } -} From f562050ea3befbd2a0bf62d16b7d6c81364c01cd Mon Sep 17 00:00:00 2001 From: Andrew Burkett Date: Sat, 18 Sep 2021 00:38:34 -0700 Subject: [PATCH 076/283] Fix Unusable Sink Implementation on scan (#2499) The current Sink implementation on stream::Scan requires the stream and state types are the same which doesn't practically allow that implementation to be used. This adds a separate generic to the Sink implementation for state. --- futures-util/src/stream/stream/scan.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/stream/scan.rs b/futures-util/src/stream/stream/scan.rs index 8724145ef3..f5cfde9c36 100644 --- a/futures-util/src/stream/stream/scan.rs +++ b/futures-util/src/stream/stream/scan.rs @@ -118,11 +118,11 @@ where // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Scan +impl Sink for Scan where - S: Stream + Sink, + St: Stream + Sink, { - type Error = S::Error; + type Error = St::Error; delegate_sink!(stream, Item); } From c4bd5ce67a12f37b34b510365d1b6f6f06502dd1 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 8 Oct 2021 09:10:44 -0700 Subject: [PATCH 077/283] Make `futures::task::noop_waker_ref` available without `std`. (#2505) Commit 232ed5feb0bc31cf6b548e9506dbedba201ec3ad removed the std dependency, and 4c09f82a1e60c2e44b9b467e05e0a6b086c061be made it exported unconditionally from `futures_task`, but the `cfg` on the reexport from `futures_util` (and hence indirectly `futures`) was left in place until now. --- futures-util/src/task/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index eff6d48d2f..0a31eeac14 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -16,7 +16,6 @@ pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; pub use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, UnsafeFutureObj}; pub use futures_task::noop_waker; -#[cfg(feature = "std")] pub use futures_task::noop_waker_ref; #[cfg(not(futures_no_atomic_cas))] From d849698e4004647e4b75ad5a997919b39d2a5b82 Mon Sep 17 00:00:00 2001 From: Felipe Lema <1232306+FelipeLema@users.noreply.github.com> Date: Fri, 8 Oct 2021 13:25:39 -0300 Subject: [PATCH 078/283] Add async `LineWriter` (#2477) --- futures-util/src/io/buf_writer.rs | 65 +++++++++++- futures-util/src/io/line_writer.rs | 155 +++++++++++++++++++++++++++++ futures-util/src/io/mod.rs | 3 + futures/tests/io_line_writer.rs | 73 ++++++++++++++ 4 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 futures-util/src/io/line_writer.rs create mode 100644 futures/tests/io_line_writer.rs diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index f292b871f1..cb74863ad0 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -6,6 +6,7 @@ use pin_project_lite::pin_project; use std::fmt; use std::io::{self, Write}; use std::pin::Pin; +use std::ptr; pin_project! { /// Wraps a writer and buffers its output. @@ -49,7 +50,7 @@ impl BufWriter { Self { inner, buf: Vec::with_capacity(cap), written: 0 } } - fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + pub(super) fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); let len = this.buf.len(); @@ -83,6 +84,68 @@ impl BufWriter { pub fn buffer(&self) -> &[u8] { &self.buf } + + /// Capacity of `buf`. how many chars can be held in buffer + pub(super) fn capacity(&self) -> usize { + self.buf.capacity() + } + + /// Remaining number of bytes to reach `buf` 's capacity + #[inline] + pub(super) fn spare_capacity(&self) -> usize { + self.buf.capacity() - self.buf.len() + } + + /// Write a byte slice directly into buffer + /// + /// Will truncate the number of bytes written to `spare_capacity()` so you want to + /// calculate the size of your slice to avoid losing bytes + /// + /// Based on `std::io::BufWriter` + pub(super) fn write_to_buf(self: Pin<&mut Self>, buf: &[u8]) -> usize { + let available = self.spare_capacity(); + let amt_to_buffer = available.min(buf.len()); + + // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. + unsafe { + self.write_to_buffer_unchecked(&buf[..amt_to_buffer]); + } + + amt_to_buffer + } + + /// Write byte slice directly into `self.buf` + /// + /// Based on `std::io::BufWriter` + #[inline] + unsafe fn write_to_buffer_unchecked(self: Pin<&mut Self>, buf: &[u8]) { + debug_assert!(buf.len() <= self.spare_capacity()); + let this = self.project(); + let old_len = this.buf.len(); + let buf_len = buf.len(); + let src = buf.as_ptr(); + let dst = this.buf.as_mut_ptr().add(old_len); + ptr::copy_nonoverlapping(src, dst, buf_len); + this.buf.set_len(old_len + buf_len); + } + + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.project().inner.poll_write(cx, buf) + } + + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + self.project().inner.poll_write_vectored(cx, bufs) + } } impl AsyncWrite for BufWriter { diff --git a/futures-util/src/io/line_writer.rs b/futures-util/src/io/line_writer.rs new file mode 100644 index 0000000000..71cd668325 --- /dev/null +++ b/futures-util/src/io/line_writer.rs @@ -0,0 +1,155 @@ +use super::buf_writer::BufWriter; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use futures_io::AsyncWrite; +use futures_io::IoSlice; +use pin_project_lite::pin_project; +use std::io; +use std::pin::Pin; + +pin_project! { +/// Wrap a writer, like [`BufWriter`] does, but prioritizes buffering lines +/// +/// This was written based on `std::io::LineWriter` which goes into further details +/// explaining the code. +/// +/// Buffering is actually done using `BufWriter`. This class will leverage `BufWriter` +/// to write on-each-line. +#[derive(Debug)] +pub struct LineWriter { + #[pin] + buf_writer: BufWriter, +} +} + +impl LineWriter { + /// Create a new `LineWriter` with default buffer capacity. The default is currently 1KB + /// which was taken from `std::io::LineWriter` + pub fn new(inner: W) -> LineWriter { + LineWriter::with_capacity(1024, inner) + } + + /// Creates a new `LineWriter` with the specified buffer capacity. + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { + LineWriter { buf_writer: BufWriter::with_capacity(capacity, inner) } + } + + /// Flush `buf_writer` if last char is "new line" + fn flush_if_completed_line(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.buf_writer.buffer().last().copied() { + Some(b'\n') => this.buf_writer.flush_buf(cx), + _ => Poll::Ready(Ok(())), + } + } + + /// Returns a reference to `buf_writer`'s internally buffered data. + pub fn buffer(&self) -> &[u8] { + self.buf_writer.buffer() + } + + /// Acquires a reference to the underlying sink or stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &W { + self.buf_writer.get_ref() + } +} + +impl AsyncWrite for LineWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.as_mut().project(); + let newline_index = match memchr::memrchr(b'\n', buf) { + None => { + ready!(self.as_mut().flush_if_completed_line(cx)?); + return self.project().buf_writer.poll_write(cx, buf); + } + Some(newline_index) => newline_index + 1, + }; + + ready!(this.buf_writer.as_mut().poll_flush(cx)?); + + let lines = &buf[..newline_index]; + + let flushed = { ready!(this.buf_writer.as_mut().inner_poll_write(cx, lines))? }; + + if flushed == 0 { + return Poll::Ready(Ok(0)); + } + + let tail = if flushed >= newline_index { + &buf[flushed..] + } else if newline_index - flushed <= this.buf_writer.capacity() { + &buf[flushed..newline_index] + } else { + let scan_area = &buf[flushed..]; + let scan_area = &scan_area[..this.buf_writer.capacity()]; + match memchr::memrchr(b'\n', scan_area) { + Some(newline_index) => &scan_area[..newline_index + 1], + None => scan_area, + } + }; + + let buffered = this.buf_writer.as_mut().write_to_buf(tail); + Poll::Ready(Ok(flushed + buffered)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + let mut this = self.as_mut().project(); + // `is_write_vectored()` is handled in original code, but not in this crate + // see https://github.com/rust-lang/rust/issues/70436 + + let last_newline_buf_idx = bufs + .iter() + .enumerate() + .rev() + .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); + let last_newline_buf_idx = match last_newline_buf_idx { + None => { + ready!(self.as_mut().flush_if_completed_line(cx)?); + return self.project().buf_writer.poll_write_vectored(cx, bufs); + } + Some(i) => i, + }; + + ready!(this.buf_writer.as_mut().poll_flush(cx)?); + + let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); + + let flushed = { ready!(this.buf_writer.as_mut().inner_poll_write_vectored(cx, lines))? }; + if flushed == 0 { + return Poll::Ready(Ok(0)); + } + + let lines_len = lines.iter().map(|buf| buf.len()).sum(); + if flushed < lines_len { + return Poll::Ready(Ok(flushed)); + } + + let buffered: usize = tail + .iter() + .filter(|buf| !buf.is_empty()) + .map(|buf| this.buf_writer.as_mut().write_to_buf(buf)) + .take_while(|&n| n > 0) + .sum(); + + Poll::Ready(Ok(flushed + buffered)) + } + + /// Forward to `buf_writer` 's `BufWriter::poll_flush()` + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().buf_writer.poll_flush(cx) + } + + /// Forward to `buf_writer` 's `BufWriter::poll_close()` + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().buf_writer.poll_close(cx) + } +} diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 16cf5a7bab..4a51804802 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -61,6 +61,9 @@ pub use self::buf_reader::{BufReader, SeeKRelative}; mod buf_writer; pub use self::buf_writer::BufWriter; +mod line_writer; +pub use self::line_writer::LineWriter; + mod chain; pub use self::chain::Chain; diff --git a/futures/tests/io_line_writer.rs b/futures/tests/io_line_writer.rs new file mode 100644 index 0000000000..b483e0ff77 --- /dev/null +++ b/futures/tests/io_line_writer.rs @@ -0,0 +1,73 @@ +use futures::executor::block_on; +use futures::io::{AsyncWriteExt, LineWriter}; +use std::io; + +#[test] +fn line_writer() { + let mut writer = LineWriter::new(Vec::new()); + + block_on(writer.write(&[0])).unwrap(); + assert_eq!(*writer.get_ref(), []); + + block_on(writer.write(&[1])).unwrap(); + assert_eq!(*writer.get_ref(), []); + + block_on(writer.flush()).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + block_on(writer.write(&[0, b'\n', 1, b'\n', 2])).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + + block_on(writer.flush()).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + + block_on(writer.write(&[3, b'\n'])).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); +} + +#[test] +fn line_vectored() { + let mut line_writer = LineWriter::new(Vec::new()); + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(b"\n"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"a"), + ])) + .unwrap(), + 2 + ); + assert_eq!(line_writer.get_ref(), b"\n"); + + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(b"b"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"a"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"c"), + ])) + .unwrap(), + 3 + ); + assert_eq!(line_writer.get_ref(), b"\n"); + block_on(line_writer.flush()).unwrap(); + assert_eq!(line_writer.get_ref(), b"\nabac"); + assert_eq!(block_on(line_writer.write_vectored(&[])).unwrap(), 0); + + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + ])) + .unwrap(), + 0 + ); + + assert_eq!(block_on(line_writer.write_vectored(&[io::IoSlice::new(b"a\nb")])).unwrap(), 3); + assert_eq!(line_writer.get_ref(), b"\nabaca\nb"); +} From eeae63413b2cfbe84b1a8bf2b33b7a227928db39 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 08:53:51 +0900 Subject: [PATCH 079/283] Remove doc URL from Cargo.toml https://doc.rust-lang.org/cargo/reference/manifest.html#the-documentation-field > If no URL is specified in the manifest file, crates.io will > automatically link your crate to the corresponding docs.rs page. --- futures-channel/Cargo.toml | 1 - futures-core/Cargo.toml | 1 - futures-executor/Cargo.toml | 1 - futures-io/Cargo.toml | 1 - futures-macro/Cargo.toml | 1 - futures-sink/Cargo.toml | 1 - futures-task/Cargo.toml | 1 - futures-test/Cargo.toml | 1 - futures-util/Cargo.toml | 1 - futures/Cargo.toml | 1 - 10 files changed, 10 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 6bd240cacf..91c3b71590 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ Channels for asynchronous communication using futures-rs. """ diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 29134f2954..485fb02551 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ The core traits and types in for the `futures` library. """ diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 6d6f71bded..359576f101 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ Executors for asynchronous tasks based on the futures-rs library. """ diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 2e5abba5f0..9558b9167f 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ The `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` traits for the futures-rs library. """ diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 3b2740c4fd..9512f71a98 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -6,7 +6,6 @@ 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-sink/0.3" description = """ The asynchronous `Sink` trait for the futures-rs library. """ diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 23e5aed8b3..31ee26118a 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -6,7 +6,6 @@ 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-task/0.3" description = """ Tools for working with tasks. """ diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index efd18db2e0..d68296000e 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ Common utilities for testing components built off futures-rs. """ diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index a8e93620b3..6dae53d56e 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -6,7 +6,6 @@ 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" description = """ Common utilities and extension traits for the futures-rs library. """ diff --git a/futures/Cargo.toml b/futures/Cargo.toml index b01b12e06d..e3d8ce7b9d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -8,7 +8,6 @@ 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" description = """ An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. From 09b018648b3ffa101ccdae0c38b4cc7b2d57bbbe Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 09:08:57 +0900 Subject: [PATCH 080/283] Remove dependency on proc-macro-hack --- futures-macro/Cargo.toml | 4 --- futures-macro/build.rs | 28 ------------------- futures-macro/src/lib.rs | 17 ++++------- futures-util/Cargo.toml | 7 +---- futures-util/build.rs | 19 ------------- futures-util/src/async_await/join_mod.rs | 2 -- futures-util/src/async_await/select_mod.rs | 5 ---- .../src/async_await/stream_select_mod.rs | 5 ---- 8 files changed, 7 insertions(+), 80 deletions(-) delete mode 100644 futures-macro/build.rs diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 9512f71a98..67331f0ade 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -15,11 +15,7 @@ proc-macro = true [features] -[build-dependencies] -autocfg = "1" - [dependencies] proc-macro2 = "1.0" -proc-macro-hack = "0.5.19" quote = "1.0" syn = { version = "1.0.56", features = ["full"] } diff --git a/futures-macro/build.rs b/futures-macro/build.rs deleted file mode 100644 index ff8630ce69..0000000000 --- a/futures-macro/build.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use autocfg::AutoCfg; - -// The rustc-cfg strings below are *not* public API. Please let us know by -// opening a GitHub issue if your build environment requires some way to enable -// these cfgs other than by executing our build script. -fn main() { - let cfg = match AutoCfg::new() { - Ok(cfg) => cfg, - Err(e) => { - println!( - "cargo:warning={}: unable to determine rustc version: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Function like procedural macros in expressions patterns statements stabilized in Rust 1.45: - // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements - if cfg.probe_rustc_version(1, 45) { - println!("cargo:rustc-cfg=fn_like_proc_macro"); - } - - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index fa93e48f1f..0afe34b83b 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -22,34 +22,30 @@ mod select; mod stream_select; /// The `join!` macro. -#[cfg_attr(fn_like_proc_macro, proc_macro)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +#[proc_macro] pub fn join_internal(input: TokenStream) -> TokenStream { crate::join::join(input) } /// The `try_join!` macro. -#[cfg_attr(fn_like_proc_macro, proc_macro)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +#[proc_macro] pub fn try_join_internal(input: TokenStream) -> TokenStream { crate::join::try_join(input) } /// The `select!` macro. -#[cfg_attr(fn_like_proc_macro, proc_macro)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +#[proc_macro] pub fn select_internal(input: TokenStream) -> TokenStream { crate::select::select(input) } /// The `select_biased!` macro. -#[cfg_attr(fn_like_proc_macro, proc_macro)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +#[proc_macro] pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } -// TODO: Change this to doc comment once rustdoc bug fixed. +// TODO: Change this to doc comment once rustdoc bug fixed: https://github.com/rust-lang/futures-rs/pull/2435 // The `test` attribute. #[proc_macro_attribute] pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { @@ -57,8 +53,7 @@ pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { } /// The `stream_select!` macro. -#[cfg_attr(fn_like_proc_macro, proc_macro)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack)] +#[proc_macro] pub fn stream_select_internal(input: TokenStream) -> TokenStream { crate::stream_select::stream_select(input.into()) .unwrap_or_else(syn::Error::into_compile_error) diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 6dae53d56e..43b15e8170 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -15,7 +15,7 @@ default = ["std", "async-await", "async-await-macro"] std = ["alloc", "futures-core/std", "futures-task/std", "slab"] alloc = ["futures-core/alloc", "futures-task/alloc"] async-await = [] -async-await-macro = ["async-await", "futures-macro", "proc-macro-hack", "proc-macro-nested"] +async-await-macro = ["async-await", "futures-macro"] compat = ["std", "futures_01"] io-compat = ["io", "compat", "tokio-io"] sink = ["futures-sink"] @@ -34,9 +34,6 @@ write-all-vectored = ["io"] # TODO: remove in the next major version. cfg-target-has-atomic = [] -[build-dependencies] -autocfg = "1" - [dependencies] futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } @@ -44,8 +41,6 @@ futures-channel = { path = "../futures-channel", version = "0.3.17", default-fea futures-io = { path = "../futures-io", version = "0.3.17", default-features = false, features = ["std"], optional = true } futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false, optional = true } futures-macro = { path = "../futures-macro", version = "=0.3.17", default-features = false, optional = true } -proc-macro-hack = { version = "0.5.19", 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" } diff --git a/futures-util/build.rs b/futures-util/build.rs index f8aa5fe4f4..07b50bd552 100644 --- a/futures-util/build.rs +++ b/futures-util/build.rs @@ -1,6 +1,5 @@ #![warn(rust_2018_idioms, single_use_lifetimes)] -use autocfg::AutoCfg; use std::env; include!("no_atomic_cas.rs"); @@ -39,23 +38,5 @@ fn main() { println!("cargo:rustc-cfg=futures_no_atomic_cas"); } - let cfg = match AutoCfg::new() { - Ok(cfg) => cfg, - Err(e) => { - println!( - "cargo:warning={}: unable to determine rustc version: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Function like procedural macros in expressions patterns statements stabilized in Rust 1.45: - // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements - if cfg.probe_rustc_version(1, 45) { - println!("cargo:rustc-cfg=fn_like_proc_macro"); - } - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); } diff --git a/futures-util/src/async_await/join_mod.rs b/futures-util/src/async_await/join_mod.rs index c5cdd9babc..28f3b232e7 100644 --- a/futures-util/src/async_await/join_mod.rs +++ b/futures-util/src/async_await/join_mod.rs @@ -81,12 +81,10 @@ macro_rules! document_join_macro { #[allow(unreachable_pub)] #[doc(hidden)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::join_internal; #[allow(unreachable_pub)] #[doc(hidden)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::try_join_internal; document_join_macro! { diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 37e938da55..1d13067d38 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -29,9 +29,6 @@ macro_rules! document_select_macro { /// It is also gated behind the `async-await` feature of this library, which is /// activated by default. /// - /// Note that `select!` relies on `proc-macro-hack`, and may require to set the - /// compiler's recursion limit very high, e.g. `#![recursion_limit="1024"]`. - /// /// # Examples /// /// ``` @@ -309,12 +306,10 @@ macro_rules! document_select_macro { #[cfg(feature = "std")] #[allow(unreachable_pub)] #[doc(hidden)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::select_internal; #[allow(unreachable_pub)] #[doc(hidden)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::select_biased_internal; document_select_macro! { diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs index 7743406dab..1c8002fff3 100644 --- a/futures-util/src/async_await/stream_select_mod.rs +++ b/futures-util/src/async_await/stream_select_mod.rs @@ -3,7 +3,6 @@ #[cfg(feature = "std")] #[allow(unreachable_pub)] #[doc(hidden)] -#[cfg_attr(not(fn_like_proc_macro), proc_macro_hack::proc_macro_hack(support_nested))] pub use futures_macro::stream_select_internal; /// Combines several streams, all producing the same `Item` type, into one stream. @@ -13,10 +12,6 @@ pub use futures_macro::stream_select_internal; /// /// If multiple streams are ready, one will be pseudo randomly selected at runtime. /// -/// This macro is gated behind the `async-await` feature of this library, which is activated by default. -/// Note that `stream_select!` relies on `proc-macro-hack`, and may require to set the compiler's recursion -/// limit very high, e.g. `#![recursion_limit="1024"]`. -/// /// # Examples /// /// ``` From cc19f6c6ec5524b8e8a3eec25d13bf7c5308d6d6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 09:41:38 +0900 Subject: [PATCH 081/283] Bump MSRV of utility crates to 1.45 --- .github/workflows/ci.yml | 6 +++--- README.md | 30 ++++++------------------------ futures-channel/README.md | 23 +++++++++++++++++++++++ futures-core/README.md | 23 +++++++++++++++++++++++ futures-executor/README.md | 23 +++++++++++++++++++++++ futures-io/README.md | 23 +++++++++++++++++++++++ futures-sink/README.md | 23 +++++++++++++++++++++++ futures-task/README.md | 23 +++++++++++++++++++++++ futures-test/README.md | 23 +++++++++++++++++++++++ futures-util/README.md | 23 +++++++++++++++++++++++ 10 files changed, 193 insertions(+), 27 deletions(-) create mode 100644 futures-channel/README.md create mode 100644 futures-core/README.md create mode 100644 futures-executor/README.md create mode 100644 futures-io/README.md create mode 100644 futures-sink/README.md create mode 100644 futures-task/README.md create mode 100644 futures-test/README.md create mode 100644 futures-util/README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c204ef6670..81a956c353 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,8 +61,8 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task. - # When updating this, the reminder to update the minimum required version in .clippy.toml. - - 1.36.0 + # When updating this, the reminder to update the minimum required version in README.md and .clippy.toml. + - 1.36 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -93,7 +93,7 @@ jobs: rust: # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-channel, futures-test. # When updating this, the reminder to update the minimum required version in README.md. - - 1.41.0 + - 1.45 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index b97693fb83..e6127fd6f8 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,6 @@ Crates.io - - - Rustc Version -

@@ -42,13 +38,7 @@ Add this to your `Cargo.toml`: futures = "0.3" ``` -Now, you can use futures-rs: - -```rust -use futures::future::Future; -``` - -The current futures-rs requires Rust 1.41 or later. +The current `futures` requires Rust 1.45 or later. ### Feature `std` @@ -61,19 +51,11 @@ a `#[no_std]` environment, use: futures = { version = "0.3", default-features = false } ``` -# License - -This project is licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - https://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - https://opensource.org/licenses/MIT) - -at your option. +## License -### Contribution +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in futures-rs by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-channel/README.md b/futures-channel/README.md new file mode 100644 index 0000000000..3287be924c --- /dev/null +++ b/futures-channel/README.md @@ -0,0 +1,23 @@ +# futures-channel + +Channels for asynchronous communication using futures-rs. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-channel = "0.3" +``` + +The current `futures-channel` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-core/README.md b/futures-core/README.md new file mode 100644 index 0000000000..96e0e064bc --- /dev/null +++ b/futures-core/README.md @@ -0,0 +1,23 @@ +# futures-core + +The core traits and types in for the `futures` library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-core = "0.3" +``` + +The current `futures-core` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-executor/README.md b/futures-executor/README.md new file mode 100644 index 0000000000..67086851e4 --- /dev/null +++ b/futures-executor/README.md @@ -0,0 +1,23 @@ +# futures-executor + +Executors for asynchronous tasks based on the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-executor = "0.3" +``` + +The current `futures-executor` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-io/README.md b/futures-io/README.md new file mode 100644 index 0000000000..da6eec28ba --- /dev/null +++ b/futures-io/README.md @@ -0,0 +1,23 @@ +# futures-io + +The `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` traits for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-io = "0.3" +``` + +The current `futures-io` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-sink/README.md b/futures-sink/README.md new file mode 100644 index 0000000000..1d683e95b5 --- /dev/null +++ b/futures-sink/README.md @@ -0,0 +1,23 @@ +# futures-sink + +The asynchronous `Sink` trait for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-sink = "0.3" +``` + +The current `futures-sink` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-task/README.md b/futures-task/README.md new file mode 100644 index 0000000000..79f12da887 --- /dev/null +++ b/futures-task/README.md @@ -0,0 +1,23 @@ +# futures-task + +Tools for working with tasks. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-task = "0.3" +``` + +The current `futures-task` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-test/README.md b/futures-test/README.md new file mode 100644 index 0000000000..b3c30e5d6c --- /dev/null +++ b/futures-test/README.md @@ -0,0 +1,23 @@ +# futures-test + +Common utilities for testing components built off futures-rs. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-test = "0.3" +``` + +The current `futures-test` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-util/README.md b/futures-util/README.md new file mode 100644 index 0000000000..6e0aaed847 --- /dev/null +++ b/futures-util/README.md @@ -0,0 +1,23 @@ +# futures-util + +Common utilities and extension traits for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-util = "0.3" +``` + +The current `futures-util` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. From c2f4aeac23447348cc94cc5a92c9e947d48c2e6d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 09:42:09 +0900 Subject: [PATCH 082/283] Add rust-version field to Cargo.toml https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field --- .github/workflows/ci.yml | 4 ++-- futures-channel/Cargo.toml | 3 ++- futures-core/Cargo.toml | 3 ++- futures-executor/Cargo.toml | 3 ++- futures-io/Cargo.toml | 3 ++- futures-macro/Cargo.toml | 3 ++- futures-sink/Cargo.toml | 3 ++- futures-task/Cargo.toml | 3 ++- futures-test/Cargo.toml | 3 ++- futures-util/Cargo.toml | 3 ++- futures/Cargo.toml | 3 ++- 11 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81a956c353..ec836214e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task. - # When updating this, the reminder to update the minimum required version in README.md and .clippy.toml. + # When updating this, the reminder to update the minimum required version in README.md, Cargo.toml, and .clippy.toml. - 1.36 runs-on: ubuntu-latest steps: @@ -92,7 +92,7 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-channel, futures-test. - # When updating this, the reminder to update the minimum required version in README.md. + # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - 1.45 runs-on: ubuntu-latest steps: diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 91c3b71590..01375bedae 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-channel" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 485fb02551..0cd1d1dfe0 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-core" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.36" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 359576f101..1fbe4290c4 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-executor" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 9558b9167f..c2b21c601f 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-io" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.36" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 67331f0ade..0441092a87 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-macro" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Taylor Cramer ", "Taiki Endo "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 555a594ad4..919525f8dd 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-sink" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.36" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 31ee26118a..ceb293d34e 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-task" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.36" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index d68296000e..dff72dcc85 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-test" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Wim Looman "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 43b15e8170..600ef5a25f 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures-util" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" diff --git a/futures/Cargo.toml b/futures/Cargo.toml index e3d8ce7b9d..eb987fab1d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "futures" -edition = "2018" version = "0.3.17" +edition = "2018" +rust-version = "1.45" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" From d5c7ba7a17b862401bc70352e492b220a850dd20 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 10:09:06 +0900 Subject: [PATCH 083/283] Remove authors field from Cargo.toml (#2521) https://rust-lang.github.io/rfcs/3052-optional-authors-field.html --- examples/functional/Cargo.toml | 1 - examples/imperative/Cargo.toml | 1 - futures-channel/Cargo.toml | 1 - futures-core/Cargo.toml | 1 - futures-executor/Cargo.toml | 1 - futures-io/Cargo.toml | 1 - futures-macro/Cargo.toml | 1 - futures-sink/Cargo.toml | 1 - futures-task/Cargo.toml | 1 - futures-test/Cargo.toml | 1 - futures-util/Cargo.toml | 1 - futures/Cargo.toml | 1 - futures/tests/macro-reexport/Cargo.toml | 1 - futures/tests/macro-tests/Cargo.toml | 1 - futures/tests/no-std/Cargo.toml | 1 - 15 files changed, 15 deletions(-) diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml index 4376909be2..7b8b494d98 100644 --- a/examples/functional/Cargo.toml +++ b/examples/functional/Cargo.toml @@ -2,7 +2,6 @@ name = "futures-example-functional" edition = "2018" version = "0.1.0" -authors = ["Alex Crichton "] publish = false [dependencies] diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml index e048316cfd..3405451f00 100644 --- a/examples/imperative/Cargo.toml +++ b/examples/imperative/Cargo.toml @@ -2,7 +2,6 @@ name = "futures-example-imperative" edition = "2018" version = "0.1.0" -authors = ["Alex Crichton "] publish = false [dependencies] diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 01375bedae..a64232aec1 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-channel" version = "0.3.17" edition = "2018" rust-version = "1.45" -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" diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 0cd1d1dfe0..e6dbc315c2 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-core" version = "0.3.17" edition = "2018" rust-version = "1.36" -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" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 1fbe4290c4..115cf66b08 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-executor" version = "0.3.17" edition = "2018" rust-version = "1.45" -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" diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index c2b21c601f..3192017ea4 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-io" version = "0.3.17" edition = "2018" rust-version = "1.36" -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" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 0441092a87..2fc76ce6b9 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-macro" version = "0.3.17" edition = "2018" rust-version = "1.45" -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" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 919525f8dd..802362f83f 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-sink" version = "0.3.17" edition = "2018" rust-version = "1.36" -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" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index ceb293d34e..c26a44122b 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-task" version = "0.3.17" edition = "2018" rust-version = "1.36" -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" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index dff72dcc85..6f782366c0 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-test" version = "0.3.17" edition = "2018" rust-version = "1.45" -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" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 600ef5a25f..0e7a22bc9f 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -3,7 +3,6 @@ name = "futures-util" version = "0.3.17" edition = "2018" rust-version = "1.45" -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" diff --git a/futures/Cargo.toml b/futures/Cargo.toml index eb987fab1d..d8b7a1534e 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -3,7 +3,6 @@ name = "futures" version = "0.3.17" edition = "2018" rust-version = "1.45" -authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml index 940b537ea7..a648ee54a5 100644 --- a/futures/tests/macro-reexport/Cargo.toml +++ b/futures/tests/macro-reexport/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "macro-reexport" version = "0.1.0" -authors = ["Taiki Endo "] edition = "2018" publish = false diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml index 3d30563868..963da731ac 100644 --- a/futures/tests/macro-tests/Cargo.toml +++ b/futures/tests/macro-tests/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "macro-tests" version = "0.1.0" -authors = ["Taiki Endo "] edition = "2018" publish = false diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml index 9526732e34..ed5d0c146a 100644 --- a/futures/tests/no-std/Cargo.toml +++ b/futures/tests/no-std/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "no-std" version = "0.1.0" -authors = ["Taiki Endo "] edition = "2018" publish = false From 67b08562de536370a8830d7ac5d9cc266fadc14e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 11:07:08 +0900 Subject: [PATCH 084/283] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f9d944346..4a69b973ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.3.18 - 2021-11-23 + +* Fix unusable `Sink` implementation on `stream::Scan` (#2499) +* Make `task::noop_waker_ref` available without `std` feature (#2505) +* Add async `LineWriter` (#2477) +* Remove dependency on `proc-macro-hack`. This raises MSRV of utility crates to 1.45. (#2520) + # 0.3.17 - 2021-08-30 * Use `FuturesOrdered` in `join_all` (#2412) From 97ee8e6d0949bea54b211547f2ccf648395b16b5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 23 Nov 2021 11:07:34 +0900 Subject: [PATCH 085/283] Release 0.3.18 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index a64232aec1..c70e3afe59 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index e6dbc315c2..8663854bd8 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 115cf66b08..181408b862 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.17", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.18", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 3192017ea4..a3895b8057 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 2fc76ce6b9..5e71a8037b 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 802362f83f..4d445c5b2c 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index c26a44122b..4d058351e5 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 6f782366c0..94520d50f1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.17", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.17", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.17", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.17", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.17", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.17", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.17", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.18", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.18", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.18", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.18", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.18", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.18", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.18", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 0e7a22bc9f..276c392504 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.17", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.17", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.17", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.18", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.18", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.18", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index d8b7a1534e..730c89d697 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.17" +version = "0.3.18" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.17", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.17", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.17", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.17", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.17", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.17", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.17", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.18", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.18", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.18", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.18", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From b41761fd9f70064db472b051f401e74395916437 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 18 Dec 2021 22:54:32 +0900 Subject: [PATCH 086/283] Remove read-initializer feature --- futures-io/Cargo.toml | 1 - futures-io/src/lib.rs | 45 ------------------------- futures-util/Cargo.toml | 1 - futures-util/src/compat/compat01as03.rs | 12 ------- futures-util/src/compat/compat03as01.rs | 12 +------ futures-util/src/future/either.rs | 10 ------ futures-util/src/io/allow_std.rs | 11 ------ futures-util/src/io/buf_reader.rs | 8 ----- futures-util/src/io/chain.rs | 12 ------- futures-util/src/io/empty.rs | 8 ----- futures-util/src/io/mod.rs | 12 +------ futures-util/src/io/repeat.rs | 8 ----- futures-util/src/io/take.rs | 7 ---- futures-util/src/lib.rs | 9 ----- futures/Cargo.toml | 1 - futures/src/lib.rs | 4 --- 16 files changed, 2 insertions(+), 159 deletions(-) diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index a3895b8057..f87c34a9c1 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -18,7 +18,6 @@ std = [] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. unstable = [] -read-initializer = [] [dependencies] diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index 7e03b8e5a0..e91eb78492 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -8,7 +8,6 @@ //! All items of this library are only available when the `std` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(all(feature = "read-initializer", feature = "std"), feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] // It cannot be included in the published code because this lints have false positives in the minimum required version. @@ -22,9 +21,6 @@ ))] #![cfg_attr(docsrs, feature(doc_cfg))] -#[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"); - #[cfg(feature = "std")] mod if_std { use std::io; @@ -34,11 +30,6 @@ mod if_std { // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. - #[cfg(feature = "read-initializer")] - #[cfg_attr(docsrs, doc(cfg(feature = "read-initializer")))] - #[doc(no_inline)] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use io::Initializer; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 #[doc(no_inline)] pub use io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; @@ -51,27 +42,6 @@ mod if_std { /// for wakeup and return if data is not yet available, rather than blocking /// the calling thread. pub trait AsyncRead { - /// Determines if this `AsyncRead`er can work with buffers of - /// uninitialized memory. - /// - /// The default implementation returns an initializer which will zero - /// buffers. - /// - /// This method is only available when the `read-initializer` feature of this - /// library is activated. - /// - /// # Safety - /// - /// This method is `unsafe` because an `AsyncRead`er could otherwise - /// return a non-zeroing `Initializer` from another `AsyncRead` type - /// without an `unsafe` block. - #[cfg(feature = "read-initializer")] - #[cfg_attr(docsrs, doc(cfg(feature = "read-initializer")))] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::zeroing() - } - /// Attempt to read from the `AsyncRead` into `buf`. /// /// On success, returns `Poll::Ready(Ok(num_bytes_read))`. @@ -329,11 +299,6 @@ mod if_std { macro_rules! deref_async_read { () => { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -365,11 +330,6 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncRead, { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -389,11 +349,6 @@ mod if_std { macro_rules! delegate_async_read_to_stdio { () => { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - io::Read::initializer(self) - } - fn poll_read( mut self: Pin<&mut Self>, _: &mut Context<'_>, diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 276c392504..a8f29d4cb4 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -27,7 +27,6 @@ channel = ["std", "futures-channel"] # `unstable` feature as an explicit opt-in to unstable API. unstable = ["futures-core/unstable", "futures-task/unstable"] bilock = [] -read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] write-all-vectored = ["io"] # These features are no longer used. diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 17239a4e57..754e3d82a1 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -351,8 +351,6 @@ unsafe impl UnsafeNotify01 for NotifyWaker { #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] mod io { use super::*; - #[cfg(feature = "read-initializer")] - use futures_io::Initializer; use futures_io::{AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03}; use std::io::Error; use tokio_io::{AsyncRead as AsyncRead01, AsyncWrite as AsyncWrite01}; @@ -416,16 +414,6 @@ mod io { impl AsyncWrite01CompatExt for W {} impl AsyncRead03 for Compat01As03 { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - // check if `prepare_uninitialized_buffer` needs zeroing - if self.inner.get_ref().prepare_uninitialized_buffer(&mut [1]) { - Initializer::zeroing() - } else { - Initializer::nop() - } - } - fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 2573fe7a74..5d3a6e920b 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -236,17 +236,7 @@ mod io { } } - impl AsyncRead01 for Compat { - #[cfg(feature = "read-initializer")] - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - let initializer = self.inner.initializer(); - let does_init = initializer.should_initialize(); - if does_init { - initializer.initialize(buf); - } - does_init - } - } + impl AsyncRead01 for Compat {} impl std::io::Write for Compat { fn write(&mut self, buf: &[u8]) -> std::io::Result { diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 35650daa99..9602de7a42 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -184,8 +184,6 @@ mod if_std { use core::pin::Pin; use core::task::{Context, Poll}; - #[cfg(feature = "read-initializer")] - use futures_io::Initializer; use futures_io::{ AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, Result, SeekFrom, }; @@ -195,14 +193,6 @@ mod if_std { A: AsyncRead, B: AsyncRead, { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - match self { - Either::Left(x) => x.initializer(), - Either::Right(x) => x.initializer(), - } - } - fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/futures-util/src/io/allow_std.rs b/futures-util/src/io/allow_std.rs index 1d13e0c177..ec30ee31e5 100644 --- a/futures-util/src/io/allow_std.rs +++ b/futures-util/src/io/allow_std.rs @@ -1,6 +1,4 @@ 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 std::pin::Pin; use std::{fmt, io}; @@ -121,10 +119,6 @@ where fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.0.read_vectored(bufs) } - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.0.initializer() - } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { self.0.read_to_end(buf) } @@ -155,11 +149,6 @@ where ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.read_vectored(bufs)))) } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.0.initializer() - } } impl io::Seek for AllowStdIo diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 2d585a9eb6..0334a9f081 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -2,8 +2,6 @@ use super::DEFAULT_BUF_SIZE; use futures_core::future::Future; use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSliceMut, SeekFrom}; use pin_project_lite::pin_project; use std::io::{self, Read}; @@ -144,12 +142,6 @@ impl AsyncRead for BufReader { self.consume(nread); Poll::Ready(Ok(nread)) } - - // 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() - } } impl AsyncBufRead for BufReader { diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index a35c50de35..728a3d2dc0 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -1,7 +1,5 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead, IoSliceMut}; use pin_project_lite::pin_project; use std::fmt; @@ -111,16 +109,6 @@ where } this.second.poll_read_vectored(cx, bufs) } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - let initializer = self.first.initializer(); - if initializer.should_initialize() { - initializer - } else { - self.second.initializer() - } - } } impl AsyncBufRead for Chain diff --git a/futures-util/src/io/empty.rs b/futures-util/src/io/empty.rs index ab2395a8af..02f6103f54 100644 --- a/futures-util/src/io/empty.rs +++ b/futures-util/src/io/empty.rs @@ -1,6 +1,4 @@ use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead}; use std::fmt; use std::io; @@ -43,12 +41,6 @@ impl AsyncRead for Empty { ) -> Poll> { Poll::Ready(Ok(0)) } - - #[cfg(feature = "read-initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl AsyncBufRead for Empty { diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 4a51804802..4dd2e029bf 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -26,10 +26,6 @@ use std::{pin::Pin, ptr}; // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. #[doc(no_inline)] -#[cfg(feature = "read-initializer")] -#[cfg_attr(docsrs, doc(cfg(feature = "read-initializer")))] -pub use std::io::Initializer; -#[doc(no_inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; pub use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; @@ -40,15 +36,9 @@ const DEFAULT_BUF_SIZE: usize = 8 * 1024; /// Initializes a buffer if necessary. /// -/// A buffer is always initialized if `read-initializer` feature is disabled. +/// A buffer is currently always initialized. #[inline] unsafe fn initialize(_reader: &R, buf: &mut [u8]) { - #[cfg(feature = "read-initializer")] - { - if !_reader.initializer().should_initialize() { - return; - } - } ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } diff --git a/futures-util/src/io/repeat.rs b/futures-util/src/io/repeat.rs index 4cefcb2a28..2828bf0114 100644 --- a/futures-util/src/io/repeat.rs +++ b/futures-util/src/io/repeat.rs @@ -1,7 +1,5 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncRead, IoSliceMut}; use std::fmt; use std::io; @@ -59,12 +57,6 @@ impl AsyncRead for Repeat { } Poll::Ready(Ok(nwritten)) } - - #[cfg(feature = "read-initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl fmt::Debug for Repeat { diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index 05830203d0..2c494804d9 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -1,7 +1,5 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead}; use pin_project_lite::pin_project; use std::pin::Pin; @@ -100,11 +98,6 @@ impl AsyncRead for Take { *this.limit -= n as u64; Poll::Ready(Ok(n)) } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } } impl AsyncBufRead for Take { diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 76d37994ff..9a10c93c9a 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -1,7 +1,6 @@ //! Combinators and utilities for working with `Future`s, `Stream`s, `Sink`s, //! and the `AsyncRead` and `AsyncWrite` traits. -#![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( @@ -23,9 +22,6 @@ #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); -#[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"); - #[cfg(feature = "alloc")] extern crate alloc; @@ -148,11 +144,6 @@ macro_rules! delegate_async_write { #[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<'_>, diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 730c89d697..43f0b10ffb 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -47,7 +47,6 @@ thread-pool = ["executor", "futures-executor/thread-pool"] # `unstable` feature as an explicit opt-in to unstable API. unstable = ["futures-core/unstable", "futures-task/unstable", "futures-channel/unstable", "futures-io/unstable", "futures-util/unstable"] bilock = ["futures-util/bilock"] -read-initializer = ["futures-io/read-initializer", "futures-util/read-initializer"] write-all-vectored = ["futures-util/write-all-vectored"] # These features are no longer used. diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 362aa3cd99..8e21c8ebe8 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -78,7 +78,6 @@ //! The majority of examples and code snippets in this crate assume that they are //! inside an async block as written above. -#![cfg_attr(feature = "read-initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] #![warn( missing_debug_implementations, @@ -99,9 +98,6 @@ #[cfg(all(feature = "bilock", not(feature = "unstable")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); -#[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"); - #[doc(no_inline)] pub use futures_core::future::{Future, TryFuture}; #[doc(no_inline)] From 6ffb4c508ce1f63d700d96a2460aefb61a89c7a8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 18 Dec 2021 23:15:25 +0900 Subject: [PATCH 087/283] Revert "Remove implicit clear in `ReadyToRunQueue::drop` (#2493)" This reverts commit 37dfb05b6c9414ede608d61e4c4507cd7c148038. --- .../src/stream/futures_unordered/mod.rs | 21 ++++++++++++++++--- .../futures_unordered/ready_to_run_queue.rs | 15 ++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 6918a26b91..4a05d8823e 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -558,7 +558,7 @@ impl FuturesUnordered { pub fn clear(&mut self) { self.clear_head_all(); - // SAFETY: we just cleared all the tasks and we have &mut self + // we just cleared all the tasks, and we have &mut self, so this is safe. unsafe { self.ready_to_run_queue.clear() }; self.is_terminated.store(false, Relaxed); @@ -575,9 +575,24 @@ impl FuturesUnordered { impl Drop for FuturesUnordered { fn drop(&mut self) { + // When a `FuturesUnordered` is dropped we want to drop all futures + // associated with it. At the same time though there may be tons of + // wakers flying around which contain `Task` references + // inside them. We'll let those naturally get deallocated. self.clear_head_all(); - // SAFETY: we just cleared all the tasks and we have &mut self - unsafe { self.ready_to_run_queue.clear() }; + + // Note that at this point we could still have a bunch of tasks in the + // ready to run queue. None of those tasks, however, have futures + // associated with them so they're safe to destroy on any thread. At + // this point the `FuturesUnordered` struct, the owner of the one strong + // reference to the ready to run queue will drop the strong reference. + // At that point whichever thread releases the strong refcount last (be + // it this thread or some other thread as part of an `upgrade`) will + // clear out the ready to run queue and free all remaining tasks. + // + // While that freeing operation isn't guaranteed to happen here, it's + // guaranteed to happen "promptly" as no more "blocking work" will + // happen while there's a strong refcount held. } } diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 2bc208682a..5ef6cde83d 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -94,7 +94,7 @@ impl ReadyToRunQueue { // // # Safety // - // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear_head_all) + // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear) // - The caller **must** guarantee unique access to `self` pub(crate) unsafe fn clear(&self) { loop { @@ -107,3 +107,16 @@ impl ReadyToRunQueue { } } } + +impl Drop for ReadyToRunQueue { + fn drop(&mut self) { + // Once we're in the destructor for `Inner` we need to clear out + // the ready to run queue of tasks if there's anything left in there. + + // All tasks have had their futures dropped already by the `FuturesUnordered` + // destructor above, and we have &mut self, so this is safe. + unsafe { + self.clear(); + } + } +} From fb271ed82150ef80dc168ed6042368be477093b3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 18 Dec 2021 23:56:35 +0900 Subject: [PATCH 088/283] FuturesUnordered: Limit max value of yield_every --- .../src/stream/futures_unordered/mod.rs | 45 ++++++++++++------- futures/tests/stream_futures_unordered.rs | 2 +- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 4a05d8823e..aab2bb4467 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -6,6 +6,7 @@ use crate::task::AtomicWaker; use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; +use core::cmp; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::marker::PhantomData; @@ -30,6 +31,33 @@ use self::task::Task; mod ready_to_run_queue; use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue}; +/// Constant used for a `FuturesUnordered` to determine how many times it is +/// allowed to poll underlying futures without yielding. +/// +/// A single call to `poll_next` may potentially do a lot of work before +/// yielding. This happens in particular if the underlying futures are awoken +/// frequently but continue to return `Pending`. This is problematic if other +/// tasks are waiting on the executor, since they do not get to run. This value +/// caps the number of calls to `poll` on underlying futures a single call to +/// `poll_next` is allowed to make. +/// +/// The value itself is chosen somewhat arbitrarily. It needs to be high enough +/// that amortize wakeup and scheduling costs, but low enough that we do not +/// starve other tasks for long. +/// +/// See also https://github.com/rust-lang/futures-rs/issues/2047. +/// +/// Note that using the length of the `FuturesUnordered` instead of this value +/// may cause problems if the number of futures is large. +/// See also https://github.com/rust-lang/futures-rs/pull/2527. +/// +/// Additionally, polling the same future twice per iteration may cause another +/// problem. So, when using this value, it is necessary to limit the max value +/// based on the length of the `FuturesUnordered`. +/// (e.g., `cmp::min(self.len(), YIELD_EVERY)`) +/// See also https://github.com/rust-lang/futures-rs/pull/2333. +const YIELD_EVERY: usize = 32; + /// A set of futures which may complete in any order. /// /// This structure is optimized to manage a large number of futures. @@ -383,21 +411,8 @@ impl Stream for FuturesUnordered { type Item = Fut::Output; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - // Variable to determine how many times it is allowed to poll underlying - // futures without yielding. - // - // A single call to `poll_next` may potentially do a lot of work before - // yielding. This happens in particular if the underlying futures are awoken - // frequently but continue to return `Pending`. This is problematic if other - // tasks are waiting on the executor, since they do not get to run. This value - // caps the number of calls to `poll` on underlying futures a single call to - // `poll_next` is allowed to make. - // - // The value is the length of FuturesUnordered. This ensures that each - // future is polled only once at most per iteration. - // - // See also https://github.com/rust-lang/futures-rs/issues/2047. - let yield_every = self.len(); + // See YIELD_EVERY docs for more. + let yield_every = cmp::min(self.len(), YIELD_EVERY); // Keep track of how many child futures we have polled, // in case we want to forcibly yield. diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 4b9afccaf9..439c809bef 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -340,7 +340,7 @@ fn polled_only_once_at_most_per_iteration() { let mut tasks = FuturesUnordered::from_iter(vec![F::default(); 33]); assert!(tasks.poll_next_unpin(cx).is_pending()); - assert_eq!(33, tasks.iter().filter(|f| f.polled).count()); + assert_eq!(32, tasks.iter().filter(|f| f.polled).count()); let mut tasks = FuturesUnordered::::new(); assert_eq!(Poll::Ready(None), tasks.poll_next_unpin(cx)); From 440cfdbc86522d099a06c0d443b4eb3789738563 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Wed, 8 Sep 2021 16:34:23 +0200 Subject: [PATCH 089/283] futures-util: add StreamExt::count method Signed-off-by: Petros Angelatos --- futures-util/src/stream/stream/count.rs | 53 +++++++++++++++++++++++++ futures-util/src/stream/stream/mod.rs | 36 +++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 futures-util/src/stream/stream/count.rs diff --git a/futures-util/src/stream/stream/count.rs b/futures-util/src/stream/stream/count.rs new file mode 100644 index 0000000000..513cab7b6a --- /dev/null +++ b/futures-util/src/stream/stream/count.rs @@ -0,0 +1,53 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`count`](super::StreamExt::count) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Count { + #[pin] + stream: St, + count: usize + } +} + +impl fmt::Debug for Count +where + St: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Count").field("stream", &self.stream).field("count", &self.count).finish() + } +} + +impl Count { + pub(super) fn new(stream: St) -> Self { + Self { stream, count: 0 } + } +} + +impl FusedFuture for Count { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + } +} + +impl Future for Count { + type Output = usize; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + Poll::Ready(loop { + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(_) => *this.count += 1, + None => break *this.count, + } + }) + } +} diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 86997f45c9..9cfcc09bac 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -40,6 +40,10 @@ mod concat; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::concat::Concat; +mod count; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::count::Count; + mod cycle; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::cycle::Cycle; @@ -576,6 +580,38 @@ pub trait StreamExt: Stream { assert_future::(Concat::new(self)) } + /// Drives the stream to completion, counting the number of items. + /// + /// # Overflow Behavior + /// + /// The method does no guarding against overflows, so counting elements of a + /// stream with more than [`usize::MAX`] elements either produces the wrong + /// result or panics. If debug assertions are enabled, a panic is guaranteed. + /// + /// # Panics + /// + /// This function might panic if the iterator has more than [`usize::MAX`] + /// elements. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..=10); + /// let count = stream.count().await; + /// + /// assert_eq!(count, 10); + /// # }); + /// ``` + fn count(self) -> Count + where + Self: Sized, + { + assert_future::(Count::new(self)) + } + /// Repeats a stream endlessly. /// /// The stream never terminates. Note that you likely want to avoid From 2f55317f3c6de828232de9f31a996bf691d4b9a0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 19 Dec 2021 00:45:56 +0900 Subject: [PATCH 090/283] Update changelog --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a69b973ce..1c39f858f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ +# 0.3.19 - 2021-12-18 + +* Remove unstable `read-initializer` feature (#2534) +* Fix panic in `FuturesUnordered` (#2535) +* Fix compatibility issue with `FuturesUnordered` and tokio's cooperative scheduling (#2527) +* Add `StreamExt::count` (#2495) + # 0.3.18 - 2021-11-23 +NOTE: This release has been yanked. See #2529 for details. + * Fix unusable `Sink` implementation on `stream::Scan` (#2499) * Make `task::noop_waker_ref` available without `std` feature (#2505) * Add async `LineWriter` (#2477) @@ -62,10 +71,14 @@ # 0.3.10 - 2021-01-13 +NOTE: This release has been yanked. See #2310 for details. + * Fixed type-inference in `sink::unfold` by specifying more of its types (breaking change -- see #2311) # 0.3.9 - 2021-01-08 +NOTE: This release has been yanked. See #2310 for details. + * Significantly improved compile time when `async-await` crate feature is disabled (#2273) * Added `stream::repeat_with` (#2279) * Added `StreamExt::unzip` (#2263) @@ -77,6 +90,8 @@ # 0.3.8 - 2020-11-04 +NOTE: This release has been yanked. See #2310 for details. + * Switched proc-macros to use native `#[proc_macro]` at Rust 1.45+ (#2243) * Added `WeakShared` (#2169) * Added `TryStreamExt::try_buffered` (#2245) @@ -86,12 +101,16 @@ # 0.3.7 - 2020-10-23 +NOTE: This release has been yanked. See #2310 for details. + * Fixed unsoundness in `MappedMutexGuard` (#2240) * Re-exported `TakeUntil` (#2235) * futures-test: Prevent double panic in `panic_waker` (#2236) # 0.3.6 - 2020-10-06 +NOTE: This release has been yanked. See #2310 for details. + * Fixed UB due to missing 'static on `task::waker` (#2206) * Added `AsyncBufReadExt::fill_buf` (#2225) * Added `TryStreamExt::try_take_while` (#2212) @@ -106,6 +125,8 @@ # 0.3.5 - 2020-05-08 +NOTE: This release has been yanked. See #2310 for details. + * Added `StreamExt::flat_map`. * Added `StreamExt::ready_chunks`. * Added `*_unpin` methods to `SinkExt`. @@ -124,14 +145,20 @@ # 0.3.4 - 2020-02-06 +NOTE: This release has been yanked. See #2310 for details. + * Fixed missing `Drop` for `UnboundedReceiver` (#2064) # 0.3.3 - 2020-02-04 +NOTE: This release has been yanked. See #2310 for details. + * Fixed compatibility issue with pinned facade (#2062) # 0.3.2 - 2020-02-03 +NOTE: This release has been yanked. See #2310 for details. + * Improved buffering performance of `SplitSink` (#1969) * Added `select_biased!` macro (#1976) * Added `hash_receiver` method to mpsc channel (#1962) @@ -151,10 +178,14 @@ # 0.3.1 - 2019-11-07 +NOTE: This release has been yanked. See #2310 for details. + * Fix signature of `LocalSpawn` trait (breaking change -- see #1959) # 0.3.0 - 2019-11-05 +NOTE: This release has been yanked. See #2310 for details. + * Stable release along with stable async/await! * Added async/await to default features (#1953) * Changed `Spawn` trait and `FuturesUnordered::push` to take `&self` (#1950) From 29a736f9edc2ba8abcf45ef037919c9df47dc754 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 19 Dec 2021 00:46:18 +0900 Subject: [PATCH 091/283] Release 0.3.19 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index c70e3afe59..a49c556ad1 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 8663854bd8..c3026f733c 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 181408b862..458d9e47a2 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.18", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.19", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index f87c34a9c1..5350d2157e 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 5e71a8037b..7ea489a929 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 4d445c5b2c..40c9c33f0b 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 4d058351e5..8531d8f297 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 94520d50f1..4456245f0c 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.18", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.18", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.18", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.18", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.18", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.18", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.18", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.19", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.19", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.19", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.19", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.19", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.19", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.19", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index a8f29d4cb4..687579c7dd 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.18", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.18", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.18", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.19", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.19", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.19", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 43f0b10ffb..3bec3dd7bb 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.18" +version = "0.3.19" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.18", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.18", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.18", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.18", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.18", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.18", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.18", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.19", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.19", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.19", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.19", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 9326c4e258e4a9a3bee13b9ded6bedaa19e6c9dd Mon Sep 17 00:00:00 2001 From: Kevin Cox Date: Thu, 30 Dec 2021 04:09:18 -0500 Subject: [PATCH 092/283] Improve example for TryStreamExt::try_flatten. (#2540) The previous example was confusing because it didn't show what happened to `Err(4)`. I also added some more examples to show that entries after `Err(_)` are preserved. --- futures-util/src/stream/try_stream/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 455ddca3f4..70e8f0501b 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -736,17 +736,21 @@ pub trait TryStreamExt: TryStream { /// thread::spawn(move || { /// tx2.unbounded_send(Ok(2)).unwrap(); /// tx2.unbounded_send(Err(3)).unwrap(); + /// tx2.unbounded_send(Ok(4)).unwrap(); /// }); /// thread::spawn(move || { /// tx3.unbounded_send(Ok(rx1)).unwrap(); /// tx3.unbounded_send(Ok(rx2)).unwrap(); - /// tx3.unbounded_send(Err(4)).unwrap(); + /// tx3.unbounded_send(Err(5)).unwrap(); /// }); /// /// let mut stream = rx3.try_flatten(); /// assert_eq!(stream.next().await, Some(Ok(1))); /// assert_eq!(stream.next().await, Some(Ok(2))); /// assert_eq!(stream.next().await, Some(Err(3))); + /// assert_eq!(stream.next().await, Some(Ok(4))); + /// assert_eq!(stream.next().await, Some(Err(5))); + /// assert_eq!(stream.next().await, None); /// # }); /// ``` fn try_flatten(self) -> TryFlatten From 0e6f9d96851f8cfd99e0ac59b09eac294cff91b4 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 Jan 2022 23:46:28 +0900 Subject: [PATCH 093/283] Ignore unreachable_pub false positives (#2549) --- futures/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 8e21c8ebe8..e34272dc98 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -177,10 +177,12 @@ pub mod prelude { pub use crate::stream::{self, Stream, TryStream}; #[doc(no_inline)] + #[allow(unreachable_pub)] pub use crate::future::{FutureExt as _, TryFutureExt as _}; #[doc(no_inline)] pub use crate::sink::SinkExt as _; #[doc(no_inline)] + #[allow(unreachable_pub)] pub use crate::stream::{StreamExt as _, TryStreamExt as _}; #[cfg(feature = "std")] @@ -188,6 +190,7 @@ pub mod prelude { #[cfg(feature = "std")] #[doc(no_inline)] + #[allow(unreachable_pub)] pub use crate::io::{ AsyncBufReadExt as _, AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _, }; From b29756599e6f4f8a1b173e2d99a63996239a05ae Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:06:22 +0900 Subject: [PATCH 094/283] Fix stacked borrows violations in waker_ref and FuturesUnordered (#2550) --- .github/workflows/ci.yml | 12 ++++++------ futures-task/src/waker_ref.rs | 2 +- futures-util/src/stream/futures_unordered/mod.rs | 4 ++-- .../stream/futures_unordered/ready_to_run_queue.rs | 2 +- futures-util/src/stream/futures_unordered/task.rs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec836214e2..93de872578 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,11 +56,11 @@ jobs: if: matrix.target != 'aarch64-unknown-linux-gnu' core-msrv: - name: cargo +${{ matrix.rust }} build (futures-{core, io, sink, task}) + name: cargo +${{ matrix.rust }} build (futures-{core, io, sink}) strategy: matrix: rust: - # This is the minimum Rust version supported by futures-core, futures-io, futures-sink, futures-task. + # This is the minimum Rust version supported by futures-core, futures-io, futures-sink. # When updating this, the reminder to update the minimum required version in README.md, Cargo.toml, and .clippy.toml. - 1.36 runs-on: ubuntu-latest @@ -76,22 +76,22 @@ jobs: # Check no-default-features - run: | cargo hack build --workspace --ignore-private --no-default-features \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-task --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test # Check alloc feature - run: | cargo hack build --workspace --ignore-private --no-default-features --features alloc --ignore-unknown-features \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-task --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test # Check std feature - run: | cargo hack build --workspace --ignore-private --no-default-features --features std \ - --exclude futures --exclude futures-util --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test + --exclude futures --exclude futures-util --exclude futures-task --exclude futures-macro --exclude futures-executor --exclude futures-channel --exclude futures-test util-msrv: name: cargo +${{ matrix.rust }} build strategy: matrix: rust: - # This is the minimum Rust version supported by futures, futures-util, futures-macro, futures-executor, futures-channel, futures-test. + # This is the minimum Rust version supported by futures, futures-util, futures-task, futures-macro, futures-executor, futures-channel, futures-test. # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - 1.45 runs-on: ubuntu-latest diff --git a/futures-task/src/waker_ref.rs b/futures-task/src/waker_ref.rs index 791c690120..7fb552fcfd 100644 --- a/futures-task/src/waker_ref.rs +++ b/futures-task/src/waker_ref.rs @@ -55,7 +55,7 @@ where { // simply copy the pointer instead of using Arc::into_raw, // as we don't actually keep a refcount by using ManuallyDrop.< - let ptr = (&**wake as *const W) as *const (); + let ptr = Arc::as_ptr(wake).cast::<()>(); let waker = ManuallyDrop::new(unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) }); diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index aab2bb4467..46a0ae5bde 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -150,7 +150,7 @@ impl FuturesUnordered { queued: AtomicBool::new(true), ready_to_run_queue: Weak::new(), }); - let stub_ptr = &*stub as *const Task; + let stub_ptr = Arc::as_ptr(&stub); let ready_to_run_queue = Arc::new(ReadyToRunQueue { waker: AtomicWaker::new(), head: AtomicPtr::new(stub_ptr as *mut _), @@ -403,7 +403,7 @@ impl FuturesUnordered { // The `ReadyToRunQueue` stub is never inserted into the `head_all` // list, and its pointer value will remain valid for the lifetime of // this `FuturesUnordered`, so we can make use of its value here. - &*self.ready_to_run_queue.stub as *const _ as *mut _ + Arc::as_ptr(&self.ready_to_run_queue.stub) as *mut _ } } diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 5ef6cde83d..4518705320 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -83,7 +83,7 @@ impl ReadyToRunQueue { } pub(super) fn stub(&self) -> *const Task { - &*self.stub + Arc::as_ptr(&self.stub) } // Clear the queue of tasks. diff --git a/futures-util/src/stream/futures_unordered/task.rs b/futures-util/src/stream/futures_unordered/task.rs index da2cd67d97..5216199831 100644 --- a/futures-util/src/stream/futures_unordered/task.rs +++ b/futures-util/src/stream/futures_unordered/task.rs @@ -62,7 +62,7 @@ impl ArcWake for Task { // still. let prev = arc_self.queued.swap(true, SeqCst); if !prev { - inner.enqueue(&**arc_self); + inner.enqueue(Arc::as_ptr(arc_self)); inner.waker.wake(); } } From 09b083033cf946a5e6b858f9e3d3b853a52a526d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:04:38 +0900 Subject: [PATCH 095/283] Run Miri on CI --- .github/workflows/ci.yml | 22 +++++++++++++++++++- futures-channel/tests/mpsc-close.rs | 1 + futures-channel/tests/mpsc.rs | 25 +++++++++++++++++++++-- futures-channel/tests/oneshot.rs | 14 +++++++++++-- futures-util/src/compat/compat01as03.rs | 5 +++++ futures-util/src/compat/executor.rs | 1 + futures-util/src/stream/try_stream/mod.rs | 1 + futures-util/src/task/spawn.rs | 3 +++ futures/src/lib.rs | 1 + futures/tests/compat.rs | 1 + futures/tests/eventual.rs | 2 ++ futures/tests/future_join_all.rs | 25 +++++++++++------------ futures/tests/future_shared.rs | 1 + futures/tests/future_try_join_all.rs | 24 ++++++++++++---------- futures/tests/lock_mutex.rs | 1 + futures/tests/macro_comma_support.rs | 1 + futures/tests/ready_queue.rs | 3 +++ futures/tests/recurse.rs | 1 + futures/tests/sink.rs | 1 + futures/tests/stream_futures_ordered.rs | 2 ++ futures/tests/stream_futures_unordered.rs | 2 ++ futures/tests/stream_try_stream.rs | 2 ++ futures/tests/task_atomic_waker.rs | 1 + 23 files changed, 111 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93de872578..a07211dfd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,8 @@ name: CI +permissions: + contents: read + on: pull_request: push: @@ -10,8 +13,12 @@ on: - cron: '0 1 * * *' env: - RUSTFLAGS: -D warnings + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 defaults: run: @@ -229,6 +236,18 @@ jobs: - run: ci/no_atomic_cas.sh - run: git diff --exit-code + miri: + name: cargo miri test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup toolchain install nightly --component miri && rustup default nightly + # futures-executor uses boxed futures so many tests trigger https://github.com/rust-lang/miri/issues/1038 + - run: cargo miri test --workspace --exclude futures-executor --all-features + env: + MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-tag-raw-pointers + san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} strategy: @@ -263,6 +282,7 @@ jobs: # - run: cargo clippy --workspace --all-features --all-targets fmt: + name: cargo fmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/futures-channel/tests/mpsc-close.rs b/futures-channel/tests/mpsc-close.rs index 81203d3348..1a14067eca 100644 --- a/futures-channel/tests/mpsc-close.rs +++ b/futures-channel/tests/mpsc-close.rs @@ -147,6 +147,7 @@ fn single_receiver_drop_closes_channel_and_drains() { // Stress test that `try_send()`s occurring concurrently with receiver // close/drops don't appear as successful sends. +#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn stress_try_send_as_receiver_closes() { const AMT: usize = 10000; diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index 88cdef13d6..da0899d491 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -200,6 +200,9 @@ fn tx_close_gets_none() { #[test] fn stress_shared_unbounded() { + #[cfg(miri)] + const AMT: u32 = 100; + #[cfg(not(miri))] const AMT: u32 = 10000; const NTHREADS: u32 = 8; let (tx, rx) = mpsc::unbounded::(); @@ -229,6 +232,9 @@ fn stress_shared_unbounded() { #[test] fn stress_shared_bounded_hard() { + #[cfg(miri)] + const AMT: u32 = 100; + #[cfg(not(miri))] const AMT: u32 = 10000; const NTHREADS: u32 = 8; let (tx, rx) = mpsc::channel::(0); @@ -259,6 +265,9 @@ fn stress_shared_bounded_hard() { #[allow(clippy::same_item_push)] #[test] fn stress_receiver_multi_task_bounded_hard() { + #[cfg(miri)] + const AMT: usize = 100; + #[cfg(not(miri))] const AMT: usize = 10_000; const NTHREADS: u32 = 2; @@ -327,6 +336,11 @@ fn stress_receiver_multi_task_bounded_hard() { /// after sender dropped. #[test] fn stress_drop_sender() { + #[cfg(miri)] + const ITER: usize = 100; + #[cfg(not(miri))] + const ITER: usize = 10000; + fn list() -> impl Stream { let (tx, rx) = mpsc::channel(1); thread::spawn(move || { @@ -335,7 +349,7 @@ fn stress_drop_sender() { rx } - for _ in 0..10000 { + for _ in 0..ITER { let v: Vec<_> = block_on(list().collect()); assert_eq!(v, vec![1, 2, 3]); } @@ -380,9 +394,12 @@ fn stress_close_receiver_iter() { } } +#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn stress_close_receiver() { - for _ in 0..10000 { + const ITER: usize = 10000; + + for _ in 0..ITER { stress_close_receiver_iter(); } } @@ -397,6 +414,9 @@ async fn stress_poll_ready_sender(mut sender: mpsc::Sender, count: u32) { #[allow(clippy::same_item_push)] #[test] fn stress_poll_ready() { + #[cfg(miri)] + const AMT: u32 = 100; + #[cfg(not(miri))] const AMT: u32 = 1000; const NTHREADS: u32 = 8; @@ -424,6 +444,7 @@ fn stress_poll_ready() { stress(16); } +#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn try_send_1() { const N: usize = 3000; diff --git a/futures-channel/tests/oneshot.rs b/futures-channel/tests/oneshot.rs index 979cd8a15a..c9f5508973 100644 --- a/futures-channel/tests/oneshot.rs +++ b/futures-channel/tests/oneshot.rs @@ -35,6 +35,11 @@ fn cancel_notifies() { #[test] fn cancel_lots() { + #[cfg(miri)] + const N: usize = 100; + #[cfg(not(miri))] + const N: usize = 20000; + let (tx, rx) = mpsc::channel::<(Sender<_>, mpsc::Sender<_>)>(); let t = thread::spawn(move || { for (mut tx, tx2) in rx { @@ -43,7 +48,7 @@ fn cancel_lots() { } }); - for _ in 0..20000 { + for _ in 0..N { let (otx, orx) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); tx.send((otx, tx2)).unwrap(); @@ -101,6 +106,11 @@ fn is_canceled() { #[test] fn cancel_sends() { + #[cfg(miri)] + const N: usize = 100; + #[cfg(not(miri))] + const N: usize = 20000; + let (tx, rx) = mpsc::channel::>(); let t = thread::spawn(move || { for otx in rx { @@ -108,7 +118,7 @@ fn cancel_sends() { } }); - for _ in 0..20000 { + for _ in 0..N { let (otx, mut orx) = oneshot::channel::(); tx.send(otx).unwrap(); diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 754e3d82a1..36de1da98d 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -64,6 +64,7 @@ pub trait Future01CompatExt: Future01 { /// [`Future>`](futures_core::future::Future). /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514 /// # futures::executor::block_on(async { /// # // TODO: These should be all using `futures::compat`, but that runs up against Cargo /// # // feature issues @@ -90,6 +91,7 @@ pub trait Stream01CompatExt: Stream01 { /// [`Stream>`](futures_core::stream::Stream). /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514 /// # futures::executor::block_on(async { /// use futures::stream::StreamExt; /// use futures_util::compat::Stream01CompatExt; @@ -119,6 +121,7 @@ pub trait Sink01CompatExt: Sink01 { /// [`Sink`](futures_sink::Sink). /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514 /// # futures::executor::block_on(async { /// use futures::{sink::SinkExt, stream::StreamExt}; /// use futures_util::compat::{Stream01CompatExt, Sink01CompatExt}; @@ -362,6 +365,7 @@ mod io { /// [`AsyncRead`](futures_io::AsyncRead). /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514 /// # futures::executor::block_on(async { /// use futures::io::AsyncReadExt; /// use futures_util::compat::AsyncRead01CompatExt; @@ -391,6 +395,7 @@ mod io { /// [`AsyncWrite`](futures_io::AsyncWrite). /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514 /// # futures::executor::block_on(async { /// use futures::io::AsyncWriteExt; /// use futures_util::compat::AsyncWrite01CompatExt; diff --git a/futures-util/src/compat/executor.rs b/futures-util/src/compat/executor.rs index e25705be16..ea0c67a0ae 100644 --- a/futures-util/src/compat/executor.rs +++ b/futures-util/src/compat/executor.rs @@ -17,6 +17,7 @@ pub trait Executor01CompatExt: Executor01 + Clone + Send + 'st /// futures 0.3 [`Spawn`](futures_task::Spawn). /// /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll /// use futures::task::SpawnExt; /// use futures::future::{FutureExt, TryFutureExt}; /// use futures_util::compat::Executor01CompatExt; diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 70e8f0501b..6bf2cb74a5 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -1005,6 +1005,7 @@ pub trait TryStreamExt: TryStream { /// Wraps a [`TryStream`] into a stream compatible with libraries using /// futures 0.1 `Stream`. Requires the `compat` feature to be enabled. /// ``` + /// # if cfg!(miri) { return; } // Miri does not support epoll /// use futures::future::{FutureExt, TryFutureExt}; /// # let (tx, rx) = futures::channel::oneshot::channel(); /// diff --git a/futures-util/src/task/spawn.rs b/futures-util/src/task/spawn.rs index f8779230ed..87ca360516 100644 --- a/futures-util/src/task/spawn.rs +++ b/futures-util/src/task/spawn.rs @@ -34,6 +34,7 @@ pub trait SpawnExt: Spawn { /// today. Feel free to use this method in the meantime. /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 /// use futures::executor::ThreadPool; /// use futures::task::SpawnExt; /// @@ -58,6 +59,7 @@ pub trait SpawnExt: Spawn { /// resolves to the output of the spawned future. /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 /// use futures::executor::{block_on, ThreadPool}; /// use futures::future; /// use futures::task::SpawnExt; @@ -136,6 +138,7 @@ pub trait LocalSpawnExt: LocalSpawn { /// resolves to the output of the spawned future. /// /// ``` + /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 /// use futures::executor::LocalPool; /// use futures::task::LocalSpawnExt; /// diff --git a/futures/src/lib.rs b/futures/src/lib.rs index e34272dc98..1385e46362 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -25,6 +25,7 @@ //! within macros and keywords such as async and await!. //! //! ```rust +//! # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 //! # use futures::channel::mpsc; //! # use futures::executor; ///standard executors to provide a context for futures and streams //! # use futures::executor::ThreadPool; diff --git a/futures/tests/compat.rs b/futures/tests/compat.rs index c4125d895b..ac04a95ea8 100644 --- a/futures/tests/compat.rs +++ b/futures/tests/compat.rs @@ -1,4 +1,5 @@ #![cfg(feature = "compat")] +#![cfg(not(miri))] // Miri does not support epoll use futures::compat::Future01CompatExt; use futures::prelude::*; diff --git a/futures/tests/eventual.rs b/futures/tests/eventual.rs index bff000dd09..34613806c4 100644 --- a/futures/tests/eventual.rs +++ b/futures/tests/eventual.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038 + use futures::channel::oneshot; use futures::executor::ThreadPool; use futures::future::{self, ok, Future, FutureExt, TryFutureExt}; diff --git a/futures/tests/future_join_all.rs b/futures/tests/future_join_all.rs index ae05a21b7c..44486e1ca3 100644 --- a/futures/tests/future_join_all.rs +++ b/futures/tests/future_join_all.rs @@ -1,22 +1,24 @@ use futures::executor::block_on; use futures::future::{join_all, ready, Future, JoinAll}; +use futures::pin_mut; use std::fmt::Debug; -fn assert_done(actual_fut: F, expected: T) +#[track_caller] +fn assert_done(actual_fut: impl Future, expected: T) where T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, { - let output = block_on(actual_fut()); + pin_mut!(actual_fut); + let output = block_on(actual_fut); assert_eq!(output, expected); } #[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]); + assert_done(join_all(vec![ready(1), ready(2)]), vec![1, 2]); + assert_done(join_all(vec![ready(1)]), vec![1]); // REVIEW: should this be implemented? - // assert_done(|| Box::new(join_all(Vec::::new())), vec![]); + // assert_done(join_all(Vec::::new()), vec![]); // TODO: needs more tests } @@ -25,18 +27,15 @@ fn collect_collects() { fn join_all_iter_lifetime() { // In futures-rs version 0.1, this function would fail to typecheck due to an overly // conservative type parameterization of `JoinAll`. - fn sizes(bufs: Vec<&[u8]>) -> Box> + Unpin> { + fn sizes(bufs: Vec<&[u8]>) -> impl Future> { let iter = bufs.into_iter().map(|b| ready::(b.len())); - Box::new(join_all(iter)) + join_all(iter) } - assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]); + assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]); } #[test] fn join_all_from_iter() { - assert_done( - || Box::new(vec![ready(1), ready(2)].into_iter().collect::>()), - vec![1, 2], - ) + assert_done(vec![ready(1), ready(2)].into_iter().collect::>(), vec![1, 2]) } diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs index 718d6c41b0..3ceaebb5c8 100644 --- a/futures/tests/future_shared.rs +++ b/futures/tests/future_shared.rs @@ -96,6 +96,7 @@ fn drop_in_poll() { assert_eq!(block_on(future1), 1); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn peek() { let mut local_pool = LocalPool::new(); diff --git a/futures/tests/future_try_join_all.rs b/futures/tests/future_try_join_all.rs index a4b3bb76a9..9a824872f7 100644 --- a/futures/tests/future_try_join_all.rs +++ b/futures/tests/future_try_join_all.rs @@ -1,24 +1,26 @@ use futures::executor::block_on; +use futures::pin_mut; use futures_util::future::{err, ok, try_join_all, TryJoinAll}; use std::fmt::Debug; use std::future::Future; -fn assert_done(actual_fut: F, expected: T) +#[track_caller] +fn assert_done(actual_fut: impl Future, expected: T) where T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, { - let output = block_on(actual_fut()); + pin_mut!(actual_fut); + let output = block_on(actual_fut); assert_eq!(output, expected); } #[test] fn collect_collects() { - 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])); + assert_done(try_join_all(vec![ok(1), ok(2)]), Ok::<_, usize>(vec![1, 2])); + assert_done(try_join_all(vec![ok(1), err(2)]), Err(2)); + assert_done(try_join_all(vec![ok(1)]), Ok::<_, usize>(vec![1])); // REVIEW: should this be implemented? - // assert_done(|| Box::new(try_join_all(Vec::::new())), Ok(vec![])); + // assert_done(try_join_all(Vec::::new()), Ok(vec![])); // TODO: needs more tests } @@ -27,18 +29,18 @@ fn collect_collects() { fn try_join_all_iter_lifetime() { // In futures-rs version 0.1, this function would fail to typecheck due to an overly // conservative type parameterization of `TryJoinAll`. - fn sizes(bufs: Vec<&[u8]>) -> Box, ()>> + Unpin> { + fn sizes(bufs: Vec<&[u8]>) -> impl Future, ()>> { let iter = bufs.into_iter().map(|b| ok::(b.len())); - Box::new(try_join_all(iter)) + try_join_all(iter) } - assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1])); + assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1])); } #[test] fn try_join_all_from_iter() { assert_done( - || Box::new(vec![ok(1), ok(2)].into_iter().collect::>()), + vec![ok(1), ok(2)].into_iter().collect::>(), Ok::<_, usize>(vec![1, 2]), ) } diff --git a/futures/tests/lock_mutex.rs b/futures/tests/lock_mutex.rs index 7c33864c76..c92ef50ad8 100644 --- a/futures/tests/lock_mutex.rs +++ b/futures/tests/lock_mutex.rs @@ -34,6 +34,7 @@ fn mutex_wakes_waiters() { assert!(waiter.poll_unpin(&mut panic_context()).is_ready()); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn mutex_contested() { let (tx, mut rx) = mpsc::unbounded(); diff --git a/futures/tests/macro_comma_support.rs b/futures/tests/macro_comma_support.rs index 85871e98be..3b082d211f 100644 --- a/futures/tests/macro_comma_support.rs +++ b/futures/tests/macro_comma_support.rs @@ -14,6 +14,7 @@ fn ready() { })) } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn poll() { use futures::poll; diff --git a/futures/tests/ready_queue.rs b/futures/tests/ready_queue.rs index 82901327fb..afba8f28b3 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -93,6 +93,9 @@ fn dropping_ready_queue() { #[test] fn stress() { + #[cfg(miri)] + const ITER: usize = 30; + #[cfg(not(miri))] const ITER: usize = 300; for i in 0..ITER { diff --git a/futures/tests/recurse.rs b/futures/tests/recurse.rs index d81753c9d7..f06524f85a 100644 --- a/futures/tests/recurse.rs +++ b/futures/tests/recurse.rs @@ -3,6 +3,7 @@ use futures::future::{self, BoxFuture, FutureExt}; use std::sync::mpsc; use std::thread; +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn lots() { #[cfg(not(futures_sanitizer))] diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index f3cf11b931..dc826bda98 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -288,6 +288,7 @@ 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_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn with_flush() { let (tx, rx) = oneshot::channel(); diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 7506c65a63..84e0bcc1df 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -26,6 +26,7 @@ fn works_1() { assert_eq!(None, iter.next()); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn works_2() { let (a_tx, a_rx) = oneshot::channel::(); @@ -54,6 +55,7 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn queue_never_unblocked() { let (_a_tx, a_rx) = oneshot::channel::>(); diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 439c809bef..bf6cac68dc 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -56,6 +56,7 @@ fn works_1() { assert_eq!(None, iter.next()); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn works_2() { let (a_tx, a_rx) = oneshot::channel::(); @@ -85,6 +86,7 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn finished_future() { let (_a_tx, a_rx) = oneshot::channel::(); diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index 194e74db74..d83fc54b1c 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038 + use futures::{ stream::{self, StreamExt, TryStreamExt}, task::Poll, diff --git a/futures/tests/task_atomic_waker.rs b/futures/tests/task_atomic_waker.rs index cec3db2876..2d1612a45d 100644 --- a/futures/tests/task_atomic_waker.rs +++ b/futures/tests/task_atomic_waker.rs @@ -6,6 +6,7 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use std::thread; +#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn basic() { let atomic_waker = Arc::new(AtomicWaker::new()); From 3979c41627d750e3c07e2034702d5022ee51e2e5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:05:35 +0900 Subject: [PATCH 096/283] Favor `.cast()` over `as` https://github.com/rust-lang/rust-clippy/issues/8017 --- futures-task/src/future_obj.rs | 2 +- futures-task/src/waker.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index 48ec12beb2..d0cd9b64c8 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -224,7 +224,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Box::from_raw(ptr as *mut F)) + drop(Box::from_raw(ptr.cast::())) } } diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs index a7310a07af..79112569c5 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -20,7 +20,7 @@ pub fn waker(wake: Arc) -> Waker where W: ArcWake + 'static, { - let ptr = Arc::into_raw(wake) as *const (); + let ptr = Arc::into_raw(wake).cast::<()>(); unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) } } @@ -31,7 +31,7 @@ where #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. unsafe fn increase_refcount(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data as *const T)); + let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); // Now increase refcount, but don't drop new refcount either let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); } @@ -43,17 +43,17 @@ unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { } unsafe fn wake_arc_raw(data: *const ()) { - let arc: Arc = Arc::from_raw(data as *const T); + let arc: Arc = Arc::from_raw(data.cast::()); ArcWake::wake(arc); } // used by `waker_ref` unsafe fn wake_by_ref_arc_raw(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data as *const T)); + let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); ArcWake::wake_by_ref(&arc); } unsafe fn drop_arc_raw(data: *const ()) { - drop(Arc::::from_raw(data as *const T)) + drop(Arc::::from_raw(data.cast::())) } From dcf2f07614315f8ecabbc5fd22f4e43d2029c400 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:23:35 +0900 Subject: [PATCH 097/283] Use ManuallyDrop instead of mem::forget-in-disguise This fixes stacked borrows violation violations. See https://github.com/tokio-rs/bytes/pull/458 for more. --- futures-task/src/future_obj.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index d0cd9b64c8..f67494adb6 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -252,10 +252,9 @@ mod if_alloc { where F: Future + 'a, { - fn into_raw(mut self) -> *mut (dyn Future + 'a) { - let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ }; - mem::forget(self); - ptr + fn into_raw(self) -> *mut (dyn Future + 'a) { + let mut this = mem::ManuallyDrop::new(self); + unsafe { this.as_mut().get_unchecked_mut() as *mut _ } } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { @@ -264,10 +263,9 @@ mod if_alloc { } unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin + 'a>> { - fn into_raw(mut self) -> *mut (dyn Future + 'a) { - let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ }; - mem::forget(self); - ptr + fn into_raw(self) -> *mut (dyn Future + 'a) { + let mut this = mem::ManuallyDrop::new(self); + unsafe { this.as_mut().get_unchecked_mut() as *mut _ } } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { @@ -276,10 +274,9 @@ mod if_alloc { } unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin + Send + 'a>> { - fn into_raw(mut self) -> *mut (dyn Future + 'a) { - let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ }; - mem::forget(self); - ptr + fn into_raw(self) -> *mut (dyn Future + 'a) { + let mut this = mem::ManuallyDrop::new(self); + unsafe { this.as_mut().get_unchecked_mut() as *mut _ } } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { From f571545bbb47a4dd89a81f980baf2198b4fe624d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Drouet?= Date: Sun, 6 Feb 2022 05:00:20 +0100 Subject: [PATCH 098/283] Fix variable name in documentation (#2555) A variable name was misspelled in the `filter` and `filter_map` functions documentation. This replaces `evens` by `events`. --- futures-util/src/stream/stream/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 9cfcc09bac..a7313401d5 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -390,9 +390,9 @@ pub trait StreamExt: Stream { /// use futures::stream::{self, StreamExt}; /// /// let stream = stream::iter(1..=10); - /// let evens = stream.filter(|x| future::ready(x % 2 == 0)); + /// let events = stream.filter(|x| future::ready(x % 2 == 0)); /// - /// assert_eq!(vec![2, 4, 6, 8, 10], evens.collect::>().await); + /// assert_eq!(vec![2, 4, 6, 8, 10], events.collect::>().await); /// # }); /// ``` fn filter(self, f: F) -> Filter @@ -422,11 +422,11 @@ pub trait StreamExt: Stream { /// use futures::stream::{self, StreamExt}; /// /// let stream = stream::iter(1..=10); - /// let evens = stream.filter_map(|x| async move { + /// let events = stream.filter_map(|x| async move { /// if x % 2 == 0 { Some(x + 1) } else { None } /// }); /// - /// assert_eq!(vec![3, 5, 7, 9, 11], evens.collect::>().await); + /// assert_eq!(vec![3, 5, 7, 9, 11], events.collect::>().await); /// # }); /// ``` fn filter_map(self, f: F) -> FilterMap From 9dc57f5055f2a337f72a966e66937455bcf73aa4 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 13:19:46 +0900 Subject: [PATCH 099/283] Enable -Zmiri-symbolic-alignment-check and -Zmiri-check-number-validity in miri CI (#2559) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a07211dfd7..57ff9be41c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -246,7 +246,7 @@ jobs: # futures-executor uses boxed futures so many tests trigger https://github.com/rust-lang/miri/issues/1038 - run: cargo miri test --workspace --exclude futures-executor --all-features env: - MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-tag-raw-pointers + MIRIFLAGS: -Zmiri-check-number-validity -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} From 746d12953302a015f006be960e91adf7ea8b2ff1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 13:39:01 +0900 Subject: [PATCH 100/283] FuturesUnordered: Respect yielding from future (#2551) Co-authored-by: Jon Gjengset --- .../src/stream/futures_unordered/mod.rs | 53 +++++++------------ .../src/stream/futures_unordered/task.rs | 9 +++- futures/tests/stream_futures_unordered.rs | 2 +- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 46a0ae5bde..fdbd53de8e 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -6,7 +6,6 @@ use crate::task::AtomicWaker; use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; -use core::cmp; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::marker::PhantomData; @@ -31,33 +30,6 @@ use self::task::Task; mod ready_to_run_queue; use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue}; -/// Constant used for a `FuturesUnordered` to determine how many times it is -/// allowed to poll underlying futures without yielding. -/// -/// A single call to `poll_next` may potentially do a lot of work before -/// yielding. This happens in particular if the underlying futures are awoken -/// frequently but continue to return `Pending`. This is problematic if other -/// tasks are waiting on the executor, since they do not get to run. This value -/// caps the number of calls to `poll` on underlying futures a single call to -/// `poll_next` is allowed to make. -/// -/// The value itself is chosen somewhat arbitrarily. It needs to be high enough -/// that amortize wakeup and scheduling costs, but low enough that we do not -/// starve other tasks for long. -/// -/// See also https://github.com/rust-lang/futures-rs/issues/2047. -/// -/// Note that using the length of the `FuturesUnordered` instead of this value -/// may cause problems if the number of futures is large. -/// See also https://github.com/rust-lang/futures-rs/pull/2527. -/// -/// Additionally, polling the same future twice per iteration may cause another -/// problem. So, when using this value, it is necessary to limit the max value -/// based on the length of the `FuturesUnordered`. -/// (e.g., `cmp::min(self.len(), YIELD_EVERY)`) -/// See also https://github.com/rust-lang/futures-rs/pull/2333. -const YIELD_EVERY: usize = 32; - /// A set of futures which may complete in any order. /// /// This structure is optimized to manage a large number of futures. @@ -149,6 +121,7 @@ impl FuturesUnordered { next_ready_to_run: AtomicPtr::new(ptr::null_mut()), queued: AtomicBool::new(true), ready_to_run_queue: Weak::new(), + woken: AtomicBool::new(false), }); let stub_ptr = Arc::as_ptr(&stub); let ready_to_run_queue = Arc::new(ReadyToRunQueue { @@ -195,6 +168,7 @@ impl FuturesUnordered { next_ready_to_run: AtomicPtr::new(ptr::null_mut()), queued: AtomicBool::new(true), ready_to_run_queue: Arc::downgrade(&self.ready_to_run_queue), + woken: AtomicBool::new(false), }); // Reset the `is_terminated` flag if we've previously marked ourselves @@ -411,12 +385,12 @@ impl Stream for FuturesUnordered { type Item = Fut::Output; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - // See YIELD_EVERY docs for more. - let yield_every = cmp::min(self.len(), YIELD_EVERY); + let len = self.len(); // Keep track of how many child futures we have polled, // in case we want to forcibly yield. let mut polled = 0; + let mut yielded = 0; // Ensure `parent` is correctly set. self.ready_to_run_queue.waker.register(cx.waker()); @@ -527,7 +501,11 @@ impl Stream for FuturesUnordered { // the internal allocation, appropriately accessing fields and // deallocating the task if need be. let res = { - let waker = Task::waker_ref(bomb.task.as_ref().unwrap()); + let task = bomb.task.as_ref().unwrap(); + // We are only interested in whether the future is awoken before it + // finishes polling, so reset the flag here. + task.woken.store(false, Relaxed); + let waker = Task::waker_ref(task); let mut cx = Context::from_waker(&waker); // Safety: We won't move the future ever again @@ -540,12 +518,17 @@ impl Stream for FuturesUnordered { match res { Poll::Pending => { let task = bomb.task.take().unwrap(); + // If the future was awoken during polling, we assume + // the future wanted to explicitly yield. + yielded += task.woken.load(Relaxed) as usize; bomb.queue.link(task); - if polled == yield_every { - // We have polled a large number of futures in a row without yielding. - // To ensure we do not starve other tasks waiting on the executor, - // we yield here, but immediately wake ourselves up to continue. + // If a future yields, we respect it and yield here. + // If all futures have been polled, we also yield here to + // avoid starving other tasks waiting on the executor. + // (polling the same future twice per iteration may cause + // the problem: https://github.com/rust-lang/futures-rs/pull/2333) + if yielded >= 2 || polled == len { cx.waker().wake_by_ref(); return Poll::Pending; } diff --git a/futures-util/src/stream/futures_unordered/task.rs b/futures-util/src/stream/futures_unordered/task.rs index 5216199831..ec2114effa 100644 --- a/futures-util/src/stream/futures_unordered/task.rs +++ b/futures-util/src/stream/futures_unordered/task.rs @@ -1,6 +1,6 @@ use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; -use core::sync::atomic::Ordering::{self, SeqCst}; +use core::sync::atomic::Ordering::{self, Relaxed, SeqCst}; use core::sync::atomic::{AtomicBool, AtomicPtr}; use super::abort::abort; @@ -31,6 +31,11 @@ pub(super) struct Task { // Whether or not this task is currently in the ready to run queue pub(super) queued: AtomicBool, + + // Whether the future was awoken during polling + // It is possible for this flag to be set to true after the polling, + // but it will be ignored. + pub(super) woken: AtomicBool, } // `Task` can be sent across threads safely because it ensures that @@ -48,6 +53,8 @@ impl ArcWake for Task { None => return, }; + arc_self.woken.store(true, Relaxed); + // It's our job to enqueue this task it into the ready to run queue. To // do this we set the `queued` flag, and if successful we then do the // actual queueing operation, ensuring that we're only queued once. diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index bf6cac68dc..f62f733610 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -342,7 +342,7 @@ fn polled_only_once_at_most_per_iteration() { let mut tasks = FuturesUnordered::from_iter(vec![F::default(); 33]); assert!(tasks.poll_next_unpin(cx).is_pending()); - assert_eq!(32, tasks.iter().filter(|f| f.polled).count()); + assert_eq!(33, tasks.iter().filter(|f| f.polled).count()); let mut tasks = FuturesUnordered::::new(); assert_eq!(Poll::Ready(None), tasks.poll_next_unpin(cx)); From 93d9de68eb239260a7ce9b0e6fd31895eb6ed861 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:01:41 +0900 Subject: [PATCH 101/283] Use install-action to install cargo-hack & cross (#2560) --- .github/workflows/ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57ff9be41c..3c20015cac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - run: cargo install cross + - name: Install cross + uses: taiki-e/install-action@cross - run: cross test --target ${{ matrix.target }} --workspace --all-features - run: cross test --target ${{ matrix.target }} --workspace --all-features --release # TODO: https://github.com/rust-lang/futures-rs/issues/2451 @@ -77,7 +78,8 @@ jobs: run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. # Refs: cargo#3620, cargo#4106, cargo#4463, cargo#4753, cargo#5015, cargo#5364, cargo#6195 - - run: cargo +stable install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace # Check no-default-features @@ -106,7 +108,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - run: cargo +stable install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace # Check default features @@ -136,7 +139,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - run: cargo install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack - run: cargo hack build --workspace --no-dev-deps - run: cargo build --tests --features default,thread-pool,io-compat --manifest-path futures/Cargo.toml @@ -147,7 +151,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - run: cargo install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace - run: cargo update -Z minimal-versions @@ -169,7 +174,8 @@ jobs: - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup target add ${{ matrix.target }} - - run: cargo install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - run: cargo hack --remove-dev-deps --workspace - run: | @@ -209,7 +215,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - run: cargo install cargo-hack + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack # Check each specified feature works properly # * `--feature-powerset` - run for the feature powerset of the package # * `--depth 2` - limit the max number of simultaneous feature flags of `--feature-powerset` From fd9d17af5d57e16aa87abdab2986079c47e2fef1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:23:43 +0900 Subject: [PATCH 102/283] Remove CODEOWNERS --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 2fdc28ffbd..0000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @taiki-e From 85706b6d67aa30e1f8380f8101c14cf43be7500e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:29:21 +0900 Subject: [PATCH 103/283] Clean up ci/no_atomic_cas.sh --- ci/no_atomic_cas.sh | 35 +++++++++++++++++++---------------- futures-channel/build.rs | 2 +- futures-core/build.rs | 2 +- futures-task/build.rs | 2 +- futures-util/build.rs | 2 +- no_atomic_cas.rs | 2 +- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh index 40b2f1b90b..ba0200d698 100755 --- a/ci/no_atomic_cas.sh +++ b/ci/no_atomic_cas.sh @@ -1,28 +1,31 @@ #!/bin/bash +set -euo pipefail +IFS=$'\n\t' +cd "$(dirname "$0")"/.. # Update the list of targets that do not support atomic CAS operations. # # Usage: # ./ci/no_atomic_cas.sh -set -euo pipefail -IFS=$'\n\t' - -cd "$(cd "$(dirname "$0")" && pwd)"/.. - file="no_atomic_cas.rs" -{ - echo "// This file is @generated by $(basename "$0")." - echo "// It is not intended for manual editing." - echo "" - echo "const NO_ATOMIC_CAS_TARGETS: &[&str] = &[" -} >"$file" - +no_atomic_cas=() for target in $(rustc --print target-list); do - res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ - | jq -r "select(.\"atomic-cas\" == false)") - [[ -z "$res" ]] || echo " \"$target\"," >>"$file" + target_spec=$(rustc --print target-spec-json -Z unstable-options --target "${target}") + res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)') + [[ -z "${res}" ]] || no_atomic_cas+=("${target}") done -echo "];" >>"$file" +cat >"${file}" <>"${file}" +done +cat >>"${file}" < Date: Sun, 6 Feb 2022 14:31:50 +0900 Subject: [PATCH 104/283] Update comments in build scripts --- futures-channel/build.rs | 19 +++++++++---------- futures-core/build.rs | 19 +++++++++---------- futures-task/build.rs | 19 +++++++++---------- futures-util/build.rs | 19 +++++++++---------- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/futures-channel/build.rs b/futures-channel/build.rs index 6fd1ebdabf..05e0496d94 100644 --- a/futures-channel/build.rs +++ b/futures-channel/build.rs @@ -1,9 +1,3 @@ -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - // The rustc-cfg listed below are considered public API, but it is *unstable* // and outside of the normal semver guarantees: // @@ -13,10 +7,15 @@ include!("no_atomic_cas.rs"); // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. // -// With the exceptions mentioned above, the rustc-cfg strings below are -// *not* public API. Please let us know by opening a GitHub issue if your build -// environment requires some way to enable these cfgs other than by executing -// our build script. +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + fn main() { let target = match env::var("TARGET") { Ok(target) => target, diff --git a/futures-core/build.rs b/futures-core/build.rs index 6fd1ebdabf..05e0496d94 100644 --- a/futures-core/build.rs +++ b/futures-core/build.rs @@ -1,9 +1,3 @@ -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - // The rustc-cfg listed below are considered public API, but it is *unstable* // and outside of the normal semver guarantees: // @@ -13,10 +7,15 @@ include!("no_atomic_cas.rs"); // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. // -// With the exceptions mentioned above, the rustc-cfg strings below are -// *not* public API. Please let us know by opening a GitHub issue if your build -// environment requires some way to enable these cfgs other than by executing -// our build script. +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + fn main() { let target = match env::var("TARGET") { Ok(target) => target, diff --git a/futures-task/build.rs b/futures-task/build.rs index 6fd1ebdabf..05e0496d94 100644 --- a/futures-task/build.rs +++ b/futures-task/build.rs @@ -1,9 +1,3 @@ -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - // The rustc-cfg listed below are considered public API, but it is *unstable* // and outside of the normal semver guarantees: // @@ -13,10 +7,15 @@ include!("no_atomic_cas.rs"); // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. // -// With the exceptions mentioned above, the rustc-cfg strings below are -// *not* public API. Please let us know by opening a GitHub issue if your build -// environment requires some way to enable these cfgs other than by executing -// our build script. +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + fn main() { let target = match env::var("TARGET") { Ok(target) => target, diff --git a/futures-util/build.rs b/futures-util/build.rs index 6fd1ebdabf..05e0496d94 100644 --- a/futures-util/build.rs +++ b/futures-util/build.rs @@ -1,9 +1,3 @@ -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - // The rustc-cfg listed below are considered public API, but it is *unstable* // and outside of the normal semver guarantees: // @@ -13,10 +7,15 @@ include!("no_atomic_cas.rs"); // need to enable it manually when building for custom targets or using // non-cargo build systems that don't run the build script. // -// With the exceptions mentioned above, the rustc-cfg strings below are -// *not* public API. Please let us know by opening a GitHub issue if your build -// environment requires some way to enable these cfgs other than by executing -// our build script. +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + fn main() { let target = match env::var("TARGET") { Ok(target) => target, From a9795a9243b0e6b836f71cbe9661bbb35bb17a7f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:46:19 +0900 Subject: [PATCH 105/283] Automatically creates PR when no_atomic_cas.rs needs to be updated --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c20015cac..b2e1890c32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -231,17 +231,40 @@ jobs: --features unstable --ignore-unknown-features # When this job failed, run ci/no_atomic_cas.sh and commit result changes. - # TODO(taiki-e): Ideally, this should be automated using a bot that creates - # PR when failed, but there is no bandwidth to implement it - # right now... codegen: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - run: ci/no_atomic_cas.sh - - run: git diff --exit-code + - run: git add -N . && git diff --exit-code + if: github.event_name != 'schedule' + - id: diff + run: | + git config user.name "Taiki Endo" + git config user.email "te316e89@gmail.com" + git add -N . + if ! git diff --exit-code; then + git add . + git commit -m "Update no_atomic_cas.rs" + echo "::set-output name=success::false" + fi + if: github.event_name == 'schedule' + - uses: peter-evans/create-pull-request@v3 + with: + title: Update no_atomic_cas.rs + body: | + Auto-generated by [create-pull-request][1] + [Please close and immediately reopen this pull request to run CI.][2] + + [1]: https://github.com/peter-evans/create-pull-request + [2]: https://github.com/peter-evans/create-pull-request/blob/HEAD/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs + branch: update-no-atomic-cas-rs + if: github.event_name == 'schedule' && steps.diff.outputs.success == 'false' miri: name: cargo miri test From dca16fafa597d8e0075a7b693904c6f2ce0322a4 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 15:49:16 +0900 Subject: [PATCH 106/283] Do not auto-create PR on fork --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2e1890c32..dfb7eea4ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,7 +242,7 @@ jobs: run: rustup update nightly && rustup default nightly - run: ci/no_atomic_cas.sh - run: git add -N . && git diff --exit-code - if: github.event_name != 'schedule' + if: github.repository_owner != 'rust-lang' || github.event_name != 'schedule' - id: diff run: | git config user.name "Taiki Endo" @@ -253,7 +253,7 @@ jobs: git commit -m "Update no_atomic_cas.rs" echo "::set-output name=success::false" fi - if: github.event_name == 'schedule' + if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' - uses: peter-evans/create-pull-request@v3 with: title: Update no_atomic_cas.rs @@ -264,7 +264,7 @@ jobs: [1]: https://github.com/peter-evans/create-pull-request [2]: https://github.com/peter-evans/create-pull-request/blob/HEAD/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs branch: update-no-atomic-cas-rs - if: github.event_name == 'schedule' && steps.diff.outputs.success == 'false' + if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' && steps.diff.outputs.success == 'false' miri: name: cargo miri test From 94b508bfcb1d1f986f34f9b12d799e5a73f65f22 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 6 Feb 2022 10:33:11 +0300 Subject: [PATCH 107/283] Basic `StreamExt::{flatten_unordered, flat_map_unordered}` impls (#2083) --- futures-util/benches/flatten_unordered.rs | 66 +++ .../src/stream/stream/flatten_unordered.rs | 501 ++++++++++++++++++ futures-util/src/stream/stream/mod.rs | 118 ++++- futures/tests/stream.rs | 272 +++++++++- 4 files changed, 955 insertions(+), 2 deletions(-) create mode 100644 futures-util/benches/flatten_unordered.rs create mode 100644 futures-util/src/stream/stream/flatten_unordered.rs diff --git a/futures-util/benches/flatten_unordered.rs b/futures-util/benches/flatten_unordered.rs new file mode 100644 index 0000000000..64d5f9a4e3 --- /dev/null +++ b/futures-util/benches/flatten_unordered.rs @@ -0,0 +1,66 @@ +#![feature(test)] + +extern crate test; +use crate::test::Bencher; + +use futures::channel::oneshot; +use futures::executor::block_on; +use futures::future::{self, FutureExt}; +use futures::stream::{self, StreamExt}; +use futures::task::Poll; +use std::collections::VecDeque; +use std::thread; + +#[bench] +fn oneshot_streams(b: &mut Bencher) { + const STREAM_COUNT: usize = 10_000; + const STREAM_ITEM_COUNT: usize = 1; + + b.iter(|| { + let mut txs = VecDeque::with_capacity(STREAM_COUNT); + let mut rxs = Vec::new(); + + for _ in 0..STREAM_COUNT { + let (tx, rx) = oneshot::channel(); + txs.push_back(tx); + rxs.push(rx); + } + + thread::spawn(move || { + let mut last = 1; + while let Some(tx) = txs.pop_front() { + let _ = tx.send(stream::iter(last..last + STREAM_ITEM_COUNT)); + last += STREAM_ITEM_COUNT; + } + }); + + let mut flatten = stream::unfold(rxs.into_iter(), |mut vals| { + async { + if let Some(next) = vals.next() { + let val = next.await.unwrap(); + Some((val, vals)) + } else { + None + } + } + .boxed() + }) + .flatten_unordered(None); + + block_on(future::poll_fn(move |cx| { + let mut count = 0; + loop { + match flatten.poll_next_unpin(cx) { + Poll::Ready(None) => break, + Poll::Ready(Some(_)) => { + count += 1; + } + _ => {} + } + } + assert_eq!(count, STREAM_COUNT * STREAM_ITEM_COUNT); + + Poll::Ready(()) + })) + }); +} diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs new file mode 100644 index 0000000000..6361565f2b --- /dev/null +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -0,0 +1,501 @@ +use alloc::sync::Arc; +use core::{ + cell::UnsafeCell, + convert::identity, + fmt, + num::NonZeroUsize, + pin::Pin, + sync::atomic::{AtomicU8, Ordering}, +}; + +use pin_project_lite::pin_project; + +use futures_core::{ + future::Future, + ready, + stream::{FusedStream, Stream}, + task::{Context, Poll, Waker}, +}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use futures_task::{waker, ArcWake}; + +use crate::stream::FuturesUnordered; + +/// There is nothing to poll and stream isn't being +/// polled or waking at the moment. +const NONE: u8 = 0; + +/// Inner streams need to be polled. +const NEED_TO_POLL_INNER_STREAMS: u8 = 1; + +/// The base stream needs to be polled. +const NEED_TO_POLL_STREAM: u8 = 0b10; + +/// It needs to poll base stream and inner streams. +const NEED_TO_POLL_ALL: u8 = NEED_TO_POLL_INNER_STREAMS | NEED_TO_POLL_STREAM; + +/// The current stream is being polled at the moment. +const POLLING: u8 = 0b100; + +/// Inner streams are being woken at the moment. +const WAKING_INNER_STREAMS: u8 = 0b1000; + +/// The base stream is being woken at the moment. +const WAKING_STREAM: u8 = 0b10000; + +/// The base stream and inner streams are being woken at the moment. +const WAKING_ALL: u8 = WAKING_STREAM | WAKING_INNER_STREAMS; + +/// The stream was waked and will be polled. +const WOKEN: u8 = 0b100000; + +/// Determines what needs to be polled, and is stream being polled at the +/// moment or not. +#[derive(Clone, Debug)] +struct SharedPollState { + state: Arc, +} + +impl SharedPollState { + /// Constructs new `SharedPollState` with the given state. + fn new(value: u8) -> SharedPollState { + SharedPollState { state: Arc::new(AtomicU8::new(value)) } + } + + /// Attempts to start polling, returning stored state in case of success. + /// Returns `None` if some waker is waking at the moment. + fn start_polling( + &self, + ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + let value = self + .state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + if value & WAKING_ALL == NONE { + Some(POLLING) + } else { + None + } + }) + .ok()?; + let bomb = PollStateBomb::new(self, SharedPollState::reset); + + Some((value, bomb)) + } + + /// Starts the waking process and performs bitwise or with the given value. + fn start_waking( + &self, + to_poll: u8, + waking: u8, + ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + let value = self + .state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + let next_value = value | to_poll; + + if value & (WOKEN | POLLING) == NONE { + Some(next_value | waking) + } else if next_value != value { + Some(next_value) + } else { + None + } + }) + .ok()?; + + if value & (WOKEN | POLLING) == NONE { + let bomb = PollStateBomb::new(self, move |state| state.stop_waking(waking)); + + Some((value, bomb)) + } else { + None + } + } + + /// Sets current state to + /// - `!POLLING` allowing to use wakers + /// - `WOKEN` if the state was changed during `POLLING` phase as waker will be called, + /// or `will_be_woken` flag supplied + /// - `!WAKING_ALL` as + /// * Wakers called during the `POLLING` phase won't propagate their calls + /// * `POLLING` phase can't start if some of the wakers are active + /// So no wrapped waker can touch the inner waker's cell, it's safe to poll again. + fn stop_polling(&self, to_poll: u8, will_be_woken: bool) -> u8 { + self.state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut value| { + let mut next_value = to_poll; + + value &= NEED_TO_POLL_ALL; + if value != NONE || will_be_woken { + next_value |= WOKEN; + } + next_value |= value; + + Some(next_value & !POLLING & !WAKING_ALL) + }) + .unwrap() + } + + /// Toggles state to non-waking, allowing to start polling. + fn stop_waking(&self, waking: u8) -> u8 { + self.state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + let next_value = value & !waking; + + if value & WAKING_ALL == waking { + Some(next_value | WOKEN) + } else if next_value != value { + Some(next_value) + } else { + None + } + }) + .unwrap_or_else(identity) + } + + /// Resets current state allowing to poll the stream and wake up wakers. + fn reset(&self) -> u8 { + self.state.swap(NEED_TO_POLL_ALL, Ordering::AcqRel) + } +} + +/// Used to execute some function on the given state when dropped. +struct PollStateBomb<'a, F: FnOnce(&SharedPollState) -> u8> { + state: &'a SharedPollState, + drop: Option, +} + +impl<'a, F: FnOnce(&SharedPollState) -> u8> PollStateBomb<'a, F> { + /// Constructs new bomb with the given state. + fn new(state: &'a SharedPollState, drop: F) -> Self { + Self { state, drop: Some(drop) } + } + + /// Deactivates bomb, forces it to not call provided function when dropped. + fn deactivate(mut self) { + self.drop.take(); + } + + /// Manually fires the bomb, returning supplied state. + fn fire(mut self) -> Option { + self.drop.take().map(|drop| (drop)(self.state)) + } +} + +impl u8> Drop for PollStateBomb<'_, F> { + fn drop(&mut self) { + if let Some(drop) = self.drop.take() { + (drop)(self.state); + } + } +} + +/// Will update state with the provided value on `wake_by_ref` call +/// and then, if there is a need, call `inner_waker`. +struct InnerWaker { + inner_waker: UnsafeCell>, + poll_state: SharedPollState, + need_to_poll: u8, +} + +unsafe impl Send for InnerWaker {} +unsafe impl Sync for InnerWaker {} + +impl InnerWaker { + /// 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, cx: &Context<'_>) -> Waker { + *self_arc.inner_waker.get() = cx.waker().clone().into(); + waker(self_arc.clone()) + } + + /// Attempts to start the waking process for the waker with the given value. + /// If succeeded, then the stream isn't yet woken and not being polled at the moment. + fn start_waking(&self) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + self.poll_state.start_waking(self.need_to_poll, self.waking_state()) + } + + /// Returns the corresponding waking state toggled by this waker. + fn waking_state(&self) -> u8 { + self.need_to_poll << 3 + } +} + +impl ArcWake for InnerWaker { + fn wake_by_ref(self_arc: &Arc) { + if let Some((_, state_bomb)) = self_arc.start_waking() { + // Safety: now state is not `POLLING` + let waker_opt = unsafe { self_arc.inner_waker.get().as_ref().unwrap() }; + + if let Some(inner_waker) = waker_opt.clone() { + // Stop waking to allow polling stream + let poll_state_value = state_bomb.fire().unwrap(); + + // Here we want to call waker only if stream isn't woken yet and + // also to optimize the case when two wakers are called at the same time. + // + // In this case the best strategy will be to propagate only the latest waker's awake, + // and then poll both entities in a single `poll_next` call + if poll_state_value & (WOKEN | WAKING_ALL) == self_arc.waking_state() { + // Wake up inner waker + inner_waker.wake(); + } + } + } + } +} + +pin_project! { + /// Future which contains optional stream. + /// + /// If it's `Some`, it will attempt to call `poll_next` on it, + /// returning `Some((item, next_item_fut))` in case of `Poll::Ready(Some(...))` + /// or `None` in case of `Poll::Ready(None)`. + /// + /// If `poll_next` will return `Poll::Pending`, it will be forwarded to + /// the future and current task will be notified by waker. + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct PollStreamFut { + #[pin] + stream: Option, + } +} + +impl PollStreamFut { + /// Constructs new `PollStreamFut` using given `stream`. + fn new(stream: impl Into>) -> Self { + Self { stream: stream.into() } + } +} + +impl Future for PollStreamFut { + type Output = Option<(St::Item, PollStreamFut)>; + + fn poll(self: Pin<&mut Self>, cx: &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(cx)) + } else { + None + }; + let next_item_fut = PollStreamFut::new(stream.get_mut().take()); + let out = item.map(|item| (item, next_item_fut)); + + Poll::Ready(out) + } +} + +pin_project! { + /// Stream for the [`flatten_unordered`](super::StreamExt::flatten_unordered) + /// method. + #[project = FlattenUnorderedProj] + #[must_use = "streams do nothing unless polled"] + pub struct FlattenUnordered where St: Stream { + #[pin] + inner_streams: FuturesUnordered>, + #[pin] + stream: St, + poll_state: SharedPollState, + limit: Option, + is_stream_done: bool, + inner_streams_waker: Arc, + stream_waker: Arc, + } +} + +impl fmt::Debug for FlattenUnordered +where + St: Stream + fmt::Debug, + St::Item: Stream + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlattenUnordered") + .field("poll_state", &self.poll_state) + .field("inner_streams", &self.inner_streams) + .field("limit", &self.limit) + .field("stream", &self.stream) + .field("is_stream_done", &self.is_stream_done) + .finish() + } +} + +impl FlattenUnordered +where + St: Stream, + St::Item: Stream + Unpin, +{ + pub(super) fn new(stream: St, limit: Option) -> FlattenUnordered { + let poll_state = SharedPollState::new(NEED_TO_POLL_STREAM); + + FlattenUnordered { + inner_streams: FuturesUnordered::new(), + stream, + is_stream_done: false, + limit: limit.and_then(NonZeroUsize::new), + inner_streams_waker: Arc::new(InnerWaker { + inner_waker: UnsafeCell::new(None), + poll_state: poll_state.clone(), + need_to_poll: NEED_TO_POLL_INNER_STREAMS, + }), + stream_waker: Arc::new(InnerWaker { + inner_waker: UnsafeCell::new(None), + poll_state: poll_state.clone(), + need_to_poll: NEED_TO_POLL_STREAM, + }), + poll_state, + } + } + + delegate_access_inner!(stream, St, ()); +} + +impl FlattenUnorderedProj<'_, St> +where + St: Stream, +{ + /// Checks if current `inner_streams` size is less than optional limit. + fn is_exceeded_limit(&self) -> bool { + self.limit.map_or(false, |limit| self.inner_streams.len() >= limit.get()) + } +} + +impl FusedStream for FlattenUnordered +where + St: FusedStream, + St::Item: FusedStream + Unpin, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.inner_streams.is_empty() + } +} + +impl Stream for FlattenUnordered +where + St: Stream, + St::Item: Stream + Unpin, +{ + type Item = ::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut next_item = None; + let mut need_to_poll_next = NONE; + + let mut this = self.as_mut().project(); + + let (mut poll_state_value, state_bomb) = match this.poll_state.start_polling() { + Some(value) => value, + _ => { + // Waker was called, just wait for the next poll + return Poll::Pending; + } + }; + + if poll_state_value & NEED_TO_POLL_STREAM != NONE { + // Safety: now state is `POLLING`. + let stream_waker = unsafe { InnerWaker::replace_waker(this.stream_waker, cx) }; + + // Here we need to poll the base stream. + // + // To improve performance, we will attempt to place as many items as we can + // to the `FuturesUnordered` bucket before polling inner streams + loop { + if this.is_exceeded_limit() || *this.is_stream_done { + // We either exceeded the limit or the stream is exhausted + if !*this.is_stream_done { + // The stream needs to be polled in the next iteration + need_to_poll_next |= NEED_TO_POLL_STREAM; + } + + break; + } else { + match this.stream.as_mut().poll_next(&mut Context::from_waker(&stream_waker)) { + Poll::Ready(Some(inner_stream)) => { + // Add new stream to the inner streams bucket + this.inner_streams.as_mut().push(PollStreamFut::new(inner_stream)); + // Inner streams must be polled afterward + poll_state_value |= NEED_TO_POLL_INNER_STREAMS; + } + Poll::Ready(None) => { + // Mark the stream as done + *this.is_stream_done = true; + } + Poll::Pending => { + break; + } + } + } + } + } + + if poll_state_value & NEED_TO_POLL_INNER_STREAMS != NONE { + // Safety: now state is `POLLING`. + let inner_streams_waker = + unsafe { InnerWaker::replace_waker(this.inner_streams_waker, cx) }; + + match this + .inner_streams + .as_mut() + .poll_next(&mut Context::from_waker(&inner_streams_waker)) + { + Poll::Ready(Some(Some((item, next_item_fut)))) => { + // Push next inner stream item future to the list of inner streams futures + this.inner_streams.as_mut().push(next_item_fut); + // Take the received item + next_item = Some(item); + // On the next iteration, inner streams must be polled again + need_to_poll_next |= NEED_TO_POLL_INNER_STREAMS; + } + Poll::Ready(Some(None)) => { + // On the next iteration, inner streams must be polled again + need_to_poll_next |= NEED_TO_POLL_INNER_STREAMS; + } + _ => {} + } + } + + // We didn't have any `poll_next` panic, so it's time to deactivate the bomb + state_bomb.deactivate(); + + let mut force_wake = + // we need to poll the stream and didn't reach the limit yet + need_to_poll_next & NEED_TO_POLL_STREAM != NONE && !this.is_exceeded_limit() + // or we need to poll inner streams again + || need_to_poll_next & NEED_TO_POLL_INNER_STREAMS != NONE; + + // Stop polling and swap the latest state + poll_state_value = this.poll_state.stop_polling(need_to_poll_next, force_wake); + // If state was changed during `POLLING` phase, need to manually call a waker + force_wake |= poll_state_value & NEED_TO_POLL_ALL != NONE; + + let is_done = *this.is_stream_done && this.inner_streams.is_empty(); + + if next_item.is_some() || is_done { + Poll::Ready(next_item) + } else { + if force_wake { + cx.waker().wake_by_ref(); + } + + Poll::Pending + } + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for FlattenUnordered +where + St: Stream + Sink, +{ + 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 a7313401d5..642b91e823 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -197,6 +197,25 @@ mod buffered; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::buffered::Buffered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod flatten_unordered; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] +pub use self::flatten_unordered::FlattenUnordered; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +delegate_all!( + /// Stream for the [`flat_map_unordered`](StreamExt::flat_map_unordered) 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, U: Unpin, F: FnMut(St::Item) -> U +); + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] mod for_each_concurrent; @@ -754,13 +773,57 @@ pub trait StreamExt: Stream { assert_stream::<::Item, _>(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(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn flatten_unordered(self, limit: impl Into>) -> FlattenUnordered + where + Self::Item: Stream + Unpin, + 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 produce inner streams is executed over all elements + /// 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 @@ -788,6 +851,59 @@ pub trait StreamExt: Stream { assert_stream::(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. + /// + /// [`StreamExt::map`] is very useful, but if it produces `Stream`s + /// instead, and you need to poll all of them concurrently, you would + /// 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 + /// `Into>`, and so can be provided as either `None`, + /// `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 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. + /// + /// Note that this function consumes the stream passed into it and + /// returns a wrapped version of it. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..5); + /// let stream = stream.flat_map_unordered(1, |x| stream::iter(vec![x; x])); + /// let mut values = stream.collect::>().await; + /// values.sort(); + /// + /// assert_eq!(vec![1usize, 2, 2, 3, 3, 3, 4, 4, 4, 4], values); + /// # }); + /// ``` + #[cfg(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn flat_map_unordered( + self, + limit: impl Into>, + f: F, + ) -> FlatMapUnordered + where + U: Stream + Unpin, + F: FnMut(Self::Item) -> U, + Self: Sized, + { + FlatMapUnordered::new(self, limit.into(), f) + } + /// Combinator similar to [`StreamExt::fold`] that holds internal state /// and produces a new stream. /// diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 0d453d1752..71ec654bfb 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,10 +1,14 @@ +use std::iter; +use std::sync::Arc; + use futures::channel::mpsc; use futures::executor::block_on; use futures::future::{self, Future}; +use futures::lock::Mutex; use futures::sink::SinkExt; use futures::stream::{self, StreamExt}; use futures::task::Poll; -use futures::FutureExt; +use futures::{ready, FutureExt}; use futures_test::task::noop_context; #[test] @@ -49,6 +53,272 @@ fn scan() { }); } +#[test] +fn flatten_unordered() { + use futures::executor::block_on; + use futures::stream::*; + use futures::task::*; + use std::convert::identity; + use std::pin::Pin; + use std::thread; + use std::time::Duration; + + struct DataStream { + data: Vec, + polled: bool, + wake_immediately: bool, + } + + 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 sleep_time = + Duration::from_millis(*self.data.first().unwrap_or(&0) as u64 / 10); + thread::spawn(move || { + thread::sleep(sleep_time); + waker.wake_by_ref(); + }); + } else { + ctx.waker().wake_by_ref(); + } + self.polled = true; + Poll::Pending + } else { + self.polled = false; + Poll::Ready(self.data.pop()) + } + } + } + + struct Interchanger { + polled: bool, + base: u8, + wake_immediately: bool, + } + + 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 sleep_time = Duration::from_millis(self.base as u64); + thread::spawn(move || { + thread::sleep(sleep_time); + waker.wake_by_ref(); + }); + } else { + ctx.waker().wake_by_ref(); + } + Poll::Pending + } else { + let data: Vec<_> = (0..6).rev().map(|v| v + self.base * 6).collect(); + self.base += 1; + self.polled = false; + Poll::Ready(Some(DataStream { + polled: false, + data, + wake_immediately: self.wake_immediately && self.base % 2 == 0, + })) + } + } + } + + // basic behaviour + { + block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(10..=12), + ]); + + let fl_unordered = st.flatten_unordered(3).collect::>().await; + + assert_eq!(fl_unordered, vec![0, 6, 10, 1, 7, 11, 2, 8, 12, 3, 9, 4, 10]); + }); + + block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(0..=2), + ]); + + let mut fm_unordered = st + .flat_map_unordered(1, |s| s.filter(|v| futures::future::ready(v % 2 == 0))) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, vec![0, 0, 2, 2, 4, 6, 8, 10]); + }); + } + + // wake up immediately + { + block_on(async { + let mut fl_unordered = Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) + .collect::>() + .await; + + fl_unordered.sort_unstable(); + + assert_eq!(fl_unordered, (0..60).collect::>()); + }); + + block_on(async { + let mut fm_unordered = Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + } + + // wake up after delay + { + block_on(async { + let mut fl_unordered = Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) + .collect::>() + .await; + + fl_unordered.sort_unstable(); + + assert_eq!(fl_unordered, (0..60).collect::>()); + }); + + block_on(async { + let mut fm_unordered = Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + + block_on(async { + let (mut fm_unordered, mut fl_unordered) = futures_util::join!( + Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>(), + Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) + .collect::>() + ); + + fm_unordered.sort_unstable(); + fl_unordered.sort_unstable(); + + assert_eq!(fm_unordered, fl_unordered); + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + } + + // waker panics + { + let stream = Arc::new(Mutex::new( + Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)), + )); + + struct PanicWaker; + + impl ArcWake for PanicWaker { + fn wake_by_ref(_arc_self: &Arc) { + panic!("WAKE UP"); + } + } + + std::thread::spawn({ + let stream = stream.clone(); + move || { + let mut st = poll_fn(|cx| { + let mut lock = ready!(stream.lock().poll_unpin(cx)); + + let panic_waker = waker(Arc::new(PanicWaker)); + let mut panic_cx = Context::from_waker(&panic_waker); + let _ = ready!(lock.poll_next_unpin(&mut panic_cx)); + + Poll::Ready(Some(())) + }); + + block_on(st.next()) + } + }) + .join() + .unwrap_err(); + + block_on(async move { + let mut values: Vec<_> = stream.lock().await.by_ref().collect().await; + values.sort_unstable(); + + assert_eq!(values, (0..60).collect::>()); + }); + } + + // stream panics + { + let st = stream::iter(iter::once( + once(Box::pin(async { panic!("Polled") })).left_stream::(), + )) + .chain( + Interchanger { polled: false, base: 0, wake_immediately: true } + .map(|stream| stream.right_stream()) + .take(10), + ); + + let stream = Arc::new(Mutex::new(st.flatten_unordered(10))); + + std::thread::spawn({ + let stream = stream.clone(); + move || { + let mut st = poll_fn(|cx| { + let mut lock = ready!(stream.lock().poll_unpin(cx)); + let data = ready!(lock.poll_next_unpin(cx)); + + Poll::Ready(data) + }); + + block_on(st.next()) + } + }) + .join() + .unwrap_err(); + + block_on(async move { + let mut values: Vec<_> = stream.lock().await.by_ref().collect().await; + values.sort_unstable(); + + assert_eq!(values, (0..60).collect::>()); + }); + } +} + #[test] fn take_until() { fn make_stop_fut(stop_on: u32) -> impl Future { From 591b982af835fc5e0ffb5104bbb9e942b2ed0631 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 17:18:17 +0900 Subject: [PATCH 108/283] Redefine executor and compat modules in futures crate (#2564) --- futures/src/lib.rs | 68 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 1385e46362..b8ebc614e2 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -151,13 +151,73 @@ pub use futures_util::io; #[cfg(feature = "executor")] #[cfg_attr(docsrs, doc(cfg(feature = "executor")))] -#[doc(inline)] -pub use futures_executor as executor; +pub mod executor { + //! Built-in executors and related tools. + //! + //! All asynchronous computation occurs within an executor, which is + //! capable of spawning futures as tasks. This module provides several + //! built-in executors, as well as tools for building your own. + //! + //! + //! This module is only available when the `executor` feature of this + //! library is activated. + //! + //! # Using a thread pool (M:N task scheduling) + //! + //! Most of the time tasks should be executed on a [thread pool](ThreadPool). + //! A small set of worker threads can handle a very large set of spawned tasks + //! (which are much lighter weight than threads). Tasks spawned onto the pool + //! with the [`spawn_ok`](ThreadPool::spawn_ok) function will run ambiently on + //! the created threads. + //! + //! # Spawning additional tasks + //! + //! Tasks can be spawned onto a spawner by calling its [`spawn_obj`] method + //! directly. In the case of `!Send` futures, [`spawn_local_obj`] can be used + //! instead. + //! + //! # Single-threaded execution + //! + //! In addition to thread pools, it's possible to run a task (and the tasks + //! it spawns) entirely within a single thread via the [`LocalPool`] executor. + //! Aside from cutting down on synchronization costs, this executor also makes + //! it possible to spawn non-`Send` tasks, via [`spawn_local_obj`]. The + //! [`LocalPool`] is best suited for running I/O-bound tasks that do relatively + //! little work between I/O operations. + //! + //! There is also a convenience function [`block_on`] for simply running a + //! future to completion on the current thread. + //! + //! [`spawn_obj`]: https://docs.rs/futures/0.3/futures/task/trait.Spawn.html#tymethod.spawn_obj + //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj + + pub use futures_executor::{ + block_on, block_on_stream, enter, BlockingStream, Enter, EnterError, LocalPool, + LocalSpawner, + }; + + #[cfg(feature = "thread-pool")] + #[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] + pub use futures_executor::{ThreadPool, ThreadPoolBuilder}; +} #[cfg(feature = "compat")] #[cfg_attr(docsrs, doc(cfg(feature = "compat")))] -#[doc(inline)] -pub use futures_util::compat; +pub mod compat { + //! Interop between `futures` 0.1 and 0.3. + //! + //! This module is only available when the `compat` feature of this + //! library is activated. + + pub use futures_util::compat::{ + Compat, Compat01As03, Compat01As03Sink, CompatSink, Executor01As03, Executor01CompatExt, + Executor01Future, Future01CompatExt, Sink01CompatExt, Stream01CompatExt, + }; + + #[cfg(feature = "io-compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] + pub use futures_util::compat::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; +} pub mod prelude { //! A "prelude" for crates using the `futures` crate. From 55281c8c8de6308ba97ce8b4ffd754ba204409a0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 17:19:13 +0900 Subject: [PATCH 109/283] Release 0.3.20 --- CHANGELOG.md | 6 ++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 4 ++-- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 42 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c39f858f8..b71ab97a45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.3.20 - 2022-02-06 + +* Fix stacked borrows violations when `-Zmiri-tag-raw-pointers` is enabled. This raises MSRV of `futures-task` to 1.45. (#2548, #2550) +* Change `FuturesUnordered` to respect yielding from future (#2551) +* Add `StreamExt::{flatten_unordered, flat_map_unordered}` (#2083) + # 0.3.19 - 2021-12-18 * Remove unstable `read-initializer` feature (#2534) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index a49c556ad1..ddef847bf7 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index c3026f733c..bce1139ef5 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 458d9e47a2..eeb6e7a881 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.19", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.20", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 5350d2157e..112196f094 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 7ea489a929..e11707f269 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 40c9c33f0b..461aca464a 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 8531d8f297..0b6af24686 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "futures-task" -version = "0.3.19" +version = "0.3.20" edition = "2018" -rust-version = "1.36" +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 4456245f0c..f594d187d1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.19", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.19", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.19", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.19", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.19", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.19", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.19", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.20", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.20", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.20", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.20", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.20", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.20", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.20", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 687579c7dd..564b8be41f 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.19", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.19", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.19", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.20", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.20", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.20", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 3bec3dd7bb..ddfca034eb 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.19" +version = "0.3.20" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.19", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.19", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.19", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.19", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.19", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.19", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.19", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.20", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.20", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.20", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.20", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 75dca5ae7fabe6b7073558a8fc6793ee5caa7057 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 17:46:20 +0900 Subject: [PATCH 110/283] Fix MSRV in futures-task readme --- futures-task/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-task/README.md b/futures-task/README.md index 79f12da887..8ceeba9d57 100644 --- a/futures-task/README.md +++ b/futures-task/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-task = "0.3" ``` -The current `futures-task` requires Rust 1.36 or later. +The current `futures-task` requires Rust 1.45 or later. ## License From 20279ebbfcd326e83161e44a2cf5afe1bfc3b074 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sun, 6 Feb 2022 14:41:14 +0300 Subject: [PATCH 111/283] `FlattenUnordered`: improve wakers behavior (#2566) --- .../src/stream/stream/flatten_unordered.rs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 6361565f2b..07f971c55a 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -92,11 +92,17 @@ impl SharedPollState { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - let next_value = value | to_poll; - + // Waking process for this waker already started + if value & waking != NONE { + return None; + } + let mut next_value = value | to_poll; + // Only start the waking process if we're not in the polling phase and the stream isn't woken already if value & (WOKEN | POLLING) == NONE { - Some(next_value | waking) - } else if next_value != value { + next_value |= waking; + } + + if next_value != value { Some(next_value) } else { None @@ -141,11 +147,13 @@ impl SharedPollState { fn stop_waking(&self, waking: u8) -> u8 { self.state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - let next_value = value & !waking; - + let mut next_value = value & !waking; + // Waker will be called only if the current waking state is the same as the specified waker state if value & WAKING_ALL == waking { - Some(next_value | WOKEN) - } else if next_value != value { + next_value |= WOKEN; + } + + if next_value != value { Some(next_value) } else { None From fc1e3250219170e31cddb8857a276cba7dd08d44 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 21:04:32 +0900 Subject: [PATCH 112/283] Release 0.3.21 --- CHANGELOG.md | 6 ++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 41 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b71ab97a45..8f76048681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ +# 0.3.21 - 2022-02-06 + +* Fix potential data race in `FlattenUnordered` that introduced in 0.3.20 (#2566) + # 0.3.20 - 2022-02-06 +NOTE: This release has been yanked due to a bug fixed in 0.3.21. + * Fix stacked borrows violations when `-Zmiri-tag-raw-pointers` is enabled. This raises MSRV of `futures-task` to 1.45. (#2548, #2550) * Change `FuturesUnordered` to respect yielding from future (#2551) * Add `StreamExt::{flatten_unordered, flat_map_unordered}` (#2083) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index ddef847bf7..f356eabd98 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index bce1139ef5..4a360f8a42 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index eeb6e7a881..dae5f22e77 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.20", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.21", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 112196f094..8d446947b1 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index e11707f269..a929d0f198 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 461aca464a..2c0685ac6b 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 0b6af24686..77fec6fd92 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index f594d187d1..b5aa8a7dd1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.20", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.20", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.20", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.20", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.20", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.20", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.20", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.21", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.21", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.21", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.21", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.21", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.21", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.21", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 564b8be41f..46ec854b04 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.20", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.20", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.20", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.21", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.21", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index ddfca034eb..6871f47eaa 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.20" +version = "0.3.21" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.20", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.20", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.20", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.20", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.20", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.20", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.20", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.21", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.21", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.21", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From a291cba6961c4ace49963f8f606aa29f8f7c6cd3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 14 Feb 2022 01:03:27 +0900 Subject: [PATCH 113/283] Fix Sync impl of BiLockGuard --- futures-util/src/lock/bilock.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 2f51ae7c98..2174079c83 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -224,6 +224,9 @@ pub struct BiLockGuard<'a, T> { bilock: &'a BiLock, } +// We allow parallel access to T via Deref, so Sync bound is also needed here. +unsafe impl Sync for BiLockGuard<'_, T> {} + impl Deref for BiLockGuard<'_, T> { type Target = T; fn deref(&self) -> &T { From 07b741ef480b74ecc049c8e07211639f00f9652d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 14 Feb 2022 01:07:34 +0900 Subject: [PATCH 114/283] Fix stable_features warning ``` error: the feature `cfg_target_has_atomic` has been stable since 1.60.0 and no longer requires an attribute to enable --> futures/tests/no-std/src/lib.rs:3:12 | 3 | #![feature(cfg_target_has_atomic)] | ^^^^^^^^^^^^^^^^^^^^^ ``` --- futures/tests/no-std/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/futures/tests/no-std/src/lib.rs b/futures/tests/no-std/src/lib.rs index 308218d6b7..89a8fa1ff1 100644 --- a/futures/tests/no-std/src/lib.rs +++ b/futures/tests/no-std/src/lib.rs @@ -1,6 +1,5 @@ #![cfg(nightly)] #![no_std] -#![feature(cfg_target_has_atomic)] #[cfg(feature = "futures-core-alloc")] #[cfg(target_has_atomic = "ptr")] From 81587ba6b6e4badc3f801efa7172da84cae84385 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 14 Feb 2022 01:20:59 +0900 Subject: [PATCH 115/283] Fix clippy::single_match warning ``` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> futures-executor/src/thread_pool.rs:349:9 | 349 | / match arc_self.mutex.notify() { 350 | | Ok(task) => arc_self.exec.state.send(Message::Run(task)), 351 | | Err(()) => {} 352 | | } | |_________^ help: try this: `if let Ok(task) = arc_self.mutex.notify() { arc_self.exec.state.send(Message::Run(task)) }` | = note: `-D clippy::single-match` implied by `-D warnings` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_match ``` --- futures-executor/src/thread_pool.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 5e1f586eb8..3eca9d1440 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -346,9 +346,8 @@ impl fmt::Debug for Task { impl ArcWake for WakeHandle { fn wake_by_ref(arc_self: &Arc) { - match arc_self.mutex.notify() { - Ok(task) => arc_self.exec.state.send(Message::Run(task)), - Err(()) => {} + if let Ok(task) = arc_self.mutex.notify() { + arc_self.exec.state.send(Message::Run(task)) } } } From 8ace9682f06e093396aa81f015c24c1ae6ef28aa Mon Sep 17 00:00:00 2001 From: Bill Fraser Date: Wed, 23 Feb 2022 04:22:06 -0800 Subject: [PATCH 116/283] FuturesUnordered: fix partial iteration (#2574) The IntoIter impl advances the head pointer every iteration, but this breaks the linked list invariant that the head's prev should be null. If the iteration is not done to completion, on subsequent drop, FuturesUnordered::unlink relies on this broken invariant and ends up panicking. The fix is to maintain the `head->prev == null` invariant while iterating. Also added a test for this bug. --- futures-util/src/stream/futures_unordered/iter.rs | 4 ++++ futures/tests/stream_futures_unordered.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/futures-util/src/stream/futures_unordered/iter.rs b/futures-util/src/stream/futures_unordered/iter.rs index 04db5ee753..20248c70fe 100644 --- a/futures-util/src/stream/futures_unordered/iter.rs +++ b/futures-util/src/stream/futures_unordered/iter.rs @@ -2,6 +2,7 @@ use super::task::Task; use super::FuturesUnordered; use core::marker::PhantomData; use core::pin::Pin; +use core::ptr; use core::sync::atomic::Ordering::Relaxed; /// Mutable iterator over all futures in the unordered set. @@ -58,6 +59,9 @@ impl Iterator for IntoIter { // valid `next_all` checks can be skipped. let next = (**task).next_all.load(Relaxed); *task = next; + if !task.is_null() { + *(**task).prev_all.get() = ptr::null_mut(); + } self.len -= 1; Some(future) } diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index f62f733610..398170a7cf 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -261,6 +261,20 @@ fn into_iter_len() { assert!(into_iter.next().is_none()); } +#[test] +fn into_iter_partial() { + let stream = vec![future::ready(1), future::ready(2), future::ready(3), future::ready(4)] + .into_iter() + .collect::>(); + + let mut into_iter = stream.into_iter(); + assert!(into_iter.next().is_some()); + assert!(into_iter.next().is_some()); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 1); + // don't panic when iterator is dropped before completing +} + #[test] fn futures_not_moved_after_poll() { // Future that will be ready after being polled twice, From e075ff39ffa5376efc8b119bc1bbbde1b41c1b29 Mon Sep 17 00:00:00 2001 From: Marco Neumann Date: Wed, 2 Mar 2022 17:32:18 +0000 Subject: [PATCH 117/283] Shared: fix false detection of inner panics (#2576) Fixes #2575. --- futures-util/src/future/future/shared.rs | 22 +++++++++------- futures/tests/future_shared.rs | 32 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 9b31932fe3..985931564b 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -262,19 +262,20 @@ where let waker = waker_ref(&inner.notifier); let mut cx = Context::from_waker(&waker); - struct Reset<'a>(&'a AtomicUsize); + struct Reset<'a> { + state: &'a AtomicUsize, + did_not_panic: bool, + } impl Drop for Reset<'_> { fn drop(&mut self) { - use std::thread; - - if thread::panicking() { - self.0.store(POISONED, SeqCst); + if !self.did_not_panic { + self.state.store(POISONED, SeqCst); } } } - let _reset = Reset(&inner.notifier.state); + let mut reset = Reset { state: &inner.notifier.state, did_not_panic: false }; let output = { let future = unsafe { @@ -284,12 +285,15 @@ where } }; - match future.poll(&mut cx) { + let poll_result = future.poll(&mut cx); + reset.did_not_panic = true; + + match poll_result { Poll::Pending => { if inner.notifier.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok() { // Success - drop(_reset); + drop(reset); this.inner = Some(inner); return Poll::Pending; } else { @@ -313,7 +317,7 @@ where waker.wake(); } - drop(_reset); // Make borrow checker happy + drop(reset); // Make borrow checker happy drop(wakers_guard); // Safety: We're in the COMPLETE state diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs index 3ceaebb5c8..20a021bc35 100644 --- a/futures/tests/future_shared.rs +++ b/futures/tests/future_shared.rs @@ -3,6 +3,7 @@ use futures::executor::{block_on, LocalPool}; use futures::future::{self, FutureExt, LocalFutureObj, TryFutureExt}; use futures::task::LocalSpawn; use std::cell::{Cell, RefCell}; +use std::panic::AssertUnwindSafe; use std::rc::Rc; use std::task::Poll; use std::thread; @@ -194,3 +195,34 @@ fn shared_future_that_wakes_itself_until_pending_is_returned() { // has returned pending assert_eq!(block_on(futures::future::join(fut, async { proceed.set(true) })), ((), ())); } + +#[test] +#[should_panic(expected = "inner future panicked during poll")] +fn panic_while_poll() { + let fut = futures::future::poll_fn::(|_cx| panic!("test")).shared(); + + let fut_captured = fut.clone(); + std::panic::catch_unwind(AssertUnwindSafe(|| { + block_on(fut_captured); + })) + .unwrap_err(); + + block_on(fut); +} + +#[test] +#[should_panic(expected = "test_marker")] +fn poll_while_panic() { + struct S; + + impl Drop for S { + fn drop(&mut self) { + let fut = futures::future::ready(1).shared(); + assert_eq!(block_on(fut.clone()), 1); + assert_eq!(block_on(fut), 1); + } + } + + let _s = S {}; + panic!("test_marker"); +} From 5fbe734f204dc5153660fa31cf1d2cd758d75297 Mon Sep 17 00:00:00 2001 From: Sherlock Holo Date: Tue, 8 Mar 2022 21:01:21 +0800 Subject: [PATCH 118/283] Feat: add Mutex::lock_owned and Mutex::try_lock_owned (#2571) --- futures-util/src/lock/mod.rs | 22 ++--- futures-util/src/lock/mutex.rs | 155 +++++++++++++++++++++++++++++++-- 2 files changed, 162 insertions(+), 15 deletions(-) diff --git a/futures-util/src/lock/mod.rs b/futures-util/src/lock/mod.rs index cf374c016f..0be72717c8 100644 --- a/futures-util/src/lock/mod.rs +++ b/futures-util/src/lock/mod.rs @@ -4,11 +4,18 @@ //! library is activated, and it is activated by default. #[cfg(not(futures_no_atomic_cas))] -#[cfg(feature = "std")] -mod mutex; +#[cfg(any(feature = "sink", feature = "io"))] +#[cfg(not(feature = "bilock"))] +pub(crate) use self::bilock::BiLock; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "bilock")] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] +pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "std")] -pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture}; +pub use self::mutex::{ + MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture, OwnedMutexGuard, OwnedMutexLockFuture, +}; #[cfg(not(futures_no_atomic_cas))] #[cfg(any(feature = "bilock", feature = "sink", feature = "io"))] @@ -16,10 +23,5 @@ pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture}; #[cfg_attr(not(feature = "bilock"), allow(unreachable_pub))] mod bilock; #[cfg(not(futures_no_atomic_cas))] -#[cfg(any(feature = "sink", feature = "io"))] -#[cfg(not(feature = "bilock"))] -pub(crate) use self::bilock::BiLock; -#[cfg(not(futures_no_atomic_cas))] -#[cfg(feature = "bilock")] -#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] -pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; +#[cfg(feature = "std")] +mod mutex; diff --git a/futures-util/src/lock/mutex.rs b/futures-util/src/lock/mutex.rs index 85dcb1537b..335ad14273 100644 --- a/futures-util/src/lock/mutex.rs +++ b/futures-util/src/lock/mutex.rs @@ -1,14 +1,16 @@ -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll, Waker}; -use slab::Slab; use std::cell::UnsafeCell; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Mutex as StdMutex; +use std::sync::{Arc, Mutex as StdMutex}; use std::{fmt, mem}; +use slab::Slab; + +use futures_core::future::{FusedFuture, Future}; +use futures_core::task::{Context, Poll, Waker}; + /// A futures-aware mutex. /// /// # Fairness @@ -107,6 +109,18 @@ impl Mutex { } } + /// Attempt to acquire the lock immediately. + /// + /// If the lock is currently held, this will return `None`. + pub fn try_lock_owned(self: &Arc) -> Option> { + let old_state = self.state.fetch_or(IS_LOCKED, Ordering::Acquire); + if (old_state & IS_LOCKED) == 0 { + Some(OwnedMutexGuard { mutex: self.clone() }) + } else { + None + } + } + /// Acquire the lock asynchronously. /// /// This method returns a future that will resolve once the lock has been @@ -115,6 +129,14 @@ impl Mutex { MutexLockFuture { mutex: Some(self), wait_key: WAIT_KEY_NONE } } + /// Acquire the lock asynchronously. + /// + /// This method returns a future that will resolve once the lock has been + /// successfully acquired. + pub fn lock_owned(self: Arc) -> OwnedMutexLockFuture { + OwnedMutexLockFuture { mutex: Some(self), wait_key: WAIT_KEY_NONE } + } + /// Returns a mutable reference to the underlying data. /// /// Since this call borrows the `Mutex` mutably, no actual locking needs to @@ -173,7 +195,118 @@ impl Mutex { } // Sentinel for when no slot in the `Slab` has been dedicated to this object. -const WAIT_KEY_NONE: usize = usize::max_value(); +const WAIT_KEY_NONE: usize = usize::MAX; + +/// A future which resolves when the target mutex has been successfully acquired, owned version. +pub struct OwnedMutexLockFuture { + // `None` indicates that the mutex was successfully acquired. + mutex: Option>>, + wait_key: usize, +} + +impl fmt::Debug for OwnedMutexLockFuture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedMutexLockFuture") + .field("was_acquired", &self.mutex.is_none()) + .field("mutex", &self.mutex) + .field( + "wait_key", + &(if self.wait_key == WAIT_KEY_NONE { None } else { Some(self.wait_key) }), + ) + .finish() + } +} + +impl FusedFuture for OwnedMutexLockFuture { + fn is_terminated(&self) -> bool { + self.mutex.is_none() + } +} + +impl Future for OwnedMutexLockFuture { + type Output = OwnedMutexGuard; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + + let mutex = this.mutex.as_ref().expect("polled OwnedMutexLockFuture after completion"); + + if let Some(lock) = mutex.try_lock_owned() { + mutex.remove_waker(this.wait_key, false); + this.mutex = None; + return Poll::Ready(lock); + } + + { + let mut waiters = mutex.waiters.lock().unwrap(); + if this.wait_key == WAIT_KEY_NONE { + this.wait_key = waiters.insert(Waiter::Waiting(cx.waker().clone())); + if waiters.len() == 1 { + mutex.state.fetch_or(HAS_WAITERS, Ordering::Relaxed); // released by mutex unlock + } + } else { + waiters[this.wait_key].register(cx.waker()); + } + } + + // Ensure that we haven't raced `MutexGuard::drop`'s unlock path by + // attempting to acquire the lock again. + if let Some(lock) = mutex.try_lock_owned() { + mutex.remove_waker(this.wait_key, false); + this.mutex = None; + return Poll::Ready(lock); + } + + Poll::Pending + } +} + +impl Drop for OwnedMutexLockFuture { + fn drop(&mut self) { + if let Some(mutex) = self.mutex.as_ref() { + // This future was dropped before it acquired the mutex. + // + // Remove ourselves from the map, waking up another waiter if we + // had been awoken to acquire the lock. + mutex.remove_waker(self.wait_key, true); + } + } +} + +/// An RAII guard returned by the `lock_owned` and `try_lock_owned` methods. +/// When this structure is dropped (falls out of scope), the lock will be +/// unlocked. +pub struct OwnedMutexGuard { + mutex: Arc>, +} + +impl fmt::Debug for OwnedMutexGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedMutexGuard") + .field("value", &&**self) + .field("mutex", &self.mutex) + .finish() + } +} + +impl Drop for OwnedMutexGuard { + fn drop(&mut self) { + self.mutex.unlock() + } +} + +impl Deref for OwnedMutexGuard { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.mutex.value.get() } + } +} + +impl DerefMut for OwnedMutexGuard { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.mutex.value.get() } + } +} /// A future which resolves when the target mutex has been successfully acquired. pub struct MutexLockFuture<'a, T: ?Sized> { @@ -386,13 +519,25 @@ unsafe impl Sync for Mutex {} // It's safe to switch which thread the acquire is being attempted on so long as // `T` can be accessed on that thread. unsafe impl Send for MutexLockFuture<'_, T> {} + // doesn't have any interesting `&self` methods (only Debug) unsafe impl Sync for MutexLockFuture<'_, T> {} +// It's safe to switch which thread the acquire is being attempted on so long as +// `T` can be accessed on that thread. +unsafe impl Send for OwnedMutexLockFuture {} + +// doesn't have any interesting `&self` methods (only Debug) +unsafe impl Sync for OwnedMutexLockFuture {} + // Safe to send since we don't track any thread-specific details-- the inner // lock is essentially spinlock-equivalent (attempt to flip an atomic bool) unsafe impl Send for MutexGuard<'_, T> {} unsafe impl Sync for MutexGuard<'_, T> {} + +unsafe impl Send for OwnedMutexGuard {} +unsafe impl Sync for OwnedMutexGuard {} + unsafe impl Send for MappedMutexGuard<'_, T, U> {} unsafe impl Sync for MappedMutexGuard<'_, T, U> {} From 5714dbece8d8cb8b58c809dd311ebb3281894634 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 15 Mar 2022 01:28:29 +0000 Subject: [PATCH 119/283] Update no_atomic_cas.rs --- no_atomic_cas.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 9b05d4b9f4..341e6ac7d2 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -7,6 +7,7 @@ const NO_ATOMIC_CAS: &[&str] = &[ "bpfel-unknown-none", "msp430-none-elf", "riscv32i-unknown-none-elf", + "riscv32im-unknown-none-elf", "riscv32imc-unknown-none-elf", "thumbv4t-none-eabi", "thumbv6m-none-eabi", From a0e898f31c8e2fd869abc92f13462623e6900a34 Mon Sep 17 00:00:00 2001 From: Tim Leslie Date: Tue, 15 Mar 2022 15:30:35 +1100 Subject: [PATCH 120/283] Fix sentence in try_buffered docs. (#2579) Updates the docs to use the same phrasing as `.buffered()` https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.buffered --- futures-util/src/stream/try_stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 6bf2cb74a5..72a74f2166 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -918,7 +918,7 @@ pub trait TryStreamExt: TryStream { /// that matches the stream's `Error` type. /// /// This adaptor will buffer up to `n` futures and then return their - /// outputs in the order. If the underlying stream returns an error, it will + /// outputs in the same order as the underlying stream. If the underlying stream returns an error, it will /// be immediately propagated. /// /// The returned stream will be a stream of results, each containing either From 8c3c286fffcad89d56a5f54fc728f1e1b1084e02 Mon Sep 17 00:00:00 2001 From: Owen Shepherd <414owen@gmail.com> Date: Mon, 21 Mar 2022 10:03:33 +0000 Subject: [PATCH 121/283] Add `select` benchmark (#2582) --- futures-util/benches/select.rs | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 futures-util/benches/select.rs diff --git a/futures-util/benches/select.rs b/futures-util/benches/select.rs new file mode 100644 index 0000000000..5410a95299 --- /dev/null +++ b/futures-util/benches/select.rs @@ -0,0 +1,35 @@ +#![feature(test)] + +extern crate test; +use crate::test::Bencher; + +use futures::executor::block_on; +use futures::stream::{repeat, select, StreamExt}; + +#[bench] +fn select_streams(b: &mut Bencher) { + const STREAM_COUNT: usize = 10_000; + + b.iter(|| { + let stream1 = repeat(1).take(STREAM_COUNT); + let stream2 = repeat(2).take(STREAM_COUNT); + let stream3 = repeat(3).take(STREAM_COUNT); + let stream4 = repeat(4).take(STREAM_COUNT); + let stream5 = repeat(5).take(STREAM_COUNT); + let stream6 = repeat(6).take(STREAM_COUNT); + let stream7 = repeat(7).take(STREAM_COUNT); + let count = block_on(async { + let count = select( + stream1, + select( + stream2, + select(stream3, select(stream4, select(stream5, select(stream6, stream7)))), + ), + ) + .count() + .await; + count + }); + assert_eq!(count, STREAM_COUNT * 7); + }); +} From bd191687f07bd8b7032fbdcf0f992040ba6ca27e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 21 Mar 2022 20:53:57 +0900 Subject: [PATCH 122/283] Run more tests with Miri (#2584) --- .github/workflows/ci.yml | 4 +- futures-executor/Cargo.toml | 2 +- futures-executor/src/thread_pool.rs | 28 ++++++++----- futures-executor/tests/local_pool.rs | 3 ++ futures-util/src/task/spawn.rs | 9 ++-- futures/src/lib.rs | 4 +- futures/tests/eventual.rs | 9 ++-- futures/tests/future_shared.rs | 1 - futures/tests/lock_mutex.rs | 50 ++++++++++++----------- futures/tests/macro_comma_support.rs | 1 - futures/tests/recurse.rs | 1 - futures/tests/sink.rs | 1 - futures/tests/stream_futures_ordered.rs | 2 - futures/tests/stream_futures_unordered.rs | 2 - futures/tests/stream_try_stream.rs | 2 - 15 files changed, 64 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfb7eea4ff..110b1b7c0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -273,10 +273,10 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - # futures-executor uses boxed futures so many tests trigger https://github.com/rust-lang/miri/issues/1038 - - run: cargo miri test --workspace --exclude futures-executor --all-features + - run: cargo miri test --workspace --all-features env: MIRIFLAGS: -Zmiri-check-number-validity -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index dae5f22e77..e1ce688581 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -22,7 +22,7 @@ futures-util = { path = "../futures-util", version = "0.3.21", default-features num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] -futures = { path = "../futures" } +futures = { path = "../futures", features = ["thread-pool"] } [package.metadata.docs.rs] all-features = true diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 3eca9d1440..8c93b476bc 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -108,12 +108,15 @@ impl ThreadPool { /// completion. /// /// ``` + /// # { /// use futures::executor::ThreadPool; /// /// let pool = ThreadPool::new().unwrap(); /// /// let future = async { /* ... */ }; /// pool.spawn_ok(future); + /// # } + /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed /// ``` /// /// > **Note**: This method is similar to `SpawnExt::spawn`, except that @@ -359,16 +362,19 @@ mod tests { #[test] fn test_drop_after_start() { - let (tx, rx) = mpsc::sync_channel(2); - let _cpu_pool = ThreadPoolBuilder::new() - .pool_size(2) - .after_start(move |_| tx.send(1).unwrap()) - .create() - .unwrap(); - - // After ThreadPoolBuilder is deconstructed, the tx should be dropped - // so that we can use rx as an iterator. - let count = rx.into_iter().count(); - assert_eq!(count, 2); + { + let (tx, rx) = mpsc::sync_channel(2); + let _cpu_pool = ThreadPoolBuilder::new() + .pool_size(2) + .after_start(move |_| tx.send(1).unwrap()) + .create() + .unwrap(); + + // After ThreadPoolBuilder is deconstructed, the tx should be dropped + // so that we can use rx as an iterator. + let count = rx.into_iter().count(); + assert_eq!(count, 2); + } + std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed } } diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index 9b1316b998..8e5e27981d 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -288,6 +288,9 @@ fn run_until_stalled_runs_spawned_sub_futures() { #[test] fn run_until_stalled_executes_all_ready() { + #[cfg(miri)] + const ITER: usize = 50; + #[cfg(not(miri))] const ITER: usize = 200; const PER_ITER: usize = 3; diff --git a/futures-util/src/task/spawn.rs b/futures-util/src/task/spawn.rs index 87ca360516..8e78717c27 100644 --- a/futures-util/src/task/spawn.rs +++ b/futures-util/src/task/spawn.rs @@ -34,7 +34,7 @@ pub trait SpawnExt: Spawn { /// today. Feel free to use this method in the meantime. /// /// ``` - /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 + /// # { /// use futures::executor::ThreadPool; /// use futures::task::SpawnExt; /// @@ -42,6 +42,8 @@ pub trait SpawnExt: Spawn { /// /// let future = async { /* ... */ }; /// executor.spawn(future).unwrap(); + /// # } + /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed /// ``` #[cfg(feature = "alloc")] fn spawn(&self, future: Fut) -> Result<(), SpawnError> @@ -59,7 +61,7 @@ pub trait SpawnExt: Spawn { /// resolves to the output of the spawned future. /// /// ``` - /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 + /// # { /// use futures::executor::{block_on, ThreadPool}; /// use futures::future; /// use futures::task::SpawnExt; @@ -69,6 +71,8 @@ pub trait SpawnExt: Spawn { /// let future = future::ready(1); /// let join_handle_fut = executor.spawn_with_handle(future).unwrap(); /// assert_eq!(block_on(join_handle_fut), 1); + /// # } + /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed /// ``` #[cfg(feature = "channel")] #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] @@ -138,7 +142,6 @@ pub trait LocalSpawnExt: LocalSpawn { /// resolves to the output of the spawned future. /// /// ``` - /// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 /// use futures::executor::LocalPool; /// use futures::task::LocalSpawnExt; /// diff --git a/futures/src/lib.rs b/futures/src/lib.rs index b8ebc614e2..3ae9091dca 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -25,13 +25,13 @@ //! within macros and keywords such as async and await!. //! //! ```rust -//! # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 //! # use futures::channel::mpsc; //! # use futures::executor; ///standard executors to provide a context for futures and streams //! # use futures::executor::ThreadPool; //! # use futures::StreamExt; //! # //! fn main() { +//! # { //! let pool = ThreadPool::new().expect("Failed to build pool"); //! let (tx, rx) = mpsc::unbounded::(); //! @@ -73,6 +73,8 @@ //! let values: Vec = executor::block_on(fut_values); //! //! println!("Values={:?}", values); +//! # } +//! # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed //! } //! ``` //! diff --git a/futures/tests/eventual.rs b/futures/tests/eventual.rs index 34613806c4..96e21a12a4 100644 --- a/futures/tests/eventual.rs +++ b/futures/tests/eventual.rs @@ -1,5 +1,3 @@ -#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038 - use futures::channel::oneshot; use futures::executor::ThreadPool; use futures::future::{self, ok, Future, FutureExt, TryFutureExt}; @@ -136,6 +134,11 @@ fn select3() { #[test] fn select4() { + #[cfg(miri)] + const N: usize = 100; + #[cfg(not(miri))] + const N: usize = 10000; + let (tx, rx) = mpsc::channel::>(); let t = thread::spawn(move || { @@ -145,7 +148,7 @@ fn select4() { }); let (tx2, rx2) = mpsc::channel(); - for _ in 0..10000 { + for _ in 0..N { let (c1, p1) = oneshot::channel::(); let (c2, p2) = oneshot::channel::(); diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs index 20a021bc35..6bf43d23cf 100644 --- a/futures/tests/future_shared.rs +++ b/futures/tests/future_shared.rs @@ -97,7 +97,6 @@ fn drop_in_poll() { assert_eq!(block_on(future1), 1); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn peek() { let mut local_pool = LocalPool::new(); diff --git a/futures/tests/lock_mutex.rs b/futures/tests/lock_mutex.rs index c92ef50ad8..0bd2607565 100644 --- a/futures/tests/lock_mutex.rs +++ b/futures/tests/lock_mutex.rs @@ -34,34 +34,36 @@ fn mutex_wakes_waiters() { assert!(waiter.poll_unpin(&mut panic_context()).is_ready()); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn mutex_contested() { - let (tx, mut rx) = mpsc::unbounded(); - let pool = ThreadPool::builder().pool_size(16).create().unwrap(); + { + let (tx, mut rx) = mpsc::unbounded(); + let pool = ThreadPool::builder().pool_size(16).create().unwrap(); - let tx = Arc::new(tx); - let mutex = Arc::new(Mutex::new(0)); + let tx = Arc::new(tx); + let mutex = Arc::new(Mutex::new(0)); - let num_tasks = 1000; - for _ in 0..num_tasks { - let tx = tx.clone(); - let mutex = mutex.clone(); - pool.spawn(async move { - let mut lock = mutex.lock().await; - ready(()).pending_once().await; - *lock += 1; - tx.unbounded_send(()).unwrap(); - drop(lock); - }) - .unwrap(); - } - - block_on(async { + let num_tasks = 1000; for _ in 0..num_tasks { - rx.next().await.unwrap(); + let tx = tx.clone(); + let mutex = mutex.clone(); + pool.spawn(async move { + let mut lock = mutex.lock().await; + ready(()).pending_once().await; + *lock += 1; + tx.unbounded_send(()).unwrap(); + drop(lock); + }) + .unwrap(); } - let lock = mutex.lock().await; - assert_eq!(num_tasks, *lock); - }) + + block_on(async { + for _ in 0..num_tasks { + rx.next().await.unwrap(); + } + let lock = mutex.lock().await; + assert_eq!(num_tasks, *lock); + }); + } + std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed } diff --git a/futures/tests/macro_comma_support.rs b/futures/tests/macro_comma_support.rs index 3b082d211f..85871e98be 100644 --- a/futures/tests/macro_comma_support.rs +++ b/futures/tests/macro_comma_support.rs @@ -14,7 +14,6 @@ fn ready() { })) } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn poll() { use futures::poll; diff --git a/futures/tests/recurse.rs b/futures/tests/recurse.rs index f06524f85a..d81753c9d7 100644 --- a/futures/tests/recurse.rs +++ b/futures/tests/recurse.rs @@ -3,7 +3,6 @@ use futures::future::{self, BoxFuture, FutureExt}; use std::sync::mpsc; use std::thread; -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn lots() { #[cfg(not(futures_sanitizer))] diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index dc826bda98..f3cf11b931 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -288,7 +288,6 @@ 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_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn with_flush() { let (tx, rx) = oneshot::channel(); diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 84e0bcc1df..7506c65a63 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -26,7 +26,6 @@ fn works_1() { assert_eq!(None, iter.next()); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn works_2() { let (a_tx, a_rx) = oneshot::channel::(); @@ -55,7 +54,6 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn queue_never_unblocked() { let (_a_tx, a_rx) = oneshot::channel::>(); diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 398170a7cf..b568280479 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -56,7 +56,6 @@ fn works_1() { assert_eq!(None, iter.next()); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn works_2() { let (a_tx, a_rx) = oneshot::channel::(); @@ -86,7 +85,6 @@ fn from_iterator() { assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } -#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn finished_future() { let (_a_tx, a_rx) = oneshot::channel::(); diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index d83fc54b1c..194e74db74 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -1,5 +1,3 @@ -#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038 - use futures::{ stream::{self, StreamExt, TryStreamExt}, task::Poll, From c1c11c6d965f7c849818297b9a841adcb24d3e75 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 16 Apr 2022 23:59:20 +0900 Subject: [PATCH 123/283] Update MIRIFLAGS (#2589) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 110b1b7c0a..8c83181a6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -275,7 +275,7 @@ jobs: run: rustup toolchain install nightly --component miri && rustup default nightly - run: cargo miri test --workspace --all-features env: - MIRIFLAGS: -Zmiri-check-number-validity -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout san: From 00bee71cd97e03b0b84f7c61323eb52b6452532b Mon Sep 17 00:00:00 2001 From: maleicacid <4982384+kazuki0824@users.noreply.github.com> Date: Tue, 19 Apr 2022 10:53:15 +0900 Subject: [PATCH 124/283] Create copy_buf_abortable, which enables to stop copying in the middle (#2507) --- futures-util/src/abortable.rs | 8 +- futures-util/src/io/copy_buf_abortable.rs | 124 ++++++++++++++++++++++ futures-util/src/io/mod.rs | 3 + 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 futures-util/src/io/copy_buf_abortable.rs diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs index bb82dd0db8..e0afd47218 100644 --- a/futures-util/src/abortable.rs +++ b/futures-util/src/abortable.rs @@ -75,7 +75,7 @@ impl Abortable { /// in calls to `Abortable::new`. #[derive(Debug)] pub struct AbortRegistration { - inner: Arc, + pub(crate) inner: Arc, } /// A handle to an `Abortable` task. @@ -100,9 +100,9 @@ impl AbortHandle { // Inner type storing the waker to awaken and a bool indicating that it // should be aborted. #[derive(Debug)] -struct AbortInner { - waker: AtomicWaker, - aborted: AtomicBool, +pub(crate) struct AbortInner { + pub(crate) waker: AtomicWaker, + pub(crate) aborted: AtomicBool, } /// Indicator that the `Abortable` task was aborted. diff --git a/futures-util/src/io/copy_buf_abortable.rs b/futures-util/src/io/copy_buf_abortable.rs new file mode 100644 index 0000000000..fdbc4a5f00 --- /dev/null +++ b/futures-util/src/io/copy_buf_abortable.rs @@ -0,0 +1,124 @@ +use crate::abortable::{AbortHandle, AbortInner, Aborted}; +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; +use futures_io::{AsyncBufRead, AsyncWrite}; +use pin_project_lite::pin_project; +use std::io; +use std::pin::Pin; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +/// Creates a future which copies all the bytes from one object to another, with its `AbortHandle`. +/// +/// The returned future will copy all the bytes read from this `AsyncBufRead` into the +/// `writer` specified. This future will only complete once abort has been requested or the `reader` has hit +/// EOF and all bytes have been written to and flushed from the `writer` +/// provided. +/// +/// On success the number of bytes is returned. If aborted, `Aborted` is returned. Otherwise, the underlying error is returned. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::io::{self, AsyncWriteExt, Cursor}; +/// use futures::future::Aborted; +/// +/// let reader = Cursor::new([1, 2, 3, 4]); +/// let mut writer = Cursor::new(vec![0u8; 5]); +/// +/// let (fut, abort_handle) = io::copy_buf_abortable(reader, &mut writer); +/// let bytes = fut.await; +/// abort_handle.abort(); +/// writer.close().await.unwrap(); +/// match bytes { +/// Ok(Ok(n)) => { +/// assert_eq!(n, 4); +/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]); +/// Ok(n) +/// }, +/// Ok(Err(a)) => { +/// Err::(a) +/// } +/// Err(e) => panic!("{}", e) +/// } +/// # }).unwrap(); +/// ``` +pub fn copy_buf_abortable( + reader: R, + writer: &mut W, +) -> (CopyBufAbortable<'_, R, W>, AbortHandle) +where + R: AsyncBufRead, + W: AsyncWrite + Unpin + ?Sized, +{ + let (handle, reg) = AbortHandle::new_pair(); + (CopyBufAbortable { reader, writer, amt: 0, inner: reg.inner }, handle) +} + +pin_project! { + /// Future for the [`copy_buf()`] function. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct CopyBufAbortable<'a, R, W: ?Sized> { + #[pin] + reader: R, + writer: &'a mut W, + amt: u64, + inner: Arc + } +} + +macro_rules! ready_or_break { + ($e:expr $(,)?) => { + match $e { + $crate::task::Poll::Ready(t) => t, + $crate::task::Poll::Pending => break, + } + }; +} + +impl Future for CopyBufAbortable<'_, R, W> +where + R: AsyncBufRead, + W: AsyncWrite + Unpin + Sized, +{ + type Output = Result, io::Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + // Check if the task has been aborted + if this.inner.aborted.load(Ordering::Relaxed) { + return Poll::Ready(Ok(Err(Aborted))); + } + + // Read some bytes from the reader, and if we have reached EOF, return total bytes read + let buffer = ready_or_break!(this.reader.as_mut().poll_fill_buf(cx))?; + if buffer.is_empty() { + ready_or_break!(Pin::new(&mut this.writer).poll_flush(cx))?; + return Poll::Ready(Ok(Ok(*this.amt))); + } + + // Pass the buffer to the writer, and update the amount written + let i = ready_or_break!(Pin::new(&mut this.writer).poll_write(cx, buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *this.amt += i as u64; + this.reader.as_mut().consume(i); + } + // Schedule the task to be woken up again. + // Never called unless Poll::Pending is returned from io objects. + this.inner.waker.register(cx.waker()); + + // Check to see if the task was aborted between the first check and + // registration. + // Checking with `Relaxed` is sufficient because + // `register` introduces an `AcqRel` barrier. + if this.inner.aborted.load(Ordering::Relaxed) { + return Poll::Ready(Ok(Err(Aborted))); + } + Poll::Pending + } +} diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 4dd2e029bf..8ce3ad644b 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -66,6 +66,9 @@ pub use self::copy::{copy, Copy}; mod copy_buf; pub use self::copy_buf::{copy_buf, CopyBuf}; +mod copy_buf_abortable; +pub use self::copy_buf_abortable::{copy_buf_abortable, CopyBufAbortable}; + mod cursor; pub use self::cursor::Cursor; From c5a09bed76876ac945e110241ab8af6a5d18e883 Mon Sep 17 00:00:00 2001 From: Owen Shepherd Date: Fri, 6 May 2022 00:06:18 +0100 Subject: [PATCH 125/283] Remove `Fuse`s from `select`, and only poll non-terminated streams (#2583) --- futures-util/Cargo.toml | 2 +- .../src/stream/select_with_strategy.rs | 140 +++++++++++++----- 2 files changed, 106 insertions(+), 36 deletions(-) diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 46ec854b04..e32b642aa2 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -45,7 +45,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0" -pin-project-lite = "0.2.4" +pin-project-lite = "0.2.6" [dev-dependencies] futures = { path = "../futures", features = ["async-await", "thread-pool"] } diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs index bd86990cdb..7423519df1 100644 --- a/futures-util/src/stream/select_with_strategy.rs +++ b/futures-util/src/stream/select_with_strategy.rs @@ -1,5 +1,4 @@ use super::assert_stream; -use crate::stream::{Fuse, StreamExt}; use core::{fmt, pin::Pin}; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; @@ -18,13 +17,15 @@ impl PollNext { /// Toggle the value and return the old one. pub fn toggle(&mut self) -> Self { let old = *self; + *self = self.other(); + old + } + fn other(&self) -> PollNext { match self { - PollNext::Left => *self = PollNext::Right, - PollNext::Right => *self = PollNext::Left, + PollNext::Left => PollNext::Right, + PollNext::Right => PollNext::Left, } - - old } } @@ -34,14 +35,41 @@ impl Default for PollNext { } } +enum InternalState { + Start, + LeftFinished, + RightFinished, + BothFinished, +} + +impl InternalState { + fn finish(&mut self, ps: PollNext) { + match (&self, ps) { + (InternalState::Start, PollNext::Left) => { + *self = InternalState::LeftFinished; + } + (InternalState::Start, PollNext::Right) => { + *self = InternalState::RightFinished; + } + (InternalState::LeftFinished, PollNext::Right) + | (InternalState::RightFinished, PollNext::Left) => { + *self = InternalState::BothFinished; + } + _ => {} + } + } +} + pin_project! { /// Stream for the [`select_with_strategy()`] function. See function docs for details. #[must_use = "streams do nothing unless polled"] + #[project = SelectWithStrategyProj] pub struct SelectWithStrategy { #[pin] - stream1: Fuse, + stream1: St1, #[pin] - stream2: Fuse, + stream2: St2, + internal_state: InternalState, state: State, clos: Clos, } @@ -120,9 +148,10 @@ where State: Default, { assert_stream::(SelectWithStrategy { - stream1: stream1.fuse(), - stream2: stream2.fuse(), + stream1, + stream2, state: Default::default(), + internal_state: InternalState::Start, clos: which, }) } @@ -131,7 +160,7 @@ impl SelectWithStrategy { /// Acquires a reference to the underlying streams that this combinator is /// pulling from. pub fn get_ref(&self) -> (&St1, &St2) { - (self.stream1.get_ref(), self.stream2.get_ref()) + (&self.stream1, &self.stream2) } /// Acquires a mutable reference to the underlying streams that this @@ -140,7 +169,7 @@ impl SelectWithStrategy { /// 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 St1, &mut St2) { - (self.stream1.get_mut(), self.stream2.get_mut()) + (&mut self.stream1, &mut self.stream2) } /// Acquires a pinned mutable reference to the underlying streams that this @@ -150,7 +179,7 @@ impl SelectWithStrategy { /// stream which may otherwise confuse this combinator. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { let this = self.project(); - (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) + (this.stream1, this.stream2) } /// Consumes this combinator, returning the underlying streams. @@ -158,7 +187,7 @@ impl SelectWithStrategy { /// 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) -> (St1, St2) { - (self.stream1.into_inner(), self.stream2.into_inner()) + (self.stream1, self.stream2) } } @@ -169,47 +198,88 @@ where Clos: FnMut(&mut State) -> PollNext, { fn is_terminated(&self) -> bool { - self.stream1.is_terminated() && self.stream2.is_terminated() + match self.internal_state { + InternalState::BothFinished => true, + _ => false, + } } } -impl Stream for SelectWithStrategy +#[inline] +fn poll_side( + select: &mut SelectWithStrategyProj<'_, St1, St2, Clos, State>, + side: PollNext, + cx: &mut Context<'_>, +) -> Poll> where St1: Stream, St2: Stream, - Clos: FnMut(&mut State) -> PollNext, { - type Item = St1::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.project(); - - match (this.clos)(this.state) { - PollNext::Left => poll_inner(this.stream1, this.stream2, cx), - PollNext::Right => poll_inner(this.stream2, this.stream1, cx), - } + match side { + PollNext::Left => select.stream1.as_mut().poll_next(cx), + PollNext::Right => select.stream2.as_mut().poll_next(cx), } } -fn poll_inner( - a: Pin<&mut St1>, - b: Pin<&mut St2>, +#[inline] +fn poll_inner( + select: &mut SelectWithStrategyProj<'_, St1, St2, Clos, State>, + side: PollNext, cx: &mut Context<'_>, ) -> Poll> where St1: Stream, St2: Stream, { - let a_done = match a.poll_next(cx) { + match poll_side(select, side, cx) { Poll::Ready(Some(item)) => return Poll::Ready(Some(item)), - Poll::Ready(None) => true, - Poll::Pending => false, + Poll::Ready(None) => { + select.internal_state.finish(side); + } + Poll::Pending => (), }; + let other = side.other(); + match poll_side(select, other, cx) { + Poll::Ready(None) => { + select.internal_state.finish(other); + Poll::Ready(None) + } + a => a, + } +} + +impl Stream for SelectWithStrategy +where + St1: Stream, + St2: Stream, + Clos: FnMut(&mut State) -> PollNext, +{ + type Item = St1::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - match b.poll_next(cx) { - Poll::Ready(Some(item)) => Poll::Ready(Some(item)), - Poll::Ready(None) if a_done => Poll::Ready(None), - Poll::Ready(None) | Poll::Pending => Poll::Pending, + match this.internal_state { + InternalState::Start => { + let next_side = (this.clos)(this.state); + poll_inner(&mut this, next_side, cx) + } + InternalState::LeftFinished => match this.stream2.poll_next(cx) { + Poll::Ready(None) => { + *this.internal_state = InternalState::BothFinished; + Poll::Ready(None) + } + a => a, + }, + InternalState::RightFinished => match this.stream1.poll_next(cx) { + Poll::Ready(None) => { + *this.internal_state = InternalState::BothFinished; + Poll::Ready(None) + } + a => a, + }, + InternalState::BothFinished => Poll::Ready(None), + } } } From 3207f4f53bf9afc0a284b9a6d501a9b1ba858819 Mon Sep 17 00:00:00 2001 From: khollbach Date: Wed, 11 May 2022 15:56:40 -0400 Subject: [PATCH 126/283] Remove TryStreamExt::into_async_read Unpin bound (#2599) --- .../src/stream/try_stream/into_async_read.rs | 101 +++++++++--------- futures-util/src/stream/try_stream/mod.rs | 17 ++- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/futures-util/src/stream/try_stream/into_async_read.rs b/futures-util/src/stream/try_stream/into_async_read.rs index 914b277a02..ffbfc7eae9 100644 --- a/futures-util/src/stream/try_stream/into_async_read.rs +++ b/futures-util/src/stream/try_stream/into_async_read.rs @@ -1,30 +1,26 @@ -use crate::stream::TryStreamExt; use core::pin::Pin; use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncRead, AsyncWrite}; +use pin_project_lite::pin_project; use std::cmp; use std::io::{Error, Result}; -/// Reader for the [`into_async_read`](super::TryStreamExt::into_async_read) method. -#[derive(Debug)] -#[must_use = "readers do nothing unless polled"] -#[cfg_attr(docsrs, doc(cfg(feature = "io")))] -pub struct IntoAsyncRead -where - St: TryStream + Unpin, - St::Ok: AsRef<[u8]>, -{ - stream: St, - state: ReadState, -} - -impl Unpin for IntoAsyncRead -where - St: TryStream + Unpin, - St::Ok: AsRef<[u8]>, -{ +pin_project! { + /// Reader for the [`into_async_read`](super::TryStreamExt::into_async_read) method. + #[derive(Debug)] + #[must_use = "readers do nothing unless polled"] + #[cfg_attr(docsrs, doc(cfg(feature = "io")))] + pub struct IntoAsyncRead + where + St: TryStream, + St::Ok: AsRef<[u8]>, + { + #[pin] + stream: St, + state: ReadState, + } } #[derive(Debug)] @@ -36,7 +32,7 @@ enum ReadState> { impl IntoAsyncRead where - St: TryStream + Unpin, + St: TryStream, St::Ok: AsRef<[u8]>, { pub(super) fn new(stream: St) -> Self { @@ -46,16 +42,18 @@ where impl AsyncRead for IntoAsyncRead where - St: TryStream + Unpin, + St: TryStream, St::Ok: AsRef<[u8]>, { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { + let mut this = self.project(); + loop { - match &mut self.state { + match this.state { ReadState::Ready { chunk, chunk_start } => { let chunk = chunk.as_ref(); let len = cmp::min(buf.len(), chunk.len() - *chunk_start); @@ -64,23 +62,23 @@ where *chunk_start += len; if chunk.len() == *chunk_start { - self.state = ReadState::PendingChunk; + *this.state = ReadState::PendingChunk; } return Poll::Ready(Ok(len)); } - ReadState::PendingChunk => match ready!(self.stream.try_poll_next_unpin(cx)) { + ReadState::PendingChunk => match ready!(this.stream.as_mut().try_poll_next(cx)) { Some(Ok(chunk)) => { if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { chunk, chunk_start: 0 }; + *this.state = ReadState::Ready { chunk, chunk_start: 0 }; } } Some(Err(err)) => { - self.state = ReadState::Eof; + *this.state = ReadState::Eof; return Poll::Ready(Err(err)); } None => { - self.state = ReadState::Eof; + *this.state = ReadState::Eof; return Poll::Ready(Ok(0)); } }, @@ -94,51 +92,52 @@ where impl AsyncWrite for IntoAsyncRead where - St: TryStream + AsyncWrite + Unpin, + St: TryStream + AsyncWrite, St::Ok: AsRef<[u8]>, { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.stream).poll_write(cx, buf) + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let this = self.project(); + this.stream.poll_write(cx, buf) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.stream).poll_flush(cx) + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.stream.poll_flush(cx) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.stream).poll_close(cx) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.stream.poll_close(cx) } } impl AsyncBufRead for IntoAsyncRead where - St: TryStream + Unpin, + St: TryStream, St::Ok: AsRef<[u8]>, { - fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - while let ReadState::PendingChunk = self.state { - match ready!(self.stream.try_poll_next_unpin(cx)) { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + while let ReadState::PendingChunk = this.state { + match ready!(this.stream.as_mut().try_poll_next(cx)) { Some(Ok(chunk)) => { if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { chunk, chunk_start: 0 }; + *this.state = ReadState::Ready { chunk, chunk_start: 0 }; } } Some(Err(err)) => { - self.state = ReadState::Eof; + *this.state = ReadState::Eof; return Poll::Ready(Err(err)); } None => { - self.state = ReadState::Eof; + *this.state = ReadState::Eof; return Poll::Ready(Ok(&[])); } } } - if let ReadState::Ready { ref chunk, chunk_start } = self.into_ref().get_ref().state { + if let &mut ReadState::Ready { ref chunk, chunk_start } = this.state { let chunk = chunk.as_ref(); return Poll::Ready(Ok(&chunk[chunk_start..])); } @@ -147,16 +146,18 @@ where Poll::Ready(Ok(&[])) } - fn consume(mut self: Pin<&mut Self>, amount: usize) { + fn consume(self: Pin<&mut Self>, amount: usize) { + let this = self.project(); + // https://github.com/rust-lang/futures-rs/pull/1556#discussion_r281644295 if amount == 0 { return; } - if let ReadState::Ready { chunk, chunk_start } = &mut self.state { + if let ReadState::Ready { chunk, chunk_start } = this.state { *chunk_start += amount; debug_assert!(*chunk_start <= chunk.as_ref().len()); if *chunk_start >= chunk.as_ref().len() { - self.state = ReadState::PendingChunk; + *this.state = ReadState::PendingChunk; } } else { debug_assert!(false, "Attempted to consume from IntoAsyncRead without chunk"); diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 72a74f2166..bc4c6e4f6a 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -1031,12 +1031,7 @@ pub trait TryStreamExt: TryStream { Compat::new(self) } - /// Adapter that converts this stream into an [`AsyncRead`](crate::io::AsyncRead). - /// - /// Note that because `into_async_read` moves the stream, the [`Stream`](futures_core::stream::Stream) type must be - /// [`Unpin`]. If you want to use `into_async_read` with a [`!Unpin`](Unpin) stream, you'll - /// first have to pin the stream. This can be done by boxing the stream using [`Box::pin`] - /// or pinning it to the stack using the `pin_mut!` macro from the `pin_utils` crate. + /// Adapter that converts this stream into an [`AsyncBufRead`](crate::io::AsyncBufRead). /// /// This method is only available when the `std` feature of this /// library is activated, and it is activated by default. @@ -1048,12 +1043,12 @@ pub trait TryStreamExt: TryStream { /// use futures::stream::{self, TryStreamExt}; /// use futures::io::AsyncReadExt; /// - /// let stream = stream::iter(vec![Ok(vec![1, 2, 3, 4, 5])]); + /// let stream = stream::iter([Ok(vec![1, 2, 3]), Ok(vec![4, 5])]); /// let mut reader = stream.into_async_read(); - /// let mut buf = Vec::new(); /// - /// assert!(reader.read_to_end(&mut buf).await.is_ok()); - /// assert_eq!(buf, &[1, 2, 3, 4, 5]); + /// let mut buf = Vec::new(); + /// reader.read_to_end(&mut buf).await.unwrap(); + /// assert_eq!(buf, [1, 2, 3, 4, 5]); /// # }) /// ``` #[cfg(feature = "io")] @@ -1061,7 +1056,7 @@ pub trait TryStreamExt: TryStream { #[cfg(feature = "std")] fn into_async_read(self) -> IntoAsyncRead where - Self: Sized + TryStreamExt + Unpin, + Self: Sized + TryStreamExt, Self::Ok: AsRef<[u8]>, { crate::io::assert_read(IntoAsyncRead::new(self)) From 8f20722c7387f2044db4402500c986df688132bd Mon Sep 17 00:00:00 2001 From: khollbach Date: Wed, 11 May 2022 17:32:18 -0400 Subject: [PATCH 127/283] Make run_until_stalled handle self-waking futures (#2593) LocalPool::try_run_one and run_until_stalled now correctly re-try when a future "yields" by calling wake and returning Pending. --- futures-executor/src/local_pool.rs | 94 +++++++++++++++------------- futures-executor/tests/local_pool.rs | 64 ++++++++++++++++++- 2 files changed, 113 insertions(+), 45 deletions(-) diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index bee96d8db9..9691060725 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -106,17 +106,9 @@ fn run_executor) -> Poll>(mut f: F) -> T { }) } -fn poll_executor) -> T>(mut f: F) -> T { - let _enter = enter().expect( - "cannot execute `LocalPool` executor from within \ - another executor", - ); - - CURRENT_THREAD_NOTIFY.with(|thread_notify| { - let waker = waker_ref(thread_notify); - let mut cx = Context::from_waker(&waker); - f(&mut cx) - }) +/// Check for a wakeup, but don't consume it. +fn woken() -> bool { + CURRENT_THREAD_NOTIFY.with(|thread_notify| thread_notify.unparked.load(Ordering::SeqCst)) } impl LocalPool { @@ -212,20 +204,26 @@ impl LocalPool { /// further use of one of the pool's run or poll methods. /// Though only one task will be completed, progress may be made on multiple tasks. pub fn try_run_one(&mut self) -> bool { - poll_executor(|ctx| { + run_executor(|cx| { loop { - let ret = self.poll_pool_once(ctx); - - // return if we have executed a future - if let Poll::Ready(Some(_)) = ret { - return true; + self.drain_incoming(); + + match self.pool.poll_next_unpin(cx) { + // Success! + Poll::Ready(Some(())) => return Poll::Ready(true), + // The pool was empty. + Poll::Ready(None) => return Poll::Ready(false), + Poll::Pending => (), } - // if there are no new incoming futures - // then there is no feature that can make progress - // and we can return without having completed a single future - if self.incoming.borrow().is_empty() { - return false; + if !self.incoming.borrow().is_empty() { + // New tasks were spawned; try again. + continue; + } else if woken() { + // The pool yielded to us, but there's more progress to be made. + return Poll::Pending; + } else { + return Poll::Ready(false); } } }) @@ -257,44 +255,52 @@ impl LocalPool { /// of the pool's run or poll methods. While the function is running, all tasks /// in the pool will try to make progress. pub fn run_until_stalled(&mut self) { - poll_executor(|ctx| { - let _ = self.poll_pool(ctx); + run_executor(|cx| match self.poll_pool(cx) { + // The pool is empty. + Poll::Ready(()) => Poll::Ready(()), + Poll::Pending => { + if woken() { + Poll::Pending + } else { + // We're stalled for now. + Poll::Ready(()) + } + } }); } - // Make maximal progress on the entire pool of spawned task, returning `Ready` - // if the pool is empty and `Pending` if no further progress can be made. + /// Poll `self.pool`, re-filling it with any newly-spawned tasks. + /// Repeat until either the pool is empty, or it returns `Pending`. + /// + /// Returns `Ready` if the pool was empty, and `Pending` otherwise. + /// + /// NOTE: the pool may call `wake`, so `Pending` doesn't necessarily + /// mean that the pool can't make progress. fn poll_pool(&mut self, cx: &mut Context<'_>) -> Poll<()> { - // state for the FuturesUnordered, which will never be used loop { - let ret = self.poll_pool_once(cx); + self.drain_incoming(); - // we queued up some new tasks; add them and poll again + let pool_ret = self.pool.poll_next_unpin(cx); + + // We queued up some new tasks; add them and poll again. if !self.incoming.borrow().is_empty() { continue; } - // no queued tasks; we may be done - match ret { - Poll::Pending => return Poll::Pending, + match pool_ret { + Poll::Ready(Some(())) => continue, Poll::Ready(None) => return Poll::Ready(()), - _ => {} + Poll::Pending => return Poll::Pending, } } } - // Try make minimal progress on the pool of spawned tasks - fn poll_pool_once(&mut self, cx: &mut Context<'_>) -> Poll> { - // empty the incoming queue of newly-spawned tasks - { - let mut incoming = self.incoming.borrow_mut(); - for task in incoming.drain(..) { - self.pool.push(task) - } + /// Empty the incoming queue of newly-spawned tasks. + fn drain_incoming(&mut self) { + let mut incoming = self.incoming.borrow_mut(); + for task in incoming.drain(..) { + self.pool.push(task) } - - // try to execute the next ready future - self.pool.poll_next_unpin(cx) } } diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index 8e5e27981d..6e908d2444 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -1,7 +1,7 @@ use futures::channel::oneshot; use futures::executor::LocalPool; use futures::future::{self, lazy, poll_fn, Future}; -use futures::task::{Context, LocalSpawn, Poll, Spawn, Waker}; +use futures::task::{Context, LocalSpawn, LocalSpawnExt, Poll, Spawn, SpawnExt, Waker}; use std::cell::{Cell, RefCell}; use std::pin::Pin; use std::rc::Rc; @@ -435,3 +435,65 @@ fn park_unpark_independence() { futures::executor::block_on(future) } + +struct SelfWaking { + wakeups_remaining: Rc>, +} + +impl Future for SelfWaking { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if *self.wakeups_remaining.borrow() != 0 { + *self.wakeups_remaining.borrow_mut() -= 1; + cx.waker().wake_by_ref(); + } + + Poll::Pending + } +} + +/// Regression test for https://github.com/rust-lang/futures-rs/pull/2593 +/// +/// The issue was that self-waking futures could cause `run_until_stalled` +/// to exit early, even when progress could still be made. +#[test] +fn self_waking_run_until_stalled() { + let wakeups_remaining = Rc::new(RefCell::new(10)); + + let mut pool = LocalPool::new(); + let spawner = pool.spawner(); + for _ in 0..3 { + let wakeups_remaining = Rc::clone(&wakeups_remaining); + spawner.spawn_local(SelfWaking { wakeups_remaining }).unwrap(); + } + + // This should keep polling until there are no more wakeups. + pool.run_until_stalled(); + + assert_eq!(*wakeups_remaining.borrow(), 0); +} + +/// Regression test for https://github.com/rust-lang/futures-rs/pull/2593 +/// +/// The issue was that self-waking futures could cause `try_run_one` +/// to exit early, even when progress could still be made. +#[test] +fn self_waking_try_run_one() { + let wakeups_remaining = Rc::new(RefCell::new(10)); + + let mut pool = LocalPool::new(); + let spawner = pool.spawner(); + for _ in 0..3 { + let wakeups_remaining = Rc::clone(&wakeups_remaining); + spawner.spawn_local(SelfWaking { wakeups_remaining }).unwrap(); + } + + spawner.spawn(future::ready(())).unwrap(); + + // The `ready` future should complete. + assert!(pool.try_run_one()); + + // The self-waking futures are each polled once. + assert_eq!(*wakeups_remaining.borrow(), 7); +} From ae7dbfbd395714ee7a28883187a3b32a6255d51f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 May 2022 18:54:24 +0900 Subject: [PATCH 128/283] Fix rustdoc::broken_intra_doc_links warning (#2602) ``` error: unresolved link to `select` --> futures-util/src/future/try_future/mod.rs:272:17 | 272 | /// using [`select!`] or [`join!`]. | ^^^^^^^ no item named `select` in scope | = note: `-D rustdoc::broken-intra-doc-links` implied by `-D warnings` = note: `macro_rules` named `select` exists in this crate, but it is not in scope at this link's location error: unresolved link to `join` --> futures-util/src/future/try_future/mod.rs:272:32 | 272 | /// using [`select!`] or [`join!`]. | ^^^^^ no item named `join` in scope | = note: `macro_rules` named `join` exists in this crate, but it is not in scope at this link's location error: unresolved link to `select` --> futures-util/src/future/try_future/mod.rs:320:27 | 320 | /// type when using [`select!`] or [`join!`]. | ^^^^^^^ no item named `select` in scope | = note: `macro_rules` named `select` exists in this crate, but it is not in scope at this link's location error: unresolved link to `join` --> futures-util/src/future/try_future/mod.rs:320:42 | 320 | /// type when using [`select!`] or [`join!`]. | ^^^^^ no item named `join` in scope | = note: `macro_rules` named `join` exists in this crate, but it is not in scope at this link's location error: unresolved link to `select` --> futures-util/src/stream/stream/mod.rs:1802:15 | 1802 | /// the [`select!`] macro. | ^^^^^^^ no item named `select` in scope | = note: `macro_rules` named `select` exists in this crate, but it is not in scope at this link's location ``` --- futures-util/src/future/try_future/mod.rs | 6 ++++++ futures-util/src/stream/stream/mod.rs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index fb3bdd8a02..e5bc700714 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -302,6 +302,9 @@ pub trait TryFutureExt: TryFuture { /// assert_eq!(future.await, Ok(1)); /// # }); /// ``` + /// + /// [`join!`]: crate::join + /// [`select!`]: crate::select fn map_err(self, f: F) -> MapErr where F: FnOnce(Self::Error) -> E, @@ -332,6 +335,9 @@ pub trait TryFutureExt: TryFuture { /// let future_err_i32 = future_err_u8.err_into::(); /// # }); /// ``` + /// + /// [`join!`]: crate::join + /// [`select!`]: crate::select fn err_into(self) -> ErrInto where Self: Sized, diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 642b91e823..a823fab123 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -1674,6 +1674,8 @@ pub trait StreamExt: Stream { /// assert_eq!(total, 6); /// # }); /// ``` + /// + /// [`select!`]: crate::select fn select_next_some(&mut self) -> SelectNextSome<'_, Self> where Self: Unpin + FusedStream, From 066d2db9c24f2cc69f2c642984637673c9928f37 Mon Sep 17 00:00:00 2001 From: Basti Ortiz <39114273+Some-Dood@users.noreply.github.com> Date: Sat, 28 May 2022 17:56:17 +0800 Subject: [PATCH 129/283] Refactor: prefer early return in `future::select` (#2587) Although a relatively small change, it makes the code a little bit more readable than the originally nested `match` expressions. --- futures-util/src/future/select.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index bd44f20f77..e693a30b00 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -100,16 +100,17 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let (mut a, mut b) = self.inner.take().expect("cannot poll Select twice"); - match a.poll_unpin(cx) { - Poll::Ready(x) => Poll::Ready(Either::Left((x, b))), - Poll::Pending => match b.poll_unpin(cx) { - Poll::Ready(x) => Poll::Ready(Either::Right((x, a))), - Poll::Pending => { - self.inner = Some((a, b)); - Poll::Pending - } - }, + + if let Poll::Ready(val) = a.poll_unpin(cx) { + return Poll::Ready(Either::Left((val, b))); + } + + if let Poll::Ready(val) = b.poll_unpin(cx) { + return Poll::Ready(Either::Right((val, a))); } + + self.inner = Some((a, b)); + Poll::Pending } } From dbd958afeebb18caf1af5edbced516418ade081f Mon Sep 17 00:00:00 2001 From: Daniel Mangum <31777345+hasheddan@users.noreply.github.com> Date: Sun, 29 May 2022 10:39:26 -0500 Subject: [PATCH 130/283] Fix minor typo in enter doc comment (#2604) Fixes a minor typo by changing "a tasks" to "a task" in the documentation comment for enter. Signed-off-by: hasheddan --- futures-executor/src/enter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-executor/src/enter.rs b/futures-executor/src/enter.rs index 5895a9efb6..cb58c30bb7 100644 --- a/futures-executor/src/enter.rs +++ b/futures-executor/src/enter.rs @@ -34,7 +34,7 @@ impl std::error::Error for EnterError {} /// executor. /// /// Executor implementations should call this function before beginning to -/// execute a tasks, and drop the returned [`Enter`](Enter) value after +/// execute a task, and drop the returned [`Enter`](Enter) value after /// completing task execution: /// /// ``` From 69ea267ec71cd400f30b660dbeefce2851a1f17e Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sat, 4 Jun 2022 22:40:01 -0400 Subject: [PATCH 131/283] Switch to `FuturesOrdered` dynamically in `try_join_all` (#2556) --- futures-util/src/future/join_all.rs | 29 +++--- futures-util/src/future/try_join_all.rs | 123 +++++++++++++++++------- futures/tests/auto_traits.rs | 4 +- 3 files changed, 102 insertions(+), 54 deletions(-) diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 2e52ac17f4..7dc159ba07 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -15,7 +15,7 @@ use super::{assert_future, MaybeDone}; #[cfg(not(futures_no_atomic_cas))] use crate::stream::{Collect, FuturesOrdered, StreamExt}; -fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { +pub(crate) fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has // the same safety as a normal field pin projection. @@ -32,9 +32,9 @@ where } #[cfg(not(futures_no_atomic_cas))] -const SMALL: usize = 30; +pub(crate) const SMALL: usize = 30; -pub(crate) enum JoinAllKind +enum JoinAllKind where F: Future, { @@ -104,26 +104,25 @@ where I: IntoIterator, I::Item: Future, { + let iter = iter.into_iter(); + #[cfg(futures_no_atomic_cas)] { - let elems = iter.into_iter().map(MaybeDone::Future).collect::>().into(); - let kind = JoinAllKind::Small { elems }; + let kind = + JoinAllKind::Small { elems: iter.map(MaybeDone::Future).collect::>().into() }; + assert_future::::Output>, _>(JoinAll { kind }) } + #[cfg(not(futures_no_atomic_cas))] { - let iter = iter.into_iter(); let kind = match iter.size_hint().1 { - None => JoinAllKind::Big { fut: iter.collect::>().collect() }, - Some(max) => { - if max <= SMALL { - let elems = iter.map(MaybeDone::Future).collect::>().into(); - JoinAllKind::Small { elems } - } else { - JoinAllKind::Big { fut: iter.collect::>().collect() } - } - } + Some(max) if max <= SMALL => JoinAllKind::Small { + elems: iter.map(MaybeDone::Future).collect::>().into(), + }, + _ => JoinAllKind::Big { fut: iter.collect::>().collect() }, }; + assert_future::::Output>, _>(JoinAll { kind }) } } diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 29244af837..25fcfcb6c2 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -10,14 +10,11 @@ use core::mem; use core::pin::Pin; use core::task::{Context, Poll}; -use super::{assert_future, TryFuture, TryMaybeDone}; +use super::{assert_future, join_all, IntoFuture, 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 - // invariants aren't required to transmit through slices. Otherwise this has - // the same safety as a normal field pin projection. - unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) -} +#[cfg(not(futures_no_atomic_cas))] +use crate::stream::{FuturesOrdered, TryCollect, TryStreamExt}; +use crate::TryFutureExt; enum FinalState { Pending, @@ -31,7 +28,20 @@ pub struct TryJoinAll where F: TryFuture, { - elems: Pin]>>, + kind: TryJoinAllKind, +} + +enum TryJoinAllKind +where + F: TryFuture, +{ + Small { + elems: Pin>]>>, + }, + #[cfg(not(futures_no_atomic_cas))] + Big { + fut: TryCollect>, Vec>, + }, } impl fmt::Debug for TryJoinAll @@ -39,9 +49,16 @@ where F: TryFuture + fmt::Debug, F::Ok: fmt::Debug, F::Error: fmt::Debug, + F::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryJoinAll").field("elems", &self.elems).finish() + match self.kind { + TryJoinAllKind::Small { ref elems } => { + f.debug_struct("TryJoinAll").field("elems", elems).finish() + } + #[cfg(not(futures_no_atomic_cas))] + TryJoinAllKind::Big { ref fut, .. } => fmt::Debug::fmt(fut, f), + } } } @@ -83,15 +100,37 @@ where /// assert_eq!(try_join_all(futures).await, Err(2)); /// # }); /// ``` -pub fn try_join_all(i: I) -> TryJoinAll +pub fn try_join_all(iter: I) -> TryJoinAll where I: IntoIterator, I::Item: TryFuture, { - let elems: Box<[_]> = i.into_iter().map(TryMaybeDone::Future).collect(); - assert_future::::Ok>, ::Error>, _>( - TryJoinAll { elems: elems.into() }, - ) + let iter = iter.into_iter().map(TryFutureExt::into_future); + + #[cfg(futures_no_atomic_cas)] + { + let kind = TryJoinAllKind::Small { + elems: iter.map(TryMaybeDone::Future).collect::>().into(), + }; + + assert_future::::Ok>, ::Error>, _>( + TryJoinAll { kind }, + ) + } + + #[cfg(not(futures_no_atomic_cas))] + { + let kind = match iter.size_hint().1 { + Some(max) if max <= join_all::SMALL => TryJoinAllKind::Small { + elems: iter.map(TryMaybeDone::Future).collect::>().into(), + }, + _ => TryJoinAllKind::Big { fut: iter.collect::>().try_collect() }, + }; + + assert_future::::Ok>, ::Error>, _>( + TryJoinAll { kind }, + ) + } } impl Future for TryJoinAll @@ -101,36 +140,46 @@ where type Output = Result, F::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut state = FinalState::AllDone; - - 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; + match &mut self.kind { + TryJoinAllKind::Small { elems } => { + let mut state = FinalState::AllDone; + + for elem in join_all::iter_pin_mut(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; + } + } } - } - } - match state { - FinalState::Pending => Poll::Pending, - FinalState::AllDone => { - let mut elems = mem::replace(&mut self.elems, Box::pin([])); - let results = - iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); - Poll::Ready(Ok(results)) - } - FinalState::Error(e) => { - let _ = mem::replace(&mut self.elems, Box::pin([])); - Poll::Ready(Err(e)) + match state { + FinalState::Pending => Poll::Pending, + FinalState::AllDone => { + let mut elems = mem::replace(elems, Box::pin([])); + let results = join_all::iter_pin_mut(elems.as_mut()) + .map(|e| e.take_output().unwrap()) + .collect(); + Poll::Ready(Ok(results)) + } + FinalState::Error(e) => { + let _ = mem::replace(elems, Box::pin([])); + Poll::Ready(Err(e)) + } + } } + #[cfg(not(futures_no_atomic_cas))] + TryJoinAllKind::Big { fut } => Pin::new(fut).poll(cx), } } } -impl FromIterator for TryJoinAll { +impl FromIterator for TryJoinAll +where + F: TryFuture, +{ fn from_iter>(iter: T) -> Self { try_join_all(iter) } diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index b3d8b00773..da00ccf40c 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -576,10 +576,10 @@ pub mod future { // TryJoin3, TryJoin4, TryJoin5 are the same as TryJoin - assert_impl!(TryJoinAll>: Send); + assert_impl!(TryJoinAll>: Send); assert_not_impl!(TryJoinAll: Send); assert_not_impl!(TryJoinAll: Send); - assert_impl!(TryJoinAll>: Sync); + assert_impl!(TryJoinAll>: Sync); assert_not_impl!(TryJoinAll: Sync); assert_not_impl!(TryJoinAll: Sync); assert_impl!(TryJoinAll: Unpin); From 7786e7d671bdd4004f4af544dbe3d1e90eca9a19 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sat, 4 Jun 2022 22:51:58 -0400 Subject: [PATCH 132/283] Fix orderings in `LocalPool` waker (#2608) --- futures-executor/src/local_pool.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index 9691060725..ec1751c7c4 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -63,7 +63,7 @@ thread_local! { impl ArcWake for ThreadNotify { fn wake_by_ref(arc_self: &Arc) { // Make sure the wakeup is remembered until the next `park()`. - let unparked = arc_self.unparked.swap(true, Ordering::Relaxed); + let unparked = arc_self.unparked.swap(true, Ordering::Release); if !unparked { // If the thread has not been unparked yet, it must be done // now. If it was actually parked, it will run again, @@ -90,17 +90,13 @@ fn run_executor) -> Poll>(mut f: F) -> T { if let Poll::Ready(t) = f(&mut cx) { return t; } - // Consume the wakeup that occurred while executing `f`, if any. - let unparked = thread_notify.unparked.swap(false, Ordering::Acquire); - if !unparked { + + // Wait for a wakeup. + while !thread_notify.unparked.swap(false, Ordering::Acquire) { // No wakeup occurred. It may occur now, right before parking, // but in that case the token made available by `unpark()` // is guaranteed to still be available and `park()` is a no-op. thread::park(); - // When the thread is unparked, `unparked` will have been set - // and needs to be unset before the next call to `f` to avoid - // a redundant loop iteration. - thread_notify.unparked.store(false, Ordering::Release); } } }) From 122525bb5edaf37fbf3994d323dc1796efce5d22 Mon Sep 17 00:00:00 2001 From: jefftt Date: Mon, 6 Jun 2022 10:26:07 -0400 Subject: [PATCH 133/283] stream: Fix Chunk adapters size hints (#2611) --- futures-util/src/stream/stream/chunks.rs | 2 +- futures-util/src/stream/stream/ready_chunks.rs | 2 +- futures-util/src/stream/try_stream/try_chunks.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index 8457869999..e6d9118a0e 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -79,7 +79,7 @@ impl Stream for Chunks { 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 lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { Some(x) => x.checked_add(chunk_len), None => None, diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 5ebc9582db..44edc53521 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -87,7 +87,7 @@ impl Stream for ReadyChunks { 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 lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { Some(x) => x.checked_add(chunk_len), None => None, diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs index 07d4425a81..5b5ff1d6a6 100644 --- a/futures-util/src/stream/try_stream/try_chunks.rs +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -83,7 +83,7 @@ impl Stream for TryChunks { 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 lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { Some(x) => x.checked_add(chunk_len), None => None, From eb223d34a1889afb73ea35252b561fa0243227dc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 6 Jul 2022 03:28:07 +0900 Subject: [PATCH 134/283] Update minimal version of pin-project to 1.0.11 (#2619) --- futures-test/Cargo.toml | 2 +- futures/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index b5aa8a7dd1..90e50ff919 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -19,7 +19,7 @@ futures-executor = { version = "0.3.21", path = "../futures-executor", default-f futures-sink = { version = "0.3.21", path = "../futures-sink", default-features = false } futures-macro = { version = "=0.3.21", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } -pin-project = "1.0.1" +pin-project = "1.0.11" [dev-dependencies] futures = { path = "../futures", default-features = false, features = ["std", "executor"] } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6871f47eaa..b6ff22d387 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -27,7 +27,7 @@ futures-util = { path = "../futures-util", version = "0.3.21", default-features futures-executor = { path = "../futures-executor", features = ["thread-pool"] } futures-test = { path = "../futures-test" } assert_matches = "1.3.0" -pin-project = "1.0.1" +pin-project = "1.0.11" pin-utils = "0.1.0" static_assertions = "1" tokio = "0.1.11" From 724f58d3c02bffb03ac26e4455f31a7cd3de3f50 Mon Sep 17 00:00:00 2001 From: Conor Date: Thu, 7 Jul 2022 11:21:35 +1000 Subject: [PATCH 135/283] Add push_front and push_back to FuturesOrdered (#2591) --- futures-util/src/stream/futures_ordered.rs | 32 +++++++++- futures-util/src/stream/stream/buffered.rs | 2 +- .../src/stream/try_stream/try_buffered.rs | 2 +- futures/tests/stream_futures_ordered.rs | 64 +++++++++++++++++++ 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index f596b3b0e3..f1c93fd683 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -135,11 +135,39 @@ impl FuturesOrdered { /// This function will not call `poll` on the submitted future. The caller /// must ensure that `FuturesOrdered::poll` is called in order to receive /// task notifications. + #[deprecated(note = "use `push_back` instead")] pub fn push(&mut self, future: Fut) { + self.push_back(future); + } + + /// Pushes a future to the back of the queue. + /// + /// This function submits the given future to the internal set for managing. + /// This function will not call `poll` on the submitted future. The caller + /// must ensure that `FuturesOrdered::poll` is called in order to receive + /// task notifications. + pub fn push_back(&mut self, future: Fut) { let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; self.next_incoming_index += 1; self.in_progress_queue.push(wrapped); } + + /// Pushes a future to the front of the queue. + /// + /// This function submits the given future to the internal set for managing. + /// This function will not call `poll` on the submitted future. The caller + /// must ensure that `FuturesOrdered::poll` is called in order to receive + /// task notifications. This future will be the next future to be returned + /// complete. + pub fn push_front(&mut self, future: Fut) { + if self.next_outgoing_index == 0 { + self.push_back(future) + } else { + let wrapped = OrderWrapper { data: future, index: self.next_outgoing_index - 1 }; + self.next_outgoing_index -= 1; + self.in_progress_queue.push(wrapped); + } + } } impl Default for FuturesOrdered { @@ -196,7 +224,7 @@ impl FromIterator for FuturesOrdered { { let acc = Self::new(); iter.into_iter().fold(acc, |mut acc, item| { - acc.push(item); + acc.push_back(item); acc }) } @@ -214,7 +242,7 @@ impl Extend for FuturesOrdered { I: IntoIterator, { for item in iter { - self.push(item); + self.push_back(item); } } } diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 6052a737ba..8ca0391c55 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -64,7 +64,7 @@ where // our queue of futures. while this.in_progress_queue.len() < *this.max { match this.stream.as_mut().poll_next(cx) { - Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut), + Poll::Ready(Some(fut)) => this.in_progress_queue.push_back(fut), Poll::Ready(None) | Poll::Pending => break, } } diff --git a/futures-util/src/stream/try_stream/try_buffered.rs b/futures-util/src/stream/try_stream/try_buffered.rs index 45bd3f8c7a..9f48e5c0a7 100644 --- a/futures-util/src/stream/try_stream/try_buffered.rs +++ b/futures-util/src/stream/try_stream/try_buffered.rs @@ -54,7 +54,7 @@ where // our queue of futures. Propagate errors from the stream immediately. while this.in_progress_queue.len() < *this.max { match this.stream.as_mut().poll_next(cx)? { - Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut.into_future()), + Poll::Ready(Some(fut)) => this.in_progress_queue.push_back(fut.into_future()), Poll::Ready(None) | Poll::Pending => break, } } diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 7506c65a63..8b85a3365a 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -2,6 +2,7 @@ use futures::channel::oneshot; use futures::executor::{block_on, block_on_stream}; use futures::future::{self, join, Future, FutureExt, TryFutureExt}; use futures::stream::{FuturesOrdered, StreamExt}; +use futures::task::Poll; use futures_test::task::noop_context; use std::any::Any; @@ -45,6 +46,69 @@ fn works_2() { assert!(stream.poll_next_unpin(&mut cx).is_ready()); } +#[test] +fn test_push_front() { + let (a_tx, a_rx) = oneshot::channel::(); + let (b_tx, b_rx) = oneshot::channel::(); + let (c_tx, c_rx) = oneshot::channel::(); + let (d_tx, d_rx) = oneshot::channel::(); + + let mut stream = FuturesOrdered::new(); + + let mut cx = noop_context(); + + stream.push_back(a_rx); + stream.push_back(b_rx); + stream.push_back(c_rx); + + a_tx.send(1).unwrap(); + b_tx.send(2).unwrap(); + c_tx.send(3).unwrap(); + + // 1 and 2 should be received in order + assert_eq!(Poll::Ready(Some(Ok(1))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(2))), stream.poll_next_unpin(&mut cx)); + + stream.push_front(d_rx); + d_tx.send(4).unwrap(); + + // we pushed `d_rx` to the front and sent 4, so we should recieve 4 next + // and then 3 after it + assert_eq!(Poll::Ready(Some(Ok(4))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(3))), stream.poll_next_unpin(&mut cx)); +} + +#[test] +fn test_push_back() { + let (a_tx, a_rx) = oneshot::channel::(); + let (b_tx, b_rx) = oneshot::channel::(); + let (c_tx, c_rx) = oneshot::channel::(); + let (d_tx, d_rx) = oneshot::channel::(); + + let mut stream = FuturesOrdered::new(); + + let mut cx = noop_context(); + + stream.push_back(a_rx); + stream.push_back(b_rx); + stream.push_back(c_rx); + + a_tx.send(1).unwrap(); + b_tx.send(2).unwrap(); + c_tx.send(3).unwrap(); + + // All results should be received in order + + assert_eq!(Poll::Ready(Some(Ok(1))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(2))), stream.poll_next_unpin(&mut cx)); + + stream.push_back(d_rx); + d_tx.send(4).unwrap(); + + assert_eq!(Poll::Ready(Some(Ok(3))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(4))), stream.poll_next_unpin(&mut cx)); +} + #[test] fn from_iterator() { let stream = vec![future::ready::(1), future::ready::(2), future::ready::(3)] From a50f5dc213f8c2273d46eecceb0d2bfdc22e29b2 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Thu, 7 Jul 2022 03:52:12 +0200 Subject: [PATCH 136/283] `doc(alias)` `pending()` to `never` (#2613) It can be legitimately to look for the future that _never_ resolves using the name `never`. This PR ensures `pending()` shows up when making such queries. --- futures-util/src/future/pending.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-util/src/future/pending.rs b/futures-util/src/future/pending.rs index 92c78d52b8..b8e28686e1 100644 --- a/futures-util/src/future/pending.rs +++ b/futures-util/src/future/pending.rs @@ -33,6 +33,7 @@ impl FusedFuture for Pending { /// unreachable!(); /// # }); /// ``` +#[cfg_attr(docsrs, doc(alias = "never"))] pub fn pending() -> Pending { assert_future::(Pending { _data: marker::PhantomData }) } From 11c386b0122884520d7705cdb1e2573884b74055 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 9 Jul 2022 15:32:35 +0900 Subject: [PATCH 137/283] Fix miri test failure (#2621) --- futures-test/src/future/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-test/src/future/mod.rs b/futures-test/src/future/mod.rs index ee5c6ddd5d..cf53e871de 100644 --- a/futures-test/src/future/mod.rs +++ b/futures-test/src/future/mod.rs @@ -68,6 +68,7 @@ pub trait FutureTestExt: Future { /// /// assert_eq!(rx.await, Ok(5)); /// # }); + /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed /// ``` fn run_in_background(self) where From 4c096388c6234cb56d6c8e243822900f13cc1212 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Jul 2022 22:10:05 +0900 Subject: [PATCH 138/283] Run more tests with Miri (#2624) --- futures-channel/tests/mpsc.rs | 31 +++++++--------------------- futures-channel/tests/oneshot.rs | 10 ++------- futures-executor/src/thread_pool.rs | 4 ++-- futures-executor/tests/local_pool.rs | 5 +---- futures-test/src/future/mod.rs | 2 +- futures-util/src/task/spawn.rs | 4 ++-- futures/src/lib.rs | 2 +- futures/tests/eventual.rs | 5 +---- futures/tests/lock_mutex.rs | 2 +- futures/tests/ready_queue.rs | 5 +---- futures/tests/task_atomic_waker.rs | 1 - 11 files changed, 19 insertions(+), 52 deletions(-) diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index da0899d491..444c8e10fd 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -200,10 +200,7 @@ fn tx_close_gets_none() { #[test] fn stress_shared_unbounded() { - #[cfg(miri)] - const AMT: u32 = 100; - #[cfg(not(miri))] - const AMT: u32 = 10000; + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; const NTHREADS: u32 = 8; let (tx, rx) = mpsc::unbounded::(); @@ -232,10 +229,7 @@ fn stress_shared_unbounded() { #[test] fn stress_shared_bounded_hard() { - #[cfg(miri)] - const AMT: u32 = 100; - #[cfg(not(miri))] - const AMT: u32 = 10000; + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; const NTHREADS: u32 = 8; let (tx, rx) = mpsc::channel::(0); @@ -265,10 +259,7 @@ fn stress_shared_bounded_hard() { #[allow(clippy::same_item_push)] #[test] fn stress_receiver_multi_task_bounded_hard() { - #[cfg(miri)] - const AMT: usize = 100; - #[cfg(not(miri))] - const AMT: usize = 10_000; + const AMT: usize = if cfg!(miri) { 100 } else { 10_000 }; const NTHREADS: u32 = 2; let (mut tx, rx) = mpsc::channel::(0); @@ -336,10 +327,7 @@ fn stress_receiver_multi_task_bounded_hard() { /// after sender dropped. #[test] fn stress_drop_sender() { - #[cfg(miri)] - const ITER: usize = 100; - #[cfg(not(miri))] - const ITER: usize = 10000; + const ITER: usize = if cfg!(miri) { 100 } else { 10000 }; fn list() -> impl Stream { let (tx, rx) = mpsc::channel(1); @@ -394,10 +382,9 @@ fn stress_close_receiver_iter() { } } -#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn stress_close_receiver() { - const ITER: usize = 10000; + const ITER: usize = if cfg!(miri) { 50 } else { 10000 }; for _ in 0..ITER { stress_close_receiver_iter(); @@ -414,10 +401,7 @@ async fn stress_poll_ready_sender(mut sender: mpsc::Sender, count: u32) { #[allow(clippy::same_item_push)] #[test] fn stress_poll_ready() { - #[cfg(miri)] - const AMT: u32 = 100; - #[cfg(not(miri))] - const AMT: u32 = 1000; + const AMT: u32 = if cfg!(miri) { 100 } else { 1000 }; const NTHREADS: u32 = 8; /// Run a stress test using the specified channel capacity. @@ -444,10 +428,9 @@ fn stress_poll_ready() { stress(16); } -#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn try_send_1() { - const N: usize = 3000; + const N: usize = if cfg!(miri) { 100 } else { 3000 }; let (mut tx, rx) = mpsc::channel(0); let t = thread::spawn(move || { diff --git a/futures-channel/tests/oneshot.rs b/futures-channel/tests/oneshot.rs index c9f5508973..6b48376dc0 100644 --- a/futures-channel/tests/oneshot.rs +++ b/futures-channel/tests/oneshot.rs @@ -35,10 +35,7 @@ fn cancel_notifies() { #[test] fn cancel_lots() { - #[cfg(miri)] - const N: usize = 100; - #[cfg(not(miri))] - const N: usize = 20000; + const N: usize = if cfg!(miri) { 100 } else { 20000 }; let (tx, rx) = mpsc::channel::<(Sender<_>, mpsc::Sender<_>)>(); let t = thread::spawn(move || { @@ -106,10 +103,7 @@ fn is_canceled() { #[test] fn cancel_sends() { - #[cfg(miri)] - const N: usize = 100; - #[cfg(not(miri))] - const N: usize = 20000; + const N: usize = if cfg!(miri) { 100 } else { 20000 }; let (tx, rx) = mpsc::channel::>(); let t = thread::spawn(move || { diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 8c93b476bc..5371008953 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -116,7 +116,7 @@ impl ThreadPool { /// let future = async { /* ... */ }; /// pool.spawn_ok(future); /// # } - /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 /// ``` /// /// > **Note**: This method is similar to `SpawnExt::spawn`, except that @@ -375,6 +375,6 @@ mod tests { let count = rx.into_iter().count(); assert_eq!(count, 2); } - std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } } diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index 6e908d2444..72ce74b744 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -288,10 +288,7 @@ fn run_until_stalled_runs_spawned_sub_futures() { #[test] fn run_until_stalled_executes_all_ready() { - #[cfg(miri)] - const ITER: usize = 50; - #[cfg(not(miri))] - const ITER: usize = 200; + const ITER: usize = if cfg!(miri) { 50 } else { 200 }; const PER_ITER: usize = 3; let cnt = Rc::new(Cell::new(0)); diff --git a/futures-test/src/future/mod.rs b/futures-test/src/future/mod.rs index cf53e871de..0f52f62bb9 100644 --- a/futures-test/src/future/mod.rs +++ b/futures-test/src/future/mod.rs @@ -68,7 +68,7 @@ pub trait FutureTestExt: Future { /// /// assert_eq!(rx.await, Ok(5)); /// # }); - /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 /// ``` fn run_in_background(self) where diff --git a/futures-util/src/task/spawn.rs b/futures-util/src/task/spawn.rs index 8e78717c27..d9e9985309 100644 --- a/futures-util/src/task/spawn.rs +++ b/futures-util/src/task/spawn.rs @@ -43,7 +43,7 @@ pub trait SpawnExt: Spawn { /// let future = async { /* ... */ }; /// executor.spawn(future).unwrap(); /// # } - /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 /// ``` #[cfg(feature = "alloc")] fn spawn(&self, future: Fut) -> Result<(), SpawnError> @@ -72,7 +72,7 @@ pub trait SpawnExt: Spawn { /// let join_handle_fut = executor.spawn_with_handle(future).unwrap(); /// assert_eq!(block_on(join_handle_fut), 1); /// # } - /// # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + /// # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 /// ``` #[cfg(feature = "channel")] #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 3ae9091dca..b972f51754 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -74,7 +74,7 @@ //! //! println!("Values={:?}", values); //! # } -//! # std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed +//! # std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 //! } //! ``` //! diff --git a/futures/tests/eventual.rs b/futures/tests/eventual.rs index 96e21a12a4..951c55c214 100644 --- a/futures/tests/eventual.rs +++ b/futures/tests/eventual.rs @@ -134,10 +134,7 @@ fn select3() { #[test] fn select4() { - #[cfg(miri)] - const N: usize = 100; - #[cfg(not(miri))] - const N: usize = 10000; + const N: usize = if cfg!(miri) { 100 } else { 10000 }; let (tx, rx) = mpsc::channel::>(); diff --git a/futures/tests/lock_mutex.rs b/futures/tests/lock_mutex.rs index 0bd2607565..c15e76bd84 100644 --- a/futures/tests/lock_mutex.rs +++ b/futures/tests/lock_mutex.rs @@ -65,5 +65,5 @@ fn mutex_contested() { assert_eq!(num_tasks, *lock); }); } - std::thread::sleep(std::time::Duration::from_secs(1)); // wait for background threads closed + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } diff --git a/futures/tests/ready_queue.rs b/futures/tests/ready_queue.rs index afba8f28b3..c19d62593c 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -93,10 +93,7 @@ fn dropping_ready_queue() { #[test] fn stress() { - #[cfg(miri)] - const ITER: usize = 30; - #[cfg(not(miri))] - const ITER: usize = 300; + const ITER: usize = if cfg!(miri) { 30 } else { 300 }; for i in 0..ITER { let n = (i % 10) + 1; diff --git a/futures/tests/task_atomic_waker.rs b/futures/tests/task_atomic_waker.rs index 2d1612a45d..cec3db2876 100644 --- a/futures/tests/task_atomic_waker.rs +++ b/futures/tests/task_atomic_waker.rs @@ -6,7 +6,6 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use std::thread; -#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn basic() { let atomic_waker = Arc::new(AtomicWaker::new()); From 936537dfc8ff203adf24a231b9d842a5f759b6fd Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Jul 2022 22:13:46 +0900 Subject: [PATCH 139/283] Remove .clippy.toml Clippy now respects `rust-version` field in Cargo.toml. https://github.com/rust-lang/rust-clippy/commit/b776fb82941cadfc752368901f210831d5184d95 --- .clippy.toml | 1 - .github/workflows/ci.yml | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 .clippy.toml diff --git a/.clippy.toml b/.clippy.toml deleted file mode 100644 index 992016c29a..0000000000 --- a/.clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.36" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c83181a6f..4c909d887d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,8 +69,8 @@ jobs: matrix: rust: # This is the minimum Rust version supported by futures-core, futures-io, futures-sink. - # When updating this, the reminder to update the minimum required version in README.md, Cargo.toml, and .clippy.toml. - - 1.36 + # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. + - '1.36' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -102,7 +102,7 @@ jobs: rust: # This is the minimum Rust version supported by futures, futures-util, futures-task, futures-macro, futures-executor, futures-channel, futures-test. # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - - 1.45 + - '1.45' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 0687c5951bb2ded86ff767e15ae60c052f75c58f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Jul 2022 22:14:05 +0900 Subject: [PATCH 140/283] Remove unnecessarily allowed lints --- futures-util/src/stream/stream/split.rs | 2 +- futures/tests_disabled/stream.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index 3a72fee30b..e2034e0c27 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -35,7 +35,7 @@ impl Stream for SplitStream { } } -#[allow(bad_style)] +#[allow(non_snake_case)] fn SplitSink, Item>(lock: BiLock) -> SplitSink { SplitSink { lock, slot: None } } diff --git a/futures/tests_disabled/stream.rs b/futures/tests_disabled/stream.rs index 854dbad829..a4eec2c7aa 100644 --- a/futures/tests_disabled/stream.rs +++ b/futures/tests_disabled/stream.rs @@ -318,7 +318,6 @@ fn forward() { } #[test] -#[allow(deprecated)] fn concat() { let a = iter_ok::<_, ()>(vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]); assert_done(move || a.concat(), Ok(vec![1, 2, 3, 4, 5, 6, 7, 8, 9])); From aacd605fe52a9b84b0e2fa1cfcde1bf8e6befb2d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Jul 2022 22:14:59 +0900 Subject: [PATCH 141/283] Fix clippy::mem_replace_with_default warning ``` warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/future/select_all.rs:62:28 | 62 | let rest = mem::replace(&mut self.inner, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut self.inner)` | = note: `#[warn(clippy::mem_replace_with_default)]` on by default = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/future/select_ok.rs:62:40 | 62 | ... let rest = mem::replace(&mut self.inner, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut self.inner)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/stream/collect.rs:22:9 | 22 | mem::replace(self.project().collection, Default::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(self.project().collection)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/stream/unzip.rs:24:10 | 24 | (mem::replace(this.left, Default::default()), mem::replace(this.right, Default::default())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.left)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/stream/unzip.rs:24:55 | 24 | (mem::replace(this.left, Default::default()), mem::replace(this.right, Default::default())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.right)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/stream/chunks.rs:69:40 | 69 | let full_buf = mem::replace(this.items, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.items)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/stream/ready_chunks.rs:77:40 | 77 | let full_buf = mem::replace(this.items, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.items)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/try_stream/try_collect.rs:48:31 | 48 | None => break mem::replace(this.items, Default::default()), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.items)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/stream/try_stream/try_chunks.rs:74:40 | 74 | let full_buf = mem::replace(this.items, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.items)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/io/lines.rs:45:29 | 45 | Poll::Ready(Some(Ok(mem::replace(this.buf, String::new())))) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(this.buf)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/io/read_exact.rs:33:33 | 33 | let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut this.buf)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/io/read_line.rs:25:31 | 25 | Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, read: 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(buf)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/io/read_to_string.rs:25:31 | 25 | Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(buf)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures-util/src/io/write_all.rs:33:33 | 33 | let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut this.buf)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default warning: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` --> futures/tests/sink.rs:141:9 | 141 | mem::replace(&mut self.data, Vec::new()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut self.data)` | = note: `#[warn(clippy::mem_replace_with_default)]` on by default = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default ``` --- futures-util/src/future/select_all.rs | 2 +- futures-util/src/future/select_ok.rs | 2 +- futures-util/src/io/lines.rs | 2 +- futures-util/src/io/read_exact.rs | 2 +- futures-util/src/io/read_line.rs | 2 +- futures-util/src/io/read_to_string.rs | 2 +- futures-util/src/io/write_all.rs | 2 +- futures-util/src/stream/stream/chunks.rs | 2 +- futures-util/src/stream/stream/collect.rs | 2 +- futures-util/src/stream/stream/ready_chunks.rs | 2 +- futures-util/src/stream/stream/unzip.rs | 2 +- futures-util/src/stream/try_stream/try_chunks.rs | 2 +- futures-util/src/stream/try_stream/try_collect.rs | 2 +- futures/tests/sink.rs | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/futures-util/src/future/select_all.rs b/futures-util/src/future/select_all.rs index 106e50844c..07d65cae79 100644 --- a/futures-util/src/future/select_all.rs +++ b/futures-util/src/future/select_all.rs @@ -59,7 +59,7 @@ impl Future for SelectAll { match item { Some((idx, res)) => { let _ = self.inner.swap_remove(idx); - let rest = mem::replace(&mut self.inner, Vec::new()); + let rest = mem::take(&mut self.inner); Poll::Ready((res, idx, rest)) } None => Poll::Pending, diff --git a/futures-util/src/future/select_ok.rs b/futures-util/src/future/select_ok.rs index 0ad83c6db6..5d5579930b 100644 --- a/futures-util/src/future/select_ok.rs +++ b/futures-util/src/future/select_ok.rs @@ -59,7 +59,7 @@ impl Future for SelectOk { drop(self.inner.remove(idx)); match res { Ok(e) => { - let rest = mem::replace(&mut self.inner, Vec::new()); + let rest = mem::take(&mut self.inner); return Poll::Ready(Ok((e, rest))); } Err(e) => { diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index 13e70df238..b5561bfa7d 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -42,6 +42,6 @@ impl Stream for Lines { this.buf.pop(); } } - Poll::Ready(Some(Ok(mem::replace(this.buf, String::new())))) + Poll::Ready(Some(Ok(mem::take(this.buf)))) } } diff --git a/futures-util/src/io/read_exact.rs b/futures-util/src/io/read_exact.rs index 02e38c35be..cd0b20e597 100644 --- a/futures-util/src/io/read_exact.rs +++ b/futures-util/src/io/read_exact.rs @@ -30,7 +30,7 @@ impl Future for ReadExact<'_, R> { while !this.buf.is_empty() { let n = ready!(Pin::new(&mut this.reader).poll_read(cx, this.buf))?; { - let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n); + let (_, rest) = mem::take(&mut this.buf).split_at_mut(n); this.buf = rest; } if n == 0 { diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index c75af9471f..e1b8fc9455 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -22,7 +22,7 @@ impl Unpin for ReadLine<'_, R> {} 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: mem::replace(buf, String::new()).into_bytes(), buf, read: 0 } + Self { reader, bytes: mem::take(buf).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 457af59e4f..c175396d81 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -22,7 +22,7 @@ impl Unpin for ReadToString<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToString<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { let start_len = buf.len(); - Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len } + Self { reader, bytes: mem::take(buf).into_bytes(), buf, start_len } } } diff --git a/futures-util/src/io/write_all.rs b/futures-util/src/io/write_all.rs index b134bf1b22..08c025f94d 100644 --- a/futures-util/src/io/write_all.rs +++ b/futures-util/src/io/write_all.rs @@ -30,7 +30,7 @@ impl Future for WriteAll<'_, W> { while !this.buf.is_empty() { let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?; { - let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n); + let (_, rest) = mem::take(&mut this.buf).split_at(n); this.buf = rest; } if n == 0 { diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index e6d9118a0e..8e8ed3bd86 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -66,7 +66,7 @@ impl Stream for Chunks { let last = if this.items.is_empty() { None } else { - let full_buf = mem::replace(this.items, Vec::new()); + let full_buf = mem::take(this.items); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/collect.rs b/futures-util/src/stream/stream/collect.rs index b0e81b9ce0..970ac26dbf 100644 --- a/futures-util/src/stream/stream/collect.rs +++ b/futures-util/src/stream/stream/collect.rs @@ -19,7 +19,7 @@ pin_project! { impl Collect { fn finish(self: Pin<&mut Self>) -> C { - mem::replace(self.project().collection, Default::default()) + mem::take(self.project().collection) } pub(super) fn new(stream: St) -> Self { diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 44edc53521..d3618d81e8 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -74,7 +74,7 @@ impl Stream for ReadyChunks { let last = if this.items.is_empty() { None } else { - let full_buf = mem::replace(this.items, Vec::new()); + let full_buf = mem::take(this.items); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/unzip.rs b/futures-util/src/stream/stream/unzip.rs index 15f22e80b0..a88cf03266 100644 --- a/futures-util/src/stream/stream/unzip.rs +++ b/futures-util/src/stream/stream/unzip.rs @@ -21,7 +21,7 @@ pin_project! { impl Unzip { fn finish(self: Pin<&mut Self>) -> (FromA, FromB) { let this = self.project(); - (mem::replace(this.left, Default::default()), mem::replace(this.right, Default::default())) + (mem::take(this.left), mem::take(this.right)) } pub(super) fn new(stream: St) -> Self { diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs index 5b5ff1d6a6..7626e7124d 100644 --- a/futures-util/src/stream/try_stream/try_chunks.rs +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -70,7 +70,7 @@ impl Stream for TryChunks { let last = if this.items.is_empty() { None } else { - let full_buf = mem::replace(this.items, Vec::new()); + let full_buf = mem::take(this.items); Some(full_buf) }; diff --git a/futures-util/src/stream/try_stream/try_collect.rs b/futures-util/src/stream/try_stream/try_collect.rs index 5d3b3d7668..3e5963f033 100644 --- a/futures-util/src/stream/try_stream/try_collect.rs +++ b/futures-util/src/stream/try_stream/try_collect.rs @@ -45,7 +45,7 @@ where Poll::Ready(Ok(loop { match ready!(this.stream.as_mut().try_poll_next(cx)?) { Some(x) => this.items.extend(Some(x)), - None => break mem::replace(this.items, Default::default()), + None => break mem::take(this.items), } })) } diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index f3cf11b931..5b691e74c6 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -138,7 +138,7 @@ impl ManualFlush { for task in self.waiting_tasks.drain(..) { task.wake() } - mem::replace(&mut self.data, Vec::new()) + mem::take(&mut self.data) } } From 0f4d34cce3fbd5282679ec807acc53673fb4eb83 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Jul 2022 22:43:49 +0900 Subject: [PATCH 142/283] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f76048681..e2c410917b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -192,7 +192,7 @@ NOTE: This release has been yanked. See #2310 for details. NOTE: This release has been yanked. See #2310 for details. -* Fix signature of `LocalSpawn` trait (breaking change -- see #1959) +* Fix signature of `SpawnExt` and `LocalSpawnExt` trait (breaking change -- see #1959) # 0.3.0 - 2019-11-05 From 4145d5936e92214e05ef5d8b69c503bf65dd9bb5 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Sat, 23 Jul 2022 07:18:26 +0200 Subject: [PATCH 143/283] Inline WakerRef functions (#2626) Those functions are trivial and non-generic. It's necessary to use `#[inline]` to enable cross-crate inlining for non-generic functions when LTO is disabled. --- futures-task/src/waker_ref.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/futures-task/src/waker_ref.rs b/futures-task/src/waker_ref.rs index 7fb552fcfd..aac4109577 100644 --- a/futures-task/src/waker_ref.rs +++ b/futures-task/src/waker_ref.rs @@ -18,6 +18,7 @@ pub struct WakerRef<'a> { impl<'a> WakerRef<'a> { /// Create a new [`WakerRef`] from a [`Waker`] reference. + #[inline] pub fn new(waker: &'a Waker) -> Self { // copy the underlying (raw) waker without calling a clone, // as we won't call Waker::drop either. @@ -31,6 +32,7 @@ impl<'a> WakerRef<'a> { /// an unsafe way (that will be valid only for a lifetime to be determined /// by the caller), and the [`Waker`] doesn't need to or must not be /// destroyed. + #[inline] pub fn new_unowned(waker: ManuallyDrop) -> Self { Self { waker, _marker: PhantomData } } @@ -39,6 +41,7 @@ impl<'a> WakerRef<'a> { impl Deref for WakerRef<'_> { type Target = Waker; + #[inline] fn deref(&self) -> &Waker { &self.waker } From 74b1e346386f873fe966620976107ed70232c121 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 30 Jul 2022 13:36:20 +0900 Subject: [PATCH 144/283] Fix CI failure on 1.45 ``` error: failed to get `assert_matches` as a dependency of package `futures v0.4.0-alpha.0 (/home/runner/work/futures-rs/futures-rs/futures)` Caused by: failed to load source for dependency `assert_matches` Caused by: Unable to update registry `https://github.com/rust-lang/crates.io-index` Caused by: failed to fetch `https://github.com/rust-lang/crates.io-index` Caused by: error reading from the zlib stream; class=Zlib (5) ``` --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c909d887d..86cac19362 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,8 @@ jobs: util-msrv: name: cargo +${{ matrix.rust }} build + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true strategy: matrix: rust: From 367dbefd9a767b059b2765af9418ccbd1dd19b91 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 20:09:52 +0900 Subject: [PATCH 145/283] Update actions/checkout action to v3 --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .github/workflows/release.yml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86cac19362..691bf560b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - windows-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update nightly --no-self-update && rustup default nightly @@ -53,7 +53,7 @@ jobs: - aarch64-unknown-linux-gnu runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cross @@ -73,7 +73,7 @@ jobs: - '1.36' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. @@ -107,7 +107,7 @@ jobs: - '1.45' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -138,7 +138,7 @@ jobs: - nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -150,7 +150,7 @@ jobs: name: cargo build -Z minimal-versions runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -172,7 +172,7 @@ jobs: - thumbv6m-none-eabi runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup target add ${{ matrix.target }} @@ -204,7 +204,7 @@ jobs: name: cargo bench runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - run: cargo bench --workspace @@ -214,7 +214,7 @@ jobs: name: cargo hack check --feature-powerset runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -239,7 +239,7 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - run: ci/no_atomic_cas.sh @@ -272,7 +272,7 @@ jobs: name: cargo miri test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - run: cargo miri test --workspace --all-features @@ -291,7 +291,7 @@ jobs: - thread runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup component add rust-src @@ -308,7 +308,7 @@ jobs: # name: cargo clippy # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v2 + # - uses: actions/checkout@v3 # - name: Install Rust # run: rustup toolchain install nightly --component clippy && rustup default nightly # - run: cargo clippy --workspace --all-features --all-targets @@ -317,7 +317,7 @@ jobs: name: cargo fmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable - run: cargo fmt --all -- --check @@ -326,7 +326,7 @@ jobs: name: cargo doc runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --workspace --no-deps --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc9b65bf6e..f10a03263a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: if: github.repository_owner == 'rust-lang' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable - run: cargo build --all From 4d0a2e6867acf6f928777eabf6b0737d80b3e5e7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:01:29 +0900 Subject: [PATCH 146/283] Remove build from release workflow --- .github/workflows/release.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f10a03263a..2c52681b21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,19 +5,12 @@ on: tags: - '[0-9]+.*' -env: - RUSTFLAGS: -D warnings - RUST_BACKTRACE: 1 - jobs: create-release: if: github.repository_owner == 'rust-lang' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install Rust - run: rustup update stable - - run: cargo build --all - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md From 4b159105904267a2beb64c8bd934b332c05e23cc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:03:30 +0900 Subject: [PATCH 147/283] Relax ordering in local_pool.rs --- futures-executor/src/local_pool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index ec1751c7c4..8a9bc2fc90 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -104,7 +104,7 @@ fn run_executor) -> Poll>(mut f: F) -> T { /// Check for a wakeup, but don't consume it. fn woken() -> bool { - CURRENT_THREAD_NOTIFY.with(|thread_notify| thread_notify.unparked.load(Ordering::SeqCst)) + CURRENT_THREAD_NOTIFY.with(|thread_notify| thread_notify.unparked.load(Ordering::Acquire)) } impl LocalPool { From 1d1c1c539dad8d4f58ca4f8dea918ab6741a8c84 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:05:57 +0900 Subject: [PATCH 148/283] Update changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2c410917b..024e1c72ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# 0.3.22 - 2022-08-14 + +* Fix `Sync` impl of `BiLockGuard` (#2570) +* Fix partial iteration in `FuturesUnordered` (#2574) +* Fix false detection of inner panics in `Shared` (#2576) +* Add `Mutex::lock_owned` and `Mutex::try_lock_owned` (#2571) +* Add `io::copy_buf_abortable` (#2507) +* Remove `Unpin` bound from `TryStreamExt::into_async_read` (#2599) +* Make `run_until_stalled` handle self-waking futures (#2593) +* Use `FuturesOrdered` in `try_join_all` (#2556) +* Fix orderings in `LocalPool` waker (#2608) +* Fix `stream::Chunk` adapters size hints (#2611) +* Add `push_front` and `push_back` to `FuturesOrdered` (#2591) +* Deprecate `FuturesOrdered::push` in favor of `FuturesOrdered::push_back` (#2591) +* Performance improvements (#2583, #2626) +* Documentation improvements (#2579, #2604, #2613) + # 0.3.21 - 2022-02-06 * Fix potential data race in `FlattenUnordered` that introduced in 0.3.20 (#2566) From ccc2a24b89ece023111ab2f4e62a8264f1bf0928 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:06:07 +0900 Subject: [PATCH 149/283] Release 0.3.22 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index f356eabd98..93a0ac3bac 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 4a360f8a42..008e9d7149 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index e1ce688581..1ad326b149 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.21", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.22", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 8d446947b1..28e8e2f325 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index a929d0f198..587649600c 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 2c0685ac6b..1272c0cce1 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 77fec6fd92..9195ff1bef 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 90e50ff919..bf03f96aee 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.21", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.21", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.21", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.21", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.21", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.21", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.21", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.22", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.22", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.22", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.22", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.22", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.22", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.22", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index e32b642aa2..dc30caa4fb 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.21", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.21", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.22", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.22", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.22", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index b6ff22d387..1fa61d4a5d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.21" +version = "0.3.22" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.21", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.21", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.21", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.22", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.22", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.22", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.22", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 8db8ead5b326f54e74c3cda9d290899cc1d1e9b4 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:29:30 +0900 Subject: [PATCH 150/283] Use stable toolchain to publish --- ci/publish.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/publish.sh b/ci/publish.sh index 7c8be7926a..9666782732 100755 --- a/ci/publish.sh +++ b/ci/publish.sh @@ -25,7 +25,7 @@ set -x for i in "${!MEMBERS[@]}"; do ( cd "${MEMBERS[${i}]}" - cargo publish + cargo +stable publish ) if [[ $((i + 1)) != "${#MEMBERS[@]}" ]]; then sleep 45 From a1030cf2bed048788f6d5fe013355f209128fb7b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:30:37 +0900 Subject: [PATCH 151/283] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 024e1c72ff..f71c775f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.3.23 - 2022-08-14 + +* Work around MSRV increase due to a cargo bug. + # 0.3.22 - 2022-08-14 * Fix `Sync` impl of `BiLockGuard` (#2570) From 7bff2be5d31ccf5aca14c55db677ee0136682da6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 14 Aug 2022 21:30:44 +0900 Subject: [PATCH 152/283] Release 0.3.23 --- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 93a0ac3bac..7ecc92372e 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 008e9d7149..51acc3ee97 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 1ad326b149..b954d09160 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.22", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.23", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 28e8e2f325..9543b3e4ef 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 587649600c..f533a3dd8a 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 1272c0cce1..27869e50f2 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 9195ff1bef..e558ca373d 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index bf03f96aee..3115a4d125 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.22", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.22", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.22", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.22", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.22", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.22", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.22", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.23", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.23", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.23", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.23", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.23", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.23", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.23", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index dc30caa4fb..bbf90597d8 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.22", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.22", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.22", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.23", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.23", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.23", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 1fa61d4a5d..421e3e065d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.22" +version = "0.3.23" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.22", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.22", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.22", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.22", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.22", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.22", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.22", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.23", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.23", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.23", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.23", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 9ae19f89327fe639f5c7f281254924e0060741b1 Mon Sep 17 00:00:00 2001 From: Spencer Judge Date: Sat, 20 Aug 2022 23:11:24 -0700 Subject: [PATCH 153/283] Fix incorrect termination of `select_with_strategy` streams (#2635) --- .../src/stream/select_with_strategy.rs | 11 +++-- futures/tests/stream.rs | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs index 7423519df1..224d5f821c 100644 --- a/futures-util/src/stream/select_with_strategy.rs +++ b/futures-util/src/stream/select_with_strategy.rs @@ -231,18 +231,23 @@ where St1: Stream, St2: Stream, { - match poll_side(select, side, cx) { + let first_done = match poll_side(select, side, cx) { Poll::Ready(Some(item)) => return Poll::Ready(Some(item)), Poll::Ready(None) => { select.internal_state.finish(side); + true } - Poll::Pending => (), + Poll::Pending => false, }; let other = side.other(); match poll_side(select, other, cx) { Poll::Ready(None) => { select.internal_state.finish(other); - Poll::Ready(None) + if first_done { + Poll::Ready(None) + } else { + Poll::Pending + } } a => a, } diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 71ec654bfb..5cde45833f 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,5 +1,9 @@ +use std::cell::Cell; use std::iter; +use std::pin::Pin; +use std::rc::Rc; use std::sync::Arc; +use std::task::Context; use futures::channel::mpsc; use futures::executor::block_on; @@ -9,6 +13,7 @@ use futures::sink::SinkExt; use futures::stream::{self, StreamExt}; use futures::task::Poll; use futures::{ready, FutureExt}; +use futures_core::Stream; use futures_test::task::noop_context; #[test] @@ -419,3 +424,40 @@ fn ready_chunks() { assert_eq!(s.next().await.unwrap(), vec![4]); }); } + +struct SlowStream { + times_should_poll: usize, + times_polled: Rc>, +} +impl Stream for SlowStream { + type Item = usize; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.times_polled.set(self.times_polled.get() + 1); + if self.times_polled.get() % 2 == 0 { + cx.waker().wake_by_ref(); + return Poll::Pending; + } + if self.times_polled.get() >= self.times_should_poll { + return Poll::Ready(None); + } + Poll::Ready(Some(self.times_polled.get())) + } +} + +#[test] +fn select_with_strategy_doesnt_terminate_early() { + for side in [stream::PollNext::Left, stream::PollNext::Right] { + let times_should_poll = 10; + let count = Rc::new(Cell::new(0)); + let b = stream::iter([10, 20]); + + let mut selected = stream::select_with_strategy( + SlowStream { times_should_poll, times_polled: count.clone() }, + b, + |_: &mut ()| side, + ); + block_on(async move { while selected.next().await.is_some() {} }); + assert_eq!(count.get(), times_should_poll + 1); + } +} From ce6bee08c7927b880fb44caf8529c317a0244ae3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 24 Aug 2022 01:30:36 +0000 Subject: [PATCH 154/283] Update no_atomic_cas.rs --- no_atomic_cas.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 341e6ac7d2..629d3260d4 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -2,6 +2,7 @@ // It is not intended for manual editing. const NO_ATOMIC_CAS: &[&str] = &[ + "armv4t-none-eabi", "avr-unknown-gnu-atmega328", "bpfeb-unknown-none", "bpfel-unknown-none", From 174cb01d5b6af89ff3a960f80994dcda3a6b3f4e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 29 Aug 2022 22:19:29 +0900 Subject: [PATCH 155/283] Release 0.3.24 --- CHANGELOG.md | 4 ++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 39 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f71c775f5d..f479bce0c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.3.24 - 2022-08-29 + +* Fix incorrect termination of `select_with_strategy` streams (#2635) + # 0.3.23 - 2022-08-14 * Work around MSRV increase due to a cargo bug. diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 7ecc92372e..09dbfe693e 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 51acc3ee97..e5846c8815 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index b954d09160..b2ba996511 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.23", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.24", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 9543b3e4ef..0600ac1b07 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index f533a3dd8a..48d6478f2d 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 27869e50f2..5ac760d092 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index e558ca373d..9869aa5737 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 3115a4d125..35f39d5363 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.23", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.23", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.23", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.23", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.23", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.23", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.23", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.24", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.24", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.24", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.24", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.24", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.24", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.24", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index bbf90597d8..c5a9b01c6f 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.23", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.23", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.23", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.24", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.24", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.24", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 421e3e065d..cab5b05de3 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.23" +version = "0.3.24" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.23", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.23", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.23", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.23", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.23", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.23", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.23", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.24", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.24", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.24", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.24", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 0118f5c8f805c887e36c0310d945f37e2e95592e Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Thu, 15 Sep 2022 10:54:28 -0600 Subject: [PATCH 156/283] Fix CI (#2644) --- futures-util/src/stream/stream/chunks.rs | 2 +- futures-util/src/stream/stream/filter.rs | 2 +- futures-util/src/stream/stream/filter_map.rs | 2 +- futures-util/src/stream/stream/peek.rs | 2 +- futures-util/src/stream/stream/ready_chunks.rs | 2 +- futures-util/src/stream/stream/skip_while.rs | 2 +- futures-util/src/stream/stream/take_while.rs | 2 +- futures-util/src/stream/stream/then.rs | 2 +- futures-util/src/stream/stream/zip.rs | 4 ++-- futures-util/src/stream/try_stream/and_then.rs | 2 +- futures-util/src/stream/try_stream/or_else.rs | 2 +- futures-util/src/stream/try_stream/try_chunks.rs | 2 +- futures-util/src/stream/try_stream/try_filter.rs | 2 +- futures-util/src/stream/try_stream/try_filter_map.rs | 2 +- futures-util/src/stream/try_stream/try_skip_while.rs | 2 +- futures-util/src/stream/try_stream/try_take_while.rs | 2 +- futures/tests/async_await_macros.rs | 4 ++-- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index 8e8ed3bd86..385b9b7170 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -77,7 +77,7 @@ impl Stream for Chunks { } fn size_hint(&self) -> (usize, Option) { - let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let chunk_len = usize::from(!self.items.is_empty()); let (lower, upper) = self.stream.size_hint(); let lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index ccf1a5122f..997fe9977e 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -93,7 +93,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_item.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures-util/src/stream/stream/filter_map.rs b/futures-util/src/stream/stream/filter_map.rs index 02a0a4386e..6b7d0070df 100644 --- a/futures-util/src/stream/stream/filter_map.rs +++ b/futures-util/src/stream/stream/filter_map.rs @@ -87,7 +87,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index c72dfc3666..ea3d6243f3 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -204,7 +204,7 @@ impl Stream for Peekable { } fn size_hint(&self) -> (usize, Option) { - let peek_len = if self.peeked.is_some() { 1 } else { 0 }; + let peek_len = usize::from(self.peeked.is_some()); let (lower, upper) = self.stream.size_hint(); let lower = lower.saturating_add(peek_len); let upper = match upper { diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index d3618d81e8..93efed9d5d 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -85,7 +85,7 @@ impl Stream for ReadyChunks { } fn size_hint(&self) -> (usize, Option) { - let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let chunk_len = usize::from(!self.items.is_empty()); let (lower, upper) = self.stream.size_hint(); let lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { diff --git a/futures-util/src/stream/stream/skip_while.rs b/futures-util/src/stream/stream/skip_while.rs index 50a21a21ae..dabd5eefae 100644 --- a/futures-util/src/stream/stream/skip_while.rs +++ b/futures-util/src/stream/stream/skip_while.rs @@ -99,7 +99,7 @@ where if self.done_skipping { self.stream.size_hint() } else { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_item.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures-util/src/stream/stream/take_while.rs b/futures-util/src/stream/stream/take_while.rs index 01b27654b8..9256943010 100644 --- a/futures-util/src/stream/stream/take_while.rs +++ b/futures-util/src/stream/stream/take_while.rs @@ -91,7 +91,7 @@ where return (0, Some(0)); } - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_item.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures-util/src/stream/stream/then.rs b/futures-util/src/stream/stream/then.rs index d4531d4b94..9192c0b0cf 100644 --- a/futures-util/src/stream/stream/then.rs +++ b/futures-util/src/stream/stream/then.rs @@ -78,7 +78,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let future_len = if self.future.is_some() { 1 } else { 0 }; + let future_len = usize::from(self.future.is_some()); let (lower, upper) = self.stream.size_hint(); let lower = lower.saturating_add(future_len); let upper = match upper { diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index 360a8b63bb..25a47e96be 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -102,8 +102,8 @@ where } fn size_hint(&self) -> (usize, Option) { - let queued1_len = if self.queued1.is_some() { 1 } else { 0 }; - let queued2_len = if self.queued2.is_some() { 1 } else { 0 }; + let queued1_len = usize::from(self.queued1.is_some()); + let queued2_len = usize::from(self.queued2.is_some()); let (stream1_lower, stream1_upper) = self.stream1.size_hint(); let (stream2_lower, stream2_upper) = self.stream2.size_hint(); diff --git a/futures-util/src/stream/try_stream/and_then.rs b/futures-util/src/stream/try_stream/and_then.rs index a7b50db0b1..2f8b6f2589 100644 --- a/futures-util/src/stream/try_stream/and_then.rs +++ b/futures-util/src/stream/try_stream/and_then.rs @@ -71,7 +71,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let future_len = if self.future.is_some() { 1 } else { 0 }; + let future_len = usize::from(self.future.is_some()); let (lower, upper) = self.stream.size_hint(); let lower = lower.saturating_add(future_len); let upper = match upper { diff --git a/futures-util/src/stream/try_stream/or_else.rs b/futures-util/src/stream/try_stream/or_else.rs index cb69e81323..53aceb8e64 100644 --- a/futures-util/src/stream/try_stream/or_else.rs +++ b/futures-util/src/stream/try_stream/or_else.rs @@ -75,7 +75,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let future_len = if self.future.is_some() { 1 } else { 0 }; + let future_len = usize::from(self.future.is_some()); let (lower, upper) = self.stream.size_hint(); let lower = lower.saturating_add(future_len); let upper = match upper { diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs index 7626e7124d..3bb253a714 100644 --- a/futures-util/src/stream/try_stream/try_chunks.rs +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -81,7 +81,7 @@ impl Stream for TryChunks { } fn size_hint(&self) -> (usize, Option) { - let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let chunk_len = usize::from(!self.items.is_empty()); let (lower, upper) = self.stream.size_hint(); let lower = (lower / self.cap).saturating_add(chunk_len); let upper = match upper { diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 61e6105c37..11d58243fd 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -90,7 +90,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_fut.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_fut.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), 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 bb1b5b9db6..ed1201732b 100644 --- a/futures-util/src/stream/try_stream/try_filter_map.rs +++ b/futures-util/src/stream/try_stream/try_filter_map.rs @@ -84,7 +84,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), 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 a424b6c5b1..52aa2d478b 100644 --- a/futures-util/src/stream/try_stream/try_skip_while.rs +++ b/futures-util/src/stream/try_stream/try_skip_while.rs @@ -87,7 +87,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_item.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures-util/src/stream/try_stream/try_take_while.rs b/futures-util/src/stream/try_stream/try_take_while.rs index 3375960ef4..4b5ff1ad38 100644 --- a/futures-util/src/stream/try_stream/try_take_while.rs +++ b/futures-util/src/stream/try_stream/try_take_while.rs @@ -96,7 +96,7 @@ where return (0, Some(0)); } - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let pending_len = usize::from(self.pending_item.is_some()); let (_, upper) = self.stream.size_hint(); let upper = match upper { Some(x) => x.checked_add(pending_len), diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index ce1f3a3379..8c51d66234 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -352,14 +352,14 @@ fn join_size() { let ready = future::ready(0i32); join!(ready) }; - assert_eq!(mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 12); let fut = async { let ready1 = future::ready(0i32); let ready2 = future::ready(0i32); join!(ready1, ready2) }; - assert_eq!(mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 20); } #[test] From 8d2f3854545a0fbd3d6f8b56e33bd09f0297430d Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Fri, 16 Sep 2022 01:38:20 +0800 Subject: [PATCH 157/283] Remove some double trait bounds (#2645) Signed-off-by: TennyZhuang --- futures-util/src/stream/stream/buffer_unordered.rs | 6 +----- futures-util/src/stream/stream/chunks.rs | 5 +---- futures-util/src/stream/stream/ready_chunks.rs | 5 +---- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/futures-util/src/stream/stream/buffer_unordered.rs b/futures-util/src/stream/stream/buffer_unordered.rs index d64c142b41..91b0f6bcce 100644 --- a/futures-util/src/stream/stream/buffer_unordered.rs +++ b/futures-util/src/stream/stream/buffer_unordered.rs @@ -41,11 +41,7 @@ where St: Stream, St::Item: Future, { - pub(super) fn new(stream: St, n: usize) -> Self - where - St: Stream, - St::Item: Future, - { + pub(super) fn new(stream: St, n: usize) -> Self { Self { stream: super::Fuse::new(stream), in_progress_queue: FuturesUnordered::new(), diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index 385b9b7170..2a71ebc6cc 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -21,10 +21,7 @@ pin_project! { } } -impl Chunks -where - St: Stream, -{ +impl Chunks { pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 93efed9d5d..49116d49e7 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -20,10 +20,7 @@ pin_project! { } } -impl ReadyChunks -where - St: Stream, -{ +impl ReadyChunks { pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); From 624a7ec91719034858a631e7bf01290bd8716bd8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 22 Sep 2022 01:30:25 +0000 Subject: [PATCH 158/283] Update no_atomic_cas.rs --- no_atomic_cas.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 629d3260d4..16ec628cdf 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -3,6 +3,7 @@ const NO_ATOMIC_CAS: &[&str] = &[ "armv4t-none-eabi", + "armv5te-none-eabi", "avr-unknown-gnu-atmega328", "bpfeb-unknown-none", "bpfel-unknown-none", @@ -11,5 +12,6 @@ const NO_ATOMIC_CAS: &[&str] = &[ "riscv32im-unknown-none-elf", "riscv32imc-unknown-none-elf", "thumbv4t-none-eabi", + "thumbv5te-none-eabi", "thumbv6m-none-eabi", ]; From 485875f10044386f1ef8925a3880f889a2ff731e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 10 Oct 2022 17:42:03 +0900 Subject: [PATCH 159/283] Ignore unreachable_pub lint ``` warning: unreachable `pub` item --> futures-util/src/stream/futures_unordered/mod.rs:25:32 | 25 | pub use self::iter::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; | --- ^^^^ | | | help: consider restricting its visibility: `pub(crate)` | = help: or consider exporting it for use by other crates note: the lint level is defined here --> futures-util/src/lib.rs:11:5 | 11 | unreachable_pub | ^^^^^^^^^^^^^^^ warning: unreachable `pub` item --> futures-util/src/stream/futures_unordered/mod.rs:25:38 | 25 | pub use self::iter::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; | --- ^^^^^^^ | | | help: consider restricting its visibility: `pub(crate)` | = help: or consider exporting it for use by other crates ``` --- futures-util/src/stream/futures_unordered/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index fdbd53de8e..5e995fdb81 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -22,6 +22,7 @@ use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError}; mod abort; mod iter; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/102352 pub use self::iter::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; mod task; From 5364e6c086c246da25cd71b32814db21e77b1afd Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 10 Oct 2022 17:43:12 +0900 Subject: [PATCH 160/283] Fix clippy::unnecessary_cast warning ``` warning: casting to the same type is unnecessary (`usize` -> `usize`) --> futures-util/src/stream/stream/take.rs:57:37 | 57 | let lower = cmp::min(lower, self.remaining as usize); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self.remaining` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: `#[warn(clippy::unnecessary_cast)]` on by default warning: casting to the same type is unnecessary (`usize` -> `usize`) --> futures-util/src/stream/stream/take.rs:60:28 | 60 | Some(x) if x < self.remaining as usize => Some(x), | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self.remaining` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast warning: casting to the same type is unnecessary (`usize` -> `usize`) --> futures-util/src/stream/stream/take.rs:61:23 | 61 | _ => Some(self.remaining as usize), | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self.remaining` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast ``` --- futures-util/src/stream/stream/take.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/stream/take.rs b/futures-util/src/stream/stream/take.rs index b1c728e333..29d6c39ee3 100644 --- a/futures-util/src/stream/stream/take.rs +++ b/futures-util/src/stream/stream/take.rs @@ -54,11 +54,11 @@ where let (lower, upper) = self.stream.size_hint(); - let lower = cmp::min(lower, self.remaining as usize); + let lower = cmp::min(lower, self.remaining); let upper = match upper { - Some(x) if x < self.remaining as usize => Some(x), - _ => Some(self.remaining as usize), + Some(x) if x < self.remaining => Some(x), + _ => Some(self.remaining), }; (lower, upper) From 521f2ae50b5225167920ff746510642ec4caeded Mon Sep 17 00:00:00 2001 From: stephaneyfx Date: Mon, 10 Oct 2022 10:25:33 -0500 Subject: [PATCH 161/283] Implement `Clone` for `Drain` (#2650) --- futures-util/src/sink/drain.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/futures-util/src/sink/drain.rs b/futures-util/src/sink/drain.rs index 5295115b66..1a5480c0d6 100644 --- a/futures-util/src/sink/drain.rs +++ b/futures-util/src/sink/drain.rs @@ -32,6 +32,12 @@ pub fn drain() -> Drain { impl Unpin for Drain {} +impl Clone for Drain { + fn clone(&self) -> Self { + drain() + } +} + impl Sink for Drain { type Error = Never; From 44dc9ef033ecae40b8705d17020673b98729d2e1 Mon Sep 17 00:00:00 2001 From: Pointerbender <81013316+Pointerbender@users.noreply.github.com> Date: Thu, 13 Oct 2022 05:31:19 +0200 Subject: [PATCH 162/283] Fix soundness hole in join macros (#2649) * fix soundness hole in join macros add a miri regression test update failing tests (join sizes increased due to fix) * fix `CI / cross test` by ignoring `join_size` and `try_join_size` tests on "non-64-bit pointer" targets (e.g. `i686-unknown-linux-gnu`) (this is the same fix that was also applied in PR #2447) --- futures-macro/src/join.rs | 13 ++++++------ futures/tests/async_await_macros.rs | 10 +++++---- futures/tests/future_join.rs | 32 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 futures/tests/future_join.rs diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index d427da27a0..94e356f729 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -38,6 +38,7 @@ fn bind_futures(fut_exprs: Vec, span: Span) -> (Vec, Vec TokenStream { let poll_futures = future_names.iter().map(|fut| { quote! { __all_done &= __futures_crate::future::Future::poll( - unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); + #fut.as_mut(), __cx).is_ready(); } }); let take_outputs = future_names.iter().map(|fut| { quote! { - unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), + #fut.as_mut().take_output().unwrap(), } }); @@ -96,17 +97,17 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { let poll_futures = future_names.iter().map(|fut| { quote! { if __futures_crate::future::Future::poll( - unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() + #fut.as_mut(), __cx).is_pending() { __all_done = false; - } else if unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { + } else if #fut.as_mut().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::task::Poll::Ready( __futures_crate::Err( - unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() + #fut.as_mut().take_output().unwrap().err().unwrap() ) ); } @@ -118,7 +119,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::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), + #fut.as_mut().take_output().unwrap().ok().unwrap(), } }); diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index 8c51d66234..250e345305 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -346,36 +346,38 @@ fn stream_select() { }); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn join_size() { let fut = async { let ready = future::ready(0i32); join!(ready) }; - assert_eq!(mem::size_of_val(&fut), 12); + assert_eq!(mem::size_of_val(&fut), 24); let fut = async { let ready1 = future::ready(0i32); let ready2 = future::ready(0i32); join!(ready1, ready2) }; - assert_eq!(mem::size_of_val(&fut), 20); + assert_eq!(mem::size_of_val(&fut), 40); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn try_join_size() { let fut = async { let ready = future::ready(Ok::(0)); try_join!(ready) }; - assert_eq!(mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 24); let fut = async { let ready1 = future::ready(Ok::(0)); let ready2 = future::ready(Ok::(0)); try_join!(ready1, ready2) }; - assert_eq!(mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 48); } #[test] diff --git a/futures/tests/future_join.rs b/futures/tests/future_join.rs new file mode 100644 index 0000000000..f5df9d7775 --- /dev/null +++ b/futures/tests/future_join.rs @@ -0,0 +1,32 @@ +use futures::executor::block_on; +use futures::future::Future; +use std::task::Poll; + +/// This tests verifies (through miri) that self-referencing +/// futures are not invalidated when joining them. +#[test] +fn futures_join_macro_self_referential() { + block_on(async { futures::join!(yield_now(), trouble()) }); +} + +async fn trouble() { + let lucky_number = 42; + let problematic_variable = &lucky_number; + + yield_now().await; + + // problematic dereference + let _ = { *problematic_variable }; +} + +fn yield_now() -> impl Future { + let mut yielded = false; + std::future::poll_fn(move |cx| { + if core::mem::replace(&mut yielded, true) { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + }) +} From 77d82198c5afd04af3e760a6aa50b7e875289fc3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 20 Oct 2022 11:55:03 +0900 Subject: [PATCH 163/283] Release 0.3.25 --- CHANGELOG.md | 5 +++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 40 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f479bce0c7..4e01f89481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.3.25 - 2022-10-20 + +* Fix soundness issue in `join!` and `try_join!` macros (#2649) +* Implement `Clone` for `sink::Drain` (#2650) + # 0.3.24 - 2022-08-29 * Fix incorrect termination of `select_with_strategy` streams (#2635) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 09dbfe693e..eef8684f8a 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index e5846c8815..f80c1a4952 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index b2ba996511..beaa843f3a 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.24", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.25", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 0600ac1b07..4360b4f904 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 48d6478f2d..b2b21c2575 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 5ac760d092..cbd4a33d4e 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 9869aa5737..67af1cc894 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 35f39d5363..bd9fd223b3 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.24", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.24", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.24", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.24", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.24", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.24", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.24", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.25", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.25", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.25", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.25", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.25", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.25", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.25", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index c5a9b01c6f..aeecf0f372 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -34,12 +34,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.24", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.24", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.24", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.25", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.25", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.25", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index cab5b05de3..544a1e3869 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.24" +version = "0.3.25" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.24", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.24", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.24", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.24", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.24", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.24", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.24", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.25", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.25", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.25", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.25", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 669af283d9f3323db2ffd4b663818dc0ff3e306c Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sat, 22 Oct 2022 12:03:17 +0100 Subject: [PATCH 164/283] Do not store items field in ReadyChunks (#2656) `items` is always empty when `poll_ready` exists, so there's no reason to store it inside `ReadyChunks`. This makes code a bit more efficient, but also makes code easier to understand. --- .../src/stream/stream/ready_chunks.rs | 44 ++++++------------- futures/tests/auto_traits.rs | 4 +- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 49116d49e7..ab8637f27a 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -1,6 +1,5 @@ use crate::stream::Fuse; use alloc::vec::Vec; -use core::mem; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; @@ -15,7 +14,6 @@ pin_project! { pub struct ReadyChunks { #[pin] stream: Fuse, - items: Vec, cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 } } @@ -24,11 +22,7 @@ impl ReadyChunks { pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); - Self { - stream: super::Fuse::new(stream), - items: Vec::with_capacity(capacity), - cap: capacity, - } + Self { stream: super::Fuse::new(stream), cap: capacity } } delegate_access_inner!(stream, St, (.)); @@ -40,40 +34,33 @@ impl Stream for ReadyChunks { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.project(); + let mut items: Vec = Vec::new(); + loop { match this.stream.as_mut().poll_next(cx) { // Flush all collected data if underlying stream doesn't contain // more ready values Poll::Pending => { - return if this.items.is_empty() { - Poll::Pending - } else { - Poll::Ready(Some(mem::replace(this.items, Vec::with_capacity(*this.cap)))) - } + return if items.is_empty() { Poll::Pending } else { Poll::Ready(Some(items)) } } // 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)) => { - this.items.push(item); - if this.items.len() >= *this.cap { - return Poll::Ready(Some(mem::replace( - this.items, - Vec::with_capacity(*this.cap), - ))); + if items.is_empty() { + items.reserve(*this.cap); + } + items.push(item); + if items.len() >= *this.cap { + return Poll::Ready(Some(items)); } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. Poll::Ready(None) => { - let last = if this.items.is_empty() { - None - } else { - let full_buf = mem::take(this.items); - Some(full_buf) - }; + let last = if items.is_empty() { None } else { Some(items) }; return Poll::Ready(last); } @@ -82,20 +69,15 @@ impl Stream for ReadyChunks { } fn size_hint(&self) -> (usize, Option) { - let chunk_len = usize::from(!self.items.is_empty()); let (lower, upper) = self.stream.size_hint(); - let lower = (lower / self.cap).saturating_add(chunk_len); - let upper = match upper { - Some(x) => x.checked_add(chunk_len), - None => None, - }; + let lower = lower / self.cap; (lower, upper) } } impl FusedStream for ReadyChunks { fn is_terminated(&self) -> bool { - self.stream.is_terminated() && self.items.is_empty() + self.stream.is_terminated() } } diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index da00ccf40c..5fc0f7d675 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -1480,10 +1480,10 @@ pub mod stream { assert_not_impl!(PollImmediate: Unpin); assert_impl!(ReadyChunks>: Send); - assert_not_impl!(ReadyChunks: Send); + assert_impl!(ReadyChunks: Send); assert_not_impl!(ReadyChunks: Send); assert_impl!(ReadyChunks>: Sync); - assert_not_impl!(ReadyChunks: Sync); + assert_impl!(ReadyChunks: Sync); assert_not_impl!(ReadyChunks: Sync); assert_impl!(ReadyChunks: Unpin); assert_not_impl!(ReadyChunks: Unpin); From 0473f38b07174edc21c47313458c25c1f9175f2f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 25 Oct 2022 23:22:22 +0900 Subject: [PATCH 165/283] Fix clippy::uninlined_format_args warning (#2659) ``` error: variables can be used directly in the `format!` string --> examples/functional/src/main.rs:45:5 | 45 | println!("Values={:?}", values); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `-D clippy::uninlined-format-args` implied by `-D warnings` help: change this to | 45 - println!("Values={:?}", values); 45 + println!("Values={values:?}"); | ``` --- examples/functional/src/main.rs | 2 +- examples/imperative/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/functional/src/main.rs b/examples/functional/src/main.rs index 2ed8b37c58..d535a61b45 100644 --- a/examples/functional/src/main.rs +++ b/examples/functional/src/main.rs @@ -42,5 +42,5 @@ fn main() { // to drive all futures. Eventually fut_values will be driven to completion. let values: Vec = executor::block_on(fut_values); - println!("Values={:?}", values); + println!("Values={values:?}"); } diff --git a/examples/imperative/src/main.rs b/examples/imperative/src/main.rs index 44f4153cd9..1c952537bf 100644 --- a/examples/imperative/src/main.rs +++ b/examples/imperative/src/main.rs @@ -44,5 +44,5 @@ fn main() { // to drive all futures. Eventually fut_values will be driven to completion. let values: Vec = executor::block_on(fut_values); - println!("Values={:?}", values); + println!("Values={values:?}"); } From 3fa8b295dd2dd3e2ac7b27ce2176d745797a22a2 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 2 Nov 2022 05:46:33 +0000 Subject: [PATCH 166/283] Do not require Clone for Shared::peek (#2662) Makes it easier to write some generic code for wrappers of `Shared` future: no need to properly specify all type parameters. --- futures-util/src/future/future/shared.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 985931564b..e1ffb32c92 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -103,7 +103,6 @@ impl Shared { impl Shared where Fut: Future, - Fut::Output: Clone, { /// Returns [`Some`] containing a reference to this [`Shared`]'s output if /// it has already been computed by a clone or [`None`] if it hasn't been @@ -160,7 +159,6 @@ where impl Inner where Fut: Future, - Fut::Output: Clone, { /// Safety: callers must first ensure that `self.inner.state` /// is `COMPLETE` @@ -170,6 +168,13 @@ where FutureOrOutput::Future(_) => unreachable!(), } } +} + +impl Inner +where + Fut: Future, + Fut::Output: Clone, +{ /// Registers the current task to receive a wakeup when we are awoken. fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) { let mut wakers_guard = self.notifier.wakers.lock().unwrap(); From fe554a6705ec96fcc4506b1431a2f2114a77fc4c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 20 Nov 2022 18:46:07 +0900 Subject: [PATCH 167/283] Fix miri test failure (#2665) --- futures/tests/eventual.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/futures/tests/eventual.rs b/futures/tests/eventual.rs index 951c55c214..57a49b2417 100644 --- a/futures/tests/eventual.rs +++ b/futures/tests/eventual.rs @@ -16,6 +16,8 @@ fn join1() { run(future::try_join(ok::(1), ok(2)).map_ok(move |v| tx.send(v).unwrap())); assert_eq!(rx.recv(), Ok((1, 2))); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -30,6 +32,8 @@ fn join2() { c2.send(2).unwrap(); assert_eq!(rx.recv(), Ok((1, 2))); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -43,6 +47,8 @@ fn join3() { assert_eq!(rx.recv(), Ok(1)); assert!(rx.recv().is_err()); drop(c2); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -56,6 +62,8 @@ fn join4() { assert!(rx.recv().is_ok()); drop(c2); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -73,6 +81,8 @@ fn join5() { c3.send(3).unwrap(); assert_eq!(rx.recv(), Ok(((1, 2), 3))); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -92,6 +102,8 @@ fn select1() { c2.send(2).unwrap(); assert_eq!(rx.recv(), Ok(2)); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -111,6 +123,8 @@ fn select2() { c2.send(2).unwrap(); assert_eq!(rx.recv(), Ok(2)); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -130,6 +144,8 @@ fn select3() { drop(c2); assert_eq!(rx.recv(), Ok(2)); assert!(rx.recv().is_err()); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } #[test] @@ -158,4 +174,6 @@ fn select4() { drop(tx); t.join().unwrap(); + + std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371 } From 9e5767847eeb99942411b9eeceabb5babe0462cf Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 27 Nov 2022 17:07:55 +0900 Subject: [PATCH 168/283] Ignore clippy::unnecessary_safety_doc lint ``` warning: safe function's docs have unnecessary `# Safety` section --> futures-task/src/future_obj.rs:151:5 | 151 | fn into_raw(self) -> *mut (dyn Future + 'a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc = note: `#[warn(clippy::unnecessary_safety_doc)]` on by default warning: safe function's docs have unnecessary `# Safety` section --> futures-util/src/future/future/shared.rs:141:5 | 141 | pub fn strong_count(&self) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc = note: `#[warn(clippy::unnecessary_safety_doc)]` on by default warning: safe function's docs have unnecessary `# Safety` section --> futures-util/src/future/future/shared.rs:154:5 | 154 | pub fn weak_count(&self) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc ``` --- futures-task/src/future_obj.rs | 1 + futures-util/src/future/future/shared.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index f67494adb6..071392af6c 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -149,6 +149,7 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { /// provided `*mut (dyn Future + 'a)` into a `Pin<&mut (dyn /// Future + 'a)>` and call methods on it, non-reentrantly, /// until `UnsafeFutureObj::drop` is called with it. + #[allow(clippy::unnecessary_safety_doc)] fn into_raw(self) -> *mut (dyn Future + 'a); /// Drops the future represented by the given fat pointer. diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index e1ffb32c92..0a5256c52c 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -138,6 +138,7 @@ where /// This method by itself is safe, but using it correctly requires extra care. Another thread /// can change the strong count at any time, including potentially between calling this method /// and acting on the result. + #[allow(clippy::unnecessary_safety_doc)] pub fn strong_count(&self) -> Option { self.inner.as_ref().map(|arc| Arc::strong_count(arc)) } @@ -151,6 +152,7 @@ where /// This method by itself is safe, but using it correctly requires extra care. Another thread /// can change the weak count at any time, including potentially between calling this method /// and acting on the result. + #[allow(clippy::unnecessary_safety_doc)] pub fn weak_count(&self) -> Option { self.inner.as_ref().map(|arc| Arc::weak_count(arc)) } From d48c40e1ebaa2b65e4b1074da85990654ba5f7b1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 27 Nov 2022 17:08:35 +0900 Subject: [PATCH 169/283] Ignore clippy::let_underscore_future lint ``` warning: non-binding `let` on a future --> futures-util/src/future/select_all.rs:61:17 | 61 | let _ = self.inner.swap_remove(idx); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future = note: `#[warn(clippy::let_underscore_future)]` on by default warning: non-binding `let` on a future --> futures/tests/async_await_macros.rs:385:5 | 385 | let _ = async { join!(async {}, async {}) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future = note: `#[warn(clippy::let_underscore_future)]` on by default warning: non-binding `let` on a future --> futures/tests/async_await_macros.rs:390:5 | 390 | let _ = async { try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) },) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future ``` --- futures-util/src/future/select_all.rs | 1 + futures/tests/async_await_macros.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/futures-util/src/future/select_all.rs b/futures-util/src/future/select_all.rs index 07d65cae79..0a51d0da6c 100644 --- a/futures-util/src/future/select_all.rs +++ b/futures-util/src/future/select_all.rs @@ -58,6 +58,7 @@ impl Future for SelectAll { }); match item { Some((idx, res)) => { + #[allow(clippy::let_underscore_future)] let _ = self.inner.swap_remove(idx); let rest = mem::take(&mut self.inner); Poll::Ready((res, idx, rest)) diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index 250e345305..82a617f2c2 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -380,11 +380,13 @@ fn try_join_size() { assert_eq!(mem::size_of_val(&fut), 48); } +#[allow(clippy::let_underscore_future)] #[test] fn join_doesnt_require_unpin() { let _ = async { join!(async {}, async {}) }; } +#[allow(clippy::let_underscore_future)] #[test] fn try_join_doesnt_require_unpin() { let _ = async { try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) },) }; From d62eb6f023f839bdd38fab95a61724594ad35d0b Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 27 Nov 2022 09:41:25 +0100 Subject: [PATCH 170/283] Custom `Debug` implementations for `mpsc` (#2667) --- futures-channel/src/mpsc/mod.rs | 47 ++++++++++++++++++++++++------- futures-channel/src/mpsc/queue.rs | 2 -- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 44834b7c95..5d994bf8e7 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -94,13 +94,11 @@ mod queue; #[cfg(feature = "sink")] mod sink_impl; -#[derive(Debug)] struct UnboundedSenderInner { // Channel state shared between the sender and receiver. inner: Arc>, } -#[derive(Debug)] struct BoundedSenderInner { // Channel state shared between the sender and receiver. inner: Arc>, @@ -122,13 +120,11 @@ impl Unpin for BoundedSenderInner {} /// The transmission end of a bounded mpsc channel. /// /// This value is created by the [`channel`](channel) function. -#[derive(Debug)] pub struct Sender(Option>); /// The transmission end of an unbounded mpsc channel. /// /// This value is created by the [`unbounded`](unbounded) function. -#[derive(Debug)] pub struct UnboundedSender(Option>); trait AssertKinds: Send + Sync + Clone {} @@ -137,7 +133,6 @@ impl AssertKinds for UnboundedSender {} /// The receiving end of a bounded mpsc channel. /// /// This value is created by the [`channel`](channel) function. -#[derive(Debug)] pub struct Receiver { inner: Option>>, } @@ -145,7 +140,6 @@ pub struct Receiver { /// The receiving end of an unbounded mpsc channel. /// /// This value is created by the [`unbounded`](unbounded) function. -#[derive(Debug)] pub struct UnboundedReceiver { inner: Option>>, } @@ -261,7 +255,6 @@ impl fmt::Display for TryRecvError { impl std::error::Error for TryRecvError {} -#[derive(Debug)] struct UnboundedInner { // Internal channel state. Consists of the number of messages stored in the // channel as well as a flag signalling that the channel is closed. @@ -277,7 +270,6 @@ struct UnboundedInner { recv_task: AtomicWaker, } -#[derive(Debug)] struct BoundedInner { // Max buffer size of the channel. If `None` then the channel is unbounded. buffer: usize, @@ -300,7 +292,7 @@ struct BoundedInner { } // Struct representation of `Inner::state`. -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] struct State { // `true` when the channel is open is_open: bool, @@ -324,7 +316,6 @@ const MAX_CAPACITY: usize = !(OPEN_MASK); const MAX_BUFFER: usize = MAX_CAPACITY >> 1; // Sent to the consumer to wake up blocked producers -#[derive(Debug)] struct SenderTask { task: Option, is_parked: bool, @@ -947,6 +938,18 @@ impl Drop for BoundedSenderInner { } } +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").field("closed", &self.is_closed()).finish() + } +} + +impl fmt::Debug for UnboundedSender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UnboundedSender").field("closed", &self.is_closed()).finish() + } +} + /* * * ===== impl Receiver ===== @@ -1107,6 +1110,18 @@ impl Drop for Receiver { } } +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let closed = if let Some(ref inner) = self.inner { + decode_state(inner.state.load(SeqCst)).is_closed() + } else { + false + }; + + f.debug_struct("Receiver").field("closed", &closed).finish() + } +} + impl UnboundedReceiver { /// Closes the receiving half of a channel, without dropping it. /// @@ -1239,6 +1254,18 @@ impl Drop for UnboundedReceiver { } } +impl fmt::Debug for UnboundedReceiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let closed = if let Some(ref inner) = self.inner { + decode_state(inner.state.load(SeqCst)).is_closed() + } else { + false + }; + + f.debug_struct("Receiver").field("closed", &closed).finish() + } +} + /* * * ===== impl Inner ===== diff --git a/futures-channel/src/mpsc/queue.rs b/futures-channel/src/mpsc/queue.rs index 57dc7f5654..02ec633fe0 100644 --- a/futures-channel/src/mpsc/queue.rs +++ b/futures-channel/src/mpsc/queue.rs @@ -61,7 +61,6 @@ pub(super) enum PopResult { Inconsistent, } -#[derive(Debug)] struct Node { next: AtomicPtr, value: Option, @@ -70,7 +69,6 @@ struct Node { /// The multi-producer single-consumer structure. This is not cloneable, but it /// may be safely shared so long as it is guaranteed that there is only one /// popper at a time (many pushers are allowed). -#[derive(Debug)] pub(super) struct Queue { head: AtomicPtr>, tail: UnsafeCell<*mut Node>, From 5da7a6322056edea89624972b9772f67dddd136f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 27 Nov 2022 09:41:44 +0100 Subject: [PATCH 171/283] Remove `Debug` constraint for `oneshot` types (#2666) --- futures-channel/src/oneshot.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index 5af651b913..70449f43d6 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -390,7 +390,7 @@ impl Drop for Sender { } } -impl fmt::Debug for Sender { +impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Sender").field("complete", &self.inner.complete).finish() } @@ -481,7 +481,7 @@ impl Drop for Receiver { } } -impl fmt::Debug for Receiver { +impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Receiver").field("complete", &self.inner.complete).finish() } From da7592cae0c3756436b72debabbf7276f00f4d95 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sun, 27 Nov 2022 08:44:37 +0000 Subject: [PATCH 172/283] Stream::size_hint for mpsc channels (#2660) --- futures-channel/src/mpsc/mod.rs | 24 +++++++++++++++ futures-channel/tests/mpsc-size_hint.rs | 40 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 futures-channel/tests/mpsc-size_hint.rs diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 5d994bf8e7..cf45fe77fe 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -1078,6 +1078,14 @@ impl Stream for Receiver { } } } + + fn size_hint(&self) -> (usize, Option) { + if let Some(inner) = &self.inner { + decode_state(inner.state.load(SeqCst)).size_hint() + } else { + (0, Some(0)) + } + } } impl Drop for Receiver { @@ -1222,6 +1230,14 @@ impl Stream for UnboundedReceiver { } } } + + fn size_hint(&self) -> (usize, Option) { + if let Some(inner) = &self.inner { + decode_state(inner.state.load(SeqCst)).size_hint() + } else { + (0, Some(0)) + } + } } impl Drop for UnboundedReceiver { @@ -1312,6 +1328,14 @@ impl State { fn is_closed(&self) -> bool { !self.is_open && self.num_messages == 0 } + + fn size_hint(&self) -> (usize, Option) { + if self.is_open { + (self.num_messages, None) + } else { + (self.num_messages, Some(self.num_messages)) + } + } } /* diff --git a/futures-channel/tests/mpsc-size_hint.rs b/futures-channel/tests/mpsc-size_hint.rs new file mode 100644 index 0000000000..d9cdaa31fa --- /dev/null +++ b/futures-channel/tests/mpsc-size_hint.rs @@ -0,0 +1,40 @@ +use futures::channel::mpsc; +use futures::stream::Stream; + +#[test] +fn unbounded_size_hint() { + let (tx, mut rx) = mpsc::unbounded::(); + assert_eq!((0, None), rx.size_hint()); + tx.unbounded_send(1).unwrap(); + assert_eq!((1, None), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((0, None), rx.size_hint()); + tx.unbounded_send(2).unwrap(); + tx.unbounded_send(3).unwrap(); + assert_eq!((2, None), rx.size_hint()); + drop(tx); + assert_eq!((2, Some(2)), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((1, Some(1)), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((0, Some(0)), rx.size_hint()); +} + +#[test] +fn channel_size_hint() { + let (mut tx, mut rx) = mpsc::channel::(10); + assert_eq!((0, None), rx.size_hint()); + tx.try_send(1).unwrap(); + assert_eq!((1, None), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((0, None), rx.size_hint()); + tx.try_send(2).unwrap(); + tx.try_send(3).unwrap(); + assert_eq!((2, None), rx.size_hint()); + drop(tx); + assert_eq!((2, Some(2)), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((1, Some(1)), rx.size_hint()); + rx.try_next().unwrap().unwrap(); + assert_eq!((0, Some(0)), rx.size_hint()); +} From 4d09f7e603c9ca2790e6a963f7a7dabd1363d500 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Wed, 7 Dec 2022 09:07:15 -0800 Subject: [PATCH 173/283] Enable an option for portable-atomic (#2670) --- futures-core/Cargo.toml | 1 + futures-core/src/task/__internal/atomic_waker.rs | 11 +++++++++-- futures-util/Cargo.toml | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index f80c1a4952..9770f7eb60 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -21,6 +21,7 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] +portable-atomic = { version = "0.3.15", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures" } diff --git a/futures-core/src/task/__internal/atomic_waker.rs b/futures-core/src/task/__internal/atomic_waker.rs index d49d043619..0677e83b09 100644 --- a/futures-core/src/task/__internal/atomic_waker.rs +++ b/futures-core/src/task/__internal/atomic_waker.rs @@ -1,9 +1,16 @@ use core::cell::UnsafeCell; use core::fmt; -use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use core::task::Waker; +use atomic::AtomicUsize; +use atomic::Ordering::{AcqRel, Acquire, Release}; + +#[cfg(feature = "portable-atomic")] +use portable_atomic as atomic; + +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic; + /// A synchronization primitive for task wakeup. /// /// Sometimes the task interested in a given event will change over time. diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index aeecf0f372..da2bc5a55b 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -21,6 +21,7 @@ io-compat = ["io", "compat", "tokio-io"] sink = ["futures-sink"] io = ["std", "futures-io", "memchr"] channel = ["std", "futures-channel"] +portable-atomic = ["futures-core/portable-atomic"] # Unstable features # These features are outside of the normal semver guarantees and require the From b8ef941132bb5f88de586c0926d480a18a9bb9d6 Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Fri, 16 Dec 2022 23:30:26 -0800 Subject: [PATCH 174/283] Fix build status badge (#2675) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e6127fd6f8..45e1f5b320 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,16 @@

- Build Status + Build Status - Crates.io + crates.io

- + Documentation | Website From dca8ffa9c4fb109adbca2018ef251185fdeb1596 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Sun, 18 Dec 2022 17:49:23 -0800 Subject: [PATCH 175/283] impl FusedStream for Buffered (#2676) --- futures-util/src/stream/stream/buffered.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 8ca0391c55..5854eb7ea5 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -1,4 +1,4 @@ -use crate::stream::{Fuse, FuturesOrdered, StreamExt}; +use crate::stream::{Fuse, FusedStream, FuturesOrdered, StreamExt}; use core::fmt; use core::pin::Pin; use futures_core::future::Future; @@ -95,6 +95,16 @@ where } } +impl FusedStream for Buffered +where + St: Stream, + St::Item: Future, +{ + fn is_terminated(&self) -> bool { + self.stream.is_done() && self.in_progress_queue.is_terminated() + } +} + // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Buffered From 039a78112f0d1fedc7f809afc239e2fa64204377 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sat, 31 Dec 2022 21:51:19 -0500 Subject: [PATCH 176/283] improve `try_join_all` and `FuturesUnordered` docs (#2679) --- futures-util/src/future/try_join_all.rs | 14 ++++++++++++++ futures-util/src/stream/futures_ordered.rs | 2 +- futures-util/src/stream/futures_unordered/mod.rs | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 25fcfcb6c2..506f450657 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -77,6 +77,20 @@ where /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. /// +/// # See Also +/// +/// `try_join_all` will switch to the more powerful [`FuturesOrdered`] for performance +/// reasons if the number of futures is large. You may want to look into using it or +/// it's counterpart [`FuturesUnordered`][crate::stream::FuturesUnordered] directly. +/// +/// Some examples for additional functionality provided by these are: +/// +/// * Adding new futures to the set even after it has been started. +/// +/// * Only polling the specific futures that have been woken. In cases where +/// you have a lot of futures this will result in much more efficient polling. +/// +/// /// # Examples /// /// ``` diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index f1c93fd683..3daf537afd 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -58,7 +58,7 @@ where /// An unbounded queue of futures. /// -/// This "combinator" is similar to `FuturesUnordered`, but it imposes an order +/// This "combinator" is similar to [`FuturesUnordered`], but it imposes a FIFO order /// on top of the set of futures. While futures in the set will race to /// completion in parallel, results will only be returned in the order their /// originating futures were added to the queue. diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 5e995fdb81..6b5804dc41 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -33,6 +33,9 @@ use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue}; /// A set of futures which may complete in any order. /// +/// See [`FuturesOrdered`](crate::stream::FuturesOrdered) for a version of this +/// type that preserves a FIFO order. +/// /// This structure is optimized to manage a large number of futures. /// Futures managed by [`FuturesUnordered`] will only be polled when they /// generate wake-up notifications. This reduces the required amount of work From 98479929492384e87d2b537b0d988d1b4f049e5d Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 31 Dec 2022 21:55:26 -0500 Subject: [PATCH 177/283] Add `ptr_eq` and `ptr_hash` to `Shared` (#2673) --- futures-util/src/future/future/shared.rs | 31 ++++++++++++++++ futures/tests/future_shared.rs | 46 ++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 0a5256c52c..ecd1b426db 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -4,7 +4,9 @@ use futures_core::task::{Context, Poll, Waker}; use slab::Slab; use std::cell::UnsafeCell; use std::fmt; +use std::hash::Hasher; use std::pin::Pin; +use std::ptr; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::{Acquire, SeqCst}; use std::sync::{Arc, Mutex, Weak}; @@ -156,6 +158,35 @@ where pub fn weak_count(&self) -> Option { self.inner.as_ref().map(|arc| Arc::weak_count(arc)) } + + /// Hashes the internal state of this `Shared` in a way that's compatible with `ptr_eq`. + pub fn ptr_hash(&self, state: &mut H) { + match self.inner.as_ref() { + Some(arc) => { + state.write_u8(1); + ptr::hash(Arc::as_ptr(arc), state); + } + None => { + state.write_u8(0); + } + } + } + + /// Returns `true` if the two `Shared`s point to the same future (in a vein similar to + /// `Arc::ptr_eq`). + /// + /// Returns `false` if either `Shared` has terminated. + pub fn ptr_eq(&self, rhs: &Self) -> bool { + let lhs = match self.inner.as_ref() { + Some(lhs) => lhs, + None => return false, + }; + let rhs = match rhs.inner.as_ref() { + Some(rhs) => rhs, + None => return false, + }; + Arc::ptr_eq(lhs, rhs) + } } impl Inner diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs index 6bf43d23cf..bd69c1d7c1 100644 --- a/futures/tests/future_shared.rs +++ b/futures/tests/future_shared.rs @@ -151,6 +151,52 @@ fn downgrade() { assert!(shared2.downgrade().is_none()); } +#[test] +fn ptr_eq() { + use future::FusedFuture; + use std::collections::hash_map::DefaultHasher; + use std::hash::Hasher; + + let (tx, rx) = oneshot::channel::(); + let shared = rx.shared(); + let mut shared2 = shared.clone(); + let mut hasher = DefaultHasher::new(); + let mut hasher2 = DefaultHasher::new(); + + // Because these two futures share the same underlying future, + // `ptr_eq` should return true. + assert!(shared.ptr_eq(&shared2)); + // Equivalence relations are symmetric + assert!(shared2.ptr_eq(&shared)); + + // If `ptr_eq` returns true, they should hash to the same value. + shared.ptr_hash(&mut hasher); + shared2.ptr_hash(&mut hasher2); + assert_eq!(hasher.finish(), hasher2.finish()); + + tx.send(42).unwrap(); + assert_eq!(block_on(&mut shared2).unwrap(), 42); + + // Now that `shared2` has completed, `ptr_eq` should return false. + assert!(shared2.is_terminated()); + assert!(!shared.ptr_eq(&shared2)); + + // `ptr_eq` should continue to work for the other `Shared`. + let shared3 = shared.clone(); + let mut hasher3 = DefaultHasher::new(); + assert!(shared.ptr_eq(&shared3)); + + shared3.ptr_hash(&mut hasher3); + assert_eq!(hasher.finish(), hasher3.finish()); + + let (_tx, rx) = oneshot::channel::(); + let shared4 = rx.shared(); + + // And `ptr_eq` should return false for two futures that don't share + // the underlying future. + assert!(!shared.ptr_eq(&shared4)); +} + #[test] fn dont_clone_in_single_owner_shared_future() { let counter = CountClone(Rc::new(Cell::new(0))); From d8beb28d04e3b65b156f573fe340688019cf5541 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Tue, 10 Jan 2023 00:44:32 +0800 Subject: [PATCH 178/283] Set to `None` only if necessary (#2683) --- futures-util/src/stream/stream/chain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/futures-util/src/stream/stream/chain.rs b/futures-util/src/stream/stream/chain.rs index c5da35e25e..36ff1e533d 100644 --- a/futures-util/src/stream/stream/chain.rs +++ b/futures-util/src/stream/stream/chain.rs @@ -50,8 +50,9 @@ where if let Some(item) = ready!(first.poll_next(cx)) { return Poll::Ready(Some(item)); } + + this.first.set(None); } - this.first.set(None); this.second.poll_next(cx) } From eb680703f2878175c4cc9f5a7650607a8b3e5bfa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 11 Jan 2023 01:29:06 +0000 Subject: [PATCH 179/283] Update no_atomic_cas.rs --- no_atomic_cas.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 16ec628cdf..80a18cf335 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -5,8 +5,6 @@ const NO_ATOMIC_CAS: &[&str] = &[ "armv4t-none-eabi", "armv5te-none-eabi", "avr-unknown-gnu-atmega328", - "bpfeb-unknown-none", - "bpfel-unknown-none", "msp430-none-elf", "riscv32i-unknown-none-elf", "riscv32im-unknown-none-elf", From 71f7b78db3fd13ff9ed6f77c0a2666f14e3aa3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Er=C3=A8be=20-=20Romain=20Gerard?= Date: Sun, 15 Jan 2023 01:32:14 +0100 Subject: [PATCH 180/283] fix panic when Unfold sink return an error (#2686) - fix issue #2600. When an Unfold sink return an error it is left in an invalid state. Calling after that flush/close cause the sink to panic due to re-calling a future already completed. This patch aims to leave the sink in a valid state and allow calling flush/close without causing a panic. --- futures-util/src/sink/unfold.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/futures-util/src/sink/unfold.rs b/futures-util/src/sink/unfold.rs index 330a068c31..dea1307b66 100644 --- a/futures-util/src/sink/unfold.rs +++ b/futures-util/src/sink/unfold.rs @@ -73,7 +73,10 @@ where this.state.set(UnfoldState::Value { value: state }); Ok(()) } - Err(err) => Err(err), + Err(err) => { + this.state.set(UnfoldState::Empty); + Err(err) + } } } else { Ok(()) From 1340b16e71caa0c33f6f0e9fae4c0b17b217fe9d Mon Sep 17 00:00:00 2001 From: jongiddy Date: Sun, 15 Jan 2023 06:44:12 +0000 Subject: [PATCH 181/283] Remove incorrect error behavior from `ready_chunks` docs (#2687) Unlike `TryStream::try_chunks`, which has similar documentation and can handle `Some(Err(_))` specially, `Stream::ready_chunks` does not and cannot know whether the stream item represents an error condition, and does not include any code to return early on an error. --- futures-util/src/stream/stream/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index a823fab123..bb5e24907d 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -1513,8 +1513,7 @@ pub trait StreamExt: Stream { /// 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. + /// it will be returned. /// /// This method is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. From 03edb0f7b3942f8aa6a5cc90b267651b0d46e9e2 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 15 Jan 2023 09:50:10 +0000 Subject: [PATCH 182/283] Fix bug in FuturesOrdered::push_front (#2664) --- futures-util/src/stream/futures_ordered.rs | 16 ++++++--------- futures/tests/stream_futures_ordered.rs | 24 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 3daf537afd..618bf1b7bd 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -19,7 +19,7 @@ pin_project! { struct OrderWrapper { #[pin] data: T, // A future or a future's output - index: usize, + index: isize, } } @@ -95,8 +95,8 @@ where pub struct FuturesOrdered { in_progress_queue: FuturesUnordered>, queued_outputs: BinaryHeap>, - next_incoming_index: usize, - next_outgoing_index: usize, + next_incoming_index: isize, + next_outgoing_index: isize, } impl Unpin for FuturesOrdered {} @@ -160,13 +160,9 @@ impl FuturesOrdered { /// task notifications. This future will be the next future to be returned /// complete. pub fn push_front(&mut self, future: Fut) { - if self.next_outgoing_index == 0 { - self.push_back(future) - } else { - let wrapped = OrderWrapper { data: future, index: self.next_outgoing_index - 1 }; - self.next_outgoing_index -= 1; - self.in_progress_queue.push(wrapped); - } + let wrapped = OrderWrapper { data: future, index: self.next_outgoing_index - 1 }; + self.next_outgoing_index -= 1; + self.in_progress_queue.push(wrapped); } } diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 8b85a3365a..5a4a3e22ee 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -146,3 +146,27 @@ fn queue_never_unblocked() { assert!(stream.poll_next_unpin(cx).is_pending()); assert!(stream.poll_next_unpin(cx).is_pending()); } + +#[test] +fn test_push_front_negative() { + let (a_tx, a_rx) = oneshot::channel::(); + let (b_tx, b_rx) = oneshot::channel::(); + let (c_tx, c_rx) = oneshot::channel::(); + + let mut stream = FuturesOrdered::new(); + + let mut cx = noop_context(); + + stream.push_front(a_rx); + stream.push_front(b_rx); + stream.push_front(c_rx); + + a_tx.send(1).unwrap(); + b_tx.send(2).unwrap(); + c_tx.send(3).unwrap(); + + // These should all be recieved in reverse order + assert_eq!(Poll::Ready(Some(Ok(3))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(2))), stream.poll_next_unpin(&mut cx)); + assert_eq!(Poll::Ready(Some(Ok(1))), stream.poll_next_unpin(&mut cx)); +} From f5733c93cdcc240fe50141449e3d6ff18acfb1d7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 15 Jan 2023 19:04:19 +0900 Subject: [PATCH 183/283] Update portable-atomic to 1.0 (#2688) --- futures-core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 9770f7eb60..5440b85b5c 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -21,7 +21,7 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -portable-atomic = { version = "0.3.15", default-features = false, optional = true } +portable-atomic = { version = "1", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures" } From bf3504bbd9b1080e3bdb5988b8c573fbf3b93a7d Mon Sep 17 00:00:00 2001 From: jongiddy Date: Fri, 20 Jan 2023 12:22:01 +0000 Subject: [PATCH 184/283] Implement `FusedStream` for all streams in `ReadyChunks` (#2693) `ReadyChunks` fuses the inner stream, so `FusedStream` can be implemented for all stream types, not just those that initially implement `FusedStream`. --- futures-util/src/stream/stream/ready_chunks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index ab8637f27a..192054c4ae 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -1,4 +1,4 @@ -use crate::stream::Fuse; +use crate::stream::{Fuse, StreamExt}; use alloc::vec::Vec; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; @@ -22,7 +22,7 @@ impl ReadyChunks { pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); - Self { stream: super::Fuse::new(stream), cap: capacity } + Self { stream: stream.fuse(), cap: capacity } } delegate_access_inner!(stream, St, (.)); @@ -75,7 +75,7 @@ impl Stream for ReadyChunks { } } -impl FusedStream for ReadyChunks { +impl FusedStream for ReadyChunks { fn is_terminated(&self) -> bool { self.stream.is_terminated() } From c88ad9b9dc628617105370b4ba143c63e15a6b2c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 21 Jan 2023 12:38:12 +1100 Subject: [PATCH 185/283] Add `Either::as_pin_mut` and `Either::as_pin_ref` (#2691) --- futures-util/src/future/either.rs | 58 +++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 9602de7a42..27e5064dfb 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -33,11 +33,31 @@ pub enum Either { } impl Either { - fn project(self: Pin<&mut Self>) -> Either, Pin<&mut B>> { + /// Convert `Pin<&Either>` to `Either, Pin<&B>>`, + /// pinned projections of the inner variants. + pub fn as_pin_ref(self: Pin<&Self>) -> Either, Pin<&B>> { + // SAFETY: We can use `new_unchecked` because the `inner` parts are + // guaranteed to be pinned, as they come from `self` which is pinned. unsafe { - match self.get_unchecked_mut() { - Either::Left(a) => Either::Left(Pin::new_unchecked(a)), - Either::Right(b) => Either::Right(Pin::new_unchecked(b)), + match *Pin::get_ref(self) { + Either::Left(ref inner) => Either::Left(Pin::new_unchecked(inner)), + Either::Right(ref inner) => Either::Right(Pin::new_unchecked(inner)), + } + } + } + + /// Convert `Pin<&mut Either>` to `Either, Pin<&mut B>>`, + /// pinned projections of the inner variants. + pub fn as_pin_mut(self: Pin<&mut Self>) -> Either, Pin<&mut B>> { + // SAFETY: `get_unchecked_mut` is fine because we don't move anything. + // We can use `new_unchecked` because the `inner` parts are guaranteed + // to be pinned, as they come from `self` which is pinned, and we never + // offer an unpinned `&mut A` or `&mut B` through `Pin<&mut Self>`. We + // also don't have an implementation of `Drop`, nor manual `Unpin`. + unsafe { + match *Pin::get_unchecked_mut(self) { + Either::Left(ref mut inner) => Either::Left(Pin::new_unchecked(inner)), + Either::Right(ref mut inner) => Either::Right(Pin::new_unchecked(inner)), } } } @@ -85,7 +105,7 @@ where type Output = A::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll(cx), Either::Right(x) => x.poll(cx), } @@ -113,7 +133,7 @@ where type Item = A::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_next(cx), Either::Right(x) => x.poll_next(cx), } @@ -149,28 +169,28 @@ where type Error = A::Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_ready(cx), Either::Right(x) => x.poll_ready(cx), } } fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.start_send(item), Either::Right(x) => x.start_send(item), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_flush(cx), Either::Right(x) => x.poll_flush(cx), } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_close(cx), Either::Right(x) => x.poll_close(cx), } @@ -198,7 +218,7 @@ mod if_std { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_read(cx, buf), Either::Right(x) => x.poll_read(cx, buf), } @@ -209,7 +229,7 @@ mod if_std { cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_read_vectored(cx, bufs), Either::Right(x) => x.poll_read_vectored(cx, bufs), } @@ -226,7 +246,7 @@ mod if_std { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_write(cx, buf), Either::Right(x) => x.poll_write(cx, buf), } @@ -237,21 +257,21 @@ mod if_std { cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_write_vectored(cx, bufs), Either::Right(x) => x.poll_write_vectored(cx, bufs), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_flush(cx), Either::Right(x) => x.poll_flush(cx), } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_close(cx), Either::Right(x) => x.poll_close(cx), } @@ -268,7 +288,7 @@ mod if_std { cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_seek(cx, pos), Either::Right(x) => x.poll_seek(cx, pos), } @@ -281,14 +301,14 @@ mod if_std { B: AsyncBufRead, { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.poll_fill_buf(cx), Either::Right(x) => x.poll_fill_buf(cx), } } fn consume(self: Pin<&mut Self>, amt: usize) { - match self.project() { + match self.as_pin_mut() { Either::Left(x) => x.consume(amt), Either::Right(x) => x.consume(amt), } From cbba4e04852f5a5e672b06df43dfdda0e7572b27 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 24 Jan 2023 01:29:05 +0000 Subject: [PATCH 186/283] Update no_atomic_cas.rs --- no_atomic_cas.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs index 80a18cf335..16ec628cdf 100644 --- a/no_atomic_cas.rs +++ b/no_atomic_cas.rs @@ -5,6 +5,8 @@ const NO_ATOMIC_CAS: &[&str] = &[ "armv4t-none-eabi", "armv5te-none-eabi", "avr-unknown-gnu-atmega328", + "bpfeb-unknown-none", + "bpfel-unknown-none", "msp430-none-elf", "riscv32i-unknown-none-elf", "riscv32im-unknown-none-elf", From 5e3693a350f96244151081d2c030208cd15f9572 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 31 Jan 2023 00:49:08 +0900 Subject: [PATCH 187/283] Release 0.3.26 --- CHANGELOG.md | 10 ++++++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 45 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e01f89481..dcea5404ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 0.3.26 - 2023-01-30 + +* Add `Either::as_pin_mut` and `Either::as_pin_ref` (#2691) +* Add `Shared::ptr_eq` and `Shared::ptr_hash` (#2691) +* Implement `FusedStream` for `Buffered` (#2676) +* Implement `FusedStream` for all streams in `ReadyChunks` (#2693) +* Fix bug in `FuturesOrdered::push_front` (#2664) +* Remove `Fut::Output: Clone` bounds from some `Shared` methods (#2662) +* Remove `T: Debug` bounds from `Debug` implementations of `mpsc` and `oneshot` types (#2666, #2667) + # 0.3.25 - 2022-10-20 * Fix soundness issue in `join!` and `try_join!` macros (#2649) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index eef8684f8a..753fd46ac3 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 5440b85b5c..5b78edc285 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index beaa843f3a..3ee30591f2 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.25", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.26", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 4360b4f904..775be9b42a 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index b2b21c2575..5e2ed173a0 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index cbd4a33d4e..29174ac68a 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 67af1cc894..4c2d1df632 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index bd9fd223b3..e2ae339d3e 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.25", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.25", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.25", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.25", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.25", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.25", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.25", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.26", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.26", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.26", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.26", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.26", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.26", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.26", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index da2bc5a55b..95c3dee8e7 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.25", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.25", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.25", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.26", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.26", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.26", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 544a1e3869..e7a5f386a0 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.25" +version = "0.3.26" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.25", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.25", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.25", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.25", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.25", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.25", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.25", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.26", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.26", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.26", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.26", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From a1d09a11c0de3d2df4ddb8b1e7918f96003d4fa5 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sat, 16 Apr 2022 20:03:47 +0400 Subject: [PATCH 188/283] Add `TryFlattenUnordered` and improve `FlattenUnordered` (#2577) --- futures-util/benches/flatten_unordered.rs | 7 +- futures-util/src/future/try_select.rs | 13 +- futures-util/src/stream/mod.rs | 15 +- .../src/stream/stream/flatten_unordered.rs | 123 +++++++--------- futures-util/src/stream/stream/mod.rs | 15 +- futures-util/src/stream/try_stream/mod.rs | 65 +++++++++ .../src/stream/try_stream/try_chunks.rs | 5 +- .../try_stream/try_flatten_unordered.rs | 133 ++++++++++++++++++ futures/tests/stream_try_stream.rs | 41 ++++++ 9 files changed, 326 insertions(+), 91 deletions(-) create mode 100644 futures-util/src/stream/try_stream/try_flatten_unordered.rs diff --git a/futures-util/benches/flatten_unordered.rs b/futures-util/benches/flatten_unordered.rs index 64d5f9a4e3..b92f614914 100644 --- a/futures-util/benches/flatten_unordered.rs +++ b/futures-util/benches/flatten_unordered.rs @@ -5,7 +5,7 @@ use crate::test::Bencher; use futures::channel::oneshot; use futures::executor::block_on; -use futures::future::{self, FutureExt}; +use futures::future; use futures::stream::{self, StreamExt}; use futures::task::Poll; use std::collections::VecDeque; @@ -35,15 +35,14 @@ fn oneshot_streams(b: &mut Bencher) { }); let mut flatten = stream::unfold(rxs.into_iter(), |mut vals| { - async { + Box::pin(async { if let Some(next) = vals.next() { let val = next.await.unwrap(); Some((val, vals)) } else { None } - } - .boxed() + }) }) .flatten_unordered(None); diff --git a/futures-util/src/future/try_select.rs b/futures-util/src/future/try_select.rs index 4d0b7ff135..bc282f7db1 100644 --- a/futures-util/src/future/try_select.rs +++ b/futures-util/src/future/try_select.rs @@ -12,6 +12,9 @@ pub struct TrySelect { impl Unpin for TrySelect {} +type EitherOk = Either<(::Ok, B), (::Ok, A)>; +type EitherErr = Either<(::Error, B), (::Error, A)>; + /// Waits for either one of two differently-typed futures to complete. /// /// This function will return a new future which awaits for either one of both @@ -52,10 +55,9 @@ where A: TryFuture + Unpin, B: TryFuture + Unpin, { - super::assert_future::< - Result, Either<(A::Error, B), (B::Error, A)>>, - _, - >(TrySelect { inner: Some((future1, future2)) }) + super::assert_future::, EitherErr>, _>(TrySelect { + inner: Some((future1, future2)), + }) } impl Future for TrySelect @@ -63,8 +65,7 @@ where A: TryFuture, B: TryFuture, { - #[allow(clippy::type_complexity)] - type Output = Result, Either<(A::Error, B), (B::Error, A)>>; + type Output = Result, EitherErr>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let (mut a, mut b) = self.inner.take().expect("cannot poll Select twice"); diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index ec685b9848..bf9506147c 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -18,9 +18,10 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; #[allow(clippy::module_inception)] mod stream; pub use self::stream::{ - Chain, Collect, Concat, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, - Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, PeekMut, Peekable, Scan, SelectNextSome, - Skip, SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, + All, Any, Chain, Collect, Concat, Count, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, + Fold, ForEach, Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, PeekMut, Peekable, Scan, + SelectNextSome, Skip, SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, + Unzip, Zip, }; #[cfg(feature = "std")] @@ -38,7 +39,9 @@ pub use self::stream::Forward; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] -pub use self::stream::{BufferUnordered, Buffered, ForEachConcurrent}; +pub use self::stream::{ + BufferUnordered, Buffered, FlatMapUnordered, FlattenUnordered, ForEachConcurrent, +}; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "sink")] @@ -60,7 +63,9 @@ pub use self::try_stream::IntoAsyncRead; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] -pub use self::try_stream::{TryBufferUnordered, TryBuffered, TryForEachConcurrent}; +pub use self::try_stream::{ + TryBufferUnordered, TryBuffered, TryFlattenUnordered, TryForEachConcurrent, +}; #[cfg(feature = "alloc")] pub use self::try_stream::{TryChunks, TryChunksError}; diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 07f971c55a..66ba4d0d55 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -22,8 +22,7 @@ use futures_task::{waker, ArcWake}; use crate::stream::FuturesUnordered; -/// There is nothing to poll and stream isn't being -/// polled or waking at the moment. +/// There is nothing to poll and stream isn't being polled/waking/woken at the moment. const NONE: u8 = 0; /// Inner streams need to be polled. @@ -32,26 +31,19 @@ const NEED_TO_POLL_INNER_STREAMS: u8 = 1; /// The base stream needs to be polled. const NEED_TO_POLL_STREAM: u8 = 0b10; -/// It needs to poll base stream and inner streams. +/// Both base stream and inner streams need to be polled. const NEED_TO_POLL_ALL: u8 = NEED_TO_POLL_INNER_STREAMS | NEED_TO_POLL_STREAM; /// The current stream is being polled at the moment. const POLLING: u8 = 0b100; -/// Inner streams are being woken at the moment. -const WAKING_INNER_STREAMS: u8 = 0b1000; - -/// The base stream is being woken at the moment. -const WAKING_STREAM: u8 = 0b10000; - -/// The base stream and inner streams are being woken at the moment. -const WAKING_ALL: u8 = WAKING_STREAM | WAKING_INNER_STREAMS; +/// Stream is being woken at the moment. +const WAKING: u8 = 0b1000; /// The stream was waked and will be polled. -const WOKEN: u8 = 0b100000; +const WOKEN: u8 = 0b10000; -/// Determines what needs to be polled, and is stream being polled at the -/// moment or not. +/// Internal polling state of the stream. #[derive(Clone, Debug)] struct SharedPollState { state: Arc, @@ -71,7 +63,7 @@ impl SharedPollState { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - if value & WAKING_ALL == NONE { + if value & WAKING == NONE { Some(POLLING) } else { None @@ -83,23 +75,20 @@ impl SharedPollState { Some((value, bomb)) } - /// Starts the waking process and performs bitwise or with the given value. + /// Attempts to start the waking process and performs bitwise or with the given value. + /// + /// If some waker is already in progress or stream is already woken/being polled, waking process won't start, however + /// state will be disjuncted with the given value. fn start_waking( &self, to_poll: u8, - waking: u8, ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - // Waking process for this waker already started - if value & waking != NONE { - return None; - } let mut next_value = value | to_poll; - // Only start the waking process if we're not in the polling phase and the stream isn't woken already if value & (WOKEN | POLLING) == NONE { - next_value |= waking; + next_value |= WAKING; } if next_value != value { @@ -110,8 +99,9 @@ impl SharedPollState { }) .ok()?; - if value & (WOKEN | POLLING) == NONE { - let bomb = PollStateBomb::new(self, move |state| state.stop_waking(waking)); + // Only start the waking process if we're not in the polling phase and the stream isn't woken already + if value & (WOKEN | POLLING | WAKING) == NONE { + let bomb = PollStateBomb::new(self, SharedPollState::stop_waking); Some((value, bomb)) } else { @@ -123,7 +113,7 @@ impl SharedPollState { /// - `!POLLING` allowing to use wakers /// - `WOKEN` if the state was changed during `POLLING` phase as waker will be called, /// or `will_be_woken` flag supplied - /// - `!WAKING_ALL` as + /// - `!WAKING` as /// * Wakers called during the `POLLING` phase won't propagate their calls /// * `POLLING` phase can't start if some of the wakers are active /// So no wrapped waker can touch the inner waker's cell, it's safe to poll again. @@ -138,20 +128,16 @@ impl SharedPollState { } next_value |= value; - Some(next_value & !POLLING & !WAKING_ALL) + Some(next_value & !POLLING & !WAKING) }) .unwrap() } /// Toggles state to non-waking, allowing to start polling. - fn stop_waking(&self, waking: u8) -> u8 { + fn stop_waking(&self) -> u8 { self.state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - let mut next_value = value & !waking; - // Waker will be called only if the current waking state is the same as the specified waker state - if value & WAKING_ALL == waking { - next_value |= WOKEN; - } + let next_value = value & !WAKING; if next_value != value { Some(next_value) @@ -201,16 +187,16 @@ impl u8> Drop for PollStateBomb<'_, F> { /// Will update state with the provided value on `wake_by_ref` call /// and then, if there is a need, call `inner_waker`. -struct InnerWaker { +struct WrappedWaker { inner_waker: UnsafeCell>, poll_state: SharedPollState, need_to_poll: u8, } -unsafe impl Send for InnerWaker {} -unsafe impl Sync for InnerWaker {} +unsafe impl Send for WrappedWaker {} +unsafe impl Sync for WrappedWaker {} -impl InnerWaker { +impl WrappedWaker { /// 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. @@ -218,7 +204,7 @@ impl InnerWaker { /// ## Safety /// /// This function will modify waker's `inner_waker` via `UnsafeCell`, so - /// it should be used only during `POLLING` phase. + /// it should be used only during `POLLING` phase by one thread at the time. unsafe fn replace_waker(self_arc: &mut Arc, cx: &Context<'_>) -> Waker { *self_arc.inner_waker.get() = cx.waker().clone().into(); waker(self_arc.clone()) @@ -227,16 +213,11 @@ impl InnerWaker { /// Attempts to start the waking process for the waker with the given value. /// If succeeded, then the stream isn't yet woken and not being polled at the moment. fn start_waking(&self) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { - self.poll_state.start_waking(self.need_to_poll, self.waking_state()) - } - - /// Returns the corresponding waking state toggled by this waker. - fn waking_state(&self) -> u8 { - self.need_to_poll << 3 + self.poll_state.start_waking(self.need_to_poll) } } -impl ArcWake for InnerWaker { +impl ArcWake for WrappedWaker { fn wake_by_ref(self_arc: &Arc) { if let Some((_, state_bomb)) = self_arc.start_waking() { // Safety: now state is not `POLLING` @@ -246,12 +227,8 @@ impl ArcWake for InnerWaker { // Stop waking to allow polling stream let poll_state_value = state_bomb.fire().unwrap(); - // Here we want to call waker only if stream isn't woken yet and - // also to optimize the case when two wakers are called at the same time. - // - // In this case the best strategy will be to propagate only the latest waker's awake, - // and then poll both entities in a single `poll_next` call - if poll_state_value & (WOKEN | WAKING_ALL) == self_arc.waking_state() { + // We want to call waker only if the stream isn't woken yet + if poll_state_value & (WOKEN | WAKING) == WAKING { // Wake up inner waker inner_waker.wake(); } @@ -314,8 +291,8 @@ pin_project! { poll_state: SharedPollState, limit: Option, is_stream_done: bool, - inner_streams_waker: Arc, - stream_waker: Arc, + inner_streams_waker: Arc, + stream_waker: Arc, } } @@ -348,12 +325,12 @@ where stream, is_stream_done: false, limit: limit.and_then(NonZeroUsize::new), - inner_streams_waker: Arc::new(InnerWaker { + inner_streams_waker: Arc::new(WrappedWaker { inner_waker: UnsafeCell::new(None), poll_state: poll_state.clone(), need_to_poll: NEED_TO_POLL_INNER_STREAMS, }), - stream_waker: Arc::new(InnerWaker { + stream_waker: Arc::new(WrappedWaker { inner_waker: UnsafeCell::new(None), poll_state: poll_state.clone(), need_to_poll: NEED_TO_POLL_STREAM, @@ -369,7 +346,7 @@ impl FlattenUnorderedProj<'_, St> where St: Stream, { - /// Checks if current `inner_streams` size is less than optional limit. + /// Checks if current `inner_streams` bucket size is greater than optional limit. fn is_exceeded_limit(&self) -> bool { self.limit.map_or(false, |limit| self.inner_streams.len() >= limit.get()) } @@ -378,7 +355,7 @@ where impl FusedStream for FlattenUnordered where St: FusedStream, - St::Item: FusedStream + Unpin, + St::Item: Stream + Unpin, { fn is_terminated(&self) -> bool { self.stream.is_terminated() && self.inner_streams.is_empty() @@ -407,8 +384,7 @@ where }; if poll_state_value & NEED_TO_POLL_STREAM != NONE { - // Safety: now state is `POLLING`. - let stream_waker = unsafe { InnerWaker::replace_waker(this.stream_waker, cx) }; + let mut stream_waker = None; // Here we need to poll the base stream. // @@ -424,15 +400,24 @@ where break; } else { - match this.stream.as_mut().poll_next(&mut Context::from_waker(&stream_waker)) { + // Initialize base stream waker if it's not yet initialized + if stream_waker.is_none() { + // Safety: now state is `POLLING`. + stream_waker + .replace(unsafe { WrappedWaker::replace_waker(this.stream_waker, cx) }); + } + let mut cx = Context::from_waker(stream_waker.as_ref().unwrap()); + + match this.stream.as_mut().poll_next(&mut cx) { Poll::Ready(Some(inner_stream)) => { + let next_item_fut = PollStreamFut::new(inner_stream); // Add new stream to the inner streams bucket - this.inner_streams.as_mut().push(PollStreamFut::new(inner_stream)); + this.inner_streams.as_mut().push(next_item_fut); // Inner streams must be polled afterward poll_state_value |= NEED_TO_POLL_INNER_STREAMS; } Poll::Ready(None) => { - // Mark the stream as done + // Mark the base stream as done *this.is_stream_done = true; } Poll::Pending => { @@ -446,13 +431,10 @@ where if poll_state_value & NEED_TO_POLL_INNER_STREAMS != NONE { // Safety: now state is `POLLING`. let inner_streams_waker = - unsafe { InnerWaker::replace_waker(this.inner_streams_waker, cx) }; + unsafe { WrappedWaker::replace_waker(this.inner_streams_waker, cx) }; + let mut cx = Context::from_waker(&inner_streams_waker); - match this - .inner_streams - .as_mut() - .poll_next(&mut Context::from_waker(&inner_streams_waker)) - { + match this.inner_streams.as_mut().poll_next(&mut cx) { Poll::Ready(Some(Some((item, next_item_fut)))) => { // Push next inner stream item future to the list of inner streams futures this.inner_streams.as_mut().push(next_item_fut); @@ -472,15 +454,16 @@ where // We didn't have any `poll_next` panic, so it's time to deactivate the bomb state_bomb.deactivate(); + // Call the waker at the end of polling if let mut force_wake = // we need to poll the stream and didn't reach the limit yet need_to_poll_next & NEED_TO_POLL_STREAM != NONE && !this.is_exceeded_limit() - // or we need to poll inner streams again + // or we need to poll the inner streams again || need_to_poll_next & NEED_TO_POLL_INNER_STREAMS != NONE; // Stop polling and swap the latest state poll_state_value = this.poll_state.stop_polling(need_to_poll_next, force_wake); - // If state was changed during `POLLING` phase, need to manually call a waker + // If state was changed during `POLLING` phase, we also need to manually call a waker force_wake |= poll_state_value & NEED_TO_POLL_ALL != NONE; let is_done = *this.is_stream_done && this.inner_streams.is_empty(); diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index bb5e24907d..eb86cb757d 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -774,7 +774,14 @@ pub trait StreamExt: Stream { } /// Flattens a stream of streams into just one continuous stream. Polls - /// inner streams concurrently. + /// inner streams produced by the base stream concurrently. + /// + /// The only 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 at the same time. The `limit` argument is of type + /// `Into>`, and so can be provided as either `None`, + /// `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`. /// /// # Examples /// @@ -814,7 +821,7 @@ pub trait StreamExt: Stream { Self::Item: Stream + Unpin, Self: Sized, { - FlattenUnordered::new(self, limit.into()) + assert_stream::<::Item, _>(FlattenUnordered::new(self, limit.into())) } /// Maps a stream like [`StreamExt::map`] but flattens nested `Stream`s. @@ -863,7 +870,7 @@ pub trait StreamExt: 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 + /// will be polled at the same time. The `limit` argument is of type /// `Into>`, and so can be provided as either `None`, /// `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`. @@ -901,7 +908,7 @@ pub trait StreamExt: Stream { F: FnMut(Self::Item) -> U, Self: Sized, { - FlatMapUnordered::new(self, limit.into(), f) + assert_stream::(FlatMapUnordered::new(self, limit.into(), f)) } /// Combinator similar to [`StreamExt::fold`] that holds internal state diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index bc4c6e4f6a..42f5e7324b 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -15,6 +15,7 @@ use crate::stream::{Inspect, Map}; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::pin::Pin; + use futures_core::{ future::{Future, TryFuture}, stream::TryStream, @@ -88,6 +89,14 @@ mod try_flatten; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_flatten::TryFlatten; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod try_flatten_unordered; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_flatten_unordered::TryFlattenUnordered; + mod try_collect; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_collect::TryCollect; @@ -711,6 +720,62 @@ pub trait TryStreamExt: TryStream { assert_stream::, _>(TryFilterMap::new(self, f)) } + /// Flattens a stream of streams into just one continuous stream. Produced streams + /// will be polled concurrently and any errors are passed through without looking at them. + /// + /// The only 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 at the same time. The `limit` argument is of type + /// `Into>`, and so can be provided as either `None`, + /// `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`. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::mpsc; + /// use futures::stream::{StreamExt, TryStreamExt}; + /// use std::thread; + /// + /// let (tx1, rx1) = mpsc::unbounded(); + /// let (tx2, rx2) = mpsc::unbounded(); + /// let (tx3, rx3) = mpsc::unbounded(); + /// + /// thread::spawn(move || { + /// tx1.unbounded_send(Ok(1)).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx2.unbounded_send(Ok(2)).unwrap(); + /// tx2.unbounded_send(Err(3)).unwrap(); + /// tx2.unbounded_send(Ok(4)).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx3.unbounded_send(Ok(rx1)).unwrap(); + /// tx3.unbounded_send(Ok(rx2)).unwrap(); + /// tx3.unbounded_send(Err(5)).unwrap(); + /// }); + /// + /// let stream = rx3.try_flatten_unordered(None); + /// let mut values: Vec<_> = stream.collect().await; + /// values.sort(); + /// + /// assert_eq!(values, vec![Ok(1), Ok(2), Ok(4), Err(3), Err(5)]); + /// # }); + /// ``` + #[cfg(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn try_flatten_unordered(self, limit: impl Into>) -> TryFlattenUnordered + where + Self::Ok: TryStream + Unpin, + ::Error: From, + Self: Sized, + { + assert_stream::::Ok, ::Error>, _>( + TryFlattenUnordered::new(self, limit), + ) + } + /// Flattens a stream of streams into just one continuous stream. /// /// If this stream's elements are themselves streams then this combinator diff --git a/futures-util/src/stream/try_stream/try_chunks.rs b/futures-util/src/stream/try_stream/try_chunks.rs index 3bb253a714..ec53f4bd11 100644 --- a/futures-util/src/stream/try_stream/try_chunks.rs +++ b/futures-util/src/stream/try_stream/try_chunks.rs @@ -41,9 +41,10 @@ impl TryChunks { delegate_access_inner!(stream, St, (. .)); } +type TryChunksStreamError = TryChunksError<::Ok, ::Error>; + impl Stream for TryChunks { - #[allow(clippy::type_complexity)] - type Item = Result, TryChunksError>; + type Item = Result, TryChunksStreamError>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut this = self.as_mut().project(); diff --git a/futures-util/src/stream/try_stream/try_flatten_unordered.rs b/futures-util/src/stream/try_stream/try_flatten_unordered.rs new file mode 100644 index 0000000000..aaad910bf0 --- /dev/null +++ b/futures-util/src/stream/try_stream/try_flatten_unordered.rs @@ -0,0 +1,133 @@ +use core::pin::Pin; + +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream, TryStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; + +use pin_project_lite::pin_project; + +use crate::future::Either; +use crate::stream::stream::FlattenUnordered; +use crate::StreamExt; + +use super::IntoStream; + +delegate_all!( + /// Stream for the [`try_flatten_unordered`](super::TryStreamExt::try_flatten_unordered) method. + TryFlattenUnordered( + FlattenUnordered> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + + New[ + |stream: St, limit: impl Into>| + TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams::new(stream).flatten_unordered(limit) + ] + where + St: TryStream, + St::Ok: TryStream, + St::Ok: Unpin, + ::Error: From +); + +pin_project! { + /// Emits either successful streams or single-item streams containing the underlying errors. + /// This's a wrapper for `FlattenUnordered` to reuse its logic over `TryStream`. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams + where + St: TryStream, + St::Ok: TryStream, + St::Ok: Unpin, + ::Error: From + { + #[pin] + stream: St, + } +} + +impl TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +where + St: TryStream, + St::Ok: TryStream + Unpin, + ::Error: From, +{ + fn new(stream: St) -> Self { + Self { stream } + } + + delegate_access_inner!(stream, St, ()); +} + +impl FusedStream for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +where + St: TryStream + FusedStream, + St::Ok: TryStream + Unpin, + ::Error: From, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + } +} + +/// Emits single item immediately, then stream will be terminated. +#[derive(Debug, Clone)] +pub struct Single(Option); + +impl Unpin for Single {} + +impl Stream for Single { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.take()) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.as_ref().map_or((0, Some(0)), |_| (1, Some(1))) + } +} + +type SingleStreamResult = Single::Ok, ::Error>>; + +impl Stream for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +where + St: TryStream, + St::Ok: TryStream + Unpin, + ::Error: From, +{ + // Item is either an inner stream or a stream containing a single error. + // This will allow using `Either`'s `Stream` implementation as both branches are actually streams of `Result`'s. + type Item = Either, SingleStreamResult>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let item = ready!(self.project().stream.try_poll_next(cx)); + + let out = item.map(|res| match res { + // Emit successful inner stream as is + Ok(stream) => Either::Left(IntoStream::new(stream)), + // Wrap an error into a stream containing a single item + err @ Err(_) => { + let res = err.map(|_: St::Ok| unreachable!()).map_err(Into::into); + + Either::Right(Single(Some(res))) + } + }); + + Poll::Ready(out) + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +where + St: TryStream + Sink, + St::Ok: Stream> + Unpin, + ::Error: From<::Error>, +{ + type Error = >::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index 194e74db74..6d00097970 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -2,6 +2,7 @@ use futures::{ stream::{self, StreamExt, TryStreamExt}, task::Poll, }; +use futures_executor::block_on; use futures_test::task::noop_context; #[test] @@ -36,3 +37,43 @@ fn try_take_while_after_err() { .boxed(); assert_eq!(Poll::Ready(None), s.poll_next_unpin(cx)); } + +#[test] +fn try_flatten_unordered() { + let s = stream::iter(1..7) + .map(|val: u32| { + if val % 2 == 0 { + Ok(stream::unfold((val, 1), |(val, pow)| async move { + Some((val.pow(pow), (val, pow + 1))) + }) + .take(3) + .map(move |val| if val % 16 != 0 { Ok(val) } else { Err(val) })) + } else { + Err(val) + } + }) + .map_ok(Box::pin) + .try_flatten_unordered(None); + + block_on(async move { + assert_eq!( + // All numbers can be divided by 16 and odds must be `Err` + // For all basic evens we must have powers from 1 to 3 + vec![ + Err(1), + Ok(2), + Err(3), + Ok(4), + Err(5), + Ok(6), + Ok(4), + Err(16), + Ok(36), + Ok(8), + Err(64), + Ok(216) + ], + s.collect::>().await + ) + }) +} From f2cfb41284d365a2adb39d6ffd970331c85bf1ef Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Sat, 28 May 2022 11:56:04 +0200 Subject: [PATCH 189/283] Several tweaks to FlattenUnordered and TryFlattenUnordered (#2590) --- .../src/stream/stream/flatten_unordered.rs | 32 +++++++++---------- .../try_stream/try_flatten_unordered.rs | 8 ++--- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 66ba4d0d55..8293983d84 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -56,14 +56,14 @@ impl SharedPollState { } /// Attempts to start polling, returning stored state in case of success. - /// Returns `None` if some waker is waking at the moment. + /// Returns `None` if either waker is waking at the moment or state is empty. fn start_polling( &self, ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - if value & WAKING == NONE { + if value & WAKING == NONE && value & NEED_TO_POLL_ALL != NONE { Some(POLLING) } else { None @@ -99,8 +99,10 @@ impl SharedPollState { }) .ok()?; + debug_assert!(value & WAKING == NONE); + // Only start the waking process if we're not in the polling phase and the stream isn't woken already - if value & (WOKEN | POLLING | WAKING) == NONE { + if value & (WOKEN | POLLING) == NONE { let bomb = PollStateBomb::new(self, SharedPollState::stop_waking); Some((value, bomb)) @@ -135,9 +137,10 @@ impl SharedPollState { /// Toggles state to non-waking, allowing to start polling. fn stop_waking(&self) -> u8 { - self.state + let value = self + .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - let next_value = value & !WAKING; + let next_value = value & !WAKING | WOKEN; if next_value != value { Some(next_value) @@ -145,7 +148,10 @@ impl SharedPollState { None } }) - .unwrap_or_else(identity) + .unwrap_or_else(identity); + + debug_assert!(value & (WOKEN | POLLING | WAKING) == WAKING); + value } /// Resets current state allowing to poll the stream and wake up wakers. @@ -170,11 +176,6 @@ impl<'a, F: FnOnce(&SharedPollState) -> u8> PollStateBomb<'a, F> { fn deactivate(mut self) { self.drop.take(); } - - /// Manually fires the bomb, returning supplied state. - fn fire(mut self) -> Option { - self.drop.take().map(|drop| (drop)(self.state)) - } } impl u8> Drop for PollStateBomb<'_, F> { @@ -225,13 +226,10 @@ impl ArcWake for WrappedWaker { if let Some(inner_waker) = waker_opt.clone() { // Stop waking to allow polling stream - let poll_state_value = state_bomb.fire().unwrap(); + drop(state_bomb); - // We want to call waker only if the stream isn't woken yet - if poll_state_value & (WOKEN | WAKING) == WAKING { - // Wake up inner waker - inner_waker.wake(); - } + // Wake up inner waker + inner_waker.wake(); } } } diff --git a/futures-util/src/stream/try_stream/try_flatten_unordered.rs b/futures-util/src/stream/try_stream/try_flatten_unordered.rs index aaad910bf0..e21b514023 100644 --- a/futures-util/src/stream/try_stream/try_flatten_unordered.rs +++ b/futures-util/src/stream/try_stream/try_flatten_unordered.rs @@ -27,7 +27,7 @@ delegate_all!( St: TryStream, St::Ok: TryStream, St::Ok: Unpin, - ::Error: From + ::Error: From ); pin_project! { @@ -40,7 +40,7 @@ pin_project! { St: TryStream, St::Ok: TryStream, St::Ok: Unpin, - ::Error: From + ::Error: From { #[pin] stream: St, @@ -121,10 +121,10 @@ where // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +impl Sink for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams where St: TryStream + Sink, - St::Ok: Stream> + Unpin, + St::Ok: TryStream + Unpin, ::Error: From<::Error>, { type Error = >::Error; From 7516af9e8866cb811613419654206a1fe52221d4 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Tue, 31 May 2022 10:22:16 +0200 Subject: [PATCH 190/283] Fix incorrect assumption (#2606) --- futures-util/src/stream/stream/flatten_unordered.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 8293983d84..88006cf235 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -99,10 +99,8 @@ impl SharedPollState { }) .ok()?; - debug_assert!(value & WAKING == NONE); - - // Only start the waking process if we're not in the polling phase and the stream isn't woken already - if value & (WOKEN | POLLING) == NONE { + // Only start the waking process if we're not in the polling/waking phase and the stream isn't woken already + if value & (WOKEN | POLLING | WAKING) == NONE { let bomb = PollStateBomb::new(self, SharedPollState::stop_waking); Some((value, bomb)) @@ -236,7 +234,7 @@ impl ArcWake for WrappedWaker { } pin_project! { - /// Future which contains optional stream. + /// Future which polls optional inner stream. /// /// If it's `Some`, it will attempt to call `poll_next` on it, /// returning `Some((item, next_item_fut))` in case of `Poll::Ready(Some(...))` From 7f8442c36d76a83c1591550a9b20e1258a9f2c83 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Fri, 10 Mar 2023 15:10:45 +0100 Subject: [PATCH 191/283] `TryFlattenUnordered`: propagate base stream error (#2607) --- futures-util/benches/flatten_unordered.rs | 15 +-- .../src/stream/stream/flatten_unordered.rs | 80 ++++++++++--- futures-util/src/stream/stream/mod.rs | 2 +- futures-util/src/stream/try_stream/mod.rs | 3 +- .../try_stream/try_flatten_unordered.rs | 111 ++++++++++++------ futures/tests/stream_try_stream.rs | 65 +++++++++- 6 files changed, 209 insertions(+), 67 deletions(-) diff --git a/futures-util/benches/flatten_unordered.rs b/futures-util/benches/flatten_unordered.rs index b92f614914..517b2816c3 100644 --- a/futures-util/benches/flatten_unordered.rs +++ b/futures-util/benches/flatten_unordered.rs @@ -8,6 +8,7 @@ use futures::executor::block_on; use futures::future; use futures::stream::{self, StreamExt}; use futures::task::Poll; +use futures_util::FutureExt; use std::collections::VecDeque; use std::thread; @@ -34,17 +35,9 @@ fn oneshot_streams(b: &mut Bencher) { } }); - let mut flatten = stream::unfold(rxs.into_iter(), |mut vals| { - Box::pin(async { - if let Some(next) = vals.next() { - let val = next.await.unwrap(); - Some((val, vals)) - } else { - None - } - }) - }) - .flatten_unordered(None); + let mut flatten = stream::iter(rxs) + .map(|recv| recv.into_stream().map(|val| val.unwrap()).flatten()) + .flatten_unordered(None); block_on(future::poll_fn(move |cx| { let mut count = 0; diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 88006cf235..484c3733aa 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -3,6 +3,7 @@ use core::{ cell::UnsafeCell, convert::identity, fmt, + marker::PhantomData, num::NonZeroUsize, pin::Pin, sync::atomic::{AtomicU8, Ordering}, @@ -22,6 +23,10 @@ use futures_task::{waker, ArcWake}; use crate::stream::FuturesUnordered; +/// Stream for the [`flatten_unordered`](super::StreamExt::flatten_unordered) +/// method. +pub type FlattenUnordered = FlattenUnorderedWithFlowController; + /// There is nothing to poll and stream isn't being polled/waking/woken at the moment. const NONE: u8 = 0; @@ -154,7 +159,7 @@ impl SharedPollState { /// Resets current state allowing to poll the stream and wake up wakers. fn reset(&self) -> u8 { - self.state.swap(NEED_TO_POLL_ALL, Ordering::AcqRel) + self.state.swap(NEED_TO_POLL_ALL, Ordering::SeqCst) } } @@ -276,10 +281,10 @@ impl Future for PollStreamFut { pin_project! { /// Stream for the [`flatten_unordered`](super::StreamExt::flatten_unordered) - /// method. - #[project = FlattenUnorderedProj] + /// method with ability to specify flow controller. + #[project = FlattenUnorderedWithFlowControllerProj] #[must_use = "streams do nothing unless polled"] - pub struct FlattenUnordered where St: Stream { + pub struct FlattenUnorderedWithFlowController where St: Stream { #[pin] inner_streams: FuturesUnordered>, #[pin] @@ -289,34 +294,40 @@ pin_project! { is_stream_done: bool, inner_streams_waker: Arc, stream_waker: Arc, + flow_controller: PhantomData } } -impl fmt::Debug for FlattenUnordered +impl fmt::Debug for FlattenUnorderedWithFlowController where St: Stream + fmt::Debug, St::Item: Stream + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenUnordered") + f.debug_struct("FlattenUnorderedWithFlowController") .field("poll_state", &self.poll_state) .field("inner_streams", &self.inner_streams) .field("limit", &self.limit) .field("stream", &self.stream) .field("is_stream_done", &self.is_stream_done) + .field("flow_controller", &self.flow_controller) .finish() } } -impl FlattenUnordered +impl FlattenUnorderedWithFlowController where St: Stream, + Fc: FlowController::Item>, St::Item: Stream + Unpin, { - pub(super) fn new(stream: St, limit: Option) -> FlattenUnordered { + pub(crate) fn new( + stream: St, + limit: Option, + ) -> FlattenUnorderedWithFlowController { let poll_state = SharedPollState::new(NEED_TO_POLL_STREAM); - FlattenUnordered { + FlattenUnorderedWithFlowController { inner_streams: FuturesUnordered::new(), stream, is_stream_done: false, @@ -332,13 +343,35 @@ where need_to_poll: NEED_TO_POLL_STREAM, }), poll_state, + flow_controller: PhantomData, } } delegate_access_inner!(stream, St, ()); } -impl FlattenUnorderedProj<'_, St> +/// Returns the next flow step based on the received item. +pub trait FlowController { + /// Handles an item producing `FlowStep` describing the next flow step. + fn next_step(item: I) -> FlowStep; +} + +impl FlowController for () { + fn next_step(item: I) -> FlowStep { + FlowStep::Continue(item) + } +} + +/// Describes the next flow step. +#[derive(Debug, Clone)] +pub enum FlowStep { + /// Just yields an item and continues standard flow. + Continue(C), + /// Immediately returns an underlying item from the function. + Return(R), +} + +impl FlattenUnorderedWithFlowControllerProj<'_, St, Fc> where St: Stream, { @@ -348,9 +381,10 @@ where } } -impl FusedStream for FlattenUnordered +impl FusedStream for FlattenUnorderedWithFlowController where St: FusedStream, + Fc: FlowController::Item>, St::Item: Stream + Unpin, { fn is_terminated(&self) -> bool { @@ -358,9 +392,10 @@ where } } -impl Stream for FlattenUnordered +impl Stream for FlattenUnorderedWithFlowController where St: Stream, + Fc: FlowController::Item>, St::Item: Stream + Unpin, { type Item = ::Item; @@ -405,8 +440,23 @@ where let mut cx = Context::from_waker(stream_waker.as_ref().unwrap()); match this.stream.as_mut().poll_next(&mut cx) { - Poll::Ready(Some(inner_stream)) => { - let next_item_fut = PollStreamFut::new(inner_stream); + Poll::Ready(Some(item)) => { + let next_item_fut = match Fc::next_step(item) { + // Propagates an item immediately (the main use-case is for errors) + FlowStep::Return(item) => { + need_to_poll_next |= NEED_TO_POLL_STREAM + | (poll_state_value & NEED_TO_POLL_INNER_STREAMS); + poll_state_value &= !NEED_TO_POLL_INNER_STREAMS; + + next_item = Some(item); + + break; + } + // Yields an item and continues processing (normal case) + FlowStep::Continue(inner_stream) => { + PollStreamFut::new(inner_stream) + } + }; // Add new stream to the inner streams bucket this.inner_streams.as_mut().push(next_item_fut); // Inner streams must be polled afterward @@ -478,7 +528,7 @@ where // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for FlattenUnordered +impl Sink for FlattenUnorderedWithFlowController where St: Stream + Sink, { diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index eb86cb757d..558dc22bd7 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -199,7 +199,7 @@ pub use self::buffered::Buffered; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] -mod flatten_unordered; +pub(crate) mod flatten_unordered; #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 42f5e7324b..414a40dbe3 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -721,7 +721,8 @@ pub trait TryStreamExt: TryStream { } /// Flattens a stream of streams into just one continuous stream. Produced streams - /// will be polled concurrently and any errors are passed through without looking at them. + /// will be polled concurrently and any errors will be passed through without looking at them. + /// If the underlying base stream returns an error, it will be **immediately** propagated. /// /// The only argument is an optional limit on the number of concurrently /// polled streams. If this limit is not `None`, no more than `limit` streams diff --git a/futures-util/src/stream/try_stream/try_flatten_unordered.rs b/futures-util/src/stream/try_stream/try_flatten_unordered.rs index e21b514023..a74dfc451d 100644 --- a/futures-util/src/stream/try_stream/try_flatten_unordered.rs +++ b/futures-util/src/stream/try_stream/try_flatten_unordered.rs @@ -1,3 +1,4 @@ +use core::marker::PhantomData; use core::pin::Pin; use futures_core::ready; @@ -9,19 +10,23 @@ use futures_sink::Sink; use pin_project_lite::pin_project; use crate::future::Either; -use crate::stream::stream::FlattenUnordered; -use crate::StreamExt; - -use super::IntoStream; +use crate::stream::stream::flatten_unordered::{ + FlattenUnorderedWithFlowController, FlowController, FlowStep, +}; +use crate::stream::IntoStream; +use crate::TryStreamExt; delegate_all!( /// Stream for the [`try_flatten_unordered`](super::TryStreamExt::try_flatten_unordered) method. TryFlattenUnordered( - FlattenUnordered> + FlattenUnorderedWithFlowController, PropagateBaseStreamError> ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[ |stream: St, limit: impl Into>| - TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams::new(stream).flatten_unordered(limit) + FlattenUnorderedWithFlowController::new( + NestedTryStreamIntoEitherTryStream::new(stream), + limit.into() + ) ] where St: TryStream, @@ -35,7 +40,7 @@ pin_project! { /// This's a wrapper for `FlattenUnordered` to reuse its logic over `TryStream`. #[derive(Debug)] #[must_use = "streams do nothing unless polled"] - pub struct TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams + pub struct NestedTryStreamIntoEitherTryStream where St: TryStream, St::Ok: TryStream, @@ -43,11 +48,11 @@ pin_project! { ::Error: From { #[pin] - stream: St, + stream: St } } -impl TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +impl NestedTryStreamIntoEitherTryStream where St: TryStream, St::Ok: TryStream + Unpin, @@ -60,21 +65,22 @@ where delegate_access_inner!(stream, St, ()); } -impl FusedStream for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams -where - St: TryStream + FusedStream, - St::Ok: TryStream + Unpin, - ::Error: From, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -/// Emits single item immediately, then stream will be terminated. +/// Emits a single item immediately, then stream will be terminated. #[derive(Debug, Clone)] pub struct Single(Option); +impl Single { + /// Constructs new `Single` with the given value. + fn new(val: T) -> Self { + Self(Some(val)) + } + + /// Attempts to take inner item immediately. Will always succeed if the stream isn't terminated. + fn next_immediate(&mut self) -> Option { + self.0.take() + } +} + impl Unpin for Single {} impl Stream for Single { @@ -89,9 +95,32 @@ impl Stream for Single { } } +/// Immediately propagates errors occurred in the base stream. +#[derive(Debug, Clone, Copy)] +pub struct PropagateBaseStreamError(PhantomData); + +type BaseStreamItem = as Stream>::Item; +type InnerStreamItem = as Stream>::Item; + +impl FlowController, InnerStreamItem> for PropagateBaseStreamError +where + St: TryStream, + St::Ok: TryStream + Unpin, + ::Error: From, +{ + fn next_step(item: BaseStreamItem) -> FlowStep, InnerStreamItem> { + match item { + // A new successful inner stream received + st @ Either::Left(_) => FlowStep::Continue(st), + // An error encountered + Either::Right(mut err) => FlowStep::Return(err.next_immediate().unwrap()), + } + } +} + type SingleStreamResult = Single::Ok, ::Error>>; -impl Stream for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +impl Stream for NestedTryStreamIntoEitherTryStream where St: TryStream, St::Ok: TryStream + Unpin, @@ -104,24 +133,38 @@ where fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let item = ready!(self.project().stream.try_poll_next(cx)); - let out = item.map(|res| match res { - // Emit successful inner stream as is - Ok(stream) => Either::Left(IntoStream::new(stream)), - // Wrap an error into a stream containing a single item - err @ Err(_) => { - let res = err.map(|_: St::Ok| unreachable!()).map_err(Into::into); - - Either::Right(Single(Some(res))) - } - }); + let out = match item { + Some(res) => match res { + // Emit successful inner stream as is + Ok(stream) => Either::Left(stream.into_stream()), + // Wrap an error into a stream containing a single item + err @ Err(_) => { + let res = err.map(|_: St::Ok| unreachable!()).map_err(Into::into); + + Either::Right(Single::new(res)) + } + }, + None => return Poll::Ready(None), + }; + + Poll::Ready(Some(out)) + } +} - Poll::Ready(out) +impl FusedStream for NestedTryStreamIntoEitherTryStream +where + St: TryStream + FusedStream, + St::Ok: TryStream + Unpin, + ::Error: From, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() } } // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for TryStreamOfTryStreamsIntoHomogeneousStreamOfTryStreams +impl Sink for NestedTryStreamIntoEitherTryStream where St: TryStream + Sink, St::Ok: TryStream + Unpin, diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index 6d00097970..b3d04b9200 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -1,8 +1,12 @@ +use core::pin::Pin; + use futures::{ - stream::{self, StreamExt, TryStreamExt}, + stream::{self, repeat, Repeat, StreamExt, TryStreamExt}, task::Poll, + Stream, }; use futures_executor::block_on; +use futures_task::Context; use futures_test::task::noop_context; #[test] @@ -40,7 +44,7 @@ fn try_take_while_after_err() { #[test] fn try_flatten_unordered() { - let s = stream::iter(1..7) + let test_st = stream::iter(1..7) .map(|val: u32| { if val % 2 == 0 { Ok(stream::unfold((val, 1), |(val, pow)| async move { @@ -61,10 +65,10 @@ fn try_flatten_unordered() { // For all basic evens we must have powers from 1 to 3 vec![ Err(1), - Ok(2), Err(3), - Ok(4), Err(5), + Ok(2), + Ok(4), Ok(6), Ok(4), Err(16), @@ -73,7 +77,58 @@ fn try_flatten_unordered() { Err(64), Ok(216) ], - s.collect::>().await + test_st.collect::>().await ) + }); + + #[derive(Clone, Debug)] + struct ErrorStream { + error_after: usize, + polled: usize, + } + + impl Stream for ErrorStream { + type Item = Result>, ()>; + + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + if self.polled > self.error_after { + panic!("Polled after error"); + } else { + let out = + if self.polled == self.error_after { Err(()) } else { Ok(repeat(Ok(()))) }; + self.polled += 1; + Poll::Ready(Some(out)) + } + } + } + + block_on(async move { + let mut st = ErrorStream { error_after: 3, polled: 0 }.try_flatten_unordered(None); + let mut ctr = 0; + while (st.try_next().await).is_ok() { + ctr += 1; + } + assert_eq!(ctr, 0); + + assert_eq!( + ErrorStream { error_after: 10, polled: 0 } + .try_flatten_unordered(None) + .inspect_ok(|_| panic!("Unexpected `Ok`")) + .try_collect::>() + .await, + Err(()) + ); + + let mut taken = 0; + assert_eq!( + ErrorStream { error_after: 10, polled: 0 } + .map_ok(|st| st.take(3)) + .try_flatten_unordered(1) + .inspect(|_| taken += 1) + .try_fold((), |(), res| async move { Ok(res) }) + .await, + Err(()) + ); + assert_eq!(taken, 31); }) } From b0d2c56e944c007cca2b35413f84b9934a8eb716 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 4 Feb 2023 07:56:09 +0800 Subject: [PATCH 192/283] Poll `Select` futures without moving them (#2704) --- futures-util/src/future/select.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index e693a30b00..80b67e670d 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -99,17 +99,16 @@ where type Output = Either<(A::Output, B), (B::Output, A)>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut a, mut b) = self.inner.take().expect("cannot poll Select twice"); + let (a, b) = self.inner.as_mut().expect("cannot poll Select twice"); if let Poll::Ready(val) = a.poll_unpin(cx) { - return Poll::Ready(Either::Left((val, b))); + return Poll::Ready(Either::Left((val, self.inner.take().unwrap().1))); } if let Poll::Ready(val) = b.poll_unpin(cx) { - return Poll::Ready(Either::Right((val, a))); + return Poll::Ready(Either::Right((val, self.inner.take().unwrap().0))); } - self.inner = Some((a, b)); Poll::Pending } } From bc85d233fedfa7858d3ee184f50e523af6a32368 Mon Sep 17 00:00:00 2001 From: Nathan West Date: Thu, 16 Feb 2023 10:52:36 -0500 Subject: [PATCH 193/283] Simplify Fuse::poll to use a match expression instead of `return` (#2694) --- futures-util/src/future/future/fuse.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/futures-util/src/future/future/fuse.rs b/futures-util/src/future/future/fuse.rs index 597aec1a40..2257906726 100644 --- a/futures-util/src/future/future/fuse.rs +++ b/futures-util/src/future/future/fuse.rs @@ -1,6 +1,5 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; -use futures_core::ready; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; @@ -81,13 +80,12 @@ impl Future for Fuse { type Output = Fut::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Poll::Ready(match self.as_mut().project().inner.as_pin_mut() { - Some(fut) => { - let output = ready!(fut.poll(cx)); + match self.as_mut().project().inner.as_pin_mut() { + Some(fut) => fut.poll(cx).map(|output| { self.project().inner.set(None); output - } - None => return Poll::Pending, - }) + }), + None => Poll::Pending, + } } } From 5b64af3f38b80d7717d26a46ed49aeedd52b5d15 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Fri, 17 Feb 2023 17:26:45 -0800 Subject: [PATCH 194/283] Ensure unreachable branch is eliminated (#2708) --- futures-util/src/future/select.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index 80b67e670d..7e33d195f7 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -99,14 +99,24 @@ where type Output = Either<(A::Output, B), (B::Output, A)>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + /// When compiled with `-C opt-level=z`, this function will help the compiler eliminate the `None` branch, where + /// `Option::unwrap` does not. + #[inline(always)] + fn unwrap_option(value: Option) -> T { + match value { + None => unreachable!(), + Some(value) => value, + } + } + let (a, b) = self.inner.as_mut().expect("cannot poll Select twice"); if let Poll::Ready(val) = a.poll_unpin(cx) { - return Poll::Ready(Either::Left((val, self.inner.take().unwrap().1))); + return Poll::Ready(Either::Left((val, unwrap_option(self.inner.take()).1))); } if let Poll::Ready(val) = b.poll_unpin(cx) { - return Poll::Ready(Either::Right((val, self.inner.take().unwrap().0))); + return Poll::Ready(Either::Right((val, unwrap_option(self.inner.take()).0))); } Poll::Pending From 9c44e14810462ac85f630cae5128494e5d58cf48 Mon Sep 17 00:00:00 2001 From: ZhennanWu <38578020+ZhennanWu@users.noreply.github.com> Date: Sat, 18 Feb 2023 18:22:28 -0800 Subject: [PATCH 195/283] add AbortHandle::is_aborted() (#2710) Co-authored-by: ZhennanWu --- futures-util/src/abortable.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs index e0afd47218..cb81ce32cd 100644 --- a/futures-util/src/abortable.rs +++ b/futures-util/src/abortable.rs @@ -182,4 +182,17 @@ impl AbortHandle { self.inner.aborted.store(true, Ordering::Relaxed); self.inner.waker.wake(); } + + /// Checks whether [`AbortHandle::abort`] was *called* on any associated + /// [`AbortHandle`]s, which includes all the [`AbortHandle`]s linked with + /// the same [`AbortRegistration`]. This means that it will return `true` + /// even if: + /// * `abort` was called after the task had completed. + /// * `abort` was called while the task was being polled - the task may still be running and + /// will not be stopped until `poll` returns. + /// + /// This operation has a Relaxed ordering. + pub fn is_aborted(&self) -> bool { + self.inner.aborted.load(Ordering::Relaxed) + } } From 4266221ae0f83cd91ab6d1d0202291bfbc2739b3 Mon Sep 17 00:00:00 2001 From: ZhennanWu <38578020+ZhennanWu@users.noreply.github.com> Date: Fri, 10 Mar 2023 05:05:43 -0800 Subject: [PATCH 196/283] Add AbortRegistration::handle (#2712) Co-authored-by: ZhennanWu --- futures-util/src/abortable.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs index cb81ce32cd..9dbcfc2b52 100644 --- a/futures-util/src/abortable.rs +++ b/futures-util/src/abortable.rs @@ -78,6 +78,17 @@ pub struct AbortRegistration { pub(crate) inner: Arc, } +impl AbortRegistration { + /// Create an [`AbortHandle`] from the given [`AbortRegistration`]. + /// + /// The created [`AbortHandle`] is functionally the same as any other + /// [`AbortHandle`]s that are associated with the same [`AbortRegistration`], + /// such as the one created by [`AbortHandle::new_pair`]. + pub fn handle(&self) -> AbortHandle { + AbortHandle { inner: self.inner.clone() } + } +} + /// A handle to an `Abortable` task. #[derive(Debug, Clone)] pub struct AbortHandle { From 0ffaaa0859db4fe29dd4221d89e3bf3495b5f3f5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 10 Mar 2023 23:18:55 +0900 Subject: [PATCH 197/283] Make BiLock strict-provenance compatible (#2716) --- futures-util/src/lock/bilock.rs | 46 +++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 2174079c83..7ddc66ad2c 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -3,11 +3,11 @@ use alloc::boxed::Box; use alloc::sync::Arc; use core::cell::UnsafeCell; -use core::fmt; use core::ops::{Deref, DerefMut}; use core::pin::Pin; -use core::sync::atomic::AtomicUsize; +use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering::SeqCst; +use core::{fmt, ptr}; #[cfg(feature = "bilock")] use futures_core::future::Future; use futures_core::task::{Context, Poll, Waker}; @@ -41,7 +41,7 @@ pub struct BiLock { #[derive(Debug)] struct Inner { - state: AtomicUsize, + state: AtomicPtr, value: Option>, } @@ -61,7 +61,10 @@ impl BiLock { /// Similarly, reuniting the lock and extracting the inner value is only /// possible when `T` is `Unpin`. pub fn new(t: T) -> (Self, Self) { - let arc = Arc::new(Inner { state: AtomicUsize::new(0), value: Some(UnsafeCell::new(t)) }); + let arc = Arc::new(Inner { + state: AtomicPtr::new(ptr::null_mut()), + value: Some(UnsafeCell::new(t)), + }); (Self { arc: arc.clone() }, Self { arc }) } @@ -87,7 +90,8 @@ impl BiLock { pub fn poll_lock(&self, cx: &mut Context<'_>) -> Poll> { let mut waker = None; loop { - match self.arc.state.swap(1, SeqCst) { + let n = self.arc.state.swap(invalid_ptr(1), SeqCst); + match n as usize { // Woohoo, we grabbed the lock! 0 => return Poll::Ready(BiLockGuard { bilock: self }), @@ -96,8 +100,8 @@ impl BiLock { // A task was previously blocked on this lock, likely our task, // so we need to update that task. - n => unsafe { - let mut prev = Box::from_raw(n as *mut Waker); + _ => unsafe { + let mut prev = Box::from_raw(n); *prev = cx.waker().clone(); waker = Some(prev); }, @@ -105,9 +109,9 @@ impl BiLock { // type ascription for safety's sake! let me: Box = waker.take().unwrap_or_else(|| Box::new(cx.waker().clone())); - let me = Box::into_raw(me) as usize; + let me = Box::into_raw(me); - match self.arc.state.compare_exchange(1, me, SeqCst, SeqCst) { + match self.arc.state.compare_exchange(invalid_ptr(1), me, SeqCst, SeqCst) { // The lock is still locked, but we've now parked ourselves, so // just report that we're scheduled to receive a notification. Ok(_) => return Poll::Pending, @@ -115,8 +119,8 @@ impl BiLock { // Oops, looks like the lock was unlocked after our swap above // and before the compare_exchange. Deallocate what we just // allocated and go through the loop again. - Err(0) => unsafe { - waker = Some(Box::from_raw(me as *mut Waker)); + Err(n) if n.is_null() => unsafe { + waker = Some(Box::from_raw(me)); }, // The top of this loop set the previous state to 1, so if we @@ -125,7 +129,7 @@ impl BiLock { // but we're trying to acquire the lock and there's only one // other reference of the lock, so it should be impossible for // that task to ever block itself. - Err(n) => panic!("invalid state: {}", n), + Err(n) => panic!("invalid state: {}", n as usize), } } } @@ -164,7 +168,8 @@ impl BiLock { } fn unlock(&self) { - match self.arc.state.swap(0, SeqCst) { + let n = self.arc.state.swap(ptr::null_mut(), SeqCst); + match n as usize { // we've locked the lock, shouldn't be possible for us to see an // unlocked lock. 0 => panic!("invalid unlocked state"), @@ -174,8 +179,8 @@ impl BiLock { // Another task has parked themselves on this lock, let's wake them // up as its now their turn. - n => unsafe { - Box::from_raw(n as *mut Waker).wake(); + _ => unsafe { + Box::from_raw(n).wake(); }, } } @@ -189,7 +194,7 @@ impl Inner { impl Drop for Inner { fn drop(&mut self) { - assert_eq!(self.state.load(SeqCst), 0); + assert!(self.state.load(SeqCst).is_null()); } } @@ -277,3 +282,12 @@ impl<'a, T> Future for BiLockAcquire<'a, T> { self.bilock.poll_lock(cx) } } + +// Based on core::ptr::invalid_mut. Equivalent to `addr as *mut T`, but is strict-provenance compatible. +#[allow(clippy::useless_transmute)] +#[inline] +fn invalid_ptr(addr: usize) -> *mut T { + // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that + // pointer). + unsafe { core::mem::transmute(addr) } +} From d1acf088521f1a0a509ee1a5ba8d5c4e86beb4a2 Mon Sep 17 00:00:00 2001 From: Dmitry Rodionov Date: Fri, 10 Mar 2023 20:00:23 +0200 Subject: [PATCH 198/283] tests: restore disabled tests and benches for BiLock (#2715) --- futures-util/benches/bilock.rs | 68 +++++++++++ futures-util/benches_disabled/bilock.rs | 122 -------------------- futures/{tests_disabled => tests}/bilock.rs | 68 +++++------ 3 files changed, 103 insertions(+), 155 deletions(-) create mode 100644 futures-util/benches/bilock.rs delete mode 100644 futures-util/benches_disabled/bilock.rs rename futures/{tests_disabled => tests}/bilock.rs (52%) diff --git a/futures-util/benches/bilock.rs b/futures-util/benches/bilock.rs new file mode 100644 index 0000000000..013f3351e4 --- /dev/null +++ b/futures-util/benches/bilock.rs @@ -0,0 +1,68 @@ +#![feature(test)] +#![cfg(feature = "bilock")] + +extern crate test; + +use futures::task::Poll; +use futures_test::task::noop_context; +use futures_util::lock::BiLock; + +use crate::test::Bencher; + +#[bench] +fn contended(b: &mut Bencher) { + let mut context = noop_context(); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + for _ in 0..1000 { + let x_guard = match x.poll_lock(&mut context) { + Poll::Ready(guard) => guard, + _ => panic!(), + }; + + // Try poll second lock while first lock still holds the lock + match y.poll_lock(&mut context) { + Poll::Pending => (), + _ => panic!(), + }; + + drop(x_guard); + + let y_guard = match y.poll_lock(&mut context) { + Poll::Ready(guard) => guard, + _ => panic!(), + }; + + drop(y_guard); + } + (x, y) + }); +} + +#[bench] +fn lock_unlock(b: &mut Bencher) { + let mut context = noop_context(); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + for _ in 0..1000 { + let x_guard = match x.poll_lock(&mut context) { + Poll::Ready(guard) => guard, + _ => panic!(), + }; + + drop(x_guard); + + let y_guard = match y.poll_lock(&mut context) { + Poll::Ready(guard) => guard, + _ => panic!(), + }; + + drop(y_guard); + } + (x, y) + }) +} diff --git a/futures-util/benches_disabled/bilock.rs b/futures-util/benches_disabled/bilock.rs deleted file mode 100644 index 417f75d31e..0000000000 --- a/futures-util/benches_disabled/bilock.rs +++ /dev/null @@ -1,122 +0,0 @@ -#![feature(test)] - -#[cfg(feature = "bilock")] -mod bench { - use futures::executor::LocalPool; - use futures::task::{Context, Waker}; - use futures_util::lock::BiLock; - use futures_util::lock::BiLockAcquire; - use futures_util::lock::BiLockAcquired; - use futures_util::task::ArcWake; - - use std::sync::Arc; - use test::Bencher; - - fn notify_noop() -> Waker { - struct Noop; - - impl ArcWake for Noop { - fn wake(_: &Arc) {} - } - - ArcWake::into_waker(Arc::new(Noop)) - } - - /// Pseudo-stream which simply calls `lock.poll()` on `poll` - struct LockStream { - lock: BiLockAcquire, - } - - impl LockStream { - fn new(lock: BiLock) -> Self { - Self { lock: lock.lock() } - } - - /// Release a lock after it was acquired in `poll`, - /// so `poll` could be called again. - fn release_lock(&mut self, guard: BiLockAcquired) { - self.lock = guard.unlock().lock() - } - } - - impl Stream for LockStream { - type Item = BiLockAcquired; - type Error = (); - - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll, Self::Error> { - self.lock.poll(cx).map(|a| a.map(Some)) - } - } - - #[bench] - fn contended(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); - - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - // Try poll second lock while first lock still holds the lock - match y.poll_next(&mut waker) { - Ok(Poll::Pending) => (), - _ => panic!(), - }; - - x.release_lock(x_guard); - - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - y.release_lock(y_guard); - } - (x, y) - }); - } - - #[bench] - fn lock_unlock(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); - - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - x.release_lock(x_guard); - - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - y.release_lock(y_guard); - } - (x, y) - }) - } -} diff --git a/futures/tests_disabled/bilock.rs b/futures/tests/bilock.rs similarity index 52% rename from futures/tests_disabled/bilock.rs rename to futures/tests/bilock.rs index 0166ca48ba..b103487849 100644 --- a/futures/tests_disabled/bilock.rs +++ b/futures/tests/bilock.rs @@ -1,34 +1,38 @@ +#![cfg(feature = "bilock")] + +use futures::executor::block_on; use futures::future; use futures::stream; -use futures::task; +use futures::task::{Context, Poll}; +use futures::Future; +use futures::StreamExt; +use futures_test::task::noop_context; use futures_util::lock::BiLock; +use std::pin::Pin; use std::thread; -// mod support; -// use support::*; - #[test] fn smoke() { - let future = future::lazy(|_| { + let future = future::lazy(|cx| { let (a, b) = BiLock::new(1); { - let mut lock = match a.poll_lock() { + let mut lock = match a.poll_lock(cx) { Poll::Ready(l) => l, Poll::Pending => panic!("poll not ready"), }; assert_eq!(*lock, 1); *lock = 2; - assert!(b.poll_lock().is_pending()); - assert!(a.poll_lock().is_pending()); + assert!(b.poll_lock(cx).is_pending()); + assert!(a.poll_lock(cx).is_pending()); } - assert!(b.poll_lock().is_ready()); - assert!(a.poll_lock().is_ready()); + assert!(b.poll_lock(cx).is_ready()); + assert!(a.poll_lock(cx).is_ready()); { - let lock = match b.poll_lock() { + let lock = match b.poll_lock(cx) { Poll::Ready(l) => l, Poll::Pending => panic!("poll not ready"), }; @@ -40,34 +44,32 @@ fn smoke() { Ok::<(), ()>(()) }); - assert!(task::spawn(future) - .poll_future_notify(¬ify_noop(), 0) - .expect("failure in poll") - .is_ready()); + assert_eq!(block_on(future), Ok(())); } #[test] fn concurrent() { const N: usize = 10000; + let mut cx = noop_context(); let (a, b) = BiLock::new(0); let a = Increment { a: Some(a), remaining: N }; - let b = stream::iter_ok(0..N).fold(b, |b, _n| { - b.lock().map(|mut b| { - *b += 1; - b.unlock() - }) + let b = stream::iter(0..N).fold(b, |b, _n| async { + let mut g = b.lock().await; + *g += 1; + drop(g); + b }); - let t1 = thread::spawn(move || a.wait()); - let b = b.wait().expect("b error"); - let a = t1.join().unwrap().expect("a error"); + let t1 = thread::spawn(move || block_on(a)); + let b = block_on(b); + let a = t1.join().unwrap(); - match a.poll_lock() { + match a.poll_lock(&mut cx) { Poll::Ready(l) => assert_eq!(*l, 2 * N), Poll::Pending => panic!("poll not ready"), } - match b.poll_lock() { + match b.poll_lock(&mut cx) { Poll::Ready(l) => assert_eq!(*l, 2 * N), Poll::Pending => panic!("poll not ready"), } @@ -80,22 +82,22 @@ fn concurrent() { } impl Future for Increment { - type Item = BiLock; - type Error = (); + type Output = BiLock; - fn poll(&mut self) -> Poll, ()> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { if self.remaining == 0 { - return Ok(self.a.take().unwrap().into()); + return self.a.take().unwrap().into(); } - let a = self.a.as_ref().unwrap(); - let mut a = match a.poll_lock() { + let a = self.a.as_mut().unwrap(); + let mut a = match a.poll_lock(cx) { Poll::Ready(l) => l, - Poll::Pending => return Ok(Poll::Pending), + Poll::Pending => return Poll::Pending, }; - self.remaining -= 1; *a += 1; + drop(a); + self.remaining -= 1; } } } From 4b9dddf220d9757c65df198b35825988d7bdee87 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 11 Mar 2023 23:30:32 +0900 Subject: [PATCH 199/283] Remove dead code read_initializer feature has already been removed. --- futures-util/src/io/cursor.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/futures-util/src/io/cursor.rs b/futures-util/src/io/cursor.rs index b6fb3724c7..c6e2aeea28 100644 --- a/futures-util/src/io/cursor.rs +++ b/futures-util/src/io/cursor.rs @@ -1,6 +1,4 @@ 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 std::io; use std::pin::Pin; @@ -159,12 +157,6 @@ where } impl + Unpin> AsyncRead for Cursor { - #[cfg(feature = "read_initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - io::Read::initializer(&self.inner) - } - fn poll_read( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, From 335b43173f6a0361680a9cfcfccf86bc358ba388 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 11 Mar 2023 23:33:40 +0900 Subject: [PATCH 200/283] Automatically cancel outdated CI runs on PR --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 691bf560b8..c1635c6772 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,10 @@ defaults: run: shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: test: name: cargo test (${{ matrix.os }}) From cfc501c9c7ef53c531ed43533bb02845f65ff79e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 11 Mar 2023 23:58:42 +0900 Subject: [PATCH 201/283] Release 0.3.27 --- CHANGELOG.md | 7 +++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 42 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcea5404ec..90ff4ed719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.3.27 - 2023-03-11 + +* Add `TryFlattenUnordered` (#2577, #2590, #2606, #2607) +* Add `AbortHandle::is_aborted` (#2710) +* Add `AbortRegistration::handle` (#2712) +* Make BiLock strict-provenance compatible (#2716) + # 0.3.26 - 2023-01-30 * Add `Either::as_pin_mut` and `Either::as_pin_ref` (#2691) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 753fd46ac3..4caea4e27f 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 5b78edc285..0fddd70680 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 3ee30591f2..d1a44011e7 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.26", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.27", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 775be9b42a..b4f0383c25 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 5e2ed173a0..d865c1e3b6 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 29174ac68a..b2234a3eae 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 4c2d1df632..34f66e4eab 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index e2ae339d3e..7d9cecab4d 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.26", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.26", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.26", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.26", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.26", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.26", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.26", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.27", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.27", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.27", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.27", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.27", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.27", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.27", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 95c3dee8e7..ca05fe43a9 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.26", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.26", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.26", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.27", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.27", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.27", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index e7a5f386a0..f2832f27b3 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.26" +version = "0.3.27" edition = "2018" rust-version = "1.45" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.26", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.26", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.26", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.26", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.26", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.26", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.26", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.27", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.27", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.27", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.27", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 8869f59dcc8551de403ea5126851a99638a4d487 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 12 Mar 2023 01:18:39 +0900 Subject: [PATCH 202/283] Tweak changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ff4ed719..0814ccdf52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Add `TryFlattenUnordered` (#2577, #2590, #2606, #2607) * Add `AbortHandle::is_aborted` (#2710) * Add `AbortRegistration::handle` (#2712) -* Make BiLock strict-provenance compatible (#2716) +* Make `BiLock` strict-provenance compatible (#2716) # 0.3.26 - 2023-01-30 From 4b86e46e40c89bbcd248c9d9204b345abf9101fd Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 12 Mar 2023 15:16:26 +0900 Subject: [PATCH 203/283] Use setup-cross-toolchain-action instead of cross --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1635c6772..07a39cc1ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,24 +48,24 @@ jobs: - run: cargo test --workspace --all-features --release cross: - name: cross test --target ${{ matrix.target }} + name: cargo test --target ${{ matrix.target }} strategy: fail-fast: false matrix: target: - - i686-unknown-linux-gnu - aarch64-unknown-linux-gnu + - armv7-unknown-linux-gnueabihf + - i686-unknown-linux-gnu runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust run: rustup update nightly && rustup default nightly - - name: Install cross - uses: taiki-e/install-action@cross - - run: cross test --target ${{ matrix.target }} --workspace --all-features - - run: cross test --target ${{ matrix.target }} --workspace --all-features --release - # TODO: https://github.com/rust-lang/futures-rs/issues/2451 - if: matrix.target != 'aarch64-unknown-linux-gnu' + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: ${{ matrix.target }} + - run: cargo test --target ${{ matrix.target }} --workspace --all-features $DOCTEST_XCOMPILE + - run: cargo test --target ${{ matrix.target }} --workspace --all-features --release $DOCTEST_XCOMPILE core-msrv: name: cargo +${{ matrix.rust }} build (futures-{core, io, sink}) From 890f8934fdb89478f0610a388b3cd819656f4700 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 12 Mar 2023 15:17:42 +0900 Subject: [PATCH 204/283] Fix rustdoc warning ``` warning: `crate::task::panic_waker` is both a module and a function --> futures-test/src/task/mod.rs:26:23 | 26 | //! - [`panic_waker`](crate::task::panic_waker) creates a waker that panics when | ^^^^^^^^^^^^^^^^^^^^^^^^ ambiguous link | = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default help: to link to the module, prefix with `mod@` | 26 | //! - [`panic_waker`](mod@crate::task::panic_waker) creates a waker that panics when | ++++ help: to link to the function, add parentheses | 26 | //! - [`panic_waker`](crate::task::panic_waker()) creates a waker that panics when | ++ ``` --- futures-test/src/task/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-test/src/task/mod.rs b/futures-test/src/task/mod.rs index cec645d9f2..118291c0c9 100644 --- a/futures-test/src/task/mod.rs +++ b/futures-test/src/task/mod.rs @@ -23,7 +23,7 @@ //! Test wakers: //! - [`noop_waker`](crate::task::noop_waker) creates a waker that ignores calls to //! [`wake`](futures_core::task::Waker). -//! - [`panic_waker`](crate::task::panic_waker) creates a waker that panics when +//! - [`panic_waker`](crate::task::panic_waker()) creates a waker that panics when //! [`wake`](futures_core::task::Waker) is called. //! - [`new_count_waker`](crate::task::new_count_waker) creates a waker that increments a counter whenever //! [`wake`](futures_core::task::Waker) is called. From a730a19c8f933254a9d571a153275e3d96391ab3 Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Wed, 22 Mar 2023 16:50:45 +0100 Subject: [PATCH 205/283] FlattenUnordered: always replace inner wakers (#2726) --- .../src/stream/stream/flatten_unordered.rs | 23 +++++----- futures/tests/no-std/src/lib.rs | 1 + futures/tests/stream.rs | 43 +++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 484c3733aa..f5430bc309 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -209,9 +209,8 @@ impl WrappedWaker { /// /// This function will modify waker's `inner_waker` via `UnsafeCell`, so /// it should be used only during `POLLING` phase by one thread at the time. - unsafe fn replace_waker(self_arc: &mut Arc, cx: &Context<'_>) -> Waker { + unsafe fn replace_waker(self_arc: &mut Arc, cx: &Context<'_>) { *self_arc.inner_waker.get() = cx.waker().clone().into(); - waker(self_arc.clone()) } /// Attempts to start the waking process for the waker with the given value. @@ -414,6 +413,12 @@ where } }; + // Safety: now state is `POLLING`. + unsafe { + WrappedWaker::replace_waker(this.stream_waker, cx); + WrappedWaker::replace_waker(this.inner_streams_waker, cx) + }; + if poll_state_value & NEED_TO_POLL_STREAM != NONE { let mut stream_waker = None; @@ -431,13 +436,9 @@ where break; } else { - // Initialize base stream waker if it's not yet initialized - if stream_waker.is_none() { - // Safety: now state is `POLLING`. - stream_waker - .replace(unsafe { WrappedWaker::replace_waker(this.stream_waker, cx) }); - } - let mut cx = Context::from_waker(stream_waker.as_ref().unwrap()); + let mut cx = Context::from_waker( + stream_waker.get_or_insert_with(|| waker(this.stream_waker.clone())), + ); match this.stream.as_mut().poll_next(&mut cx) { Poll::Ready(Some(item)) => { @@ -475,9 +476,7 @@ where } if poll_state_value & NEED_TO_POLL_INNER_STREAMS != NONE { - // Safety: now state is `POLLING`. - let inner_streams_waker = - unsafe { WrappedWaker::replace_waker(this.inner_streams_waker, cx) }; + let inner_streams_waker = waker(this.inner_streams_waker.clone()); let mut cx = Context::from_waker(&inner_streams_waker); match this.inner_streams.as_mut().poll_next(&mut cx) { diff --git a/futures/tests/no-std/src/lib.rs b/futures/tests/no-std/src/lib.rs index 89a8fa1ff1..4e2196860b 100644 --- a/futures/tests/no-std/src/lib.rs +++ b/futures/tests/no-std/src/lib.rs @@ -1,5 +1,6 @@ #![cfg(nightly)] #![no_std] +#![allow(useless_anonymous_reexport)] #[cfg(feature = "futures-core-alloc")] #[cfg(target_has_atomic = "ptr")] diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 5cde45833f..9d61cb60c8 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -14,6 +14,7 @@ use futures::stream::{self, StreamExt}; use futures::task::Poll; use futures::{ready, FutureExt}; use futures_core::Stream; +use futures_executor::ThreadPool; use futures_test::task::noop_context; #[test] @@ -65,6 +66,7 @@ fn flatten_unordered() { use futures::task::*; use std::convert::identity; use std::pin::Pin; + use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::Duration; @@ -322,6 +324,47 @@ fn flatten_unordered() { assert_eq!(values, (0..60).collect::>()); }); } + + // nested `flatten_unordered` + let te = ThreadPool::new().unwrap(); + let handle = te + .spawn_with_handle(async move { + let inner = stream::iter(0..10) + .then(|_| { + let task = Arc::new(AtomicBool::new(false)); + let mut spawned = false; + + future::poll_fn(move |cx| { + if !spawned { + let waker = cx.waker().clone(); + let task = task.clone(); + + std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(500)); + task.store(true, Ordering::Release); + + waker.wake_by_ref() + }); + spawned = true; + } + + if task.load(Ordering::Acquire) { + Poll::Ready(Some(())) + } else { + Poll::Pending + } + }) + }) + .map(|_| stream::once(future::ready(()))) + .flatten_unordered(None); + + let stream = stream::once(future::ready(inner)).flatten_unordered(None); + + assert_eq!(stream.count().await, 10); + }) + .unwrap(); + + block_on(handle); } #[test] From 94e020dac72530f487debd63dc7d6244763a7f6e Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 22 Mar 2023 16:53:09 +0100 Subject: [PATCH 206/283] Use `Waker::will_wake()` to avoid a cloning op (#2723) --- futures-core/src/task/__internal/atomic_waker.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/futures-core/src/task/__internal/atomic_waker.rs b/futures-core/src/task/__internal/atomic_waker.rs index 0677e83b09..2fc594b8a9 100644 --- a/futures-core/src/task/__internal/atomic_waker.rs +++ b/futures-core/src/task/__internal/atomic_waker.rs @@ -271,7 +271,12 @@ impl AtomicWaker { WAITING => { unsafe { // Locked acquired, update the waker cell - *self.waker.get() = Some(waker.clone()); + + // Avoid cloning the waker if the old waker will awaken the same task. + match &*self.waker.get() { + Some(old_waker) if old_waker.will_wake(waker) => (), + _ => *self.waker.get() = Some(waker.clone()), + } // Release the lock. If the state transitioned to include // the `WAKING` bit, this means that at least one wake has From 6a436ebd15fc2d69398b279a8d01ea51abf6750c Mon Sep 17 00:00:00 2001 From: Oleg Nosov Date: Thu, 23 Mar 2023 00:28:15 +0100 Subject: [PATCH 207/283] Don't ignore empty state polling (#2728) * Don't ignore empty state polling * Test case * Start polling in a loop to ensure we don't wait for an outdated waker --- .../src/stream/stream/flatten_unordered.rs | 13 ++- futures/tests/stream.rs | 95 ++++++++++++------- 2 files changed, 69 insertions(+), 39 deletions(-) diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index f5430bc309..44c6ace2f7 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -61,14 +61,14 @@ impl SharedPollState { } /// Attempts to start polling, returning stored state in case of success. - /// Returns `None` if either waker is waking at the moment or state is empty. + /// Returns `None` if either waker is waking at the moment. fn start_polling( &self, ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { - if value & WAKING == NONE && value & NEED_TO_POLL_ALL != NONE { + if value & WAKING == NONE { Some(POLLING) } else { None @@ -405,11 +405,10 @@ where let mut this = self.as_mut().project(); - let (mut poll_state_value, state_bomb) = match this.poll_state.start_polling() { - Some(value) => value, - _ => { - // Waker was called, just wait for the next poll - return Poll::Pending; + // Attempt to start polling, in case some waker is holding the lock, wait in loop + let (mut poll_state_value, state_bomb) = loop { + if let Some(value) = this.poll_state.start_polling() { + break value; } }; diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 9d61cb60c8..79d8e233cc 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -325,46 +325,77 @@ fn flatten_unordered() { }); } + fn timeout(time: Duration, value: I) -> impl Future { + let ready = Arc::new(AtomicBool::new(false)); + let mut spawned = false; + + future::poll_fn(move |cx| { + if !spawned { + let waker = cx.waker().clone(); + let ready = ready.clone(); + + std::thread::spawn(move || { + std::thread::sleep(time); + ready.store(true, Ordering::Release); + + waker.wake_by_ref() + }); + spawned = true; + } + + if ready.load(Ordering::Acquire) { + Poll::Ready(value.clone()) + } else { + Poll::Pending + } + }) + } + + fn build_nested_fu(st: S) -> impl Stream + Unpin + where + S::Item: Clone, + { + let inner = st + .then(|item| timeout(Duration::from_millis(50), item)) + .enumerate() + .map(|(idx, value)| { + stream::once(if idx % 2 == 0 { + future::ready(value).left_future() + } else { + timeout(Duration::from_millis(100), value).right_future() + }) + }) + .flatten_unordered(None); + + stream::once(future::ready(inner)).flatten_unordered(None) + } + // nested `flatten_unordered` let te = ThreadPool::new().unwrap(); - let handle = te + let base_handle = te .spawn_with_handle(async move { - let inner = stream::iter(0..10) - .then(|_| { - let task = Arc::new(AtomicBool::new(false)); - let mut spawned = false; - - future::poll_fn(move |cx| { - if !spawned { - let waker = cx.waker().clone(); - let task = task.clone(); - - std::thread::spawn(move || { - std::thread::sleep(Duration::from_millis(500)); - task.store(true, Ordering::Release); - - waker.wake_by_ref() - }); - spawned = true; - } - - if task.load(Ordering::Acquire) { - Poll::Ready(Some(())) - } else { - Poll::Pending - } - }) - }) - .map(|_| stream::once(future::ready(()))) - .flatten_unordered(None); + let fu = build_nested_fu(stream::iter(1..=10)); - let stream = stream::once(future::ready(inner)).flatten_unordered(None); + assert_eq!(fu.count().await, 10); + }) + .unwrap(); + + block_on(base_handle); + + let empty_state_move_handle = te + .spawn_with_handle(async move { + let mut fu = build_nested_fu(stream::iter(1..10)); + { + let mut cx = noop_context(); + let _ = fu.poll_next_unpin(&mut cx); + let _ = fu.poll_next_unpin(&mut cx); + } - assert_eq!(stream.count().await, 10); + assert_eq!(fu.count().await, 9); }) .unwrap(); - block_on(handle); + block_on(empty_state_move_handle); } #[test] From 3bee39695eae555346669e504af22d1b27f52bd9 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 23 Mar 2023 15:03:46 +0000 Subject: [PATCH 208/283] SelectAll doesn't need pin-project (#2729) Closes #2724 --- futures-util/src/stream/select_all.rs | 35 ++++++++++++--------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index 3474331adc..121b6a0e59 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -8,29 +8,24 @@ use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_project_lite::pin_project; - use super::assert_stream; use crate::stream::{futures_unordered, FuturesUnordered, StreamExt, StreamFuture}; -pin_project! { - /// An unbounded set of streams - /// - /// This "combinator" provides the ability to maintain a set of streams - /// and drive them all to completion. - /// - /// Streams are pushed into this set and their realized values are - /// yielded as they become ready. Streams will only be polled when they - /// generate notifications. This allows to coordinate a large number of streams. - /// - /// Note that you can create a ready-made `SelectAll` via the - /// `select_all` function in the `stream` module, or you can start with an - /// empty set with the `SelectAll::new` constructor. - #[must_use = "streams do nothing unless polled"] - pub struct SelectAll { - #[pin] - inner: FuturesUnordered>, - } +/// An unbounded set of streams +/// +/// This "combinator" provides the ability to maintain a set of streams +/// and drive them all to completion. +/// +/// Streams are pushed into this set and their realized values are +/// yielded as they become ready. Streams will only be polled when they +/// generate notifications. This allows to coordinate a large number of streams. +/// +/// Note that you can create a ready-made `SelectAll` via the +/// `select_all` function in the `stream` module, or you can start with an +/// empty set with the `SelectAll::new` constructor. +#[must_use = "streams do nothing unless polled"] +pub struct SelectAll { + inner: FuturesUnordered>, } impl Debug for SelectAll { From a3f80e6b19315d24bde80eeefc595c614c9e0e9d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 31 Mar 2023 01:26:51 +0900 Subject: [PATCH 209/283] Fix unknown_lints and unused_imports warnings in test (#2732) ``` error: unknown lint: `useless_anonymous_reexport` --> futures/tests/no-std/src/lib.rs:3:10 | 3 | #![allow(useless_anonymous_reexport)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D unknown-lints` implied by `-D warnings` error: unused import: `futures_core::task::__internal::AtomicWaker as _` --> futures/tests/no-std/src/lib.rs:7:9 | 7 | pub use futures_core::task::__internal::AtomicWaker as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D unused-imports` implied by `-D warnings` error: unused import: `futures_channel::oneshot as _` --> futures/tests/no-std/src/lib.rs:15:9 | 15 | pub use futures_channel::oneshot as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unused import: `futures::task::AtomicWaker as _` --> futures/tests/no-std/src/lib.rs:19:9 | 19 | pub use futures::task::AtomicWaker as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unused import: `futures::stream::FuturesOrdered as _` --> futures/tests/no-std/src/lib.rs:23:9 | 23 | pub use futures::stream::FuturesOrdered as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unused import: `futures_util::task::AtomicWaker as _` --> futures/tests/no-std/src/lib.rs:27:9 | 27 | pub use futures_util::task::AtomicWaker as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unused import: `futures_util::stream::FuturesOrdered as _` --> futures/tests/no-std/src/lib.rs:31:9 | 31 | pub use futures_util::stream::FuturesOrdered as _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` --- futures/tests/no-std/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures/tests/no-std/src/lib.rs b/futures/tests/no-std/src/lib.rs index 4e2196860b..7209e7f6a1 100644 --- a/futures/tests/no-std/src/lib.rs +++ b/futures/tests/no-std/src/lib.rs @@ -1,6 +1,6 @@ #![cfg(nightly)] #![no_std] -#![allow(useless_anonymous_reexport)] +#![allow(unused_imports)] #[cfg(feature = "futures-core-alloc")] #[cfg(target_has_atomic = "ptr")] From 98e80d4d2863028a86a0c95616ad1efdaafd35d2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 31 Mar 2023 01:42:21 +0900 Subject: [PATCH 210/283] Bump MSRV of utility crates to 1.56 (#2733) syn 2.0 requires 1.56. --- .github/workflows/ci.yml | 2 +- README.md | 2 +- futures-channel/Cargo.toml | 2 +- futures-channel/README.md | 2 +- futures-executor/Cargo.toml | 2 +- futures-executor/README.md | 2 +- futures-macro/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-task/README.md | 2 +- futures-test/Cargo.toml | 2 +- futures-test/README.md | 2 +- futures-util/Cargo.toml | 2 +- futures-util/README.md | 2 +- futures/Cargo.toml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07a39cc1ce..9d1be25f68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,7 +108,7 @@ jobs: rust: # This is the minimum Rust version supported by futures, futures-util, futures-task, futures-macro, futures-executor, futures-channel, futures-test. # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - - '1.45' + - '1.56' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index 45e1f5b320..355d6078e3 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Add this to your `Cargo.toml`: futures = "0.3" ``` -The current `futures` requires Rust 1.45 or later. +The current `futures` requires Rust 1.56 or later. ### Feature `std` diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 4caea4e27f..0c1119a112 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-channel" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-channel/README.md b/futures-channel/README.md index 3287be924c..e886bd1cad 100644 --- a/futures-channel/README.md +++ b/futures-channel/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-channel = "0.3" ``` -The current `futures-channel` requires Rust 1.45 or later. +The current `futures-channel` requires Rust 1.56 or later. ## License diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index d1a44011e7..0347d1f6d8 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-executor" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-executor/README.md b/futures-executor/README.md index 67086851e4..724ff5bb33 100644 --- a/futures-executor/README.md +++ b/futures-executor/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-executor = "0.3" ``` -The current `futures-executor` requires Rust 1.45 or later. +The current `futures-executor` requires Rust 1.56 or later. ## License diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index d865c1e3b6..38e4229b21 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-macro" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 34f66e4eab..2fc9b25461 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-task" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-task/README.md b/futures-task/README.md index 8ceeba9d57..1ebec2d73d 100644 --- a/futures-task/README.md +++ b/futures-task/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-task = "0.3" ``` -The current `futures-task` requires Rust 1.45 or later. +The current `futures-task` requires Rust 1.56 or later. ## License diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 7d9cecab4d..4e0b774c5e 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-test" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-test/README.md b/futures-test/README.md index b3c30e5d6c..34595aaf30 100644 --- a/futures-test/README.md +++ b/futures-test/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-test = "0.3" ``` -The current `futures-test` requires Rust 1.45 or later. +The current `futures-test` requires Rust 1.56 or later. ## License diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index ca05fe43a9..db43936dba 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -2,7 +2,7 @@ name = "futures-util" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" diff --git a/futures-util/README.md b/futures-util/README.md index 6e0aaed847..60e2c2109a 100644 --- a/futures-util/README.md +++ b/futures-util/README.md @@ -11,7 +11,7 @@ Add this to your `Cargo.toml`: futures-util = "0.3" ``` -The current `futures-util` requires Rust 1.45 or later. +The current `futures-util` requires Rust 1.56 or later. ## License diff --git a/futures/Cargo.toml b/futures/Cargo.toml index f2832f27b3..976e83873f 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -2,7 +2,7 @@ name = "futures" version = "0.3.27" edition = "2018" -rust-version = "1.45" +rust-version = "1.56" license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] From 206b12b05ba1db8d1ab4f61c1cef808838be1274 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Thu, 30 Mar 2023 09:42:58 -0700 Subject: [PATCH 211/283] Update to syn 2 (#2730) --- futures-macro/Cargo.toml | 2 +- futures-macro/src/executor.rs | 1 + futures-macro/src/select.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 38e4229b21..046a84c5f0 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -18,4 +18,4 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "1.0.56", features = ["full"] } +syn = { version = "2.0.8", features = ["full"] } diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index 40a091f94c..7f1d0a93f4 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -44,6 +44,7 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { #path::block_on(async #body) }) .unwrap(), + None, )]; let gen = quote! { diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index 0c8e5f1ca0..2789b3e659 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -51,7 +51,7 @@ impl Parse for Select { CaseKind::Default } else { // ` = ` - let pat = input.parse()?; + let pat = Pat::parse_multi_with_leading_vert(input)?; input.parse::()?; let expr = input.parse()?; CaseKind::Normal(pat, expr) From 1685f8bdabb8e7f007610f3494425aeabf28117d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 31 Mar 2023 01:56:24 +0900 Subject: [PATCH 212/283] Release 0.3.28 --- CHANGELOG.md | 5 +++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 40 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0814ccdf52..f23a1c84e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.3.28 - 2023-03-30 + +* Update to syn 2. This raises MSRV of utility crates to 1.56. (#2730, #2733) +* Fix bug in `FlattenUnordered` (#2726, #2728) + # 0.3.27 - 2023-03-11 * Add `TryFlattenUnordered` (#2577, #2590, #2606, #2607) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 0c1119a112..588cf69b2f 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 0fddd70680..a65e8fb034 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 0347d1f6d8..bd47e42a4e 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.27", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.28", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index b4f0383c25..dac499faab 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 046a84c5f0..23a2529cd3 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index b2234a3eae..ec760185ba 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 2fc9b25461..8ae7c3f5ad 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 4e0b774c5e..6b16a384ab 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.27", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.27", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.27", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.27", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.27", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.27", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.27", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.28", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.28", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.28", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.28", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.28", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.28", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.28", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index db43936dba..93924692f0 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.27", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.27", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.27", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.28", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.28", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.28", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 976e83873f..140fa210f0 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.27" +version = "0.3.28" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.27", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.27", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.27", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.27", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.27", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.27", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.27", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.28", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.28", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.28", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.28", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From aeeb76c408435f5e607474dc3828382ea9a42a6c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 7 Sep 2023 02:16:13 +0900 Subject: [PATCH 213/283] Update actions/checkout action to v4 --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .github/workflows/release.yml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d1be25f68..c4e89a163f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: - windows-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update nightly --no-self-update && rustup default nightly @@ -58,7 +58,7 @@ jobs: - i686-unknown-linux-gnu runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - uses: taiki-e/setup-cross-toolchain-action@v1 @@ -77,7 +77,7 @@ jobs: - '1.36' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. @@ -111,7 +111,7 @@ jobs: - '1.56' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -142,7 +142,7 @@ jobs: - nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -154,7 +154,7 @@ jobs: name: cargo build -Z minimal-versions runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -176,7 +176,7 @@ jobs: - thumbv6m-none-eabi runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup target add ${{ matrix.target }} @@ -208,7 +208,7 @@ jobs: name: cargo bench runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - run: cargo bench --workspace @@ -218,7 +218,7 @@ jobs: name: cargo hack check --feature-powerset runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -243,7 +243,7 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - run: ci/no_atomic_cas.sh @@ -276,7 +276,7 @@ jobs: name: cargo miri test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - run: cargo miri test --workspace --all-features @@ -295,7 +295,7 @@ jobs: - thread runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup component add rust-src @@ -312,7 +312,7 @@ jobs: # name: cargo clippy # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v3 + # - uses: actions/checkout@v4 # - name: Install Rust # run: rustup toolchain install nightly --component clippy && rustup default nightly # - run: cargo clippy --workspace --all-features --all-targets @@ -321,7 +321,7 @@ jobs: name: cargo fmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo fmt --all -- --check @@ -330,7 +330,7 @@ jobs: name: cargo doc runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update nightly && rustup default nightly - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --workspace --no-deps --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c52681b21..5fec08e5bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: if: github.repository_owner == 'rust-lang' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md From da4a268c085a76d5706ff4e4caba44c82e763b27 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 Jul 2023 01:33:27 +0900 Subject: [PATCH 214/283] ci: Fix MSRV build ``` error package `log v0.4.19` cannot be built because it requires rustc 1.60.0 or newer, while the currently active rustc version is 1.56.1 ``` --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4e89a163f..6dba814623 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,7 +127,8 @@ jobs: # Check std feature - run: cargo hack build --workspace --ignore-private --no-default-features --features std --ignore-unknown-features # Check compat feature (futures, futures-util) - - run: cargo hack build -p futures -p futures-util --no-default-features --features std,io-compat + # Exclude io-compat feature because the MSRV when it is enabled depends on the MSRV of tokio 0.1. + - run: cargo hack build -p futures -p futures-util --no-default-features --features std,compat # Check thread-pool feature (futures, futures-executor) - run: cargo hack build -p futures -p futures-executor --no-default-features --features std,thread-pool From 1483b674ab9673e09ca166646faf7bf000a44dc3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 Jul 2023 01:37:42 +0900 Subject: [PATCH 215/283] Fix build error with -Z minimal-versions ``` error[E0635]: unknown feature `proc_macro_span_shrink` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.52/src/lib.rs:92:30 | 92 | feature(proc_macro_span, proc_macro_span_shrink) | ^^^^^^^^^^^^^^^^^^^^^^ ``` --- .github/workflows/ci.yml | 9 ++++----- futures-macro/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dba814623..a532333d0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - run: cargo build --tests --features default,thread-pool,io-compat --manifest-path futures/Cargo.toml minimal-versions: - name: cargo build -Z minimal-versions + name: cargo minimal-versions build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -160,10 +160,9 @@ jobs: run: rustup update nightly && rustup default nightly - name: Install cargo-hack uses: taiki-e/install-action@cargo-hack - # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - - run: cargo hack --remove-dev-deps --workspace - - run: cargo update -Z minimal-versions - - run: cargo build --workspace --all-features + - name: Install cargo-minimal-versions + uses: taiki-e/install-action@cargo-minimal-versions + - run: cargo minimal-versions build --workspace --ignore-private --all-features no-std: name: cargo build --target ${{ matrix.target }} diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 23a2529cd3..b8982377e1 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -16,6 +16,6 @@ proc-macro = true [features] [dependencies] -proc-macro2 = "1.0" +proc-macro2 = "1.0.60" quote = "1.0" syn = { version = "2.0.8", features = ["full"] } From 17e6cd88974ae5210b27c61ee39a9c474ae346a4 Mon Sep 17 00:00:00 2001 From: Dzenan Jupic <56133904+DzenanJupic@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:09:32 +0200 Subject: [PATCH 216/283] Implemented `TryStreamExt::try_ready_chunks` (#2757) --- futures-util/src/stream/mod.rs | 2 +- futures-util/src/stream/try_stream/mod.rs | 55 ++++++++ .../src/stream/try_stream/try_ready_chunks.rs | 126 ++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 futures-util/src/stream/try_stream/try_ready_chunks.rs diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index bf9506147c..34d68a80be 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -68,7 +68,7 @@ pub use self::try_stream::{ }; #[cfg(feature = "alloc")] -pub use self::try_stream::{TryChunks, TryChunksError}; +pub use self::try_stream::{TryChunks, TryChunksError, TryReadyChunks, TryReadyChunksError}; // Primitive streams diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 414a40dbe3..ab976b145a 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -111,6 +111,12 @@ mod try_chunks; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_chunks::{TryChunks, TryChunksError}; +#[cfg(feature = "alloc")] +mod try_ready_chunks; +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_ready_chunks::{TryReadyChunks, TryReadyChunksError}; + mod try_fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_fold::TryFold; @@ -640,6 +646,55 @@ pub trait TryStreamExt: TryStream { ) } + /// An adaptor for chunking up successful, ready items of the stream inside a vector. + /// + /// This combinator will attempt to pull successful 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 the underlying stream + /// returns `Poll::Pending`, and the collected chunk is not empty, it will + /// be immidiatly returned. + /// + /// Note that the vectors returned from this iterator may not always have + /// `capacity` elements. 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. + /// + /// This function is similar to + /// [`StreamExt::ready_chunks`](crate::stream::StreamExt::ready_chunks) but exits + /// early if an error occurs. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, TryReadyChunksError, TryStreamExt}; + /// + /// let stream = stream::iter(vec![Ok::(1), Ok(2), Ok(3), Err(4), Ok(5), Ok(6)]); + /// let mut stream = stream.try_ready_chunks(2); + /// + /// assert_eq!(stream.try_next().await, Ok(Some(vec![1, 2]))); + /// assert_eq!(stream.try_next().await, Err(TryReadyChunksError(vec![3], 4))); + /// assert_eq!(stream.try_next().await, Ok(Some(vec![5, 6]))); + /// # }) + /// ``` + /// + /// # Panics + /// + /// This method will panic if `capacity` is zero. + #[cfg(feature = "alloc")] + fn try_ready_chunks(self, capacity: usize) -> TryReadyChunks + where + Self: Sized, + { + assert_stream::, TryReadyChunksError>, _>( + TryReadyChunks::new(self, capacity), + ) + } + /// Attempt to filter the values produced by this stream according to the /// provided asynchronous closure. /// diff --git a/futures-util/src/stream/try_stream/try_ready_chunks.rs b/futures-util/src/stream/try_stream/try_ready_chunks.rs new file mode 100644 index 0000000000..8b1470ea26 --- /dev/null +++ b/futures-util/src/stream/try_stream/try_ready_chunks.rs @@ -0,0 +1,126 @@ +use crate::stream::{Fuse, IntoStream, StreamExt}; + +use alloc::vec::Vec; +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_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_ready_chunks`](super::TryStreamExt::try_ready_chunks) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryReadyChunks { + #[pin] + stream: Fuse>, + cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 + } +} + +impl TryReadyChunks { + pub(super) fn new(stream: St, capacity: usize) -> Self { + assert!(capacity > 0); + + Self { stream: IntoStream::new(stream).fuse(), cap: capacity } + } + + delegate_access_inner!(stream, St, (. .)); +} + +type TryReadyChunksStreamError = + TryReadyChunksError<::Ok, ::Error>; + +impl Stream for TryReadyChunks { + type Item = Result, TryReadyChunksStreamError>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.as_mut().project(); + + let mut items: Vec = Vec::new(); + + loop { + match this.stream.as_mut().poll_next(cx) { + // Flush all the collected data if the underlying stream doesn't + // contain more ready values + Poll::Pending => { + return if items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(Ok(items))) + } + } + + // Push the ready item into the buffer and check whether it is full. + // If so, return the buffer. + Poll::Ready(Some(Ok(item))) => { + if items.is_empty() { + items.reserve_exact(*this.cap); + } + items.push(item); + if items.len() >= *this.cap { + return Poll::Ready(Some(Ok(items))); + } + } + + // Return the already collected items and the error. + Poll::Ready(Some(Err(e))) => { + return Poll::Ready(Some(Err(TryReadyChunksError(items, e)))); + } + + // 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 { Some(Ok(items)) }; + return Poll::Ready(last); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (lower, upper) = self.stream.size_hint(); + let lower = lower / self.cap; + (lower, upper) + } +} + +impl FusedStream for TryReadyChunks { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TryReadyChunks +where + S: TryStream + Sink, +{ + type Error = >::Error; + + delegate_sink!(stream, Item); +} + +/// Error indicating, that while chunk was collected inner stream produced an error. +/// +/// Contains all items that were collected before an error occurred, and the stream error itself. +#[derive(PartialEq, Eq)] +pub struct TryReadyChunksError(pub Vec, pub E); + +impl fmt::Debug for TryReadyChunksError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.1.fmt(f) + } +} + +impl fmt::Display for TryReadyChunksError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.1.fmt(f) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryReadyChunksError {} From 3271492b3a4559ae962329173b0dc06bcd8cdf74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Wed, 19 Jul 2023 13:09:56 -0300 Subject: [PATCH 217/283] docs: futures-util::StreamExt: cite map and then in docs (#2756) --- futures-util/src/stream/stream/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 558dc22bd7..3978d188fc 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -323,6 +323,9 @@ pub trait StreamExt: Stream { /// wrapped version of it, similar to the existing `map` methods in the /// standard library. /// + /// See [`StreamExt::then`](Self::then) if you want to use a closure that + /// returns a future instead of a value. + /// /// # Examples /// /// ``` @@ -467,6 +470,9 @@ pub trait StreamExt: Stream { /// Note that this function consumes the stream passed into it and returns a /// wrapped version of it. /// + /// See [`StreamExt::map`](Self::map) if you want to use a closure that + /// returns a value instead of a future. + /// /// # Examples /// /// ``` From c801589d7ebbaf26394f3e1777b1d9125dd37ade Mon Sep 17 00:00:00 2001 From: thement <40525767+thement@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:40:09 +0200 Subject: [PATCH 218/283] Add `len`, `is_empty` methods for `UnboundedSender` (#2750) - add `len`, `is_empty` methods to inspect how many messages are enqueued in the message queue. - add test for `len` and `is_empty` Co-authored-by: Jakub Horak --- futures-channel/src/mpsc/mod.rs | 14 ++++++++++++++ futures-channel/tests/mpsc.rs | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index cf45fe77fe..ddf2340425 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -842,6 +842,20 @@ impl UnboundedSender { let ptr = self.0.as_ref().map(|inner| inner.ptr()); ptr.hash(hasher); } + + /// Return the number of messages in the queue or 0 if channel is disconnected. + pub fn len(&self) -> usize { + if let Some(sender) = &self.0 { + decode_state(sender.inner.state.load(SeqCst)).num_messages + } else { + 0 + } + } + + /// Return false is channel has no queued messages, true otherwise. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } impl Clone for Sender { diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index 444c8e10fd..f5d7198d22 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -632,3 +632,26 @@ fn send_backpressure_multi_senders() { let item = block_on(rx.next()).unwrap(); assert_eq!(item, 2); } + +/// Test that empty channel has zero length and that non-empty channel has length equal to number +/// of enqueued items +#[test] +fn unbounded_len() { + let (tx, mut rx) = mpsc::unbounded(); + assert_eq!(tx.len(), 0); + assert!(tx.is_empty()); + tx.unbounded_send(1).unwrap(); + assert_eq!(tx.len(), 1); + assert!(!tx.is_empty()); + tx.unbounded_send(2).unwrap(); + assert_eq!(tx.len(), 2); + assert!(!tx.is_empty()); + let item = block_on(rx.next()).unwrap(); + assert_eq!(item, 1); + assert_eq!(tx.len(), 1); + assert!(!tx.is_empty()); + let item = block_on(rx.next()).unwrap(); + assert_eq!(item, 2); + assert_eq!(tx.len(), 0); + assert!(tx.is_empty()); +} From cdf8f201a60653dc252315a05bbda065ac670644 Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Mon, 31 Jul 2023 21:52:21 -0500 Subject: [PATCH 219/283] Fix typo (it's/its) in join_all docs (#2766) --- futures-util/src/future/join_all.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 7dc159ba07..11b6f27288 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -77,7 +77,7 @@ where /// /// `join_all` will switch to the more powerful [`FuturesOrdered`] for performance /// reasons if the number of futures is large. You may want to look into using it or -/// it's counterpart [`FuturesUnordered`][crate::stream::FuturesUnordered] directly. +/// its counterpart [`FuturesUnordered`][crate::stream::FuturesUnordered] directly. /// /// Some examples for additional functionality provided by these are: /// From 5de1462084189d49e018db98dba3edf5cd087ae6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 5 Aug 2023 17:13:15 +0900 Subject: [PATCH 220/283] Update CI config (#2768) --- .github/workflows/ci.yml | 47 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a532333d0e..edf4396c8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,42 +30,36 @@ concurrency: jobs: test: - name: cargo test (${{ matrix.os }}) + name: cargo test strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest + include: + - os: ubuntu-latest + - os: macos-latest + - os: windows-latest + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu + - os: ubuntu-latest + target: armv7-unknown-linux-gnueabihf + - os: ubuntu-latest + target: armv5te-unknown-linux-gnueabi + - os: ubuntu-latest + target: i686-unknown-linux-gnu runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update nightly --no-self-update && rustup default nightly - - run: cargo test --workspace --all-features - - run: cargo test --workspace --all-features --release - - cross: - name: cargo test --target ${{ matrix.target }} - strategy: - fail-fast: false - matrix: - target: - - aarch64-unknown-linux-gnu - - armv7-unknown-linux-gnueabihf - - i686-unknown-linux-gnu - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: rustup update nightly && rustup default nightly - uses: taiki-e/setup-cross-toolchain-action@v1 with: target: ${{ matrix.target }} - - run: cargo test --target ${{ matrix.target }} --workspace --all-features $DOCTEST_XCOMPILE - - run: cargo test --target ${{ matrix.target }} --workspace --all-features --release $DOCTEST_XCOMPILE + if: matrix.target != '' + - run: cargo test --workspace --all-features $DOCTEST_XCOMPILE + - run: cargo test --workspace --all-features --release $DOCTEST_XCOMPILE + # TODO: https://github.com/rust-lang/futures-rs/issues/2451 + if: matrix.target != 'aarch64-unknown-linux-gnu' core-msrv: name: cargo +${{ matrix.rust }} build (futures-{core, io, sink}) @@ -260,7 +254,7 @@ jobs: echo "::set-output name=success::false" fi if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' - - uses: peter-evans/create-pull-request@v3 + - uses: peter-evans/create-pull-request@v5 with: title: Update no_atomic_cas.rs body: | @@ -297,8 +291,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Rust - run: rustup update nightly && rustup default nightly - - run: rustup component add rust-src + run: rustup toolchain install nightly --component rust-src && rustup default nightly - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests env: # TODO: Once `cfg(sanitize = "..")` is stable, replace From 00904b975ef56efbaca2de18de8ce9ade4bffba3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Aug 2023 10:57:10 +0900 Subject: [PATCH 221/283] Fix rustdoc::redundant_explicit_links warning (#2769) --- futures-channel/src/mpsc/mod.rs | 13 ++++++------- futures-channel/src/oneshot.rs | 18 +++++++++--------- futures-test/src/lib.rs | 2 +- futures-test/src/task/mod.rs | 20 ++++++++++---------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index ddf2340425..64f7526fa4 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -119,12 +119,12 @@ impl Unpin for BoundedSenderInner {} /// The transmission end of a bounded mpsc channel. /// -/// This value is created by the [`channel`](channel) function. +/// This value is created by the [`channel`] function. pub struct Sender(Option>); /// The transmission end of an unbounded mpsc channel. /// -/// This value is created by the [`unbounded`](unbounded) function. +/// This value is created by the [`unbounded`] function. pub struct UnboundedSender(Option>); trait AssertKinds: Send + Sync + Clone {} @@ -132,14 +132,14 @@ impl AssertKinds for UnboundedSender {} /// The receiving end of a bounded mpsc channel. /// -/// This value is created by the [`channel`](channel) function. +/// This value is created by the [`channel`] function. pub struct Receiver { inner: Option>>, } /// The receiving end of an unbounded mpsc channel. /// -/// This value is created by the [`unbounded`](unbounded) function. +/// This value is created by the [`unbounded`] function. pub struct UnboundedReceiver { inner: Option>>, } @@ -343,9 +343,8 @@ impl SenderTask { /// guaranteed slot in the channel capacity, and on top of that there are /// `buffer` "first come, first serve" slots available to all senders. /// -/// The [`Receiver`](Receiver) returned implements the -/// [`Stream`](futures_core::stream::Stream) trait, while [`Sender`](Sender) implements -/// `Sink`. +/// The [`Receiver`] returned implements the [`Stream`] trait, while [`Sender`] +/// implements `Sink`. pub fn channel(buffer: usize) -> (Sender, Receiver) { // Check that the requested buffer size does not exceed the maximum buffer // size permitted by the system. diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index 70449f43d6..fe5b115a33 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -14,7 +14,7 @@ use crate::lock::Lock; /// A future for a value that will be provided by another asynchronous task. /// -/// This is created by the [`channel`](channel) function. +/// This is created by the [`channel`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Receiver { inner: Arc>, @@ -22,7 +22,7 @@ pub struct Receiver { /// A means of transmitting a single value to another task. /// -/// This is created by the [`channel`](channel) function. +/// This is created by the [`channel`] function. pub struct Sender { inner: Arc>, } @@ -332,8 +332,8 @@ impl Sender { /// Completes this oneshot with a successful result. /// /// This function will consume `self` and indicate to the other end, the - /// [`Receiver`](Receiver), that the value provided is the result of the - /// computation this represents. + /// [`Receiver`], that the value provided is the result of the computation + /// this represents. /// /// If the value is successfully enqueued for the remote end to receive, /// then `Ok(())` is returned. If the receiving end was dropped before @@ -343,7 +343,7 @@ impl Sender { } /// Polls this `Sender` half to detect whether its associated - /// [`Receiver`](Receiver) has been dropped. + /// [`Receiver`] has been dropped. /// /// # Return values /// @@ -359,10 +359,10 @@ impl Sender { } /// Creates a future that resolves when this `Sender`'s corresponding - /// [`Receiver`](Receiver) half has hung up. + /// [`Receiver`] half has hung up. /// /// This is a utility wrapping [`poll_canceled`](Sender::poll_canceled) - /// to expose a [`Future`](core::future::Future). + /// to expose a [`Future`]. pub fn cancellation(&mut self) -> Cancellation<'_, T> { Cancellation { inner: self } } @@ -413,8 +413,8 @@ impl Future for Cancellation<'_, T> { } } -/// Error returned from a [`Receiver`](Receiver) when the corresponding -/// [`Sender`](Sender) is dropped. +/// Error returned from a [`Receiver`] when the corresponding [`Sender`] is +/// dropped. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Canceled; diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 2eb4a1c4cd..49f834846e 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -61,7 +61,7 @@ mod interleave_pending; mod track_closed; /// Enables an `async` test function. The generated future will be run to completion with -/// [`futures_executor::block_on`](futures_executor::block_on). +/// [`futures_executor::block_on`]. /// /// ``` /// #[futures_test::test] diff --git a/futures-test/src/task/mod.rs b/futures-test/src/task/mod.rs index 118291c0c9..e10ecbb875 100644 --- a/futures-test/src/task/mod.rs +++ b/futures-test/src/task/mod.rs @@ -15,29 +15,29 @@ //! [`Spawn`](futures_task::Spawn) implementations. //! //! Test contexts: -//! - [`noop_context`](crate::task::noop_context) creates a context that ignores calls to +//! - [`noop_context`] creates a context that ignores calls to //! [`cx.waker().wake_by_ref()`](futures_core::task::Waker). -//! - [`panic_context`](crate::task::panic_context) creates a context that panics when +//! - [`panic_context`] creates a context that panics when //! [`cx.waker().wake_by_ref()`](futures_core::task::Waker) is called. //! //! Test wakers: -//! - [`noop_waker`](crate::task::noop_waker) creates a waker that ignores calls to +//! - [`noop_waker`] creates a waker that ignores calls to //! [`wake`](futures_core::task::Waker). -//! - [`panic_waker`](crate::task::panic_waker()) creates a waker that panics when +//! - [`panic_waker`](panic_waker()) creates a waker that panics when //! [`wake`](futures_core::task::Waker) is called. -//! - [`new_count_waker`](crate::task::new_count_waker) creates a waker that increments a counter whenever +//! - [`new_count_waker`] creates a waker that increments a counter whenever //! [`wake`](futures_core::task::Waker) is called. //! //! Test spawners: -//! - [`NoopSpawner`](crate::task::NoopSpawner) ignores calls to +//! - [`NoopSpawner`] ignores calls to //! [`spawn`](futures_util::task::SpawnExt::spawn) -//! - [`PanicSpawner`](crate::task::PanicSpawner) panics if [`spawn`](futures_util::task::SpawnExt::spawn) is +//! - [`PanicSpawner`] panics if [`spawn`](futures_util::task::SpawnExt::spawn) is //! called. -//! - [`RecordSpawner`](crate::task::RecordSpawner) records the spawned futures. +//! - [`RecordSpawner`] records the spawned futures. //! //! For convenience there additionally exist various functions that directly -//! return waker/spawner references: [`noop_waker_ref`](crate::task::noop_waker_ref), -//! [`panic_waker_ref`](crate::task::panic_waker_ref), [`noop_spawner_mut`](crate::task::noop_spawner_mut) and [`panic_spawner_mut`](crate::task::panic_spawner_mut). +//! return waker/spawner references: [`noop_waker_ref`], [`panic_waker_ref`], +//! [`noop_spawner_mut`] and [`panic_spawner_mut`]. mod context; pub use self::context::{noop_context, panic_context}; From a300c89b3f0c3e2b41d22484eb1e956e326f6ace Mon Sep 17 00:00:00 2001 From: tz629 <142957239+tz629@users.noreply.github.com> Date: Wed, 23 Aug 2023 20:19:48 -0600 Subject: [PATCH 222/283] Fix typo (#2770) --- futures-util/src/io/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/io/window.rs b/futures-util/src/io/window.rs index 77b7267c69..d857282383 100644 --- a/futures-util/src/io/window.rs +++ b/futures-util/src/io/window.rs @@ -1,6 +1,6 @@ use std::ops::{Bound, Range, RangeBounds}; -/// A owned window around an underlying buffer. +/// An owned window around an underlying buffer. /// /// Normally slices work great for considering sub-portions of a buffer, but /// unfortunately a slice is a *borrowed* type in Rust which has an associated From 78c7b79e9b5a87b230fe0a02664e0e7f556a1d74 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Mon, 25 Sep 2023 20:54:02 +0400 Subject: [PATCH 223/283] Remove an outdated comment from docs (#2774) --- futures-util/src/future/future/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index c11d108207..955af3776a 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -463,10 +463,6 @@ pub trait FutureExt: Future { /// ``` /// /// ``` - /// // Note, unlike most examples this is written in the context of a - /// // synchronous function to better illustrate the cross-thread aspect of - /// // the `shared` combinator. - /// /// # futures::executor::block_on(async { /// use futures::future::FutureExt; /// use futures::executor::block_on; From 418c0d078fc3587ea1757a521a551b2e5b242587 Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Sun, 8 Oct 2023 19:21:49 -0500 Subject: [PATCH 224/283] Update `FuturesOrdered` docs to refer to `poll_next` instead of `poll` and `push_back` instead of `push` (which is deprecated). --- futures-util/src/stream/futures_ordered.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 618bf1b7bd..2fe67be30e 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -70,18 +70,18 @@ where /// large numbers of futures. /// /// When a `FuturesOrdered` is first created, it does not contain any futures. -/// Calling `poll` in this state will result in `Poll::Ready(None))` to be -/// returned. Futures are submitted to the queue using `push`; however, the +/// Calling `poll_next` in this state will result in `Poll::Ready(None))` to be +/// returned. Futures are submitted to the queue using `push_back` (or `push_front`); however, the /// future will **not** be polled at this point. `FuturesOrdered` will only -/// poll managed futures when `FuturesOrdered::poll` is called. As such, it -/// is important to call `poll` after pushing new futures. +/// poll managed futures when `FuturesOrdered::poll_next` is called. As such, it +/// is important to call `poll_next` after pushing new futures. /// -/// If `FuturesOrdered::poll` returns `Poll::Ready(None)` this means that +/// If `FuturesOrdered::poll_next` returns `Poll::Ready(None)` this means that /// the queue is currently not managing any futures. A future may be submitted /// to the queue at a later time. At that point, a call to -/// `FuturesOrdered::poll` will either return the future's resolved value +/// `FuturesOrdered::poll_next` will either return the future's resolved value /// **or** `Poll::Pending` if the future has not yet completed. When -/// multiple futures are submitted to the queue, `FuturesOrdered::poll` will +/// multiple futures are submitted to the queue, `FuturesOrdered::poll_next` will /// return `Poll::Pending` until the first future completes, even if /// some of the later futures have already completed. /// @@ -133,7 +133,7 @@ impl FuturesOrdered { /// /// This function submits the given future to the internal set for managing. /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll` is called in order to receive + /// must ensure that `FuturesOrdered::poll_next` is called in order to receive /// task notifications. #[deprecated(note = "use `push_back` instead")] pub fn push(&mut self, future: Fut) { @@ -144,7 +144,7 @@ impl FuturesOrdered { /// /// This function submits the given future to the internal set for managing. /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll` is called in order to receive + /// must ensure that `FuturesOrdered::poll_next` is called in order to receive /// task notifications. pub fn push_back(&mut self, future: Fut) { let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; @@ -156,7 +156,7 @@ impl FuturesOrdered { /// /// This function submits the given future to the internal set for managing. /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll` is called in order to receive + /// must ensure that `FuturesOrdered::poll_next` is called in order to receive /// task notifications. This future will be the next future to be returned /// complete. pub fn push_front(&mut self, future: Fut) { From 6a0606c76f44255effc71edcc34b17822f60f9fb Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sun, 8 Oct 2023 19:43:00 -0500 Subject: [PATCH 225/283] Add doclinks to `FuturesOrdered`'s docs. --- futures-util/src/stream/futures_ordered.rs | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 2fe67be30e..23d00ce6b0 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -65,29 +65,29 @@ where /// /// Futures are pushed into this queue and their realized values are yielded in /// order. This structure is optimized to manage a large number of futures. -/// Futures managed by `FuturesOrdered` will only be polled when they generate +/// Futures managed by [`FuturesOrdered`] will only be polled when they generate /// notifications. This reduces the required amount of work needed to coordinate /// large numbers of futures. /// -/// When a `FuturesOrdered` is first created, it does not contain any futures. -/// Calling `poll_next` in this state will result in `Poll::Ready(None))` to be -/// returned. Futures are submitted to the queue using `push_back` (or `push_front`); however, the -/// future will **not** be polled at this point. `FuturesOrdered` will only -/// poll managed futures when `FuturesOrdered::poll_next` is called. As such, it -/// is important to call `poll_next` after pushing new futures. +/// When a [`FuturesOrdered`] is first created, it does not contain any futures. +/// Calling [`poll_next`](FuturesOrdered::poll_next) in this state will result in [`Poll::Ready(None)`](Poll::Ready) to be +/// returned. Futures are submitted to the queue using [`push_back`](FuturesOrdered::push_back) (or [`push_front`](FuturesOrdered::push_front)); however, the +/// future will **not** be polled at this point. [`FuturesOrdered`] will only +/// poll managed futures when [`FuturesOrdered::poll_next`] is called. As such, it +/// is important to call [`poll_next`](FuturesOrdered::poll_next) after pushing new futures. /// -/// If `FuturesOrdered::poll_next` returns `Poll::Ready(None)` this means that +/// If [`FuturesOrdered::poll_next`] returns [`Poll::Ready(None)`](Poll::Ready) this means that /// the queue is currently not managing any futures. A future may be submitted /// to the queue at a later time. At that point, a call to -/// `FuturesOrdered::poll_next` will either return the future's resolved value -/// **or** `Poll::Pending` if the future has not yet completed. When -/// multiple futures are submitted to the queue, `FuturesOrdered::poll_next` will -/// return `Poll::Pending` until the first future completes, even if +/// [`FuturesOrdered::poll_next`] will either return the future's resolved value +/// **or** [`Poll::Pending`] if the future has not yet completed. When +/// multiple futures are submitted to the queue, [`FuturesOrdered::poll_next`] will +/// return [`Poll::Pending`] until the first future completes, even if /// some of the later futures have already completed. /// -/// Note that you can create a ready-made `FuturesOrdered` via the +/// Note that you can create a ready-made [`FuturesOrdered`] via the /// [`collect`](Iterator::collect) method, or you can start with an empty queue -/// with the `FuturesOrdered::new` constructor. +/// with the [`FuturesOrdered::new`] constructor. /// /// This type is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. @@ -104,8 +104,8 @@ impl Unpin for FuturesOrdered {} impl FuturesOrdered { /// Constructs a new, empty `FuturesOrdered` /// - /// The returned `FuturesOrdered` does not contain any futures and, in this - /// state, `FuturesOrdered::poll_next` will return `Poll::Ready(None)`. + /// The returned [`FuturesOrdered`] does not contain any futures and, in this + /// state, [`FuturesOrdered::poll_next`] will return [`Poll::Ready(None)`](Poll::Ready). pub fn new() -> Self { Self { in_progress_queue: FuturesUnordered::new(), @@ -132,8 +132,8 @@ impl FuturesOrdered { /// Push a future into the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll_next` is called in order to receive + /// This function will not call [`poll`](Future::poll) on the submitted future. The caller + /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive /// task notifications. #[deprecated(note = "use `push_back` instead")] pub fn push(&mut self, future: Fut) { @@ -143,8 +143,8 @@ impl FuturesOrdered { /// Pushes a future to the back of the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll_next` is called in order to receive + /// This function will not call [`poll`](Future::poll) on the submitted future. The caller + /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive /// task notifications. pub fn push_back(&mut self, future: Fut) { let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; @@ -155,8 +155,8 @@ impl FuturesOrdered { /// Pushes a future to the front of the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call `poll` on the submitted future. The caller - /// must ensure that `FuturesOrdered::poll_next` is called in order to receive + /// This function will not call [`poll`](Future::poll) on the submitted future. The caller + /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive /// task notifications. This future will be the next future to be returned /// complete. pub fn push_front(&mut self, future: Fut) { From 82327a2ef74ac48508f2b0269fc7b0a7e97eb8c1 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sun, 8 Oct 2023 19:54:47 -0500 Subject: [PATCH 226/283] Wrap doc comments in futures_ordered.rs --- futures-util/src/stream/futures_ordered.rs | 52 ++++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 23d00ce6b0..3aaef8bdef 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -58,8 +58,8 @@ where /// An unbounded queue of futures. /// -/// This "combinator" is similar to [`FuturesUnordered`], but it imposes a FIFO order -/// on top of the set of futures. While futures in the set will race to +/// This "combinator" is similar to [`FuturesUnordered`], but it imposes a FIFO +/// order on top of the set of futures. While futures in the set will race to /// completion in parallel, results will only be returned in the order their /// originating futures were added to the queue. /// @@ -70,19 +70,22 @@ where /// large numbers of futures. /// /// When a [`FuturesOrdered`] is first created, it does not contain any futures. -/// Calling [`poll_next`](FuturesOrdered::poll_next) in this state will result in [`Poll::Ready(None)`](Poll::Ready) to be -/// returned. Futures are submitted to the queue using [`push_back`](FuturesOrdered::push_back) (or [`push_front`](FuturesOrdered::push_front)); however, the -/// future will **not** be polled at this point. [`FuturesOrdered`] will only -/// poll managed futures when [`FuturesOrdered::poll_next`] is called. As such, it -/// is important to call [`poll_next`](FuturesOrdered::poll_next) after pushing new futures. +/// Calling [`poll_next`](FuturesOrdered::poll_next) in this state will result +/// in [`Poll::Ready(None)`](Poll::Ready) to be returned. Futures are submitted +/// to the queue using [`push_back`](FuturesOrdered::push_back) (or +/// [`push_front`](FuturesOrdered::push_front)); however, the future will +/// **not** be polled at this point. [`FuturesOrdered`] will only poll managed +/// futures when [`FuturesOrdered::poll_next`] is called. As such, it +/// is important to call [`poll_next`](FuturesOrdered::poll_next) after pushing +/// new futures. /// -/// If [`FuturesOrdered::poll_next`] returns [`Poll::Ready(None)`](Poll::Ready) this means that -/// the queue is currently not managing any futures. A future may be submitted -/// to the queue at a later time. At that point, a call to +/// If [`FuturesOrdered::poll_next`] returns [`Poll::Ready(None)`](Poll::Ready) +/// this means that the queue is currently not managing any futures. A future +/// may be submitted to the queue at a later time. At that point, a call to /// [`FuturesOrdered::poll_next`] will either return the future's resolved value /// **or** [`Poll::Pending`] if the future has not yet completed. When -/// multiple futures are submitted to the queue, [`FuturesOrdered::poll_next`] will -/// return [`Poll::Pending`] until the first future completes, even if +/// multiple futures are submitted to the queue, [`FuturesOrdered::poll_next`] +/// will return [`Poll::Pending`] until the first future completes, even if /// some of the later futures have already completed. /// /// Note that you can create a ready-made [`FuturesOrdered`] via the @@ -104,8 +107,9 @@ impl Unpin for FuturesOrdered {} impl FuturesOrdered { /// Constructs a new, empty `FuturesOrdered` /// - /// The returned [`FuturesOrdered`] does not contain any futures and, in this - /// state, [`FuturesOrdered::poll_next`] will return [`Poll::Ready(None)`](Poll::Ready). + /// The returned [`FuturesOrdered`] does not contain any futures and, in + /// this state, [`FuturesOrdered::poll_next`] will return + /// [`Poll::Ready(None)`](Poll::Ready). pub fn new() -> Self { Self { in_progress_queue: FuturesUnordered::new(), @@ -132,9 +136,9 @@ impl FuturesOrdered { /// Push a future into the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call [`poll`](Future::poll) on the submitted future. The caller - /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive - /// task notifications. + /// This function will not call [`poll`](Future::poll) on the submitted + /// future. The caller must ensure that [`FuturesOrdered::poll_next`] is + /// called in order to receive task notifications. #[deprecated(note = "use `push_back` instead")] pub fn push(&mut self, future: Fut) { self.push_back(future); @@ -143,9 +147,9 @@ impl FuturesOrdered { /// Pushes a future to the back of the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call [`poll`](Future::poll) on the submitted future. The caller - /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive - /// task notifications. + /// This function will not call [`poll`](Future::poll) on the submitted + /// future. The caller must ensure that [`FuturesOrdered::poll_next`] is + /// called in order to receive task notifications. pub fn push_back(&mut self, future: Fut) { let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; self.next_incoming_index += 1; @@ -155,10 +159,10 @@ impl FuturesOrdered { /// Pushes a future to the front of the queue. /// /// This function submits the given future to the internal set for managing. - /// This function will not call [`poll`](Future::poll) on the submitted future. The caller - /// must ensure that [`FuturesOrdered::poll_next`] is called in order to receive - /// task notifications. This future will be the next future to be returned - /// complete. + /// This function will not call [`poll`](Future::poll) on the submitted + /// future. The caller must ensure that [`FuturesOrdered::poll_next`] is + /// called in order to receive task notifications. This future will be + /// the next future to be returned complete. pub fn push_front(&mut self, future: Fut) { let wrapped = OrderWrapper { data: future, index: self.next_outgoing_index - 1 }; self.next_outgoing_index -= 1; From 14d8c0862faea8b87bcd4c61d87de4db78db6925 Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 21 Oct 2023 13:56:34 -0700 Subject: [PATCH 227/283] Panic if `All` or `Any` are polled after completing due to a short-circuit These futures should panic if they are polled after completing. Currently they do so but only if they complete due to exhausting the `Stream` that they pull data from. If they complete due to short-circuiting, they are left in a state where `fut` and `accum` are still `Some`. This means that if they are polled again, they end up polling the inner `fut` again. That usually causes a panic but the error message will likely reference the internal `Future`, not `All` / `Any`. With this commit, `All` and `Any`'s internal state will be set such that if they are polled again after completing, they will panic without polling `fut`. --- futures-util/src/stream/stream/all.rs | 3 ++- futures-util/src/stream/stream/any.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/futures-util/src/stream/stream/all.rs b/futures-util/src/stream/stream/all.rs index ba2baa5cf1..b2aaa6b78a 100644 --- a/futures-util/src/stream/stream/all.rs +++ b/futures-util/src/stream/stream/all.rs @@ -69,11 +69,12 @@ where if let Some(fut) = this.future.as_mut().as_pin_mut() { // we're currently processing a future to produce a new accum value let acc = this.accum.unwrap() && ready!(fut.poll(cx)); + this.future.set(None); if !acc { + this.accum.take().unwrap(); break false; } // early exit *this.accum = Some(acc); - this.future.set(None); } else if this.accum.is_some() { // we're waiting on a new item from the stream match ready!(this.stream.as_mut().poll_next(cx)) { diff --git a/futures-util/src/stream/stream/any.rs b/futures-util/src/stream/stream/any.rs index f023125c70..f8b2a5829a 100644 --- a/futures-util/src/stream/stream/any.rs +++ b/futures-util/src/stream/stream/any.rs @@ -69,11 +69,12 @@ where if let Some(fut) = this.future.as_mut().as_pin_mut() { // we're currently processing a future to produce a new accum value let acc = this.accum.unwrap() || ready!(fut.poll(cx)); + this.future.set(None); if acc { + this.accum.take().unwrap(); break true; } // early exit *this.accum = Some(acc); - this.future.set(None); } else if this.accum.is_some() { // we're waiting on a new item from the stream match ready!(this.stream.as_mut().poll_next(cx)) { From 1acbd8fb27d660a9805acf37dd075dad81b7d0a1 Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 21 Oct 2023 14:10:31 -0700 Subject: [PATCH 228/283] Replace `All` and `Any`'s `accum` field with `done` It looks like `All` was originally implemented by copying from `TryFold` from which it inherited its `accum` field. However, `accum` can only ever be one of two values: `None` (if `All` has already completed) or `Some(true)` (if it's still processing values from the inner `Stream`). It doesn't need to keep track of an accumulator because the very fact that it hasn't short-circuited yet means that the accumulated value can't be `Some(false)`. Therefore, we only need two values here and we can represent them with a `bool` indicating whether or not `All` has already completed. The same principle applies for `Any` but substituting `Some(false)` for `Some(true)`. --- futures-util/src/stream/stream/all.rs | 22 +++++++++++----------- futures-util/src/stream/stream/any.rs | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/futures-util/src/stream/stream/all.rs b/futures-util/src/stream/stream/all.rs index b2aaa6b78a..1435c798f2 100644 --- a/futures-util/src/stream/stream/all.rs +++ b/futures-util/src/stream/stream/all.rs @@ -13,7 +13,7 @@ pin_project! { #[pin] stream: St, f: F, - accum: Option, + done: bool, #[pin] future: Option, } @@ -27,7 +27,7 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("All") .field("stream", &self.stream) - .field("accum", &self.accum) + .field("done", &self.done) .field("future", &self.future) .finish() } @@ -40,7 +40,7 @@ where Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f, accum: Some(true), future: None } + Self { stream, f, done: false, future: None } } } @@ -51,7 +51,7 @@ where Fut: Future, { fn is_terminated(&self) -> bool { - self.accum.is_none() && self.future.is_none() + self.done && self.future.is_none() } } @@ -67,22 +67,22 @@ where let mut this = self.project(); Poll::Ready(loop { if let Some(fut) = this.future.as_mut().as_pin_mut() { - // we're currently processing a future to produce a new accum value - let acc = this.accum.unwrap() && ready!(fut.poll(cx)); + // we're currently processing a future to produce a new value + let res = ready!(fut.poll(cx)); this.future.set(None); - if !acc { - this.accum.take().unwrap(); + if !res { + *this.done = true; break false; } // early exit - *this.accum = Some(acc); - } else if this.accum.is_some() { + } else if !*this.done { // we're waiting on a new item from the stream match ready!(this.stream.as_mut().poll_next(cx)) { Some(item) => { this.future.set(Some((this.f)(item))); } None => { - break this.accum.take().unwrap(); + *this.done = true; + break true; } } } else { diff --git a/futures-util/src/stream/stream/any.rs b/futures-util/src/stream/stream/any.rs index f8b2a5829a..cc3d695b9d 100644 --- a/futures-util/src/stream/stream/any.rs +++ b/futures-util/src/stream/stream/any.rs @@ -13,7 +13,7 @@ pin_project! { #[pin] stream: St, f: F, - accum: Option, + done: bool, #[pin] future: Option, } @@ -27,7 +27,7 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Any") .field("stream", &self.stream) - .field("accum", &self.accum) + .field("done", &self.done) .field("future", &self.future) .finish() } @@ -40,7 +40,7 @@ where Fut: Future, { pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f, accum: Some(false), future: None } + Self { stream, f, done: false, future: None } } } @@ -51,7 +51,7 @@ where Fut: Future, { fn is_terminated(&self) -> bool { - self.accum.is_none() && self.future.is_none() + self.done && self.future.is_none() } } @@ -67,22 +67,22 @@ where let mut this = self.project(); Poll::Ready(loop { if let Some(fut) = this.future.as_mut().as_pin_mut() { - // we're currently processing a future to produce a new accum value - let acc = this.accum.unwrap() || ready!(fut.poll(cx)); + // we're currently processing a future to produce a new value + let res = ready!(fut.poll(cx)); this.future.set(None); - if acc { - this.accum.take().unwrap(); + if res { + *this.done = true; break true; } // early exit - *this.accum = Some(acc); - } else if this.accum.is_some() { + } else if !*this.done { // we're waiting on a new item from the stream match ready!(this.stream.as_mut().poll_next(cx)) { Some(item) => { this.future.set(Some((this.f)(item))); } None => { - break this.accum.take().unwrap(); + *this.done = true; + break false; } } } else { From 43c0c5f81a790738fc00b28a60dfb513de892a5d Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 21 Oct 2023 18:56:11 -0700 Subject: [PATCH 229/283] Add tests for `StreamExt::all` and `StreamExt::any` --- futures/tests/stream.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 79d8e233cc..6cbef7516c 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -535,3 +535,43 @@ fn select_with_strategy_doesnt_terminate_early() { assert_eq!(count.get(), times_should_poll + 1); } } + +async fn is_even(number: u8) -> bool { + number % 2 == 0 +} + +#[test] +fn all() { + block_on(async { + let empty: [u8; 0] = []; + let st = stream::iter(empty); + let all = st.all(is_even).await; + assert!(all); + + let st = stream::iter([2, 4, 6, 8]); + let all = st.all(is_even).await; + assert!(all); + + let st = stream::iter([2, 3, 4]); + let all = st.all(is_even).await; + assert!(!all); + }); +} + +#[test] +fn any() { + block_on(async { + let empty: [u8; 0] = []; + let st = stream::iter(empty); + let any = st.any(is_even).await; + assert!(!any); + + let st = stream::iter([1, 2, 3]); + let any = st.any(is_even).await; + assert!(any); + + let st = stream::iter([1, 3, 5]); + let any = st.any(is_even).await; + assert!(!any); + }); +} From 8570ea6a7abcaeee86d72ce2b12e3ce2950d5c50 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 25 Oct 2023 23:51:43 +0900 Subject: [PATCH 230/283] Fix unused_imports warning ``` error: unused import: `self::stream_select_mod::*` --> futures-util/src/async_await/mod.rs:38:9 | 38 | pub use self::stream_select_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` ``` --- futures-util/src/async_await/mod.rs | 2 ++ futures-util/src/async_await/stream_select_mod.rs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 7276da227a..7e3f12c99f 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -31,9 +31,11 @@ mod select_mod; pub use self::select_mod::*; // Primary export is a macro +#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] mod stream_select_mod; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 +#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] pub use self::stream_select_mod::*; diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs index 1c8002fff3..61e3fa1c66 100644 --- a/futures-util/src/async_await/stream_select_mod.rs +++ b/futures-util/src/async_await/stream_select_mod.rs @@ -1,6 +1,5 @@ //! The `stream_select` macro. -#[cfg(feature = "std")] #[allow(unreachable_pub)] #[doc(hidden)] pub use futures_macro::stream_select_internal; @@ -28,7 +27,6 @@ pub use futures_macro::stream_select_internal; /// } /// # }); /// ``` -#[cfg(feature = "std")] #[macro_export] macro_rules! stream_select { ($($tokens:tt)*) => {{ From 2f2ec390e47576c4964995a31197499d4d19d7ab Mon Sep 17 00:00:00 2001 From: Olivier ROLAND Date: Tue, 24 Oct 2023 18:49:03 +0200 Subject: [PATCH 231/283] Fix infinite loop caused by invalid UTF-8 bytes --- futures-util/src/io/read_line.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index e1b8fc9455..df782c9570 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -35,6 +35,7 @@ pub(super) fn read_line_internal( ) -> Poll> { let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); if str::from_utf8(bytes).is_err() { + bytes.clear(); Poll::Ready(ret.and_then(|_| { Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")) })) From f392082f3a2546b2a2bd56b230e7035394d8427e Mon Sep 17 00:00:00 2001 From: Olivier ROLAND Date: Wed, 25 Oct 2023 09:30:45 +0200 Subject: [PATCH 232/283] Extend io::AsyncBufReadExt::lines example with invalid UTF-8 --- futures-util/src/io/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 8ce3ad644b..fdad60b1fa 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -804,11 +804,11 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// use futures::io::{AsyncBufReadExt, Cursor}; /// use futures::stream::StreamExt; /// - /// let cursor = Cursor::new(b"lorem\nipsum\r\ndolor"); + /// let cursor = Cursor::new(b"lorem\nipsum\xc2\r\ndolor"); /// - /// let mut lines_stream = cursor.lines().map(|l| l.unwrap()); + /// let mut lines_stream = cursor.lines().map(|l| l.unwrap_or(String::from("invalid UTF_8"))); /// assert_eq!(lines_stream.next().await, Some(String::from("lorem"))); - /// assert_eq!(lines_stream.next().await, Some(String::from("ipsum"))); + /// assert_eq!(lines_stream.next().await, Some(String::from("invalid UTF_8"))); /// assert_eq!(lines_stream.next().await, Some(String::from("dolor"))); /// assert_eq!(lines_stream.next().await, None); /// # Ok::<(), Box>(()) }).unwrap(); From 60a86e1532ec890cfa8e4c35e89b2170a748a5d6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Oct 2023 21:47:53 +0900 Subject: [PATCH 233/283] Fix Sync impl of FuturesUnordered Fixes #2786 --- .../src/stream/futures_unordered/mod.rs | 2 +- futures/tests/auto_traits.rs | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 6b5804dc41..0dbaea9080 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -62,7 +62,7 @@ pub struct FuturesUnordered { } unsafe impl Send for FuturesUnordered {} -unsafe impl Sync for FuturesUnordered {} +unsafe impl Sync for FuturesUnordered {} impl Unpin for FuturesUnordered {} impl Spawn for FuturesUnordered> { diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index 5fc0f7d675..004fda1e71 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -18,6 +18,8 @@ pub type SendFuture = Pin + Send>>; pub type SendTryFuture = SendFuture>; pub type SyncFuture = Pin + Sync>>; pub type SyncTryFuture = SyncFuture>; +pub type SendSyncFuture = Pin + Send + Sync>>; +pub type SendSyncTryFuture = SendSyncFuture>; pub type UnpinFuture = LocalFuture; pub type UnpinTryFuture = UnpinFuture>; pub struct PinnedFuture(PhantomPinned, PhantomData); @@ -35,6 +37,8 @@ pub type SendStream = Pin + Send>>; pub type SendTryStream = SendStream>; pub type SyncStream = Pin + Sync>>; pub type SyncTryStream = SyncStream>; +pub type SendSyncStream = Pin + Send + Sync>>; +pub type SendSyncTryStream = SendSyncStream>; pub type UnpinStream = LocalStream; pub type UnpinTryStream = UnpinStream>; pub struct PinnedStream(PhantomPinned, PhantomData); @@ -365,9 +369,10 @@ pub mod future { assert_impl!(JoinAll>: Send); assert_not_impl!(JoinAll: Send); assert_not_impl!(JoinAll: Send); - assert_impl!(JoinAll>: Sync); - assert_not_impl!(JoinAll: Sync); - assert_not_impl!(JoinAll: Sync); + assert_impl!(JoinAll>: Sync); + assert_not_impl!(JoinAll>: Sync); + assert_not_impl!(JoinAll>: Sync); + assert_not_impl!(JoinAll: Sync); assert_impl!(JoinAll: Unpin); assert_impl!(Lazy<()>: Send); @@ -579,9 +584,10 @@ pub mod future { assert_impl!(TryJoinAll>: Send); assert_not_impl!(TryJoinAll: Send); assert_not_impl!(TryJoinAll: Send); - assert_impl!(TryJoinAll>: Sync); - assert_not_impl!(TryJoinAll: Sync); - assert_not_impl!(TryJoinAll: Sync); + assert_impl!(TryJoinAll>: Sync); + assert_not_impl!(TryJoinAll>: Sync); + assert_not_impl!(TryJoinAll>: Sync); + assert_not_impl!(TryJoinAll: Sync); assert_impl!(TryJoinAll: Unpin); assert_impl!(TrySelect: Send); @@ -1118,10 +1124,9 @@ pub mod stream { assert_not_impl!(Buffered>: Send); assert_not_impl!(Buffered>: Send); assert_not_impl!(Buffered>>: Send); - assert_impl!(Buffered>>: Sync); - assert_not_impl!(Buffered>: Sync); - assert_not_impl!(Buffered>: Sync); - assert_not_impl!(Buffered>>: Sync); + assert_impl!(Buffered>>: Sync); + assert_not_impl!(Buffered>>: Sync); + assert_not_impl!(Buffered>>: Sync); assert_impl!(Buffered>: Unpin); assert_not_impl!(Buffered>: Unpin); @@ -1303,9 +1308,10 @@ pub mod stream { assert_impl!(FuturesOrdered>: Send); assert_not_impl!(FuturesOrdered: Send); assert_not_impl!(FuturesOrdered: Send); - assert_impl!(FuturesOrdered>: Sync); - assert_not_impl!(FuturesOrdered>: Sync); - assert_not_impl!(FuturesOrdered>: Sync); + assert_impl!(FuturesOrdered>: Sync); + assert_not_impl!(FuturesOrdered>: Sync); + assert_not_impl!(FuturesOrdered>: Sync); + assert_not_impl!(FuturesOrdered: Sync); assert_impl!(FuturesOrdered: Unpin); assert_impl!(FuturesUnordered<()>: Send); @@ -1647,11 +1653,12 @@ pub mod stream { assert_not_impl!(TryBuffered>>: Send); assert_not_impl!(TryBuffered>>: Send); assert_not_impl!(TryBuffered>>: Send); - assert_impl!(TryBuffered>>: Sync); - assert_not_impl!(TryBuffered>>: Sync); - assert_not_impl!(TryBuffered>>: Sync); - assert_not_impl!(TryBuffered>>: Sync); - assert_not_impl!(TryBuffered>>: Sync); + assert_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); assert_impl!(TryBuffered>: Unpin); assert_not_impl!(TryBuffered>: Unpin); From 5051335313576ea32d172a68beda3cdfcb2823de Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Oct 2023 21:52:55 +0900 Subject: [PATCH 234/283] Remove unsafe code from AssertUnmoved --- futures-test/src/assert_unmoved.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/futures-test/src/assert_unmoved.rs b/futures-test/src/assert_unmoved.rs index 95d9a095f2..baeaeb59ab 100644 --- a/futures-test/src/assert_unmoved.rs +++ b/futures-test/src/assert_unmoved.rs @@ -7,7 +7,6 @@ use futures_io::{ use futures_sink::Sink; use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; -use std::ptr; use std::thread::panicking; /// Combinator that asserts that the underlying type is not moved after being polled. @@ -24,26 +23,21 @@ use std::thread::panicking; pub struct AssertUnmoved { #[pin] inner: T, - this_ptr: *const Self, + this_addr: usize, } -// 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(crate) fn new(inner: T) -> Self { - Self { inner, this_ptr: ptr::null() } + Self { inner, this_addr: 0 } } fn poll_with<'a, U>(mut self: Pin<&'a mut Self>, f: impl FnOnce(Pin<&'a mut T>) -> U) -> U { - let cur_this = &*self as *const Self; - if self.this_ptr.is_null() { + let cur_this = &*self as *const Self as usize; + if self.this_addr == 0 { // First time being polled - *self.as_mut().project().this_ptr = cur_this; + *self.as_mut().project().this_addr = cur_this; } else { - assert_eq!(self.this_ptr, cur_this, "AssertUnmoved moved between poll calls"); + assert_eq!(self.this_addr, cur_this, "AssertUnmoved moved between poll calls"); } f(self.project().inner) } @@ -166,9 +160,9 @@ 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() { - let cur_this = &*self as *const Self; - assert_eq!(self.this_ptr, cur_this, "AssertUnmoved moved before drop"); + if !panicking() && self.this_addr != 0 { + let cur_this = &*self as *const Self as usize; + assert_eq!(self.this_addr, cur_this, "AssertUnmoved moved before drop"); } } } From e60a439bde9d0444aaddc7fb51c5d9d78547f14c Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 21 Oct 2023 14:51:49 -0700 Subject: [PATCH 235/283] Add `TryAll` adapter --- futures-util/src/stream/try_stream/mod.rs | 33 +++++++ futures-util/src/stream/try_stream/try_all.rs | 98 +++++++++++++++++++ futures/tests/stream_try_stream.rs | 27 +++++ 3 files changed, 158 insertions(+) create mode 100644 futures-util/src/stream/try_stream/try_all.rs diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index ab976b145a..7cb9f3e1cd 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -166,6 +166,10 @@ mod into_async_read; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_async_read::IntoAsyncRead; +mod try_all; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_all::TryAll; + impl TryStreamExt for S {} /// Adapters specific to `Result`-returning streams @@ -1182,4 +1186,33 @@ pub trait TryStreamExt: TryStream { { crate::io::assert_read(IntoAsyncRead::new(self)) } + + /// Attempt to execute a predicate over an asynchronous stream and evaluate if all items + /// satisfy the predicate. Exits early if an `Err` is encountered or if an `Ok` item is found + /// that does not satisfy the predicate. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt, TryStreamExt}; + /// use std::convert::Infallible; + /// + /// let number_stream = stream::iter(1..10).map(Ok::<_, Infallible>); + /// let positive = number_stream.try_all(|i| async move { i > 0 }); + /// assert_eq!(positive.await, Ok(true)); + /// + /// let stream_with_errors = stream::iter([Ok(1), Err("err"), Ok(3)]); + /// let positive = stream_with_errors.try_all(|i| async move { i > 0 }); + /// assert_eq!(positive.await, Err("err")); + /// # }); + /// ``` + fn try_all(self, f: F) -> TryAll + where + Self: Sized, + F: FnMut(Self::Ok) -> Fut, + Fut: Future, + { + assert_future::, _>(TryAll::new(self, f)) + } } diff --git a/futures-util/src/stream/try_stream/try_all.rs b/futures-util/src/stream/try_stream/try_all.rs new file mode 100644 index 0000000000..8179f86afc --- /dev/null +++ b/futures-util/src/stream/try_stream/try_all.rs @@ -0,0 +1,98 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::TryStream; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`try_all`](super::TryStreamExt::try_all) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryAll { + #[pin] + stream: St, + f: F, + done: bool, + #[pin] + future: Option, + } +} + +impl fmt::Debug for TryAll +where + St: fmt::Debug, + Fut: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TryAll") + .field("stream", &self.stream) + .field("done", &self.done) + .field("future", &self.future) + .finish() + } +} + +impl TryAll +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, done: false, future: None } + } +} + +impl FusedFuture for TryAll +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.done && self.future.is_none() + } +} + +impl Future for TryAll +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(fut) = this.future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new value + let acc = ready!(fut.poll(cx)); + this.future.set(None); + if !acc { + *this.done = true; + break Ok(false); + } // early exit + } else if !*this.done { + // we're waiting on a new item from the stream + match ready!(this.stream.as_mut().try_poll_next(cx)) { + Some(Ok(item)) => { + this.future.set(Some((this.f)(item))); + } + Some(Err(err)) => { + *this.done = true; + break Err(err); + } + None => { + *this.done = true; + break Ok(true); + } + } + } else { + panic!("TryAll polled after completion") + } + }) + } +} diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index b3d04b9200..4095fa536f 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -1,4 +1,5 @@ use core::pin::Pin; +use std::convert::Infallible; use futures::{ stream::{self, repeat, Repeat, StreamExt, TryStreamExt}, @@ -132,3 +133,29 @@ fn try_flatten_unordered() { assert_eq!(taken, 31); }) } + +async fn is_even(number: u8) -> bool { + number % 2 == 0 +} + +#[test] +fn try_all() { + block_on(async { + let empty: [Result; 0] = []; + let st = stream::iter(empty); + let all = st.try_all(is_even).await; + assert_eq!(Ok(true), all); + + let st = stream::iter([Ok::<_, Infallible>(2), Ok(4), Ok(6), Ok(8)]); + let all = st.try_all(is_even).await; + assert_eq!(Ok(true), all); + + let st = stream::iter([Ok::<_, Infallible>(2), Ok(3), Ok(4)]); + let all = st.try_all(is_even).await; + assert_eq!(Ok(false), all); + + let st = stream::iter([Ok(2), Ok(4), Err("err"), Ok(8)]); + let all = st.try_all(is_even).await; + assert_eq!(Err("err"), all); + }); +} From 24cca65c7ab4677b399a25038721b64cb9cd4da7 Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 21 Oct 2023 14:59:20 -0700 Subject: [PATCH 236/283] Add `TryAny` adapter --- futures-util/src/stream/try_stream/mod.rs | 33 +++++++ futures-util/src/stream/try_stream/try_any.rs | 98 +++++++++++++++++++ futures/tests/stream_try_stream.rs | 22 +++++ 3 files changed, 153 insertions(+) create mode 100644 futures-util/src/stream/try_stream/try_any.rs diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 7cb9f3e1cd..5d5702f363 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -170,6 +170,10 @@ mod try_all; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_all::TryAll; +mod try_any; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_any::TryAny; + impl TryStreamExt for S {} /// Adapters specific to `Result`-returning streams @@ -1215,4 +1219,33 @@ pub trait TryStreamExt: TryStream { { assert_future::, _>(TryAll::new(self, f)) } + + /// Attempt to execute a predicate over an asynchronous stream and evaluate if any items + /// satisfy the predicate. Exits early if an `Err` is encountered or if an `Ok` item is found + /// that satisfies the predicate. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt, TryStreamExt}; + /// use std::convert::Infallible; + /// + /// let number_stream = stream::iter(0..10).map(Ok::<_, Infallible>); + /// let contain_three = number_stream.try_any(|i| async move { i == 3 }); + /// assert_eq!(contain_three.await, Ok(true)); + /// + /// let stream_with_errors = stream::iter([Ok(1), Err("err"), Ok(3)]); + /// let contain_three = stream_with_errors.try_any(|i| async move { i == 3 }); + /// assert_eq!(contain_three.await, Err("err")); + /// # }); + /// ``` + fn try_any(self, f: F) -> TryAny + where + Self: Sized, + F: FnMut(Self::Ok) -> Fut, + Fut: Future, + { + assert_future::, _>(TryAny::new(self, f)) + } } diff --git a/futures-util/src/stream/try_stream/try_any.rs b/futures-util/src/stream/try_stream/try_any.rs new file mode 100644 index 0000000000..15adb30971 --- /dev/null +++ b/futures-util/src/stream/try_stream/try_any.rs @@ -0,0 +1,98 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::TryStream; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`any`](super::StreamExt::any) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryAny { + #[pin] + stream: St, + f: F, + done: bool, + #[pin] + future: Option, + } +} + +impl fmt::Debug for TryAny +where + St: fmt::Debug, + Fut: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TryAny") + .field("stream", &self.stream) + .field("done", &self.done) + .field("future", &self.future) + .finish() + } +} + +impl TryAny +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, done: false, future: None } + } +} + +impl FusedFuture for TryAny +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.done && self.future.is_none() + } +} + +impl Future for TryAny +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(fut) = this.future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new value + let acc = ready!(fut.poll(cx)); + this.future.set(None); + if acc { + *this.done = true; + break Ok(true); + } // early exit + } else if !*this.done { + // we're waiting on a new item from the stream + match ready!(this.stream.as_mut().try_poll_next(cx)) { + Some(Ok(item)) => { + this.future.set(Some((this.f)(item))); + } + Some(Err(err)) => { + *this.done = true; + break Err(err); + } + None => { + *this.done = true; + break Ok(false); + } + } + } else { + panic!("TryAny polled after completion") + } + }) + } +} diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index 4095fa536f..ef38c510b8 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -159,3 +159,25 @@ fn try_all() { assert_eq!(Err("err"), all); }); } + +#[test] +fn try_any() { + block_on(async { + let empty: [Result; 0] = []; + let st = stream::iter(empty); + let any = st.try_any(is_even).await; + assert_eq!(Ok(false), any); + + let st = stream::iter([Ok::<_, Infallible>(1), Ok(2), Ok(3)]); + let any = st.try_any(is_even).await; + assert_eq!(Ok(true), any); + + let st = stream::iter([Ok::<_, Infallible>(1), Ok(3), Ok(5)]); + let any = st.try_any(is_even).await; + assert_eq!(Ok(false), any); + + let st = stream::iter([Ok(1), Ok(3), Err("err"), Ok(8)]); + let any = st.try_any(is_even).await; + assert_eq!(Err("err"), any); + }); +} From a40204d698c0f435fe53e4d6a3191239375c9103 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Oct 2023 22:43:29 +0900 Subject: [PATCH 237/283] Provide AtomicWaker if portable-atomic feature is enabled, even if atomic CAS is not available --- futures-core/Cargo.toml | 2 +- futures-core/src/task/__internal/mod.rs | 4 ++-- futures-util/src/task/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index a65e8fb034..b3499c223a 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -21,7 +21,7 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -portable-atomic = { version = "1", default-features = false, optional = true } +portable-atomic = { version = "1.3", optional = true, default-features = false, features = ["require-cas"] } [dev-dependencies] futures = { path = "../futures" } diff --git a/futures-core/src/task/__internal/mod.rs b/futures-core/src/task/__internal/mod.rs index c902eb4bfb..377f3e286c 100644 --- a/futures-core/src/task/__internal/mod.rs +++ b/futures-core/src/task/__internal/mod.rs @@ -1,4 +1,4 @@ -#[cfg(not(futures_no_atomic_cas))] +#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] mod atomic_waker; -#[cfg(not(futures_no_atomic_cas))] +#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] pub use self::atomic_waker::AtomicWaker; diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index 0a31eeac14..3ed4bfadad 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -30,7 +30,7 @@ pub use futures_task::waker; #[cfg(feature = "alloc")] pub use futures_task::{waker_ref, WakerRef}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] pub use futures_core::task::__internal::AtomicWaker; mod spawn; From 86f2a6aa8c0a188c8b75be0e4eac711321fd13d7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Oct 2023 23:02:13 +0900 Subject: [PATCH 238/283] Release 0.3.29 --- CHANGELOG.md | 9 +++++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 44 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f23a1c84e9..e689f36917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.3.29 - 2023-10-26 + +* Add `TryStreamExt::try_ready_chunks` (#2757) +* Add `TryStreamExt::{try_all,try_any}` (#2783) +* Add `UnboundedSender::{len,is_empty}` (#2750) +* Fix `Sync` impl of `FuturesUnordered` (#2788) +* Fix infinite loop caused by invalid UTF-8 bytes (#2785) +* Fix build error with -Z minimal-versions (#2761) + # 0.3.28 - 2023-03-30 * Update to syn 2. This raises MSRV of utility crates to 1.56. (#2730, #2733) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 588cf69b2f..1f50e60b44 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index b3499c223a..704861cc3e 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index bd47e42a4e..22cf99636c 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.28", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.29", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index dac499faab..a0699de52a 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index b8982377e1..f180d9e9f6 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index ec760185ba..dbf5e91be7 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 8ae7c3f5ad..b3bf447d39 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 6b16a384ab..81f00e9147 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.28", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.28", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.28", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.28", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.28", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.28", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.28", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.29", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.29", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.29", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.29", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.29", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.29", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.29", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 93924692f0..27e9e94985 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.28", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.28", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.28", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.29", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.29", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.29", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 140fa210f0..96db108d37 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.28" +version = "0.3.29" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.28", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.28", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.28", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.28", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.28", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.28", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.28", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.29", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.29", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.29", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.29", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From f75c4c1cdefe57a6a6a5301c2c18172e2b27f5b2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Oct 2023 23:24:13 +0900 Subject: [PATCH 239/283] Update ci/publish.sh --- ci/publish.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ci/publish.sh b/ci/publish.sh index 9666782732..cf898ba491 100755 --- a/ci/publish.sh +++ b/ci/publish.sh @@ -2,10 +2,11 @@ set -euo pipefail IFS=$'\n\t' +cd "$(dirname "$0")"/.. # A list of paths to the crate to be published. # It will be published in the order listed. -MEMBERS=( +members=( "futures-core" "futures-io" "futures-sink" @@ -18,16 +19,10 @@ MEMBERS=( "futures-test" ) -cd "$(cd "$(dirname "$0")" && pwd)"/.. - -set -x - -for i in "${!MEMBERS[@]}"; do +for member in "${members[@]}"; do ( - cd "${MEMBERS[${i}]}" + set -x + cd "${member}" cargo +stable publish ) - if [[ $((i + 1)) != "${#MEMBERS[@]}" ]]; then - sleep 45 - fi done From 0fe4b8853b5560d4565d52d23a8a6f96fe446fec Mon Sep 17 00:00:00 2001 From: edef Date: Sat, 18 Nov 2023 22:56:02 +0000 Subject: [PATCH 240/283] FillBuf: don't poll a second time on EOF There is no hard guarantee that polling a second time will return Poll::Ready, and this is particularly likely to break in the EOF case, which is precisely where we don't need to do so at all. Both tokio::io::BufReader and futures::io::BufReader always attempt to read from the underlying reader when the buffer is empty, rather than fusing EOF. --- futures-util/src/io/fill_buf.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs index a1484c0322..6c14f71cd7 100644 --- a/futures-util/src/io/fill_buf.rs +++ b/futures-util/src/io/fill_buf.rs @@ -30,6 +30,8 @@ where let reader = this.reader.take().expect("Polled FillBuf after completion"); match Pin::new(&mut *reader).poll_fill_buf(cx) { + // We don't need to poll a second time for EOF, and doing so is likely to return Poll::Pending + Poll::Ready(Ok(&[])) => Poll::Ready(Ok(&[])), // With polonius it is possible to remove this inner match and just have the correct // lifetime of the reference inferred based on which branch is taken Poll::Ready(Ok(_)) => match Pin::new(reader).poll_fill_buf(cx) { From 8189e72a4b67df66f0d82e5ca4f04baefd83b79f Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 28 Oct 2023 09:31:12 -0700 Subject: [PATCH 241/283] Import `TryAll` and `TryAny` in the `stream` module This makes them show up on the documentation page for the `stream` module. It also makes the return types on the `TryStreamExt` documentation page link to the adapter types. --- futures-util/src/stream/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 34d68a80be..92a3724b4e 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -51,9 +51,9 @@ pub use self::stream::{ReuniteError, SplitSink, SplitStream}; mod try_stream; pub use self::try_stream::{ - try_unfold, AndThen, ErrInto, InspectErr, InspectOk, IntoStream, MapErr, MapOk, OrElse, - TryCollect, TryConcat, TryFilter, TryFilterMap, TryFlatten, TryFold, TryForEach, TryNext, - TrySkipWhile, TryStreamExt, TryTakeWhile, TryUnfold, + try_unfold, AndThen, ErrInto, InspectErr, InspectOk, IntoStream, MapErr, MapOk, OrElse, TryAll, + TryAny, TryCollect, TryConcat, TryFilter, TryFilterMap, TryFlatten, TryFold, TryForEach, + TryNext, TrySkipWhile, TryStreamExt, TryTakeWhile, TryUnfold, }; #[cfg(feature = "io")] From 40eed5c17971a15572985698970a75ed1c5c17cb Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Sat, 28 Oct 2023 09:33:08 -0700 Subject: [PATCH 242/283] Fix the documentation for the `TryAny` adapter --- futures-util/src/stream/try_stream/try_any.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/stream/try_stream/try_any.rs b/futures-util/src/stream/try_stream/try_any.rs index 15adb30971..55e876be05 100644 --- a/futures-util/src/stream/try_stream/try_any.rs +++ b/futures-util/src/stream/try_stream/try_any.rs @@ -7,7 +7,7 @@ use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; pin_project! { - /// Future for the [`any`](super::StreamExt::any) method. + /// Future for the [`try_any`](super::TryStreamExt::try_any) method. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryAny { #[pin] From 49107994e567323aa026f3f4a3cdb19bc04cf24b Mon Sep 17 00:00:00 2001 From: Paul Sbarra Date: Mon, 6 Nov 2023 21:37:17 -0600 Subject: [PATCH 243/283] provide a non-destructive mechanism to determine if a sink/stream are paired --- futures-util/src/lock/bilock.rs | 7 ++- futures-util/src/stream/stream/split.rs | 80 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 7ddc66ad2c..a89678e05f 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -149,6 +149,11 @@ impl BiLock { BiLockAcquire { bilock: self } } + /// Returns `true` only if the other `BiLock` originated from the same call to `BiLock::new`. + pub fn is_pair_of(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.arc, &other.arc) + } + /// 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`. @@ -156,7 +161,7 @@ impl BiLock { where T: Unpin, { - if Arc::ptr_eq(&self.arc, &other.arc) { + if self.is_pair_of(&other) { drop(other); let inner = Arc::try_unwrap(self.arc) .ok() diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index e2034e0c27..1a7fdcb387 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -15,6 +15,13 @@ pub struct SplitStream(BiLock); impl Unpin for SplitStream {} +impl SplitStream { + /// Returns `true` if the `SplitStream` and `SplitSink` originate from the same call to `StreamExt::split`. + pub fn is_pair_of(&self, other: &SplitSink) -> bool { + other.is_pair_of(&self) + } +} + impl SplitStream { /// Attempts to put the two "halves" of a split `Stream + Sink` back /// together. Succeeds only if the `SplitStream` and `SplitSink` are @@ -60,6 +67,13 @@ impl + Unpin, Item> SplitSink { } } +impl SplitSink { + /// Returns `true` if the `SplitStream` and `SplitSink` originate from the same call to `StreamExt::split`. + pub fn is_pair_of(&self, other: &SplitStream) -> bool { + self.lock.is_pair_of(&other.0) + } +} + impl, Item> SplitSink { fn poll_flush_slot( mut inner: Pin<&mut S>, @@ -142,3 +156,69 @@ impl fmt::Display for ReuniteError { #[cfg(feature = "std")] impl std::error::Error for ReuniteError {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{sink::Sink, stream::StreamExt}; + use core::marker::PhantomData; + + struct NopStream { + phantom: PhantomData, + } + + impl Stream for NopStream { + type Item = Item; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + todo!() + } + } + + impl Sink for NopStream { + type Error = (); + + fn poll_ready( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + todo!() + } + + fn start_send(self: Pin<&mut Self>, _item: Item) -> Result<(), Self::Error> { + todo!() + } + + fn poll_flush( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + todo!() + } + + fn poll_close( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + todo!() + } + } + + #[test] + fn test_pairing() { + let s1 = NopStream::<()> { phantom: PhantomData }; + let (sink1, stream1) = s1.split(); + assert!(sink1.is_pair_of(&stream1)); + assert!(stream1.is_pair_of(&sink1)); + + let s2 = NopStream::<()> { phantom: PhantomData }; + let (sink2, stream2) = s2.split(); + assert!(sink2.is_pair_of(&stream2)); + assert!(stream2.is_pair_of(&sink2)); + + assert!(!sink1.is_pair_of(&stream2)); + assert!(!stream1.is_pair_of(&sink2)); + assert!(!sink2.is_pair_of(&stream1)); + assert!(!stream2.is_pair_of(&sink1)); + } +} From 17851c1f05598a0ec223bca292befbf1d225ac42 Mon Sep 17 00:00:00 2001 From: Paul Sbarra Date: Sun, 12 Nov 2023 19:23:53 -0600 Subject: [PATCH 244/283] provide a mechanism to determine if io read/write halves are from the same stream --- futures-util/src/io/split.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/futures-util/src/io/split.rs b/futures-util/src/io/split.rs index 3f1b9af456..81d1e6dcb5 100644 --- a/futures-util/src/io/split.rs +++ b/futures-util/src/io/split.rs @@ -31,6 +31,13 @@ pub(super) fn split(t: T) -> (ReadHalf, WriteHalf< (ReadHalf { handle: a }, WriteHalf { handle: b }) } +impl ReadHalf { + /// Checks if this `ReadHalf` and some `WriteHalf` were split from the same stream. + pub fn is_pair_of(&self, other: &WriteHalf) -> bool { + self.handle.is_pair_of(&other.handle) + } +} + impl ReadHalf { /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are @@ -42,6 +49,13 @@ impl ReadHalf { } } +impl WriteHalf { + /// Checks if this `WriteHalf` and some `ReadHalf` were split from the same stream. + pub fn is_pair_of(&self, other: &ReadHalf) -> bool { + self.handle.is_pair_of(&other.handle) + } +} + impl WriteHalf { /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are From e4aa659649940c6412eaf6c4c38b939de3163430 Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Wed, 15 Nov 2023 18:02:41 +0800 Subject: [PATCH 245/283] remove redundant impl Unpin --- futures-util/src/future/future/shared.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index ecd1b426db..9ab3b4f1d6 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -37,10 +37,6 @@ impl Clone for WeakShared { } } -// The future itself is polled behind the `Arc`, so it won't be moved -// when `Shared` is moved. -impl Unpin for Shared {} - impl fmt::Debug for Shared { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Shared") From 04d01a00a2924525658b19794534bec5672500e6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 8 Dec 2023 00:42:09 +0900 Subject: [PATCH 246/283] FuturesOrdered: Use 64-bit index --- futures-util/src/stream/futures_ordered.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 3aaef8bdef..2cc144e81d 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -19,7 +19,8 @@ pin_project! { struct OrderWrapper { #[pin] data: T, // A future or a future's output - index: isize, + // Use i64 for index since isize may overflow in 32-bit targets. + index: i64, } } @@ -98,8 +99,8 @@ where pub struct FuturesOrdered { in_progress_queue: FuturesUnordered>, queued_outputs: BinaryHeap>, - next_incoming_index: isize, - next_outgoing_index: isize, + next_incoming_index: i64, + next_outgoing_index: i64, } impl Unpin for FuturesOrdered {} From e6735f3d01b9ced68a60e521f4f7961dc5e9ab0d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 8 Dec 2023 01:22:51 +0900 Subject: [PATCH 247/283] FuturesUnordered: Fix clear implementation --- .../src/stream/futures_unordered/mod.rs | 21 +++-------- .../futures_unordered/ready_to_run_queue.rs | 37 ++++++------------- futures/tests/stream_futures_unordered.rs | 25 +++++++++++++ 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 0dbaea9080..dedf75dee2 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -558,20 +558,7 @@ impl Debug for FuturesUnordered { impl FuturesUnordered { /// Clears the set, removing all futures. pub fn clear(&mut self) { - self.clear_head_all(); - - // we just cleared all the tasks, and we have &mut self, so this is safe. - unsafe { self.ready_to_run_queue.clear() }; - - self.is_terminated.store(false, Relaxed); - } - - fn clear_head_all(&mut self) { - while !self.head_all.get_mut().is_null() { - let head = *self.head_all.get_mut(); - let task = unsafe { self.unlink(head) }; - self.release_task(task); - } + *self = Self::new(); } } @@ -581,7 +568,11 @@ impl Drop for FuturesUnordered { // associated with it. At the same time though there may be tons of // wakers flying around which contain `Task` references // inside them. We'll let those naturally get deallocated. - self.clear_head_all(); + while !self.head_all.get_mut().is_null() { + let head = *self.head_all.get_mut(); + let task = unsafe { self.unlink(head) }; + self.release_task(task); + } // Note that at this point we could still have a bunch of tasks in the // ready to run queue. None of those tasks, however, have futures diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 4518705320..a924935d23 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -85,38 +85,25 @@ impl ReadyToRunQueue { pub(super) fn stub(&self) -> *const Task { Arc::as_ptr(&self.stub) } - - // Clear the queue of tasks. - // - // Note that each task has a strong reference count associated with it - // which is owned by the ready to run queue. This method just pulls out - // tasks and drops their refcounts. - // - // # Safety - // - // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear) - // - The caller **must** guarantee unique access to `self` - pub(crate) unsafe fn clear(&self) { - loop { - // SAFETY: We have the guarantee of mutual exclusion required by `dequeue`. - match self.dequeue() { - Dequeue::Empty => break, - Dequeue::Inconsistent => abort("inconsistent in drop"), - Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), - } - } - } } impl Drop for ReadyToRunQueue { fn drop(&mut self) { // Once we're in the destructor for `Inner` we need to clear out // the ready to run queue of tasks if there's anything left in there. - - // All tasks have had their futures dropped already by the `FuturesUnordered` - // destructor above, and we have &mut self, so this is safe. + // + // Note that each task has a strong reference count associated with it + // which is owned by the ready to run queue. All tasks should have had + // their futures dropped already by the `FuturesUnordered` destructor + // above, so we're just pulling out tasks and dropping their refcounts. unsafe { - self.clear(); + loop { + match self.dequeue() { + Dequeue::Empty => break, + Dequeue::Inconsistent => abort("inconsistent in drop"), + Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), + } + } } } } diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index b568280479..7bdf5432ca 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -381,3 +381,28 @@ fn clear() { tasks.clear(); assert!(!tasks.is_terminated()); } + +// https://github.com/rust-lang/futures-rs/issues/2529#issuecomment-997290279 +#[test] +fn clear_in_loop() { + const N: usize = + if cfg!(miri) || option_env!("QEMU_LD_PREFIX").is_some() { 100 } else { 10_000 }; + futures::executor::block_on(async { + async fn task() { + let (s, r) = oneshot::channel(); + std::thread::spawn(|| { + std::thread::sleep(std::time::Duration::from_micros(100)); + let _ = s.send(()); + }); + r.await.unwrap() + } + let mut futures = FuturesUnordered::new(); + for _ in 0..N { + for _ in 0..24 { + futures.push(task()); + } + let _ = futures.next().await; + futures.clear(); + } + }); +} From c179201bd1ed11fe3200ca345a3aba3ddfd9497f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 8 Dec 2023 02:19:35 +0900 Subject: [PATCH 248/283] FillBuf: Do not call poll_fill_buf twice --- futures-util/src/io/fill_buf.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs index 6c14f71cd7..45862b8e29 100644 --- a/futures-util/src/io/fill_buf.rs +++ b/futures-util/src/io/fill_buf.rs @@ -3,6 +3,7 @@ use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; use std::io; use std::pin::Pin; +use std::slice; /// Future for the [`fill_buf`](super::AsyncBufReadExt::fill_buf) method. #[derive(Debug)] @@ -30,19 +31,12 @@ where let reader = this.reader.take().expect("Polled FillBuf after completion"); match Pin::new(&mut *reader).poll_fill_buf(cx) { - // We don't need to poll a second time for EOF, and doing so is likely to return Poll::Pending - Poll::Ready(Ok(&[])) => Poll::Ready(Ok(&[])), - // With polonius it is possible to remove this inner match and just have the correct - // lifetime of the reference inferred based on which branch is taken - Poll::Ready(Ok(_)) => match Pin::new(reader).poll_fill_buf(cx) { - Poll::Ready(Ok(slice)) => Poll::Ready(Ok(slice)), - Poll::Ready(Err(err)) => { - unreachable!("reader indicated readiness but then returned an error: {:?}", err) - } - Poll::Pending => { - unreachable!("reader indicated readiness but then returned pending") - } - }, + Poll::Ready(Ok(slice)) => { + // With polonius it is possible to remove this lifetime transmutation and just have + // the correct lifetime of the reference inferred based on which branch is taken + let slice: &'a [u8] = unsafe { slice::from_raw_parts(slice.as_ptr(), slice.len()) }; + Poll::Ready(Ok(slice)) + } Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Pending => { this.reader = Some(reader); From 272a3c709f7502df32a70c621ffe02e1e24ca5a4 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 8 Dec 2023 01:41:53 +0900 Subject: [PATCH 249/283] Use cfg(target_has_atomic) on no-std targets --- .github/workflows/ci.yml | 36 -------------------- ci/no_atomic_cas.sh | 31 ----------------- futures-channel/build.rs | 41 ----------------------- futures-channel/no_atomic_cas.rs | 1 - futures-channel/src/lib.rs | 8 ++--- futures-core/build.rs | 41 ----------------------- futures-core/no_atomic_cas.rs | 1 - futures-core/src/task/__internal/mod.rs | 7 ++-- futures-task/build.rs | 41 ----------------------- futures-task/no_atomic_cas.rs | 1 - futures-task/src/lib.rs | 12 +++---- futures-task/src/spawn.rs | 4 +-- futures-util/build.rs | 41 ----------------------- futures-util/no_atomic_cas.rs | 1 - futures-util/src/future/join_all.rs | 15 +++++---- futures-util/src/future/mod.rs | 6 ++-- futures-util/src/future/try_join_all.rs | 13 +++---- futures-util/src/lib.rs | 2 +- futures-util/src/lock/mod.rs | 10 +++--- futures-util/src/stream/mod.rs | 24 ++++++------- futures-util/src/stream/stream/mod.rs | 34 +++++++++---------- futures-util/src/stream/try_stream/mod.rs | 24 ++++++------- futures-util/src/task/mod.rs | 11 +++--- no_atomic_cas.rs | 17 ---------- 24 files changed, 89 insertions(+), 333 deletions(-) delete mode 100755 ci/no_atomic_cas.sh delete mode 100644 futures-channel/build.rs delete mode 120000 futures-channel/no_atomic_cas.rs delete mode 100644 futures-core/build.rs delete mode 120000 futures-core/no_atomic_cas.rs delete mode 100644 futures-task/build.rs delete mode 120000 futures-task/no_atomic_cas.rs delete mode 100644 futures-util/build.rs delete mode 120000 futures-util/no_atomic_cas.rs delete mode 100644 no_atomic_cas.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edf4396c8e..50557c4bbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -230,42 +230,6 @@ jobs: --workspace --exclude futures-test \ --features unstable --ignore-unknown-features - # When this job failed, run ci/no_atomic_cas.sh and commit result changes. - codegen: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: rustup update nightly && rustup default nightly - - run: ci/no_atomic_cas.sh - - run: git add -N . && git diff --exit-code - if: github.repository_owner != 'rust-lang' || github.event_name != 'schedule' - - id: diff - run: | - git config user.name "Taiki Endo" - git config user.email "te316e89@gmail.com" - git add -N . - if ! git diff --exit-code; then - git add . - git commit -m "Update no_atomic_cas.rs" - echo "::set-output name=success::false" - fi - if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' - - uses: peter-evans/create-pull-request@v5 - with: - title: Update no_atomic_cas.rs - body: | - Auto-generated by [create-pull-request][1] - [Please close and immediately reopen this pull request to run CI.][2] - - [1]: https://github.com/peter-evans/create-pull-request - [2]: https://github.com/peter-evans/create-pull-request/blob/HEAD/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs - branch: update-no-atomic-cas-rs - if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' && steps.diff.outputs.success == 'false' - miri: name: cargo miri test runs-on: ubuntu-latest diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh deleted file mode 100755 index ba0200d698..0000000000 --- a/ci/no_atomic_cas.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' -cd "$(dirname "$0")"/.. - -# Update the list of targets that do not support atomic CAS operations. -# -# Usage: -# ./ci/no_atomic_cas.sh - -file="no_atomic_cas.rs" - -no_atomic_cas=() -for target in $(rustc --print target-list); do - target_spec=$(rustc --print target-spec-json -Z unstable-options --target "${target}") - res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)') - [[ -z "${res}" ]] || no_atomic_cas+=("${target}") -done - -cat >"${file}" <>"${file}" -done -cat >>"${file}" < target, - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_*`, not `has_*`. This allows treating - // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't - // run. This is needed for compatibility with non-cargo build systems that - // don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=futures_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); -} diff --git a/futures-channel/no_atomic_cas.rs b/futures-channel/no_atomic_cas.rs deleted file mode 120000 index 3d7380fadd..0000000000 --- a/futures-channel/no_atomic_cas.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 4cd936d552..f611e6b9f5 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -27,16 +27,16 @@ ) ))] -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod lock; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "std")] pub mod mpsc; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub mod oneshot; diff --git a/futures-core/build.rs b/futures-core/build.rs deleted file mode 100644 index 05e0496d94..0000000000 --- a/futures-core/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `futures_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => target, - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_*`, not `has_*`. This allows treating - // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't - // run. This is needed for compatibility with non-cargo build systems that - // don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=futures_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); -} diff --git a/futures-core/no_atomic_cas.rs b/futures-core/no_atomic_cas.rs deleted file mode 120000 index 3d7380fadd..0000000000 --- a/futures-core/no_atomic_cas.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-core/src/task/__internal/mod.rs b/futures-core/src/task/__internal/mod.rs index 377f3e286c..c248742280 100644 --- a/futures-core/src/task/__internal/mod.rs +++ b/futures-core/src/task/__internal/mod.rs @@ -1,4 +1,7 @@ -#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] +#[cfg_attr(target_os = "none", cfg(any(target_has_atomic = "ptr", feature = "portable-atomic")))] mod atomic_waker; -#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] +#[cfg_attr( + target_os = "none", + cfg(any(target_has_atomic = "ptr", feature = "portable-atomic")) +)] pub use self::atomic_waker::AtomicWaker; diff --git a/futures-task/build.rs b/futures-task/build.rs deleted file mode 100644 index 05e0496d94..0000000000 --- a/futures-task/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `futures_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => target, - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_*`, not `has_*`. This allows treating - // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't - // run. This is needed for compatibility with non-cargo build systems that - // don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=futures_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); -} diff --git a/futures-task/no_atomic_cas.rs b/futures-task/no_atomic_cas.rs deleted file mode 120000 index 3d7380fadd..0000000000 --- a/futures-task/no_atomic_cas.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index c72460744c..33896d8b14 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -18,24 +18,24 @@ extern crate alloc; mod spawn; pub use crate::spawn::{LocalSpawn, Spawn, SpawnError}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod arc_wake; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use crate::arc_wake::ArcWake; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod waker; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use crate::waker::waker; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod waker_ref; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use crate::waker_ref::{waker_ref, WakerRef}; diff --git a/futures-task/src/spawn.rs b/futures-task/src/spawn.rs index f4e63397bd..4a9a45a446 100644 --- a/futures-task/src/spawn.rs +++ b/futures-task/src/spawn.rs @@ -168,7 +168,7 @@ mod if_alloc { } } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] impl Spawn for alloc::sync::Arc { fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { (**self).spawn_obj(future) @@ -179,7 +179,7 @@ mod if_alloc { } } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] impl LocalSpawn for alloc::sync::Arc { fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { (**self).spawn_local_obj(future) diff --git a/futures-util/build.rs b/futures-util/build.rs deleted file mode 100644 index 05e0496d94..0000000000 --- a/futures-util/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The rustc-cfg listed below are considered public API, but it is *unstable* -// and outside of the normal semver guarantees: -// -// - `futures_no_atomic_cas` -// Assume the target does *not* support atomic CAS operations. -// This is usually detected automatically by the build script, but you may -// need to enable it manually when building for custom targets or using -// non-cargo build systems that don't run the build script. -// -// With the exceptions mentioned above, the rustc-cfg emitted by the build -// script are *not* public API. - -#![warn(rust_2018_idioms, single_use_lifetimes)] - -use std::env; - -include!("no_atomic_cas.rs"); - -fn main() { - let target = match env::var("TARGET") { - Ok(target) => target, - Err(e) => { - println!( - "cargo:warning={}: unable to get TARGET environment variable: {}", - env!("CARGO_PKG_NAME"), - e - ); - return; - } - }; - - // Note that this is `no_*`, not `has_*`. This allows treating - // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't - // run. This is needed for compatibility with non-cargo build systems that - // don't run the build script. - if NO_ATOMIC_CAS.contains(&&*target) { - println!("cargo:rustc-cfg=futures_no_atomic_cas"); - } - - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); -} diff --git a/futures-util/no_atomic_cas.rs b/futures-util/no_atomic_cas.rs deleted file mode 120000 index 3d7380fadd..0000000000 --- a/futures-util/no_atomic_cas.rs +++ /dev/null @@ -1 +0,0 @@ -../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 11b6f27288..79eee8dffc 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -12,7 +12,7 @@ use core::task::{Context, Poll}; use super::{assert_future, MaybeDone}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] use crate::stream::{Collect, FuturesOrdered, StreamExt}; pub(crate) fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { @@ -31,7 +31,7 @@ where kind: JoinAllKind, } -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] pub(crate) const SMALL: usize = 30; enum JoinAllKind @@ -41,7 +41,7 @@ where Small { elems: Pin]>>, }, - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] Big { fut: Collect, Vec>, }, @@ -57,7 +57,7 @@ where JoinAllKind::Small { ref elems } => { f.debug_struct("JoinAll").field("elems", elems).finish() } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] JoinAllKind::Big { ref fut, .. } => fmt::Debug::fmt(fut, f), } } @@ -106,7 +106,8 @@ where { let iter = iter.into_iter(); - #[cfg(futures_no_atomic_cas)] + #[cfg(target_os = "none")] + #[cfg_attr(target_os = "none", cfg(not(target_has_atomic = "ptr")))] { let kind = JoinAllKind::Small { elems: iter.map(MaybeDone::Future).collect::>().into() }; @@ -114,7 +115,7 @@ where assert_future::::Output>, _>(JoinAll { kind }) } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] { let kind = match iter.size_hint().1 { Some(max) if max <= SMALL => JoinAllKind::Small { @@ -153,7 +154,7 @@ where Poll::Pending } } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] JoinAllKind::Big { fut } => Pin::new(fut).poll(cx), } } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 374e36512f..2d8fa4f654 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -111,13 +111,13 @@ pub use self::select_ok::{select_ok, SelectOk}; mod either; pub use self::either::Either; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod abortable; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use crate::abortable::{AbortHandle, AbortRegistration, Abortable, Aborted}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use abortable::abortable; diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 506f450657..2d6a2a02cb 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -12,7 +12,7 @@ use core::task::{Context, Poll}; use super::{assert_future, join_all, IntoFuture, TryFuture, TryMaybeDone}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] use crate::stream::{FuturesOrdered, TryCollect, TryStreamExt}; use crate::TryFutureExt; @@ -38,7 +38,7 @@ where Small { elems: Pin>]>>, }, - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] Big { fut: TryCollect>, Vec>, }, @@ -56,7 +56,7 @@ where TryJoinAllKind::Small { ref elems } => { f.debug_struct("TryJoinAll").field("elems", elems).finish() } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] TryJoinAllKind::Big { ref fut, .. } => fmt::Debug::fmt(fut, f), } } @@ -121,7 +121,8 @@ where { let iter = iter.into_iter().map(TryFutureExt::into_future); - #[cfg(futures_no_atomic_cas)] + #[cfg(target_os = "none")] + #[cfg_attr(target_os = "none", cfg(not(target_has_atomic = "ptr")))] { let kind = TryJoinAllKind::Small { elems: iter.map(TryMaybeDone::Future).collect::>().into(), @@ -132,7 +133,7 @@ where ) } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] { let kind = match iter.size_hint().1 { Some(max) if max <= join_all::SMALL => TryJoinAllKind::Small { @@ -184,7 +185,7 @@ where } } } - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] TryJoinAllKind::Big { fut } => Pin::new(fut).poll(cx), } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 9a10c93c9a..208eb73aa5 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -329,7 +329,7 @@ pub use crate::io::{ #[cfg(feature = "alloc")] pub mod lock; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod abortable; diff --git a/futures-util/src/lock/mod.rs b/futures-util/src/lock/mod.rs index 0be72717c8..8ca0ff6255 100644 --- a/futures-util/src/lock/mod.rs +++ b/futures-util/src/lock/mod.rs @@ -3,25 +3,25 @@ //! This module is only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(any(feature = "sink", feature = "io"))] #[cfg(not(feature = "bilock"))] pub(crate) use self::bilock::BiLock; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "bilock")] #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "std")] pub use self::mutex::{ MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture, OwnedMutexGuard, OwnedMutexLockFuture, }; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(any(feature = "bilock", feature = "sink", feature = "io"))] #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] #[cfg_attr(not(feature = "bilock"), allow(unreachable_pub))] mod bilock; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "std")] mod mutex; diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index 92a3724b4e..2438e58b62 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -37,13 +37,13 @@ pub use self::stream::ReadyChunks; #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::stream::Forward; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use self::stream::{ BufferUnordered, Buffered, FlatMapUnordered, FlattenUnordered, ForEachConcurrent, }; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[cfg(feature = "alloc")] @@ -61,7 +61,7 @@ pub use self::try_stream::{ #[cfg(feature = "std")] pub use self::try_stream::IntoAsyncRead; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use self::try_stream::{ TryBufferUnordered, TryBuffered, TryFlattenUnordered, TryForEachConcurrent, @@ -105,36 +105,36 @@ pub use self::select_with_strategy::{select_with_strategy, PollNext, SelectWithS mod unfold; pub use self::unfold::{unfold, Unfold}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod futures_ordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use self::futures_ordered::FuturesOrdered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub mod futures_unordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[doc(inline)] pub use self::futures_unordered::FuturesUnordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub mod select_all; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[doc(inline)] pub use self::select_all::{select_all, SelectAll}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod abortable; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use crate::abortable::{AbortHandle, AbortRegistration, Abortable, Aborted}; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use abortable::abortable; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 3978d188fc..2da7036b24 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -181,32 +181,32 @@ mod scan; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::scan::Scan; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod buffer_unordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::buffer_unordered::BufferUnordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod buffered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::buffered::Buffered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub(crate) mod flatten_unordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] pub use self::flatten_unordered::FlattenUnordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] delegate_all!( /// Stream for the [`flat_map_unordered`](StreamExt::flat_map_unordered) method. @@ -216,20 +216,20 @@ delegate_all!( where St: Stream, U: Stream, U: Unpin, F: FnMut(St::Item) -> U ); -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod for_each_concurrent; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::for_each_concurrent::ForEachConcurrent; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[cfg(feature = "alloc")] mod split; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[cfg(feature = "alloc")] @@ -820,7 +820,7 @@ pub trait StreamExt: Stream { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # }); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn flatten_unordered(self, limit: impl Into>) -> FlattenUnordered where @@ -902,7 +902,7 @@ pub trait StreamExt: Stream { /// assert_eq!(vec![1usize, 2, 2, 3, 3, 3, 4, 4, 4, 4], values); /// # }); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn flat_map_unordered( self, @@ -1142,7 +1142,7 @@ pub trait StreamExt: Stream { /// fut.await; /// # }) /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn for_each_concurrent( self, @@ -1365,7 +1365,7 @@ pub trait StreamExt: Stream { /// /// This method is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn buffered(self, n: usize) -> Buffered where @@ -1410,7 +1410,7 @@ pub trait StreamExt: Stream { /// assert_eq!(buffered.next().await, None); /// # Ok::<(), i32>(()) }).unwrap(); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn buffer_unordered(self, n: usize) -> BufferUnordered where @@ -1577,7 +1577,7 @@ pub trait StreamExt: Stream { /// library is activated, and it is activated by default. #[cfg(feature = "sink")] #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn split(self) -> (SplitSink, SplitStream) where diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 5d5702f363..7b55444b3e 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -89,10 +89,10 @@ mod try_flatten; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_flatten::TryFlatten; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod try_flatten_unordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_flatten_unordered::TryFlattenUnordered; @@ -133,26 +133,26 @@ mod try_take_while; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_take_while::TryTakeWhile; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod try_buffer_unordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_buffer_unordered::TryBufferUnordered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod try_buffered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_buffered::TryBuffered; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] mod try_for_each_concurrent; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_for_each_concurrent::TryForEachConcurrent; @@ -551,7 +551,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(Err(oneshot::Canceled), fut.await); /// # }) /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn try_for_each_concurrent( self, @@ -827,7 +827,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(values, vec![Ok(1), Ok(2), Ok(4), Err(3), Err(5)]); /// # }); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn try_flatten_unordered(self, limit: impl Into>) -> TryFlattenUnordered where @@ -1029,7 +1029,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn try_buffer_unordered(self, n: usize) -> TryBufferUnordered where @@ -1105,7 +1105,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - #[cfg(not(futures_no_atomic_cas))] + #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] fn try_buffered(self, n: usize) -> TryBuffered where diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index 3ed4bfadad..7a9e993e5e 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -18,19 +18,22 @@ pub use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, pub use futures_task::noop_waker; pub use futures_task::noop_waker_ref; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use futures_task::ArcWake; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use futures_task::waker; -#[cfg(not(futures_no_atomic_cas))] +#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] pub use futures_task::{waker_ref, WakerRef}; -#[cfg(any(not(futures_no_atomic_cas), feature = "portable-atomic"))] +#[cfg_attr( + target_os = "none", + cfg(any(target_has_atomic = "ptr", feature = "portable-atomic")) +)] pub use futures_core::task::__internal::AtomicWaker; mod spawn; diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs deleted file mode 100644 index 16ec628cdf..0000000000 --- a/no_atomic_cas.rs +++ /dev/null @@ -1,17 +0,0 @@ -// This file is @generated by no_atomic_cas.sh. -// It is not intended for manual editing. - -const NO_ATOMIC_CAS: &[&str] = &[ - "armv4t-none-eabi", - "armv5te-none-eabi", - "avr-unknown-gnu-atmega328", - "bpfeb-unknown-none", - "bpfel-unknown-none", - "msp430-none-elf", - "riscv32i-unknown-none-elf", - "riscv32im-unknown-none-elf", - "riscv32imc-unknown-none-elf", - "thumbv4t-none-eabi", - "thumbv5te-none-eabi", - "thumbv6m-none-eabi", -]; From fdd2ce716c1b1064332e4d20f23a58696a2c1c6f Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 12 Dec 2023 17:12:44 -0500 Subject: [PATCH 250/283] Fix broken link in `CopyBufAbortable` docs (#2815) Also re-enable docs in CI. --- futures-util/src/io/copy_buf_abortable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/io/copy_buf_abortable.rs b/futures-util/src/io/copy_buf_abortable.rs index fdbc4a5f00..ed22d62338 100644 --- a/futures-util/src/io/copy_buf_abortable.rs +++ b/futures-util/src/io/copy_buf_abortable.rs @@ -57,7 +57,7 @@ where } pin_project! { - /// Future for the [`copy_buf()`] function. + /// Future for the [`copy_buf_abortable()`] function. #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct CopyBufAbortable<'a, R, W: ?Sized> { From 68d284545f79ca77690d935859ba0cfe0a1be4ac Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Thu, 21 Dec 2023 00:40:25 +0800 Subject: [PATCH 251/283] Remove a redundant space in example (#2816) --- futures-util/src/stream/unfold.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/stream/unfold.rs b/futures-util/src/stream/unfold.rs index 7d8ef6babc..2f48cccb44 100644 --- a/futures-util/src/stream/unfold.rs +++ b/futures-util/src/stream/unfold.rs @@ -36,7 +36,7 @@ use pin_project_lite::pin_project; /// let stream = stream::unfold(0, |state| async move { /// if state <= 2 { /// let next_state = state + 1; -/// let yielded = state * 2; +/// let yielded = state * 2; /// Some((yielded, next_state)) /// } else { /// None From de1a0fd64a1bcae9a1534ed4da1699632993cc26 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 24 Dec 2023 23:22:18 +0900 Subject: [PATCH 252/283] Release 0.3.30 --- CHANGELOG.md | 8 ++++++++ futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- 11 files changed, 43 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e689f36917..a1b633b2bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.3.30 - 2023-12-24 + +* Add `{BiLock,SplitStream,SplitSink,ReadHalf,WriteHalf}::is_pair_of` (#2797) +* Fix panic in `FuturesUnordered::clear` (#2809) +* Fix panic in `AsyncBufReadExt::fill_buf` (#2801, #2812) +* Improve support for targets without atomic CAS (#2811) +* Remove build scripts (#2811) + # 0.3.29 - 2023-10-26 * Add `TryStreamExt::try_ready_chunks` (#2757) diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 1f50e60b44..7e0e9dde2c 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 704861cc3e..0e27db0a66 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 22cf99636c..391a5adc4d 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.29", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.30", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index a0699de52a..9911316ac4 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index f180d9e9f6..6bd4baff12 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index dbf5e91be7..6994ba6fcd 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index b3bf447d39..4116a3144b 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 81f00e9147..8499e034df 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.29", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.29", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.29", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.29", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.29", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.29", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.29", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.30", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.30", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.30", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.30", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.30", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.30", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.30", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 27e9e94985..dcdbce459e 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.29", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.29", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.29", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.30", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.30", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.30", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 96db108d37..6208f616ea 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.29" +version = "0.3.30" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.29", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.29", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.29", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.29", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.29", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.29", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.29", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.30", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.30", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.30", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.30", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } From 5fec1cbab0edb64f4fc1d153ee33d36e7eddb69e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 6 Jan 2024 15:45:50 +0900 Subject: [PATCH 253/283] tests: Fix dead_code warning for tuple struct ``` error: field `0` is never read --> futures-executor/tests/local_pool.rs:13:16 | 13 | struct Pending(Rc<()>); | ------- ^^^^^^ | | | field in this struct | = note: `-D dead-code` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(dead_code)]` help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field | 13 | struct Pending(()); | ~~ ``` --- futures-executor/tests/local_pool.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index 72ce74b744..d9b2e9d797 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -3,6 +3,7 @@ use futures::executor::LocalPool; use futures::future::{self, lazy, poll_fn, Future}; use futures::task::{Context, LocalSpawn, LocalSpawnExt, Poll, Spawn, SpawnExt, Waker}; use std::cell::{Cell, RefCell}; +use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -10,7 +11,7 @@ use std::sync::Arc; use std::thread; use std::time::Duration; -struct Pending(Rc<()>); +struct Pending(PhantomData>); impl Future for Pending { type Output = (); @@ -21,7 +22,7 @@ impl Future for Pending { } fn pending() -> Pending { - Pending(Rc::new(())) + Pending(PhantomData) } #[test] From 4cd69065fcacc9be2053a989db7783e69c9232bb Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:12:20 +0800 Subject: [PATCH 254/283] Fix typos (#2821) --- futures-util/src/stream/try_stream/mod.rs | 2 +- futures/tests/stream_futures_ordered.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 7b55444b3e..ee004b51e8 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -660,7 +660,7 @@ pub trait TryStreamExt: TryStream { /// them into a local vector. At most `capacity` items will get buffered /// before they're yielded from the returned stream. If the underlying stream /// returns `Poll::Pending`, and the collected chunk is not empty, it will - /// be immidiatly returned. + /// be immediately returned. /// /// Note that the vectors returned from this iterator may not always have /// `capacity` elements. If the underlying stream ended and only a partial diff --git a/futures/tests/stream_futures_ordered.rs b/futures/tests/stream_futures_ordered.rs index 5a4a3e22ee..f06ce263eb 100644 --- a/futures/tests/stream_futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -72,7 +72,7 @@ fn test_push_front() { stream.push_front(d_rx); d_tx.send(4).unwrap(); - // we pushed `d_rx` to the front and sent 4, so we should recieve 4 next + // we pushed `d_rx` to the front and sent 4, so we should receive 4 next // and then 3 after it assert_eq!(Poll::Ready(Some(Ok(4))), stream.poll_next_unpin(&mut cx)); assert_eq!(Poll::Ready(Some(Ok(3))), stream.poll_next_unpin(&mut cx)); @@ -165,7 +165,7 @@ fn test_push_front_negative() { b_tx.send(2).unwrap(); c_tx.send(3).unwrap(); - // These should all be recieved in reverse order + // These should all be received in reverse order assert_eq!(Poll::Ready(Some(Ok(3))), stream.poll_next_unpin(&mut cx)); assert_eq!(Poll::Ready(Some(Ok(2))), stream.poll_next_unpin(&mut cx)); assert_eq!(Poll::Ready(Some(Ok(1))), stream.poll_next_unpin(&mut cx)); From c6ae4e923bd5c7e00232fc1ce495573777899628 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 5 Oct 2024 15:38:08 +0900 Subject: [PATCH 255/283] Use [lints] in Cargo.toml --- Cargo.toml | 6 + examples/functional/Cargo.toml | 3 + examples/imperative/Cargo.toml | 3 + futures-channel/Cargo.toml | 3 + futures-channel/src/lib.rs | 8 +- futures-core/Cargo.toml | 3 + futures-core/src/lib.rs | 4 +- futures-executor/Cargo.toml | 3 + futures-executor/src/lib.rs | 8 +- futures-io/Cargo.toml | 3 + futures-io/src/lib.rs | 4 +- futures-macro/Cargo.toml | 3 + futures-macro/src/lib.rs | 1 - futures-sink/Cargo.toml | 3 + futures-sink/src/lib.rs | 4 +- futures-task/Cargo.toml | 3 + futures-task/src/lib.rs | 4 +- futures-test/Cargo.toml | 3 + futures-test/src/lib.rs | 8 +- futures-util/Cargo.toml | 3 + futures-util/src/lib.rs | 8 +- futures/Cargo.toml | 3 + futures/src/lib.rs | 8 +- futures/tests/auto_traits.rs | 191 +++++++++++----------- futures/tests/io_read_to_end.rs | 2 +- futures/tests/macro-reexport/Cargo.toml | 3 + futures/tests/macro-tests/Cargo.toml | 3 + futures/tests/no-std/Cargo.toml | 3 + futures/tests/stream.rs | 4 +- futures/tests/stream_futures_unordered.rs | 2 +- futures/tests/stream_try_stream.rs | 2 +- 31 files changed, 161 insertions(+), 148 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d27a9f2885..8cdf42c16a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,9 @@ members = [ "examples/functional", "examples/imperative", ] + +[workspace.lints.rust] +missing_debug_implementations = "warn" +rust_2018_idioms = "warn" +single_use_lifetimes = "warn" +unreachable_pub = "warn" diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml index 7b8b494d98..a1618d312d 100644 --- a/examples/functional/Cargo.toml +++ b/examples/functional/Cargo.toml @@ -6,3 +6,6 @@ publish = false [dependencies] futures = { path = "../../futures", features = ["thread-pool"] } + +[lints] +workspace = true diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml index 3405451f00..c8076e4b91 100644 --- a/examples/imperative/Cargo.toml +++ b/examples/imperative/Cargo.toml @@ -6,3 +6,6 @@ publish = false [dependencies] futures = { path = "../../futures", features = ["thread-pool"] } + +[lints] +workspace = true diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 7e0e9dde2c..81ebc42de8 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -32,3 +32,6 @@ futures-test = { path = "../futures-test", default-features = true } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index f611e6b9f5..819a266eb2 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -12,13 +12,7 @@ //! library is activated, and it is activated by default. #![cfg_attr(not(feature = "std"), no_std)] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - single_use_lifetimes, - unreachable_pub -)] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 0e27db0a66..137385c413 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -29,3 +29,6 @@ futures = { path = "../futures" } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 9c31d8d90b..1592596fc9 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,9 +1,7 @@ //! Core traits and types for asynchronous operations in Rust. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 391a5adc4d..87201d3844 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -27,3 +27,6 @@ futures = { path = "../futures", features = ["thread-pool"] } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index b1af87545f..9b8d7af731 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -37,13 +37,7 @@ //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj #![cfg_attr(not(feature = "std"), no_std)] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - single_use_lifetimes, - unreachable_pub -)] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 9911316ac4..d51c4c9d65 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -24,3 +24,6 @@ unstable = [] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index e91eb78492..53cea7c5b6 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -9,9 +9,7 @@ //! library is activated, and it is activated by default. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 6bd4baff12..9b23516fcd 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -19,3 +19,6 @@ proc-macro = true proc-macro2 = "1.0.60" quote = "1.0" syn = { version = "2.0.8", features = ["full"] } + +[lints] +workspace = true diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 0afe34b83b..6dd165d83d 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -1,6 +1,5 @@ //! The futures-rs procedural macro implementations. -#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)] #![doc(test( no_crate_inject, attr( diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index 6994ba6fcd..dda3504a3c 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -19,3 +19,6 @@ alloc = [] [package.metadata.docs.rs] all-features = true + +[lints] +workspace = true diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index 0328740efd..75b8385d7c 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -4,9 +4,7 @@ //! asynchronously. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 4116a3144b..40f07a835a 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -27,3 +27,6 @@ futures = { path = "../futures" } [package.metadata.docs.rs] all-features = true + +[lints] +workspace = true diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 33896d8b14..7cc761da79 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,9 +1,7 @@ //! Tools for working with tasks. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 8499e034df..901a06d889 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -30,3 +30,6 @@ std = ["futures-core/std", "futures-task/std", "futures-io/std", "futures-util/s [package.metadata.docs.rs] all-features = true + +[lints] +workspace = true diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 49f834846e..34b15bf9ed 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -1,12 +1,6 @@ //! Utilities to make testing [`Future`s](futures_core::future::Future) easier -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - single_use_lifetimes, - unreachable_pub -)] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index dcdbce459e..1b0bcb7888 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -56,3 +56,6 @@ tokio = "0.1.11" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 208eb73aa5..8c8fd403c0 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -3,13 +3,7 @@ #![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(not(feature = "std"), no_std)] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - single_use_lifetimes, - unreachable_pub -)] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6208f616ea..7a8482aa81 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -59,3 +59,6 @@ rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] features = ["std", "async-await", "compat", "io-compat", "executor", "thread-pool"] + +[lints] +workspace = true diff --git a/futures/src/lib.rs b/futures/src/lib.rs index b972f51754..682c78b0d2 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -82,13 +82,7 @@ //! inside an async block as written above. #![cfg_attr(not(feature = "std"), no_std)] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - single_use_lifetimes, - unreachable_pub -)] +#![warn(missing_docs)] #![doc(test( no_crate_inject, attr( diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs index 004fda1e71..3385360591 100644 --- a/futures/tests/auto_traits.rs +++ b/futures/tests/auto_traits.rs @@ -1,4 +1,5 @@ #![cfg(feature = "compat")] +#![allow(dead_code)] //! Assert Send/Sync/Unpin for all public types. @@ -12,49 +13,49 @@ use static_assertions::{assert_impl_all as assert_impl, assert_not_impl_all as a use std::marker::PhantomPinned; use std::{marker::PhantomData, pin::Pin}; -pub type LocalFuture = Pin>>; -pub type LocalTryFuture = LocalFuture>; -pub type SendFuture = Pin + Send>>; -pub type SendTryFuture = SendFuture>; -pub type SyncFuture = Pin + Sync>>; -pub type SyncTryFuture = SyncFuture>; -pub type SendSyncFuture = Pin + Send + Sync>>; -pub type SendSyncTryFuture = SendSyncFuture>; -pub type UnpinFuture = LocalFuture; -pub type UnpinTryFuture = UnpinFuture>; -pub struct PinnedFuture(PhantomPinned, PhantomData); +type LocalFuture = Pin>>; +type LocalTryFuture = LocalFuture>; +type SendFuture = Pin + Send>>; +type SendTryFuture = SendFuture>; +type SyncFuture = Pin + Sync>>; +type SyncTryFuture = SyncFuture>; +type SendSyncFuture = Pin + Send + Sync>>; +type SendSyncTryFuture = SendSyncFuture>; +type UnpinFuture = LocalFuture; +type UnpinTryFuture = UnpinFuture>; +struct PinnedFuture(PhantomPinned, PhantomData); impl Future for PinnedFuture { type Output = T; fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { unimplemented!() } } -pub type PinnedTryFuture = PinnedFuture>; - -pub type LocalStream = Pin>>; -pub type LocalTryStream = LocalStream>; -pub type SendStream = Pin + Send>>; -pub type SendTryStream = SendStream>; -pub type SyncStream = Pin + Sync>>; -pub type SyncTryStream = SyncStream>; -pub type SendSyncStream = Pin + Send + Sync>>; -pub type SendSyncTryStream = SendSyncStream>; -pub type UnpinStream = LocalStream; -pub type UnpinTryStream = UnpinStream>; -pub struct PinnedStream(PhantomPinned, PhantomData); +type PinnedTryFuture = PinnedFuture>; + +type LocalStream = Pin>>; +type LocalTryStream = LocalStream>; +type SendStream = Pin + Send>>; +type SendTryStream = SendStream>; +type SyncStream = Pin + Sync>>; +type SyncTryStream = SyncStream>; +type SendSyncStream = Pin + Send + Sync>>; +type SendSyncTryStream = SendSyncStream>; +type UnpinStream = LocalStream; +type UnpinTryStream = UnpinStream>; +struct PinnedStream(PhantomPinned, PhantomData); impl Stream for PinnedStream { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { unimplemented!() } } -pub type PinnedTryStream = PinnedStream>; +type PinnedTryStream = PinnedStream>; -pub type LocalSink = Pin>>; -pub type SendSink = Pin + Send>>; -pub type SyncSink = Pin + Sync>>; -pub type UnpinSink = LocalSink; -pub struct PinnedSink(PhantomPinned, PhantomData<(T, E)>); +type LocalSink = Pin>>; +type SendSink = Pin + Send>>; +type SyncSink = Pin + Sync>>; +type UnpinSink = LocalSink; +struct PinnedSink(PhantomPinned, PhantomData<(T, E)>); impl Sink for PinnedSink { type Error = E; fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { @@ -72,7 +73,7 @@ impl Sink for PinnedSink { } /// Assert Send/Sync/Unpin for all public types in `futures::channel`. -pub mod channel { +mod channel { use super::*; use futures::channel::*; @@ -119,11 +120,11 @@ pub mod channel { assert_impl!(oneshot::Canceled: Sync); assert_impl!(oneshot::Canceled: Unpin); - assert_impl!(oneshot::Cancellation<()>: Send); - assert_not_impl!(oneshot::Cancellation<*const ()>: Send); - assert_impl!(oneshot::Cancellation<()>: Sync); - assert_not_impl!(oneshot::Cancellation<*const ()>: Sync); - assert_impl!(oneshot::Cancellation: Unpin); + assert_impl!(oneshot::Cancellation<'_, ()>: Send); + assert_not_impl!(oneshot::Cancellation<'_, *const ()>: Send); + assert_impl!(oneshot::Cancellation<'_, ()>: Sync); + assert_not_impl!(oneshot::Cancellation<'_, *const ()>: Sync); + assert_impl!(oneshot::Cancellation<'_, PhantomPinned>: Unpin); assert_impl!(oneshot::Receiver<()>: Send); assert_not_impl!(oneshot::Receiver<*const ()>: Send); @@ -139,7 +140,7 @@ pub mod channel { } /// Assert Send/Sync/Unpin for all public types in `futures::compat`. -pub mod compat { +mod compat { use super::*; use futures::compat::*; @@ -181,7 +182,7 @@ pub mod compat { } /// Assert Send/Sync/Unpin for all public types in `futures::executor`. -pub mod executor { +mod executor { use super::*; use futures::executor::*; @@ -219,7 +220,7 @@ pub mod executor { } /// Assert Send/Sync/Unpin for all public types in `futures::future`. -pub mod future { +mod future { use super::*; use futures::future::*; @@ -305,9 +306,9 @@ pub mod future { assert_impl!(Fuse: Unpin); assert_not_impl!(Fuse: Unpin); - assert_impl!(FutureObj<*const ()>: Send); - assert_not_impl!(FutureObj<()>: Sync); - assert_impl!(FutureObj: Unpin); + assert_impl!(FutureObj<'_, *const ()>: Send); + assert_not_impl!(FutureObj<'_, ()>: Sync); + assert_impl!(FutureObj<'_, PhantomPinned>: Unpin); assert_impl!(Inspect: Send); assert_not_impl!(Inspect: Send); @@ -381,9 +382,9 @@ pub mod future { assert_not_impl!(Lazy<*const ()>: Sync); assert_impl!(Lazy: Unpin); - assert_not_impl!(LocalFutureObj<()>: Send); - assert_not_impl!(LocalFutureObj<()>: Sync); - assert_impl!(LocalFutureObj: Unpin); + assert_not_impl!(LocalFutureObj<'_, ()>: Send); + assert_not_impl!(LocalFutureObj<'_, ()>: Sync); + assert_impl!(LocalFutureObj<'_, PhantomPinned>: Unpin); assert_impl!(Map: Send); assert_not_impl!(Map: Send); @@ -652,7 +653,7 @@ pub mod future { } /// Assert Send/Sync/Unpin for all public types in `futures::io`. -pub mod io { +mod io { use super::*; use futures::io::{Sink, *}; @@ -693,23 +694,23 @@ pub mod io { assert_impl!(Close<'_, ()>: Unpin); assert_not_impl!(Close<'_, PhantomPinned>: Unpin); - assert_impl!(Copy<(), ()>: Send); - assert_not_impl!(Copy<(), *const ()>: Send); - assert_not_impl!(Copy<*const (), ()>: Send); - assert_impl!(Copy<(), ()>: Sync); - assert_not_impl!(Copy<(), *const ()>: Sync); - assert_not_impl!(Copy<*const (), ()>: Sync); - assert_impl!(Copy<(), PhantomPinned>: Unpin); - assert_not_impl!(Copy: Unpin); - - assert_impl!(CopyBuf<(), ()>: Send); - assert_not_impl!(CopyBuf<(), *const ()>: Send); - assert_not_impl!(CopyBuf<*const (), ()>: Send); - assert_impl!(CopyBuf<(), ()>: Sync); - assert_not_impl!(CopyBuf<(), *const ()>: Sync); - assert_not_impl!(CopyBuf<*const (), ()>: Sync); - assert_impl!(CopyBuf<(), PhantomPinned>: Unpin); - assert_not_impl!(CopyBuf: Unpin); + assert_impl!(Copy<'_, (), ()>: Send); + assert_not_impl!(Copy<'_, (), *const ()>: Send); + assert_not_impl!(Copy<'_, *const (), ()>: Send); + assert_impl!(Copy<'_, (), ()>: Sync); + assert_not_impl!(Copy<'_, (), *const ()>: Sync); + assert_not_impl!(Copy<'_, *const (), ()>: Sync); + assert_impl!(Copy<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(Copy<'_, PhantomPinned, ()>: Unpin); + + assert_impl!(CopyBuf<'_, (), ()>: Send); + assert_not_impl!(CopyBuf<'_, (), *const ()>: Send); + assert_not_impl!(CopyBuf<'_, *const (), ()>: Send); + assert_impl!(CopyBuf<'_, (), ()>: Sync); + assert_not_impl!(CopyBuf<'_, (), *const ()>: Sync); + assert_not_impl!(CopyBuf<'_, *const (), ()>: Sync); + assert_impl!(CopyBuf<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(CopyBuf<'_, PhantomPinned, ()>: Unpin); assert_impl!(Cursor<()>: Send); assert_not_impl!(Cursor<*const ()>: Send); @@ -890,7 +891,7 @@ pub mod io { } /// Assert Send/Sync/Unpin for all public types in `futures::lock`. -pub mod lock { +mod lock { use super::*; use futures::lock::*; @@ -966,7 +967,7 @@ pub mod lock { } /// Assert Send/Sync/Unpin for all public types in `futures::sink`. -pub mod sink { +mod sink { use super::*; use futures::sink::{self, *}; use std::marker::Send; @@ -1095,7 +1096,7 @@ pub mod sink { } /// Assert Send/Sync/Unpin for all public types in `futures::stream`. -pub mod stream { +mod stream { use super::*; use futures::{io, stream::*}; @@ -1835,33 +1836,33 @@ pub mod stream { assert_not_impl!(Zip: Unpin); assert_not_impl!(Zip: Unpin); - assert_impl!(futures_unordered::Iter<()>: Send); - assert_not_impl!(futures_unordered::Iter<*const ()>: Send); - assert_impl!(futures_unordered::Iter<()>: Sync); - assert_not_impl!(futures_unordered::Iter<*const ()>: Sync); - assert_impl!(futures_unordered::Iter<()>: Unpin); + assert_impl!(futures_unordered::Iter<'_, ()>: Send); + assert_not_impl!(futures_unordered::Iter<'_, *const ()>: Send); + assert_impl!(futures_unordered::Iter<'_, ()>: Sync); + assert_not_impl!(futures_unordered::Iter<'_, *const ()>: Sync); + assert_impl!(futures_unordered::Iter<'_, ()>: Unpin); // The definition of futures_unordered::Iter has `Fut: Unpin` bounds. - // assert_not_impl!(futures_unordered::Iter: Unpin); + // assert_not_impl!(futures_unordered::Iter<'_, PhantomPinned>: Unpin); - assert_impl!(futures_unordered::IterMut<()>: Send); - assert_not_impl!(futures_unordered::IterMut<*const ()>: Send); - assert_impl!(futures_unordered::IterMut<()>: Sync); - assert_not_impl!(futures_unordered::IterMut<*const ()>: Sync); - assert_impl!(futures_unordered::IterMut<()>: Unpin); + assert_impl!(futures_unordered::IterMut<'_, ()>: Send); + assert_not_impl!(futures_unordered::IterMut<'_, *const ()>: Send); + assert_impl!(futures_unordered::IterMut<'_, ()>: Sync); + assert_not_impl!(futures_unordered::IterMut<'_, *const ()>: Sync); + assert_impl!(futures_unordered::IterMut<'_, ()>: Unpin); // The definition of futures_unordered::IterMut has `Fut: Unpin` bounds. - // assert_not_impl!(futures_unordered::IterMut: Unpin); + // assert_not_impl!(futures_unordered::IterMut<'_, PhantomPinned>: Unpin); - assert_impl!(futures_unordered::IterPinMut<()>: Send); - assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Send); - assert_impl!(futures_unordered::IterPinMut<()>: Sync); - assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Sync); - assert_impl!(futures_unordered::IterPinMut: Unpin); + assert_impl!(futures_unordered::IterPinMut<'_, ()>: Send); + assert_not_impl!(futures_unordered::IterPinMut<'_, *const ()>: Send); + assert_impl!(futures_unordered::IterPinMut<'_, ()>: Sync); + assert_not_impl!(futures_unordered::IterPinMut<'_, *const ()>: Sync); + assert_impl!(futures_unordered::IterPinMut<'_, PhantomPinned>: Unpin); - assert_impl!(futures_unordered::IterPinRef<()>: Send); - assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Send); - assert_impl!(futures_unordered::IterPinRef<()>: Sync); - assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Sync); - assert_impl!(futures_unordered::IterPinRef: Unpin); + assert_impl!(futures_unordered::IterPinRef<'_, ()>: Send); + assert_not_impl!(futures_unordered::IterPinRef<'_, *const ()>: Send); + assert_impl!(futures_unordered::IterPinRef<'_, ()>: Sync); + assert_not_impl!(futures_unordered::IterPinRef<'_, *const ()>: Sync); + assert_impl!(futures_unordered::IterPinRef<'_, PhantomPinned>: Unpin); assert_impl!(futures_unordered::IntoIter<()>: Send); assert_not_impl!(futures_unordered::IntoIter<*const ()>: Send); @@ -1872,7 +1873,7 @@ pub mod stream { } /// Assert Send/Sync/Unpin for all public types in `futures::task`. -pub mod task { +mod task { use super::*; use futures::task::*; @@ -1880,13 +1881,13 @@ pub mod task { assert_impl!(AtomicWaker: Sync); assert_impl!(AtomicWaker: Unpin); - assert_impl!(FutureObj<*const ()>: Send); - assert_not_impl!(FutureObj<()>: Sync); - assert_impl!(FutureObj: Unpin); + assert_impl!(FutureObj<'_, *const ()>: Send); + assert_not_impl!(FutureObj<'_, ()>: Sync); + assert_impl!(FutureObj<'_, PhantomPinned>: Unpin); - assert_not_impl!(LocalFutureObj<()>: Send); - assert_not_impl!(LocalFutureObj<()>: Sync); - assert_impl!(LocalFutureObj: Unpin); + assert_not_impl!(LocalFutureObj<'_, ()>: Send); + assert_not_impl!(LocalFutureObj<'_, ()>: Sync); + assert_impl!(LocalFutureObj<'_, PhantomPinned>: Unpin); assert_impl!(SpawnError: Send); assert_impl!(SpawnError: Sync); diff --git a/futures/tests/io_read_to_end.rs b/futures/tests/io_read_to_end.rs index 7122511fcb..45495b0130 100644 --- a/futures/tests/io_read_to_end.rs +++ b/futures/tests/io_read_to_end.rs @@ -21,7 +21,7 @@ fn issue2310() { impl AsyncRead for MyRead { fn poll_read( mut self: Pin<&mut Self>, - _cx: &mut Context, + _cx: &mut Context<'_>, _buf: &mut [u8], ) -> Poll> { Poll::Ready(if !self.first { diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml index a648ee54a5..731ad5f455 100644 --- a/futures/tests/macro-reexport/Cargo.toml +++ b/futures/tests/macro-reexport/Cargo.toml @@ -6,3 +6,6 @@ publish = false [dependencies] futures03 = { path = "../..", package = "futures" } + +[lints] +workspace = true diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml index 963da731ac..10430ddad3 100644 --- a/futures/tests/macro-tests/Cargo.toml +++ b/futures/tests/macro-tests/Cargo.toml @@ -7,3 +7,6 @@ publish = false [dependencies] futures03 = { path = "../..", package = "futures" } macro-reexport = { path = "../macro-reexport" } + +[lints] +workspace = true diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml index ed5d0c146a..f638b8a4af 100644 --- a/futures/tests/no-std/Cargo.toml +++ b/futures/tests/no-std/Cargo.toml @@ -19,3 +19,6 @@ futures-task = { path = "../../../futures-task", optional = true, default-featur futures-channel = { path = "../../../futures-channel", optional = true, default-features = false } futures-util = { path = "../../../futures-util", optional = true, default-features = false } futures = { path = "../..", optional = true, default-features = false } + +[lints] +workspace = true diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index 6cbef7516c..3122b587d0 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -79,7 +79,7 @@ fn flatten_unordered() { impl Stream for DataStream { type Item = u8; - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { if !self.polled { if !self.wake_immediately { let waker = ctx.waker().clone(); @@ -110,7 +110,7 @@ fn flatten_unordered() { impl Stream for Interchanger { type Item = DataStream; - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { if !self.polled { self.polled = true; if !self.wake_immediately { diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 7bdf5432ca..9243c437c0 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -336,7 +336,7 @@ fn polled_only_once_at_most_per_iteration() { impl Future for F { type Output = (); - fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { if self.polled { panic!("polled twice") } else { diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs index ef38c510b8..57bdd4f3f3 100644 --- a/futures/tests/stream_try_stream.rs +++ b/futures/tests/stream_try_stream.rs @@ -91,7 +91,7 @@ fn try_flatten_unordered() { impl Stream for ErrorStream { type Item = Result>, ()>; - fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { if self.polled > self.error_after { panic!("Polled after error"); } else { From 665302a822b0cedfdb62b68b5e8344842199afa2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 24 Dec 2023 21:48:40 +0900 Subject: [PATCH 256/283] Use Self keyword instead of concrete type name --- futures-channel/tests/mpsc-close.rs | 4 +-- futures-util/src/future/either.rs | 35 +++++++++---------- futures-util/src/future/future/map.rs | 2 +- futures-util/src/future/maybe_done.rs | 10 +++--- futures-util/src/future/try_maybe_done.rs | 10 +++--- futures-util/src/io/line_writer.rs | 8 ++--- futures-util/src/io/split.rs | 2 +- .../src/stream/select_with_strategy.rs | 21 ++++++----- .../src/stream/stream/flatten_unordered.rs | 25 ++++++------- futures-util/src/unfold_state.rs | 2 +- futures/tests/io_read_to_end.rs | 4 +-- 11 files changed, 58 insertions(+), 65 deletions(-) diff --git a/futures-channel/tests/mpsc-close.rs b/futures-channel/tests/mpsc-close.rs index 1a14067eca..2d49936fc1 100644 --- a/futures-channel/tests/mpsc-close.rs +++ b/futures-channel/tests/mpsc-close.rs @@ -174,10 +174,10 @@ fn stress_try_send_as_receiver_closes() { } impl TestTask { /// Create a new TestTask - fn new() -> (TestTask, mpsc::Sender) { + fn new() -> (Self, mpsc::Sender) { let (command_tx, command_rx) = mpsc::channel::(0); ( - TestTask { + Self { command_rx, test_rx: None, countdown: 0, // 0 means no countdown is in progress. diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 27e5064dfb..c77b3bb220 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -39,9 +39,9 @@ impl Either { // SAFETY: We can use `new_unchecked` because the `inner` parts are // guaranteed to be pinned, as they come from `self` which is pinned. unsafe { - match *Pin::get_ref(self) { - Either::Left(ref inner) => Either::Left(Pin::new_unchecked(inner)), - Either::Right(ref inner) => Either::Right(Pin::new_unchecked(inner)), + match self.get_ref() { + Self::Left(inner) => Either::Left(Pin::new_unchecked(inner)), + Self::Right(inner) => Either::Right(Pin::new_unchecked(inner)), } } } @@ -55,9 +55,9 @@ impl Either { // offer an unpinned `&mut A` or `&mut B` through `Pin<&mut Self>`. We // also don't have an implementation of `Drop`, nor manual `Unpin`. unsafe { - match *Pin::get_unchecked_mut(self) { - Either::Left(ref mut inner) => Either::Left(Pin::new_unchecked(inner)), - Either::Right(ref mut inner) => Either::Right(Pin::new_unchecked(inner)), + match self.get_unchecked_mut() { + Self::Left(inner) => Either::Left(Pin::new_unchecked(inner)), + Self::Right(inner) => Either::Right(Pin::new_unchecked(inner)), } } } @@ -69,8 +69,8 @@ impl Either<(T, A), (T, B)> { /// Here, the homogeneous type is the first element of the pairs. pub fn factor_first(self) -> (T, Either) { match self { - Either::Left((x, a)) => (x, Either::Left(a)), - Either::Right((x, b)) => (x, Either::Right(b)), + Self::Left((x, a)) => (x, Either::Left(a)), + Self::Right((x, b)) => (x, Either::Right(b)), } } } @@ -81,8 +81,8 @@ impl Either<(A, T), (B, T)> { /// Here, the homogeneous type is the second element of the pairs. pub fn factor_second(self) -> (Either, T) { match self { - Either::Left((a, x)) => (Either::Left(a), x), - Either::Right((b, x)) => (Either::Right(b), x), + Self::Left((a, x)) => (Either::Left(a), x), + Self::Right((b, x)) => (Either::Right(b), x), } } } @@ -91,8 +91,7 @@ impl Either { /// Extract the value of an either over two equivalent types. pub fn into_inner(self) -> T { match self { - Either::Left(x) => x, - Either::Right(x) => x, + Self::Left(x) | Self::Right(x) => x, } } } @@ -119,8 +118,8 @@ where { fn is_terminated(&self) -> bool { match self { - Either::Left(x) => x.is_terminated(), - Either::Right(x) => x.is_terminated(), + Self::Left(x) => x.is_terminated(), + Self::Right(x) => x.is_terminated(), } } } @@ -141,8 +140,8 @@ where fn size_hint(&self) -> (usize, Option) { match self { - Either::Left(x) => x.size_hint(), - Either::Right(x) => x.size_hint(), + Self::Left(x) => x.size_hint(), + Self::Right(x) => x.size_hint(), } } } @@ -154,8 +153,8 @@ where { fn is_terminated(&self) -> bool { match self { - Either::Left(x) => x.is_terminated(), - Either::Right(x) => x.is_terminated(), + Self::Left(x) => x.is_terminated(), + Self::Right(x) => x.is_terminated(), } } } diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index 7471aba000..6991a35022 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -53,7 +53,7 @@ where match self.as_mut().project() { MapProj::Incomplete { future, .. } => { let output = ready!(future.poll(cx)); - match self.project_replace(Map::Complete) { + match self.project_replace(Self::Complete) { MapProjReplace::Incomplete { f, .. } => Poll::Ready(f.call_once(output)), MapProjReplace::Complete => unreachable!(), } diff --git a/futures-util/src/future/maybe_done.rs b/futures-util/src/future/maybe_done.rs index 26e6c27588..1e21e66f5e 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -53,7 +53,7 @@ impl MaybeDone { pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { unsafe { match self.get_unchecked_mut() { - MaybeDone::Done(res) => Some(res), + Self::Done(res) => Some(res), _ => None, } } @@ -69,7 +69,7 @@ impl MaybeDone { } unsafe { match mem::replace(self.get_unchecked_mut(), Self::Gone) { - MaybeDone::Done(output) => Some(output), + Self::Done(output) => Some(output), _ => unreachable!(), } } @@ -91,12 +91,12 @@ impl Future for MaybeDone { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { match self.as_mut().get_unchecked_mut() { - MaybeDone::Future(f) => { + Self::Future(f) => { let res = ready!(Pin::new_unchecked(f).poll(cx)); self.set(Self::Done(res)); } - MaybeDone::Done(_) => {} - MaybeDone::Gone => panic!("MaybeDone polled after value taken"), + Self::Done(_) => {} + Self::Gone => panic!("MaybeDone polled after value taken"), } } Poll::Ready(()) diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs index 24044d2c27..97af6ad437 100644 --- a/futures-util/src/future/try_maybe_done.rs +++ b/futures-util/src/future/try_maybe_done.rs @@ -38,7 +38,7 @@ impl TryMaybeDone { pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Ok> { unsafe { match self.get_unchecked_mut() { - TryMaybeDone::Done(res) => Some(res), + Self::Done(res) => Some(res), _ => None, } } @@ -54,7 +54,7 @@ impl TryMaybeDone { } unsafe { match mem::replace(self.get_unchecked_mut(), Self::Gone) { - TryMaybeDone::Done(output) => Some(output), + Self::Done(output) => Some(output), _ => unreachable!(), } } @@ -76,15 +76,15 @@ impl Future for TryMaybeDone { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { match self.as_mut().get_unchecked_mut() { - TryMaybeDone::Future(f) => match ready!(Pin::new_unchecked(f).try_poll(cx)) { + Self::Future(f) => match ready!(Pin::new_unchecked(f).try_poll(cx)) { Ok(res) => self.set(Self::Done(res)), Err(e) => { self.set(Self::Gone); return Poll::Ready(Err(e)); } }, - TryMaybeDone::Done(_) => {} - TryMaybeDone::Gone => panic!("TryMaybeDone polled after value taken"), + Self::Done(_) => {} + Self::Gone => panic!("TryMaybeDone polled after value taken"), } } Poll::Ready(Ok(())) diff --git a/futures-util/src/io/line_writer.rs b/futures-util/src/io/line_writer.rs index 71cd668325..de1bb53924 100644 --- a/futures-util/src/io/line_writer.rs +++ b/futures-util/src/io/line_writer.rs @@ -25,13 +25,13 @@ pub struct LineWriter { impl LineWriter { /// Create a new `LineWriter` with default buffer capacity. The default is currently 1KB /// which was taken from `std::io::LineWriter` - pub fn new(inner: W) -> LineWriter { - LineWriter::with_capacity(1024, inner) + pub fn new(inner: W) -> Self { + Self::with_capacity(1024, inner) } /// Creates a new `LineWriter` with the specified buffer capacity. - pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { - LineWriter { buf_writer: BufWriter::with_capacity(capacity, inner) } + pub fn with_capacity(capacity: usize, inner: W) -> Self { + Self { buf_writer: BufWriter::with_capacity(capacity, inner) } } /// Flush `buf_writer` if last char is "new line" diff --git a/futures-util/src/io/split.rs b/futures-util/src/io/split.rs index 81d1e6dcb5..5b6bc18937 100644 --- a/futures-util/src/io/split.rs +++ b/futures-util/src/io/split.rs @@ -45,7 +45,7 @@ impl ReadHalf { pub fn reunite(self, other: WriteHalf) -> Result> { self.handle .reunite(other.handle) - .map_err(|err| ReuniteError(ReadHalf { handle: err.0 }, WriteHalf { handle: err.1 })) + .map_err(|err| ReuniteError(Self { handle: err.0 }, WriteHalf { handle: err.1 })) } } diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs index 224d5f821c..56e25c70ff 100644 --- a/futures-util/src/stream/select_with_strategy.rs +++ b/futures-util/src/stream/select_with_strategy.rs @@ -21,17 +21,17 @@ impl PollNext { old } - fn other(&self) -> PollNext { + fn other(&self) -> Self { match self { - PollNext::Left => PollNext::Right, - PollNext::Right => PollNext::Left, + Self::Left => Self::Right, + Self::Right => Self::Left, } } } impl Default for PollNext { fn default() -> Self { - PollNext::Left + Self::Left } } @@ -45,15 +45,14 @@ enum InternalState { impl InternalState { fn finish(&mut self, ps: PollNext) { match (&self, ps) { - (InternalState::Start, PollNext::Left) => { - *self = InternalState::LeftFinished; + (Self::Start, PollNext::Left) => { + *self = Self::LeftFinished; } - (InternalState::Start, PollNext::Right) => { - *self = InternalState::RightFinished; + (Self::Start, PollNext::Right) => { + *self = Self::RightFinished; } - (InternalState::LeftFinished, PollNext::Right) - | (InternalState::RightFinished, PollNext::Left) => { - *self = InternalState::BothFinished; + (Self::LeftFinished, PollNext::Right) | (Self::RightFinished, PollNext::Left) => { + *self = Self::BothFinished; } _ => {} } diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 44c6ace2f7..1ce16e0ec2 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -56,15 +56,13 @@ struct SharedPollState { impl SharedPollState { /// Constructs new `SharedPollState` with the given state. - fn new(value: u8) -> SharedPollState { - SharedPollState { state: Arc::new(AtomicU8::new(value)) } + fn new(value: u8) -> Self { + Self { state: Arc::new(AtomicU8::new(value)) } } /// Attempts to start polling, returning stored state in case of success. /// Returns `None` if either waker is waking at the moment. - fn start_polling( - &self, - ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + fn start_polling(&self) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&Self) -> u8>)> { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { @@ -75,7 +73,7 @@ impl SharedPollState { } }) .ok()?; - let bomb = PollStateBomb::new(self, SharedPollState::reset); + let bomb = PollStateBomb::new(self, Self::reset); Some((value, bomb)) } @@ -87,7 +85,7 @@ impl SharedPollState { fn start_waking( &self, to_poll: u8, - ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&Self) -> u8>)> { let value = self .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { @@ -106,7 +104,7 @@ impl SharedPollState { // Only start the waking process if we're not in the polling/waking phase and the stream isn't woken already if value & (WOKEN | POLLING | WAKING) == NONE { - let bomb = PollStateBomb::new(self, SharedPollState::stop_waking); + let bomb = PollStateBomb::new(self, Self::stop_waking); Some((value, bomb)) } else { @@ -261,7 +259,7 @@ impl PollStreamFut { } impl Future for PollStreamFut { - type Output = Option<(St::Item, PollStreamFut)>; + type Output = Option<(St::Item, Self)>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut stream = self.project().stream; @@ -271,7 +269,7 @@ impl Future for PollStreamFut { } else { None }; - let next_item_fut = PollStreamFut::new(stream.get_mut().take()); + let next_item_fut = Self::new(stream.get_mut().take()); let out = item.map(|item| (item, next_item_fut)); Poll::Ready(out) @@ -320,13 +318,10 @@ where Fc: FlowController::Item>, St::Item: Stream + Unpin, { - pub(crate) fn new( - stream: St, - limit: Option, - ) -> FlattenUnorderedWithFlowController { + pub(crate) fn new(stream: St, limit: Option) -> Self { let poll_state = SharedPollState::new(NEED_TO_POLL_STREAM); - FlattenUnorderedWithFlowController { + Self { inner_streams: FuturesUnordered::new(), stream, is_stream_done: false, diff --git a/futures-util/src/unfold_state.rs b/futures-util/src/unfold_state.rs index 0edc15e437..b66956bbd8 100644 --- a/futures-util/src/unfold_state.rs +++ b/futures-util/src/unfold_state.rs @@ -29,7 +29,7 @@ impl UnfoldState { pub(crate) fn take_value(self: Pin<&mut Self>) -> Option { match &*self { - UnfoldState::Value { .. } => match self.project_replace(UnfoldState::Empty) { + Self::Value { .. } => match self.project_replace(Self::Empty) { UnfoldStateProjReplace::Value { value } => Some(value), _ => unreachable!(), }, diff --git a/futures/tests/io_read_to_end.rs b/futures/tests/io_read_to_end.rs index 45495b0130..0441b3bc4f 100644 --- a/futures/tests/io_read_to_end.rs +++ b/futures/tests/io_read_to_end.rs @@ -14,7 +14,7 @@ fn issue2310() { impl MyRead { fn new() -> Self { - MyRead { first: false } + Self { first: false } } } @@ -41,7 +41,7 @@ fn issue2310() { impl VecWrapper { fn new() -> Self { - VecWrapper { inner: Vec::new() } + Self { inner: Vec::new() } } } From 5e9b9e101a506bc956bbaf46ef1fc77a9a74a84c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 24 Dec 2023 22:05:07 +0900 Subject: [PATCH 257/283] Apply unsafe_op_in_unsafe_fn lint if available on MSRV --- futures-channel/src/lib.rs | 2 +- futures-channel/src/mpsc/queue.rs | 34 ++++++------ futures-core/src/lib.rs | 2 +- futures-executor/src/lib.rs | 2 +- futures-executor/src/unpark_mutex.rs | 4 +- futures-io/src/lib.rs | 2 +- futures-sink/src/lib.rs | 2 +- futures-task/src/future_obj.rs | 16 +++--- futures-task/src/lib.rs | 2 +- futures-task/src/waker.rs | 10 ++-- futures-test/src/lib.rs | 2 +- futures-util/src/compat/compat01as03.rs | 2 +- futures-util/src/compat/compat03as01.rs | 12 +++-- futures-util/src/future/future/shared.rs | 6 +-- futures-util/src/io/buf_writer.rs | 8 +-- futures-util/src/io/mod.rs | 2 +- futures-util/src/lib.rs | 2 +- .../src/stream/futures_unordered/mod.rs | 54 ++++++++++--------- .../futures_unordered/ready_to_run_queue.rs | 50 ++++++++--------- .../src/stream/stream/flatten_unordered.rs | 2 +- futures/src/lib.rs | 2 +- 21 files changed, 114 insertions(+), 104 deletions(-) diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index 819a266eb2..ed6999198e 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -12,7 +12,7 @@ //! library is activated, and it is activated by default. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( diff --git a/futures-channel/src/mpsc/queue.rs b/futures-channel/src/mpsc/queue.rs index 02ec633fe0..71cfe39d87 100644 --- a/futures-channel/src/mpsc/queue.rs +++ b/futures-channel/src/mpsc/queue.rs @@ -113,22 +113,24 @@ impl Queue { /// /// This function is unsafe because only one thread can call it at a time. pub(super) unsafe fn pop(&self) -> PopResult { - let tail = *self.tail.get(); - let next = (*tail).next.load(Ordering::Acquire); - - if !next.is_null() { - *self.tail.get() = next; - assert!((*tail).value.is_none()); - assert!((*next).value.is_some()); - let ret = (*next).value.take().unwrap(); - drop(Box::from_raw(tail)); - return Data(ret); - } + unsafe { + let tail = *self.tail.get(); + let next = (*tail).next.load(Ordering::Acquire); + + if !next.is_null() { + *self.tail.get() = next; + assert!((*tail).value.is_none()); + assert!((*next).value.is_some()); + let ret = (*next).value.take().unwrap(); + drop(Box::from_raw(tail)); + return Data(ret); + } - if self.head.load(Ordering::Acquire) == tail { - Empty - } else { - Inconsistent + if self.head.load(Ordering::Acquire) == tail { + Empty + } else { + Inconsistent + } } } @@ -138,7 +140,7 @@ impl Queue { /// This function is unsafe because only one thread can call it at a time. pub(super) unsafe fn pop_spin(&self) -> Option { loop { - match self.pop() { + match unsafe { self.pop() } { Empty => return None, Data(t) => return Some(t), // Inconsistent means that there will be a message to pop diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 1592596fc9..7b4255a234 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,7 +1,7 @@ //! Core traits and types for asynchronous operations in Rust. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #![doc(test( no_crate_inject, attr( diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index 9b8d7af731..108ca8753a 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -37,7 +37,7 @@ //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( diff --git a/futures-executor/src/unpark_mutex.rs b/futures-executor/src/unpark_mutex.rs index ac5112cfa2..f45164be9d 100644 --- a/futures-executor/src/unpark_mutex.rs +++ b/futures-executor/src/unpark_mutex.rs @@ -108,7 +108,7 @@ impl UnparkMutex { /// Callable only from the `POLLING`/`REPOLL` states, i.e. between /// successful calls to `notify` and `wait`/`complete`. pub(crate) unsafe fn wait(&self, data: D) -> Result<(), D> { - *self.inner.get() = Some(data); + unsafe { *self.inner.get() = Some(data) } match self.status.compare_exchange(POLLING, WAITING, SeqCst, SeqCst) { // no unparks came in while we were running @@ -119,7 +119,7 @@ impl UnparkMutex { Err(status) => { assert_eq!(status, REPOLL); self.status.store(POLLING, SeqCst); - Err((*self.inner.get()).take().unwrap()) + Err(unsafe { (*self.inner.get()).take().unwrap() }) } } } diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index 53cea7c5b6..fdba193f7b 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -9,7 +9,7 @@ //! library is activated, and it is activated by default. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #![doc(test( no_crate_inject, attr( diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index 75b8385d7c..fd58019602 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -4,7 +4,7 @@ //! asynchronously. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #![doc(test( no_crate_inject, attr( diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index 071392af6c..a611382193 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -29,14 +29,14 @@ impl Unpin for LocalFutureObj<'_, T> {} unsafe fn remove_future_lifetime<'a, T>( ptr: *mut (dyn Future + 'a), ) -> *mut (dyn Future + 'static) { - mem::transmute(ptr) + unsafe { mem::transmute(ptr) } } #[allow(single_use_lifetimes)] unsafe fn remove_drop_lifetime<'a, T>( ptr: unsafe fn(*mut (dyn Future + 'a)), ) -> unsafe fn(*mut (dyn Future + 'static)) { - mem::transmute(ptr) + unsafe { mem::transmute(ptr) } } impl<'a, T> LocalFutureObj<'a, T> { @@ -225,7 +225,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Box::from_raw(ptr.cast::())) + drop(unsafe { Box::from_raw(ptr.cast::()) }) } } @@ -235,7 +235,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Box::from_raw(ptr)) + drop(unsafe { Box::from_raw(ptr) }) } } @@ -245,7 +245,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Box::from_raw(ptr)) + drop(unsafe { Box::from_raw(ptr) }) } } @@ -259,7 +259,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Pin::from(Box::from_raw(ptr))) + drop(Pin::from(unsafe { Box::from_raw(ptr) })) } } @@ -270,7 +270,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Pin::from(Box::from_raw(ptr))) + drop(Pin::from(unsafe { Box::from_raw(ptr) })) } } @@ -281,7 +281,7 @@ mod if_alloc { } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Pin::from(Box::from_raw(ptr))) + drop(Pin::from(unsafe { Box::from_raw(ptr) })) } } diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 7cc761da79..1b50578765 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,7 +1,7 @@ //! Tools for working with tasks. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs index 79112569c5..2022422cb6 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -31,29 +31,29 @@ where #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. unsafe fn increase_refcount(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); + let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); // Now increase refcount, but don't drop new refcount either let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); } // used by `waker_ref` unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { - increase_refcount::(data); + unsafe { increase_refcount::(data) } RawWaker::new(data, waker_vtable::()) } unsafe fn wake_arc_raw(data: *const ()) { - let arc: Arc = Arc::from_raw(data.cast::()); + let arc: Arc = unsafe { Arc::from_raw(data.cast::()) }; ArcWake::wake(arc); } // used by `waker_ref` unsafe fn wake_by_ref_arc_raw(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); + let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); ArcWake::wake_by_ref(&arc); } unsafe fn drop_arc_raw(data: *const ()) { - drop(Arc::::from_raw(data.cast::())) + drop(unsafe { Arc::::from_raw(data.cast::()) }) } diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 34b15bf9ed..bb2269d0ff 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -1,6 +1,6 @@ //! Utilities to make testing [`Future`s](futures_core::future::Future) easier -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 36de1da98d..265331d4fc 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -346,7 +346,7 @@ unsafe impl UnsafeNotify01 for NotifyWaker { unsafe fn drop_raw(&self) { let ptr: *const dyn UnsafeNotify01 = self; - drop(Box::from_raw(ptr as *mut dyn UnsafeNotify01)); + drop(unsafe { Box::from_raw(ptr as *mut dyn UnsafeNotify01) }); } } diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 5d3a6e920b..2f9d65e71c 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -156,7 +156,7 @@ impl Current { fn as_waker(&self) -> WakerRef<'_> { unsafe fn ptr_to_current<'a>(ptr: *const ()) -> &'a Current { - &*(ptr as *const Current) + unsafe { &*(ptr as *const Current) } } fn current_to_ptr(current: &Current) -> *const () { current as *const Current as *const () @@ -166,13 +166,15 @@ impl Current { // Lazily create the `Arc` only when the waker is actually cloned. // FIXME: remove `transmute` when a `Waker` -> `RawWaker` conversion // function is landed in `core`. - mem::transmute::(task03::waker(Arc::new( - ptr_to_current(ptr).clone(), - ))) + unsafe { + mem::transmute::(task03::waker(Arc::new( + ptr_to_current(ptr).clone(), + ))) + } } unsafe fn drop(_: *const ()) {} unsafe fn wake(ptr: *const ()) { - ptr_to_current(ptr).0.notify() + unsafe { ptr_to_current(ptr).0.notify() } } let ptr = current_to_ptr(self); diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 9ab3b4f1d6..3f3098f052 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -192,8 +192,8 @@ where /// Safety: callers must first ensure that `self.inner.state` /// is `COMPLETE` unsafe fn output(&self) -> &Fut::Output { - match &*self.future_or_output.get() { - FutureOrOutput::Output(ref item) => item, + match unsafe { &*self.future_or_output.get() } { + FutureOrOutput::Output(item) => item, FutureOrOutput::Future(_) => unreachable!(), } } @@ -235,7 +235,7 @@ where FutureOrOutput::Output(item) => item, FutureOrOutput::Future(_) => unreachable!(), }, - Err(inner) => inner.output().clone(), + Err(inner) => unsafe { inner.output().clone() }, } } } diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index cb74863ad0..1c2ad3dc81 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -124,9 +124,11 @@ impl BufWriter { let old_len = this.buf.len(); let buf_len = buf.len(); let src = buf.as_ptr(); - let dst = this.buf.as_mut_ptr().add(old_len); - ptr::copy_nonoverlapping(src, dst, buf_len); - this.buf.set_len(old_len + buf_len); + unsafe { + let dst = this.buf.as_mut_ptr().add(old_len); + ptr::copy_nonoverlapping(src, dst, buf_len); + this.buf.set_len(old_len + buf_len); + } } /// Write directly using `inner`, bypassing buffering diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index fdad60b1fa..520435fb42 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -39,7 +39,7 @@ const DEFAULT_BUF_SIZE: usize = 8 * 1024; /// A buffer is currently always initialized. #[inline] unsafe fn initialize(_reader: &R, buf: &mut [u8]) { - ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) + unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } } mod allow_std; diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 8c8fd403c0..b27184fc09 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index dedf75dee2..83f076fed3 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -324,35 +324,37 @@ impl FuturesUnordered { /// This method is unsafe because it has be guaranteed that `task` is a /// valid pointer. unsafe fn unlink(&mut self, task: *const Task) -> Arc> { - // Compute the new list length now in case we're removing the head node - // and won't be able to retrieve the correct length later. - let head = *self.head_all.get_mut(); - debug_assert!(!head.is_null()); - let new_len = *(*head).len_all.get() - 1; - - let task = Arc::from_raw(task); - let next = task.next_all.load(Relaxed); - let prev = *task.prev_all.get(); - task.next_all.store(self.pending_next_all(), Relaxed); - *task.prev_all.get() = ptr::null_mut(); - - if !next.is_null() { - *(*next).prev_all.get() = prev; - } + unsafe { + // Compute the new list length now in case we're removing the head node + // and won't be able to retrieve the correct length later. + let head = *self.head_all.get_mut(); + debug_assert!(!head.is_null()); + let new_len = *(*head).len_all.get() - 1; - if !prev.is_null() { - (*prev).next_all.store(next, Relaxed); - } else { - *self.head_all.get_mut() = next; - } + let task = Arc::from_raw(task); + let next = task.next_all.load(Relaxed); + let prev = *task.prev_all.get(); + task.next_all.store(self.pending_next_all(), Relaxed); + *task.prev_all.get() = ptr::null_mut(); - // Store the new list length in the head node. - let head = *self.head_all.get_mut(); - if !head.is_null() { - *(*head).len_all.get() = new_len; - } + if !next.is_null() { + *(*next).prev_all.get() = prev; + } - task + if !prev.is_null() { + (*prev).next_all.store(next, Relaxed); + } else { + *self.head_all.get_mut() = next; + } + + // Store the new list length in the head node. + let head = *self.head_all.get_mut(); + if !head.is_null() { + *(*head).len_all.get() = new_len; + } + + task + } } /// Returns the reserved value for `Task::next_all` to indicate a pending diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index a924935d23..77abdf4ea4 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -47,39 +47,41 @@ impl ReadyToRunQueue { /// Note that this is unsafe as it required mutual exclusion (only one /// thread can call this) to be guaranteed elsewhere. pub(super) unsafe fn dequeue(&self) -> Dequeue { - let mut tail = *self.tail.get(); - let mut next = (*tail).next_ready_to_run.load(Acquire); + unsafe { + let mut tail = *self.tail.get(); + let mut next = (*tail).next_ready_to_run.load(Acquire); + + if tail == self.stub() { + if next.is_null() { + return Dequeue::Empty; + } - if tail == self.stub() { - if next.is_null() { - return Dequeue::Empty; + *self.tail.get() = next; + tail = next; + next = (*next).next_ready_to_run.load(Acquire); } - *self.tail.get() = next; - tail = next; - next = (*next).next_ready_to_run.load(Acquire); - } + if !next.is_null() { + *self.tail.get() = next; + debug_assert!(tail != self.stub()); + return Dequeue::Data(tail); + } - if !next.is_null() { - *self.tail.get() = next; - debug_assert!(tail != self.stub()); - return Dequeue::Data(tail); - } + if self.head.load(Acquire) as *const _ != tail { + return Dequeue::Inconsistent; + } - if self.head.load(Acquire) as *const _ != tail { - return Dequeue::Inconsistent; - } + self.enqueue(self.stub()); - self.enqueue(self.stub()); + next = (*tail).next_ready_to_run.load(Acquire); - next = (*tail).next_ready_to_run.load(Acquire); + if !next.is_null() { + *self.tail.get() = next; + return Dequeue::Data(tail); + } - if !next.is_null() { - *self.tail.get() = next; - return Dequeue::Data(tail); + Dequeue::Inconsistent } - - Dequeue::Inconsistent } pub(super) fn stub(&self) -> *const Task { diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 1ce16e0ec2..37811a1023 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -208,7 +208,7 @@ impl WrappedWaker { /// This function will modify waker's `inner_waker` via `UnsafeCell`, so /// it should be used only during `POLLING` phase by one thread at the time. unsafe fn replace_waker(self_arc: &mut Arc, cx: &Context<'_>) { - *self_arc.inner_waker.get() = cx.waker().clone().into(); + unsafe { *self_arc.inner_waker.get() = cx.waker().clone().into() } } /// Attempts to start the waking process for the waker with the given value. diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 682c78b0d2..6b31746343 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -82,7 +82,7 @@ //! inside an async block as written above. #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( From 88822a48103f61cf050c6c782c7ebadef23d9a0d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 25 Feb 2024 17:45:38 +0900 Subject: [PATCH 258/283] Always set #![no_std] and remove redundant imports ``` error: the item `Box` is imported redundantly --> futures-core/src/future.rs:89:9 | 89 | use alloc::boxed::Box; | ^^^^^^^^^^^^^^^^^ --> /rustc/381d69953bb7c3390cec0fee200f24529cb6320f/library/std/src/prelude/mod.rs:115:13 | = note: the item `Box` is already defined here | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` error: the item `Box` is imported redundantly --> futures-core/src/stream.rs:203:9 | 203 | use alloc::boxed::Box; | ^^^^^^^^^^^^^^^^^ --> /rustc/381d69953bb7c3390cec0fee200f24529cb6320f/library/std/src/prelude/mod.rs:115:13 | = note: the item `Box` is already defined here ``` --- futures-channel/src/lib.rs | 6 ++++-- futures-channel/src/mpsc/queue.rs | 1 + futures-core/src/lib.rs | 6 ++++-- futures-executor/src/enter.rs | 2 +- futures-executor/src/lib.rs | 7 +++++-- futures-executor/src/local_pool.rs | 3 ++- futures-executor/src/thread_pool.rs | 4 +++- futures-io/src/lib.rs | 9 +++++++-- futures-sink/src/lib.rs | 6 ++++-- futures-task/src/lib.rs | 6 ++++-- futures-test/src/lib.rs | 3 ++- futures-util/src/async_await/random.rs | 2 +- futures-util/src/compat/compat01as03.rs | 1 + futures-util/src/future/either.rs | 2 -- .../src/future/future/catch_unwind.rs | 1 + .../src/future/future/remote_handle.rs | 1 + futures-util/src/io/allow_std.rs | 2 ++ futures-util/src/io/buf_reader.rs | 2 ++ futures-util/src/io/buf_writer.rs | 1 + futures-util/src/io/cursor.rs | 2 ++ futures-util/src/io/lines.rs | 2 ++ futures-util/src/io/mod.rs | 2 +- futures-util/src/io/read_line.rs | 2 ++ futures-util/src/io/read_to_string.rs | 1 + futures-util/src/io/read_until.rs | 1 + futures-util/src/io/write_all_vectored.rs | 2 ++ futures-util/src/lib.rs | 8 +++++--- futures-util/src/lock/mutex.rs | 20 ++++++++++++------- .../src/stream/stream/catch_unwind.rs | 1 + futures-util/src/stream/stream/split.rs | 2 +- futures/src/lib.rs | 4 ++-- 31 files changed, 79 insertions(+), 33 deletions(-) diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index ed6999198e..09d6a1e2de 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -11,8 +11,7 @@ //! All items are only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![no_std] #![doc(test( no_crate_inject, attr( @@ -20,10 +19,13 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; #[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))] #[cfg(feature = "alloc")] diff --git a/futures-channel/src/mpsc/queue.rs b/futures-channel/src/mpsc/queue.rs index 71cfe39d87..78cbdce6ec 100644 --- a/futures-channel/src/mpsc/queue.rs +++ b/futures-channel/src/mpsc/queue.rs @@ -43,6 +43,7 @@ pub(super) use self::PopResult::*; +use std::boxed::Box; use std::cell::UnsafeCell; use std::ptr; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 7b4255a234..6ff6b974b1 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,7 +1,6 @@ //! Core traits and types for asynchronous operations in Rust. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 +#![no_std] #![doc(test( no_crate_inject, attr( @@ -9,9 +8,12 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; pub mod future; #[doc(no_inline)] diff --git a/futures-executor/src/enter.rs b/futures-executor/src/enter.rs index cb58c30bb7..b0e43e0593 100644 --- a/futures-executor/src/enter.rs +++ b/futures-executor/src/enter.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use std::fmt; -thread_local!(static ENTERED: Cell = Cell::new(false)); +std::thread_local!(static ENTERED: Cell = Cell::new(false)); /// Represents an executor context. /// diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index 108ca8753a..a0dc49d7ba 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -36,8 +36,7 @@ //! [`spawn_obj`]: https://docs.rs/futures/0.3/futures/task/trait.Spawn.html#tymethod.spawn_obj //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![no_std] #![doc(test( no_crate_inject, attr( @@ -45,8 +44,12 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "std")] +extern crate std; + #[cfg(feature = "std")] mod local_pool; #[cfg(feature = "std")] diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index 8a9bc2fc90..bd156db0fc 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -15,6 +15,7 @@ use std::sync::{ Arc, }; use std::thread::{self, Thread}; +use std::vec::Vec; /// A single-threaded task pool for polling futures to completion. /// @@ -53,7 +54,7 @@ pub(crate) struct ThreadNotify { unparked: AtomicBool, } -thread_local! { +std::thread_local! { static CURRENT_THREAD_NOTIFY: Arc = Arc::new(ThreadNotify { thread: thread::current(), unparked: AtomicBool::new(false), diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 5371008953..2e0d028dc3 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -5,9 +5,12 @@ use futures_core::task::{Context, Poll}; use futures_task::{waker_ref, ArcWake}; use futures_task::{FutureObj, Spawn, SpawnError}; use futures_util::future::FutureExt; +use std::boxed::Box; use std::cmp; use std::fmt; +use std::format; use std::io; +use std::string::String; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc; use std::sync::{Arc, Mutex}; @@ -358,7 +361,6 @@ impl ArcWake for WakeHandle { #[cfg(test)] mod tests { use super::*; - use std::sync::mpsc; #[test] fn test_drop_after_start() { diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index fdba193f7b..9c2fa056ad 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -8,8 +8,7 @@ //! All items of this library are only available when the `std` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 +#![no_std] #![doc(test( no_crate_inject, attr( @@ -17,14 +16,20 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "std")] +extern crate std; + #[cfg(feature = "std")] mod if_std { + use std::boxed::Box; use std::io; use std::ops::DerefMut; use std::pin::Pin; use std::task::{Context, Poll}; + use std::vec::Vec; // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index fd58019602..52394e8c2a 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -3,8 +3,7 @@ //! This crate contains the `Sink` trait which allows values to be sent //! asynchronously. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 +#![no_std] #![doc(test( no_crate_inject, attr( @@ -12,9 +11,12 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, /* unsafe_op_in_unsafe_fn */)] // unsafe_op_in_unsafe_fn requires Rust 1.52 #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; use core::ops::DerefMut; use core::pin::Pin; diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index 1b50578765..c119b6b1e4 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,7 +1,6 @@ //! Tools for working with tasks. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![no_std] #![doc(test( no_crate_inject, attr( @@ -9,9 +8,12 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; mod spawn; pub use crate::spawn::{LocalSpawn, Spawn, SpawnError}; diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index bb2269d0ff..4e420eac88 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -1,6 +1,5 @@ //! Utilities to make testing [`Future`s](futures_core::future::Future) easier -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![doc(test( no_crate_inject, attr( @@ -8,6 +7,8 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![allow(clippy::test_attr_in_doctest)] #[cfg(not(feature = "std"))] compile_error!( diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 4f8c7254b4..2ac2f78a87 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -25,7 +25,7 @@ fn gen_index(n: usize) -> usize { /// /// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift* fn random() -> u64 { - thread_local! { + std::thread_local! { static RNG: Cell> = Cell::new(Wrapping(prng_seed())); } diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 265331d4fc..0de2d53301 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -8,6 +8,7 @@ use futures_01::{AsyncSink as AsyncSink01, Sink as Sink01}; use futures_core::{future::Future as Future03, stream::Stream as Stream03, task as task03}; #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; +use std::boxed::Box; use std::pin::Pin; use std::task::Context; diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index c77b3bb220..018e51e72f 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -201,8 +201,6 @@ where mod if_std { use super::*; - use core::pin::Pin; - use core::task::{Context, Poll}; use futures_io::{ AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, Result, SeekFrom, }; diff --git a/futures-util/src/future/future/catch_unwind.rs b/futures-util/src/future/future/catch_unwind.rs index 0e09d6eeb0..ed49e314d2 100644 --- a/futures-util/src/future/future/catch_unwind.rs +++ b/futures-util/src/future/future/catch_unwind.rs @@ -1,5 +1,6 @@ use core::any::Any; use core::pin::Pin; +use std::boxed::Box; use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use futures_core::future::Future; diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index 1358902cab..6c4b7698ae 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -9,6 +9,7 @@ use { pin_project_lite::pin_project, std::{ any::Any, + boxed::Box, fmt, panic::{self, AssertUnwindSafe}, pin::Pin, diff --git a/futures-util/src/io/allow_std.rs b/futures-util/src/io/allow_std.rs index ec30ee31e5..96133cbc6f 100644 --- a/futures-util/src/io/allow_std.rs +++ b/futures-util/src/io/allow_std.rs @@ -1,6 +1,8 @@ use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; use std::pin::Pin; +use std::string::String; +use std::vec::Vec; use std::{fmt, io}; /// A simple wrapper type which allows types which implement only diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 0334a9f081..a8632c73e2 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -4,8 +4,10 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSliceMut, SeekFrom}; use pin_project_lite::pin_project; +use std::boxed::Box; use std::io::{self, Read}; use std::pin::Pin; +use std::vec; use std::{cmp, fmt}; pin_project! { diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index 1c2ad3dc81..e0037262b9 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -7,6 +7,7 @@ use std::fmt; use std::io::{self, Write}; use std::pin::Pin; use std::ptr; +use std::vec::Vec; pin_project! { /// Wraps a writer and buffers its output. diff --git a/futures-util/src/io/cursor.rs b/futures-util/src/io/cursor.rs index c6e2aeea28..01c8e5f4dc 100644 --- a/futures-util/src/io/cursor.rs +++ b/futures-util/src/io/cursor.rs @@ -1,7 +1,9 @@ use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; +use std::boxed::Box; use std::io; use std::pin::Pin; +use std::vec::Vec; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`AsyncSeek`] implementation. diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index b5561bfa7d..8c4d17c584 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -7,6 +7,8 @@ use pin_project_lite::pin_project; use std::io; use std::mem; use std::pin::Pin; +use std::string::String; +use std::vec::Vec; pin_project! { /// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 520435fb42..c07fe3fe47 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -21,7 +21,7 @@ use crate::compat::Compat; use crate::future::assert_future; use crate::stream::assert_stream; -use std::{pin::Pin, ptr}; +use std::{pin::Pin, ptr, string::String, vec::Vec}; // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index df782c9570..b4483223dc 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -7,6 +7,8 @@ use std::io; use std::mem; use std::pin::Pin; use std::str; +use std::string::String; +use std::vec::Vec; /// Future for the [`read_line`](super::AsyncBufReadExt::read_line) method. #[derive(Debug)] diff --git a/futures-util/src/io/read_to_string.rs b/futures-util/src/io/read_to_string.rs index c175396d81..a075ca2662 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -4,6 +4,7 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncRead; use std::pin::Pin; +use std::string::String; use std::vec::Vec; use std::{io, mem, str}; diff --git a/futures-util/src/io/read_until.rs b/futures-util/src/io/read_until.rs index 72b59eab13..d6121d6f05 100644 --- a/futures-util/src/io/read_until.rs +++ b/futures-util/src/io/read_until.rs @@ -5,6 +5,7 @@ use futures_io::AsyncBufRead; use std::io; use std::mem; use std::pin::Pin; +use std::vec::Vec; /// Future for the [`read_until`](super::AsyncBufReadExt::read_until) method. #[derive(Debug)] diff --git a/futures-util/src/io/write_all_vectored.rs b/futures-util/src/io/write_all_vectored.rs index a8fc4c641c..26b3f25d92 100644 --- a/futures-util/src/io/write_all_vectored.rs +++ b/futures-util/src/io/write_all_vectored.rs @@ -49,6 +49,8 @@ mod tests { use std::io; use std::pin::Pin; use std::task::{Context, Poll}; + use std::vec; + use std::vec::Vec; use crate::io::{AsyncWrite, AsyncWriteExt, IoSlice}; use crate::task::noop_waker; diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index b27184fc09..8024f7bdfd 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -1,9 +1,7 @@ //! Combinators and utilities for working with `Future`s, `Stream`s, `Sink`s, //! and the `AsyncRead` and `AsyncWrite` traits. -#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![no_std] #![doc(test( no_crate_inject, attr( @@ -11,6 +9,8 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "bilock", not(feature = "unstable")))] @@ -18,6 +18,8 @@ compile_error!("The `bilock` feature requires the `unstable` feature as an expli #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; // Macro re-exports pub use futures_core::ready; diff --git a/futures-util/src/lock/mutex.rs b/futures-util/src/lock/mutex.rs index 335ad14273..c77048a786 100644 --- a/futures-util/src/lock/mutex.rs +++ b/futures-util/src/lock/mutex.rs @@ -541,11 +541,17 @@ unsafe impl Sync for OwnedMutexGuard {} unsafe impl Send for MappedMutexGuard<'_, T, U> {} unsafe impl Sync for MappedMutexGuard<'_, T, U> {} -#[test] -fn test_mutex_guard_debug_not_recurse() { - let mutex = Mutex::new(42); - let guard = mutex.try_lock().unwrap(); - let _ = format!("{:?}", guard); - let guard = MutexGuard::map(guard, |n| n); - let _ = format!("{:?}", guard); +#[cfg(test)] +mod tests { + use super::*; + use std::format; + + #[test] + fn test_mutex_guard_debug_not_recurse() { + let mutex = Mutex::new(42); + let guard = mutex.try_lock().unwrap(); + let _ = format!("{:?}", guard); + let guard = MutexGuard::map(guard, |n| n); + let _ = format!("{:?}", guard); + } } diff --git a/futures-util/src/stream/stream/catch_unwind.rs b/futures-util/src/stream/stream/catch_unwind.rs index 09a6dc1b76..dfc388839d 100644 --- a/futures-util/src/stream/stream/catch_unwind.rs +++ b/futures-util/src/stream/stream/catch_unwind.rs @@ -2,6 +2,7 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use pin_project_lite::pin_project; use std::any::Any; +use std::boxed::Box; use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use std::pin::Pin; diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index 1a7fdcb387..e192f8a92d 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -160,7 +160,7 @@ impl std::error::Error for ReuniteError {} #[cfg(test)] mod tests { use super::*; - use crate::{sink::Sink, stream::StreamExt}; + use crate::stream::StreamExt; use core::marker::PhantomData; struct NopStream { diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 6b31746343..654576fcda 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -81,8 +81,7 @@ //! The majority of examples and code snippets in this crate assume that they are //! inside an async block as written above. -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, unsafe_op_in_unsafe_fn)] +#![no_std] #![doc(test( no_crate_inject, attr( @@ -90,6 +89,7 @@ allow(dead_code, unused_assignments, unused_variables) ) ))] +#![warn(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "bilock", not(feature = "unstable")))] From ff38f831d5bb6921bf3889863e8577bdb35df030 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 25 Feb 2024 17:48:06 +0900 Subject: [PATCH 259/283] Ignore dead_code lint on Assert* traits ``` warning: trait `AssertSync` is never used --> futures-core/src/task/__internal/atomic_waker.rs:209:15 | 209 | trait AssertSync: Sync {} | ^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default warning: trait `AssertKinds` is never used --> futures-channel/src/mpsc/mod.rs:130:7 | 130 | trait AssertKinds: Send + Sync + Clone {} | ^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default warning: trait `AssertSendSync` is never used --> futures-executor/src/thread_pool.rs:48:7 | 48 | trait AssertSendSync: Send + Sync {} | ^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default warning: trait `AssertSend` is never used --> futures-channel/tests/mpsc.rs:13:7 | 13 | trait AssertSend: Send {} | ^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default ``` --- futures-channel/src/mpsc/mod.rs | 1 + futures-channel/tests/mpsc.rs | 1 + futures-core/src/task/__internal/atomic_waker.rs | 1 + futures-executor/src/thread_pool.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 64f7526fa4..c5051a6483 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -127,6 +127,7 @@ pub struct Sender(Option>); /// This value is created by the [`unbounded`] function. pub struct UnboundedSender(Option>); +#[allow(dead_code)] trait AssertKinds: Send + Sync + Clone {} impl AssertKinds for UnboundedSender {} diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index f5d7198d22..28d476b05a 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; +#[allow(dead_code)] trait AssertSend: Send {} impl AssertSend for mpsc::Sender {} impl AssertSend for mpsc::Receiver {} diff --git a/futures-core/src/task/__internal/atomic_waker.rs b/futures-core/src/task/__internal/atomic_waker.rs index 2fc594b8a9..3b82fb7cc8 100644 --- a/futures-core/src/task/__internal/atomic_waker.rs +++ b/futures-core/src/task/__internal/atomic_waker.rs @@ -206,6 +206,7 @@ impl AtomicWaker { /// Create an `AtomicWaker`. pub const fn new() -> Self { // Make sure that task is Sync + #[allow(dead_code)] trait AssertSync: Sync {} impl AssertSync for Waker {} diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 2e0d028dc3..c4442e4eeb 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -45,6 +45,7 @@ pub struct ThreadPoolBuilder { before_stop: Option>, } +#[allow(dead_code)] trait AssertSendSync: Send + Sync {} impl AssertSendSync for ThreadPool {} From d2a1b58a1fc4e9d4932dc5ce9863feb3794f5eb0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 25 Feb 2024 17:54:35 +0900 Subject: [PATCH 260/283] Fix rustdoc::redundant_explicit_links warning ``` error: redundant explicit link target --> futures-executor/src/local_pool.rs:314:25 | 314 | /// Use a [`LocalPool`](LocalPool) if you need finer-grained control over | ----------- ^^^^^^^^^ explicit target is redundant | | | because label contains path that resolves to same destination | = note: when a link's destination is not specified, the label is used to resolve intra-doc links = note: `-D rustdoc::redundant-explicit-links` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rustdoc::redundant_explicit_links)]` help: remove explicit link target | 314 | /// Use a [`LocalPool`] if you need finer-grained control over | ~~~~~~~~~~~~~ error: redundant explicit link target --> futures-executor/src/local_pool.rs:37:33 | 37 | /// A handle to a [`LocalPool`](LocalPool) that implements | ----------- ^^^^^^^^^ explicit target is redundant | | | because label contains path that resolves to same destination | = note: when a link's destination is not specified, the label is used to resolve intra-doc links help: remove explicit link target | 37 | /// A handle to a [`LocalPool`] that implements | ~~~~~~~~~~~~~ ``` --- futures-executor/src/local_pool.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index bd156db0fc..90c2a41520 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -34,8 +34,7 @@ pub struct LocalPool { incoming: Rc, } -/// A handle to a [`LocalPool`](LocalPool) that implements -/// [`Spawn`](futures_task::Spawn). +/// A handle to a [`LocalPool`] that implements [`Spawn`](futures_task::Spawn). #[derive(Clone, Debug)] pub struct LocalSpawner { incoming: Weak, @@ -311,8 +310,7 @@ impl Default for LocalPool { /// /// This function will block the caller until the given future has completed. /// -/// Use a [`LocalPool`](LocalPool) if you need finer-grained control over -/// spawned tasks. +/// Use a [`LocalPool`] if you need finer-grained control over spawned tasks. pub fn block_on(f: F) -> F::Output { pin_mut!(f); run_executor(|cx| f.as_mut().poll(cx)) From 738c25072c7412e6aea962179d370f01e4952856 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 26 Feb 2024 23:27:39 +0900 Subject: [PATCH 261/283] Add 'static bound to waker_ref --- futures-task/src/waker.rs | 12 +-- futures-task/src/waker_ref.rs | 2 +- .../src/stream/futures_unordered/mod.rs | 3 +- .../src/stream/futures_unordered/task.rs | 95 ++++++++++++++++++- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs index 2022422cb6..ef5abfbad2 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -3,7 +3,7 @@ use alloc::sync::Arc; use core::mem; use core::task::{RawWaker, RawWakerVTable, Waker}; -pub(super) fn waker_vtable() -> &'static RawWakerVTable { +pub(super) fn waker_vtable() -> &'static RawWakerVTable { &RawWakerVTable::new( clone_arc_raw::, wake_arc_raw::, @@ -29,7 +29,7 @@ where // code here. We should guard against this by aborting. #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. -unsafe fn increase_refcount(data: *const ()) { +unsafe fn increase_refcount(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); // Now increase refcount, but don't drop new refcount either @@ -37,23 +37,23 @@ unsafe fn increase_refcount(data: *const ()) { } // used by `waker_ref` -unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { +unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { unsafe { increase_refcount::(data) } RawWaker::new(data, waker_vtable::()) } -unsafe fn wake_arc_raw(data: *const ()) { +unsafe fn wake_arc_raw(data: *const ()) { let arc: Arc = unsafe { Arc::from_raw(data.cast::()) }; ArcWake::wake(arc); } // used by `waker_ref` -unsafe fn wake_by_ref_arc_raw(data: *const ()) { +unsafe fn wake_by_ref_arc_raw(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); ArcWake::wake_by_ref(&arc); } -unsafe fn drop_arc_raw(data: *const ()) { +unsafe fn drop_arc_raw(data: *const ()) { drop(unsafe { Arc::::from_raw(data.cast::()) }) } diff --git a/futures-task/src/waker_ref.rs b/futures-task/src/waker_ref.rs index aac4109577..5957b4d46a 100644 --- a/futures-task/src/waker_ref.rs +++ b/futures-task/src/waker_ref.rs @@ -54,7 +54,7 @@ impl Deref for WakerRef<'_> { #[inline] pub fn waker_ref(wake: &Arc) -> WakerRef<'_> where - W: ArcWake, + W: ArcWake + 'static, { // simply copy the pointer instead of using Arc::into_raw, // as we don't actually keep a refcount by using ManuallyDrop.< diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 83f076fed3..5d4b3f295e 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -511,7 +511,8 @@ impl Stream for FuturesUnordered { // We are only interested in whether the future is awoken before it // finishes polling, so reset the flag here. task.woken.store(false, Relaxed); - let waker = Task::waker_ref(task); + // SAFETY: see the comments of Bomb and this block. + let waker = unsafe { Task::waker_ref(task) }; let mut cx = Context::from_waker(&waker); // Safety: We won't move the future ever again diff --git a/futures-util/src/stream/futures_unordered/task.rs b/futures-util/src/stream/futures_unordered/task.rs index ec2114effa..2ae4cb7d85 100644 --- a/futures-util/src/stream/futures_unordered/task.rs +++ b/futures-util/src/stream/futures_unordered/task.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicBool, AtomicPtr}; use super::abort::abort; use super::ReadyToRunQueue; -use crate::task::{waker_ref, ArcWake, WakerRef}; +use crate::task::ArcWake; pub(super) struct Task { // The future @@ -77,8 +77,8 @@ impl ArcWake for Task { impl Task { /// Returns a waker reference for this task without cloning the Arc. - pub(super) fn waker_ref(this: &Arc) -> WakerRef<'_> { - waker_ref(this) + pub(super) unsafe fn waker_ref(this: &Arc) -> waker_ref::WakerRef<'_> { + unsafe { waker_ref::waker_ref(this) } } /// Spins until `next_all` is no longer set to `pending_next_all`. @@ -123,3 +123,92 @@ impl Drop for Task { } } } + +mod waker_ref { + use alloc::sync::Arc; + use core::marker::PhantomData; + use core::mem; + use core::mem::ManuallyDrop; + use core::ops::Deref; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use futures_task::ArcWake; + + pub(crate) struct WakerRef<'a> { + waker: ManuallyDrop, + _marker: PhantomData<&'a ()>, + } + + impl WakerRef<'_> { + #[inline] + fn new_unowned(waker: ManuallyDrop) -> Self { + Self { waker, _marker: PhantomData } + } + } + + impl Deref for WakerRef<'_> { + type Target = Waker; + + #[inline] + fn deref(&self) -> &Waker { + &self.waker + } + } + + /// Copy of `future_task::waker_ref` without `W: 'static` bound. + /// + /// # Safety + /// + /// The caller must guarantee that use-after-free will not occur. + #[inline] + pub(crate) unsafe fn waker_ref(wake: &Arc) -> WakerRef<'_> + where + W: ArcWake, + { + // simply copy the pointer instead of using Arc::into_raw, + // as we don't actually keep a refcount by using ManuallyDrop.< + let ptr = Arc::as_ptr(wake).cast::<()>(); + + let waker = + ManuallyDrop::new(unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) }); + WakerRef::new_unowned(waker) + } + + fn waker_vtable() -> &'static RawWakerVTable { + &RawWakerVTable::new( + clone_arc_raw::, + wake_arc_raw::, + wake_by_ref_arc_raw::, + drop_arc_raw::, + ) + } + + // FIXME: panics on Arc::clone / refcount changes could wreak havoc on the + // code here. We should guard against this by aborting. + + unsafe fn increase_refcount(data: *const ()) { + // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); + // Now increase refcount, but don't drop new refcount either + let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); + } + + unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { + unsafe { increase_refcount::(data) } + RawWaker::new(data, waker_vtable::()) + } + + unsafe fn wake_arc_raw(data: *const ()) { + let arc: Arc = unsafe { Arc::from_raw(data.cast::()) }; + ArcWake::wake(arc); + } + + unsafe fn wake_by_ref_arc_raw(data: *const ()) { + // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + let arc = mem::ManuallyDrop::new(unsafe { Arc::::from_raw(data.cast::()) }); + ArcWake::wake_by_ref(&arc); + } + + unsafe fn drop_arc_raw(data: *const ()) { + drop(unsafe { Arc::::from_raw(data.cast::()) }) + } +} From b73f29306555a293dfd650690860f04f89c83338 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 28 Feb 2024 12:13:56 -0800 Subject: [PATCH 262/283] Add test of select's grammar --- futures/tests/async_await_macros.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index 82a617f2c2..0bc79e4ae8 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -57,6 +57,18 @@ fn select() { assert!(ran); } +#[test] +fn select_grammar() { + // Parsing after `=>` using Expr::parse would parse `{}() = future::ready(())` + // as one expression. + block_on(async { + select! { + () = future::pending::<()>() => {} + () = future::ready(()) => {} + } + }); +} + #[test] fn select_biased() { let (tx1, rx1) = oneshot::channel::(); From 3ad64f9d8ba1430301c80ecd8e4a57f647ca2164 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 28 Feb 2024 12:07:45 -0800 Subject: [PATCH 263/283] Parse rhs of `select!` arms using match-arm rules --- futures-macro/Cargo.toml | 2 +- futures-macro/src/select.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 9b23516fcd..b154f125b0 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.60" quote = "1.0" -syn = { version = "2.0.8", features = ["full"] } +syn = { version = "2.0.52", features = ["full"] } [lints] workspace = true diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index 2789b3e659..36e332ddac 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -59,7 +59,7 @@ impl Parse for Select { // `=> ` input.parse::]>()?; - let expr = input.parse::()?; + let expr = Expr::parse_with_earlier_boundary_rule(input)?; // Commas after the expression are only optional if it's a `Block` // or it is the last branch in the `match`. @@ -232,7 +232,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let branches = parsed.normal_fut_handlers.into_iter().zip(variant_names.iter()).map( |((pat, expr), variant_name)| { quote! { - #enum_ident::#variant_name(#pat) => { #expr }, + #enum_ident::#variant_name(#pat) => #expr, } }, ); From 1475000372aa2e301ee19a4cc5896730920799f2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 9 Mar 2024 03:12:57 +0900 Subject: [PATCH 264/283] Use *::MAX associated constants --- futures-channel/src/mpsc/mod.rs | 4 ++-- futures-util/src/future/future/shared.rs | 2 +- futures-util/src/io/buf_reader.rs | 2 +- futures-util/src/stream/repeat.rs | 2 +- futures-util/src/stream/repeat_with.rs | 2 +- futures-util/src/stream/stream/cycle.rs | 3 +-- futures-util/src/stream/stream/mod.rs | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index c5051a6483..03a8a53898 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -303,13 +303,13 @@ struct State { } // The `is_open` flag is stored in the left-most bit of `Inner::state` -const OPEN_MASK: usize = usize::max_value() - (usize::max_value() >> 1); +const OPEN_MASK: usize = usize::MAX - (usize::MAX >> 1); // When a new channel is created, it is created in the open state with no // pending messages. const INIT_STATE: usize = OPEN_MASK; -// The maximum number of messages that a channel can track is `usize::max_value() >> 1` +// The maximum number of messages that a channel can track is `usize::MAX >> 1` const MAX_CAPACITY: usize = !(OPEN_MASK); // The maximum requested buffer size must be less than the maximum capacity of diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 3f3098f052..158fa70761 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -82,7 +82,7 @@ const POLLING: usize = 1; const COMPLETE: usize = 2; const POISONED: usize = 3; -const NULL_WAKER_KEY: usize = usize::max_value(); +const NULL_WAKER_KEY: usize = usize::MAX; impl Shared { pub(super) fn new(future: Fut) -> Self { diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index a8632c73e2..d017b1f94f 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -214,7 +214,7 @@ impl AsyncSeek for BufReader { // it should be safe to assume that remainder fits within an i64 as the alternative // means we managed to allocate 8 exbibytes and that's absurd. // But it's not out of the realm of possibility for some weird underlying reader to - // support seeking by i64::min_value() so we need to handle underflow when subtracting + // support seeking by i64::MIN so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { result = diff --git a/futures-util/src/stream/repeat.rs b/futures-util/src/stream/repeat.rs index 3f9aa87d5c..e09cfa2e41 100644 --- a/futures-util/src/stream/repeat.rs +++ b/futures-util/src/stream/repeat.rs @@ -44,7 +44,7 @@ where } fn size_hint(&self) -> (usize, Option) { - (usize::max_value(), None) + (usize::MAX, None) } } diff --git a/futures-util/src/stream/repeat_with.rs b/futures-util/src/stream/repeat_with.rs index f5a81b4ed4..a482510705 100644 --- a/futures-util/src/stream/repeat_with.rs +++ b/futures-util/src/stream/repeat_with.rs @@ -24,7 +24,7 @@ impl A> Stream for RepeatWith { } fn size_hint(&self) -> (usize, Option) { - (usize::max_value(), None) + (usize::MAX, None) } } diff --git a/futures-util/src/stream/stream/cycle.rs b/futures-util/src/stream/stream/cycle.rs index 507431d24f..60fe745399 100644 --- a/futures-util/src/stream/stream/cycle.rs +++ b/futures-util/src/stream/stream/cycle.rs @@ -1,5 +1,4 @@ use core::pin::Pin; -use core::usize; use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; @@ -48,7 +47,7 @@ where match self.orig.size_hint() { size @ (0, Some(0)) => size, (0, _) => (0, None), - _ => (usize::max_value(), None), + _ => (usize::MAX, None), } } } diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 2da7036b24..d365cfe43f 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -360,7 +360,7 @@ pub trait StreamExt: Stream { /// # Overflow Behavior /// /// The method does no guarding against overflows, so enumerating more than - /// [`prim@usize::max_value()`] elements either produces the wrong result or panics. If + /// [`usize::MAX`] elements either produces the wrong result or panics. If /// debug assertions are enabled, a panic is guaranteed. /// /// # Panics From 27d3fc0b72c6a19c128b80a061e04dbb8ea3669e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 9 Mar 2024 03:15:47 +0900 Subject: [PATCH 265/283] ci: Use taiki-e/checkout-action action https://github.com/taiki-e/checkout-action#why-not-actionscheckout --- .github/workflows/ci.yml | 26 +++++++++++++------------- .github/workflows/release.yml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50557c4bbd..20ead27e16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: target: i686-unknown-linux-gnu runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update nightly --no-self-update && rustup default nightly @@ -71,7 +71,7 @@ jobs: - '1.36' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. @@ -105,7 +105,7 @@ jobs: - '1.56' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -137,7 +137,7 @@ jobs: - nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cargo-hack @@ -149,7 +149,7 @@ jobs: name: cargo minimal-versions build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -170,7 +170,7 @@ jobs: - thumbv6m-none-eabi runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup target add ${{ matrix.target }} @@ -202,7 +202,7 @@ jobs: name: cargo bench runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - run: cargo bench --workspace @@ -212,7 +212,7 @@ jobs: name: cargo hack check --feature-powerset runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - name: Install cargo-hack @@ -234,7 +234,7 @@ jobs: name: cargo miri test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - run: cargo miri test --workspace --all-features @@ -253,7 +253,7 @@ jobs: - thread runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain install nightly --component rust-src && rustup default nightly - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests @@ -269,7 +269,7 @@ jobs: # name: cargo clippy # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v4 + # - uses: taiki-e/checkout-action@v1 # - name: Install Rust # run: rustup toolchain install nightly --component clippy && rustup default nightly # - run: cargo clippy --workspace --all-features --all-targets @@ -278,7 +278,7 @@ jobs: name: cargo fmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update stable - run: cargo fmt --all -- --check @@ -287,7 +287,7 @@ jobs: name: cargo doc runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup update nightly && rustup default nightly - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --workspace --no-deps --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fec08e5bd..d56fcdddc3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: if: github.repository_owner == 'rust-lang' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: taiki-e/checkout-action@v1 - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md From 001abd5d5e41e7e160fff55107966ebc0d9f580b Mon Sep 17 00:00:00 2001 From: Gnome! Date: Sat, 9 Mar 2024 10:55:46 +0000 Subject: [PATCH 266/283] Add a helper for always ready futures (#2825) --- futures-util/src/future/always_ready.rs | 62 +++++++++++++++++++++++++ futures-util/src/future/mod.rs | 3 ++ 2 files changed, 65 insertions(+) create mode 100644 futures-util/src/future/always_ready.rs diff --git a/futures-util/src/future/always_ready.rs b/futures-util/src/future/always_ready.rs new file mode 100644 index 0000000000..28625e7272 --- /dev/null +++ b/futures-util/src/future/always_ready.rs @@ -0,0 +1,62 @@ +use super::assert_future; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::task::{Context, Poll}; + +/// Future for the [`always_ready`](always_ready()) function. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct AlwaysReady T>(F); + +impl T> core::fmt::Debug for AlwaysReady { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("AlwaysReady").finish() + } +} + +impl T + Clone> Clone for AlwaysReady { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl T + Copy> Copy for AlwaysReady {} + +impl T> Unpin for AlwaysReady {} + +impl T> FusedFuture for AlwaysReady { + fn is_terminated(&self) -> bool { + false + } +} + +impl T> Future for AlwaysReady { + type Output = T; + + #[inline] + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0()) + } +} + +/// Creates a future that is always immediately ready with a value. +/// +/// This is particularly useful in avoiding a heap allocation when an API needs [`Box>`], +/// as [`AlwaysReady`] does not have to store a boolean for `is_finished`. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use std::mem::size_of_val; +/// +/// use futures::future; +/// +/// let a = future::always_ready(|| 1); +/// assert_eq!(size_of_val(&a), 0); +/// assert_eq!(a.await, 1); +/// assert_eq!(a.await, 1); +/// # }); +/// ``` +pub fn always_ready T>(prod: F) -> AlwaysReady { + assert_future::(AlwaysReady(prod)) +} diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 2d8fa4f654..873d2b2d10 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -74,6 +74,9 @@ pub use self::poll_immediate::{poll_immediate, PollImmediate}; mod ready; pub use self::ready::{err, ok, ready, Ready}; +mod always_ready; +pub use self::always_ready::{always_ready, AlwaysReady}; + mod join; pub use self::join::{join, join3, join4, join5, Join, Join3, Join4, Join5}; From 1448bec785c3bf56ee426ebe2728a29d79fa3624 Mon Sep 17 00:00:00 2001 From: gwo Date: Sun, 31 Mar 2024 23:59:52 +0800 Subject: [PATCH 267/283] Make non constructor methods of futures::io::{BufReader,BufWriter} not require inner trait bound (#2848) --- futures-util/src/io/buf_reader.rs | 2 ++ futures-util/src/io/buf_writer.rs | 38 ++++++++++++++++--------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index d017b1f94f..ec6a8e154e 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -55,7 +55,9 @@ impl BufReader { Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 } } } +} +impl BufReader { delegate_access_inner!(inner, R, ()); /// Returns a reference to the internally buffered data. diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index e0037262b9..ddcf5e1b4d 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -79,6 +79,26 @@ impl BufWriter { Poll::Ready(ret) } + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.project().inner.poll_write(cx, buf) + } + + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + self.project().inner.poll_write_vectored(cx, bufs) + } +} + +impl BufWriter { delegate_access_inner!(inner, W, ()); /// Returns a reference to the internally buffered data. @@ -131,24 +151,6 @@ impl BufWriter { this.buf.set_len(old_len + buf_len); } } - - /// Write directly using `inner`, bypassing buffering - pub(super) fn inner_poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.project().inner.poll_write(cx, buf) - } - - /// Write directly using `inner`, bypassing buffering - pub(super) fn inner_poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - self.project().inner.poll_write_vectored(cx, bufs) - } } impl AsyncWrite for BufWriter { From e767ad0c72c94b8202363b199591d38302958bdc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 6 Apr 2024 00:37:59 +0900 Subject: [PATCH 268/283] Remove unused direct dependency on pin-utils They use pin_mut via futures-util. --- futures-test/Cargo.toml | 1 - futures/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 901a06d889..6b5a3045d0 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -18,7 +18,6 @@ futures-util = { version = "0.3.30", path = "../futures-util", default-features futures-executor = { version = "0.3.30", path = "../futures-executor", default-features = false } futures-sink = { version = "0.3.30", path = "../futures-sink", default-features = false } futures-macro = { version = "=0.3.30", path = "../futures-macro", default-features = false } -pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.11" [dev-dependencies] diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 7a8482aa81..cd8cf82708 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -28,7 +28,6 @@ futures-executor = { path = "../futures-executor", features = ["thread-pool"] } futures-test = { path = "../futures-test" } assert_matches = "1.3.0" pin-project = "1.0.11" -pin-utils = "0.1.0" static_assertions = "1" tokio = "0.1.11" From d5fc37861bb2522649066c69409a61236bb121d7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 3 May 2024 01:24:21 +0900 Subject: [PATCH 269/283] ci: Set timeout-minutes in all jobs --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20ead27e16..436d14a9f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: - os: ubuntu-latest target: i686-unknown-linux-gnu runs-on: ${{ matrix.os }} + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -70,6 +71,7 @@ jobs: # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - '1.36' runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -104,6 +106,7 @@ jobs: # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml. - '1.56' runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -136,6 +139,7 @@ jobs: - beta - nightly runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -148,6 +152,7 @@ jobs: minimal-versions: name: cargo minimal-versions build runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -169,6 +174,7 @@ jobs: - thumbv7m-none-eabi - thumbv6m-none-eabi runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -201,6 +207,7 @@ jobs: bench: name: cargo bench runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -211,6 +218,7 @@ jobs: features: name: cargo hack check --feature-powerset runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -233,6 +241,7 @@ jobs: miri: name: cargo miri test runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -252,6 +261,7 @@ jobs: - memory - thread runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -268,6 +278,7 @@ jobs: # clippy: # name: cargo clippy # runs-on: ubuntu-latest + # timeout-minutes: 60 # steps: # - uses: taiki-e/checkout-action@v1 # - name: Install Rust @@ -277,6 +288,7 @@ jobs: fmt: name: cargo fmt runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust @@ -286,6 +298,7 @@ jobs: docs: name: cargo doc runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@v1 - name: Install Rust From a0d955467213528d4fb0c9434c7f5a7c9768d045 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 3 May 2024 01:27:06 +0900 Subject: [PATCH 270/283] Remove no longer needed `extern crate proc_macro` --- futures-macro/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 6dd165d83d..c1b94785e8 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -8,11 +8,6 @@ ) ))] -// 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`. -#[allow(unused_extern_crates)] -extern crate proc_macro; - use proc_macro::TokenStream; mod executor; From a9a4aa0d90d48781e64a5c2c6a1d49a710831670 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 3 May 2024 01:31:55 +0900 Subject: [PATCH 271/283] Tweak imports in tests --- futures/tests/future_join.rs | 4 ++-- futures/tests/future_try_join_all.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/futures/tests/future_join.rs b/futures/tests/future_join.rs index f5df9d7775..0556a6e62c 100644 --- a/futures/tests/future_join.rs +++ b/futures/tests/future_join.rs @@ -1,5 +1,5 @@ use futures::executor::block_on; -use futures::future::Future; +use futures::future::{self, Future}; use std::task::Poll; /// This tests verifies (through miri) that self-referencing @@ -21,7 +21,7 @@ async fn trouble() { fn yield_now() -> impl Future { let mut yielded = false; - std::future::poll_fn(move |cx| { + future::poll_fn(move |cx| { if core::mem::replace(&mut yielded, true) { Poll::Ready(()) } else { diff --git a/futures/tests/future_try_join_all.rs b/futures/tests/future_try_join_all.rs index 9a824872f7..892e775920 100644 --- a/futures/tests/future_try_join_all.rs +++ b/futures/tests/future_try_join_all.rs @@ -1,8 +1,7 @@ use futures::executor::block_on; +use futures::future::{err, ok, try_join_all, Future, TryJoinAll}; use futures::pin_mut; -use futures_util::future::{err, ok, try_join_all, TryJoinAll}; use std::fmt::Debug; -use std::future::Future; #[track_caller] fn assert_done(actual_fut: impl Future, expected: T) From 86dc069fa278d4197de91eb6e038e0ec64857684 Mon Sep 17 00:00:00 2001 From: Hans Kratz Date: Sat, 14 Sep 2024 14:18:31 +0200 Subject: [PATCH 272/283] Various fixes too make the CI green (#2885) * Fix unexpected `cfg` condition name: ... warnings introduced in Rust 1.80 See https://blog.rust-lang.org/2024/05/06/check-cfg.html * io_slice_advance feature is now stable * clippy: enable missing_const_for_thread_local lint, now checks for MSRV (see https://github.com/rust-lang/rust-clippy/issues/12404) * clippy: fixes for "doc list item without indentation" lint * clippy: ignore incorrect "first doc comment paragraph is too long" warning see https://github.com/rust-lang/rust-clippy/issues/13315 * clippy: allow long first paragraphs in select... fn doc comments * use workspace level setting to ignore error about the futures_sanitizer unexpected config --- Cargo.toml | 1 + futures-core/src/stream.rs | 10 +++++----- futures-util/src/abortable.rs | 4 ++-- futures-util/src/async_await/select_mod.rs | 2 ++ futures-util/src/async_await/stream_select_mod.rs | 1 + futures-util/src/compat/compat03as01.rs | 1 + futures-util/src/lib.rs | 1 - futures-util/src/stream/select_with_strategy.rs | 1 + futures-util/src/stream/stream/flatten_unordered.rs | 2 +- futures/tests/no-std/build.rs | 1 + 10 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8cdf42c16a..1a90362f31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,4 @@ missing_debug_implementations = "warn" rust_2018_idioms = "warn" single_use_lifetimes = "warn" unreachable_pub = "warn" +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(futures_sanitizer)'] } diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs index ad5350b795..19114e7dab 100644 --- a/futures-core/src/stream.rs +++ b/futures-core/src/stream.rs @@ -38,15 +38,15 @@ pub trait Stream { /// stream state: /// /// - `Poll::Pending` means that this stream's next value is not ready - /// yet. Implementations will ensure that the current task will be notified - /// when the next value may be ready. + /// yet. Implementations will ensure that the current task will be notified + /// when the next value may be ready. /// /// - `Poll::Ready(Some(val))` means that the stream has successfully - /// produced a value, `val`, and may produce further values on subsequent - /// `poll_next` calls. + /// produced a value, `val`, and may produce further values on subsequent + /// `poll_next` calls. /// /// - `Poll::Ready(None)` means that the stream has terminated, and - /// `poll_next` should not be invoked again. + /// `poll_next` should not be invoked again. /// /// # Panics /// diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs index 9dbcfc2b52..e1e79e371d 100644 --- a/futures-util/src/abortable.rs +++ b/futures-util/src/abortable.rs @@ -64,7 +64,7 @@ impl Abortable { /// This means that it will return `true` even if: /// * `abort` was called after the task had completed. /// * `abort` was called while the task was being polled - the task may still be running and - /// will not be stopped until `poll` returns. + /// will not be stopped until `poll` returns. pub fn is_aborted(&self) -> bool { self.inner.aborted.load(Ordering::Relaxed) } @@ -200,7 +200,7 @@ impl AbortHandle { /// even if: /// * `abort` was called after the task had completed. /// * `abort` was called while the task was being polled - the task may still be running and - /// will not be stopped until `poll` returns. + /// will not be stopped until `poll` returns. /// /// This operation has a Relaxed ordering. pub fn is_aborted(&self) -> bool { diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 1d13067d38..80c3c7504a 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -3,6 +3,7 @@ macro_rules! document_select_macro { // This branch is required for `futures 0.3.1`, from before select_biased was introduced ($select:item) => { + #[allow(clippy::too_long_first_doc_paragraph)] /// Polls multiple futures and streams simultaneously, executing the branch /// for the future that finishes first. If multiple futures are ready, /// one will be pseudo-randomly selected at runtime. Futures directly @@ -153,6 +154,7 @@ macro_rules! document_select_macro { ($select:item $select_biased:item) => { document_select_macro!($select); + #[allow(clippy::too_long_first_doc_paragraph)] /// Polls multiple futures and streams simultaneously, executing the branch /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready, /// one will be selected in order of declaration. Futures directly diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs index 61e3fa1c66..c3e4f0052c 100644 --- a/futures-util/src/async_await/stream_select_mod.rs +++ b/futures-util/src/async_await/stream_select_mod.rs @@ -4,6 +4,7 @@ #[doc(hidden)] pub use futures_macro::stream_select_internal; +#[allow(clippy::too_long_first_doc_paragraph)] /// Combines several streams, all producing the same `Item` type, into one stream. /// This is similar to `select_all` but does not require the streams to all be the same type. /// It also keeps the streams inline, and does not require `Box`s to be allocated. diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 2f9d65e71c..3c5bb77b49 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -15,6 +15,7 @@ use futures_sink::Sink as Sink03; use std::marker::PhantomData; use std::{mem, pin::Pin, sync::Arc, task::Context}; +#[allow(clippy::too_long_first_doc_paragraph)] // clippy bug, see https://github.com/rust-lang/rust-clippy/issues/13315 /// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture) or /// [`TryStream`](futures_core::stream::TryStream) into a futures 0.1 /// [`Future`](futures_01::future::Future) or diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 8024f7bdfd..2201e4f2c9 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -10,7 +10,6 @@ ) ))] #![warn(missing_docs, unsafe_op_in_unsafe_fn)] -#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "bilock", not(feature = "unstable")))] diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs index 56e25c70ff..4e19873af7 100644 --- a/futures-util/src/stream/select_with_strategy.rs +++ b/futures-util/src/stream/select_with_strategy.rs @@ -74,6 +74,7 @@ pin_project! { } } +#[allow(clippy::too_long_first_doc_paragraph)] /// This function will attempt to pull items from both streams. You provide a /// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can /// store state on `SelectWithStrategy` to which it will receive a `&mut` on every diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs index 37811a1023..c79edbd213 100644 --- a/futures-util/src/stream/stream/flatten_unordered.rs +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -119,7 +119,7 @@ impl SharedPollState { /// - `!WAKING` as /// * Wakers called during the `POLLING` phase won't propagate their calls /// * `POLLING` phase can't start if some of the wakers are active - /// So no wrapped waker can touch the inner waker's cell, it's safe to poll again. + /// So no wrapped waker can touch the inner waker's cell, it's safe to poll again. fn stop_polling(&self, to_poll: u8, will_be_woken: bool) -> u8 { self.state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut value| { diff --git a/futures/tests/no-std/build.rs b/futures/tests/no-std/build.rs index a96a68274b..ba79f203f2 100644 --- a/futures/tests/no-std/build.rs +++ b/futures/tests/no-std/build.rs @@ -1,6 +1,7 @@ use std::{env, process::Command}; fn main() { + println!("cargo:rustc-check-cfg=cfg(nightly)"); if is_nightly() { println!("cargo:rustc-cfg=nightly"); } From 07b004ac7e0d72e09ff652f234d00e4f26a5f558 Mon Sep 17 00:00:00 2001 From: cui fliter <15921519+cuishuang@users.noreply.github.com> Date: Sat, 14 Sep 2024 20:19:22 +0800 Subject: [PATCH 273/283] Add missing symbols (#2883) Signed-off-by: cuishuang --- futures-macro/src/join.rs | 2 +- futures-macro/src/select.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index 94e356f729..0e891b3aad 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -1,4 +1,4 @@ -//! The futures-rs `join! macro implementation. +//! The futures-rs `join!` macro implementation. use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index 36e332ddac..ca83a87e4a 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -1,4 +1,4 @@ -//! The futures-rs `select! macro implementation. +//! The futures-rs `select!` macro implementation. use proc_macro::TokenStream; use proc_macro2::Span; From 549b90b1793a044bcade67b81a3d7eabd95f3971 Mon Sep 17 00:00:00 2001 From: Bill Fraser Date: Sat, 14 Sep 2024 05:19:45 -0700 Subject: [PATCH 274/283] Add accessors for the inner of stream::Iter (#2875) --- futures-util/src/stream/iter.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/futures-util/src/stream/iter.rs b/futures-util/src/stream/iter.rs index 20471c2ed0..48b6519a39 100644 --- a/futures-util/src/stream/iter.rs +++ b/futures-util/src/stream/iter.rs @@ -10,6 +10,23 @@ pub struct Iter { iter: I, } +impl Iter { + /// Acquires a reference to the underlying iterator that this stream is pulling from. + pub fn get_ref(&self) -> &I { + &self.iter + } + + /// Acquires a mutable reference to the underlying iterator that this stream is pulling from. + pub fn get_mut(&mut self) -> &mut I { + &mut self.iter + } + + /// Consumes this stream, returning the underlying iterator. + pub fn into_inner(self) -> I { + self.iter + } +} + impl Unpin for Iter {} /// Converts an `Iterator` into a `Stream` which is always ready From 87afaf3973e8652228f7ccaadd04e7b0e456c63d Mon Sep 17 00:00:00 2001 From: AF Date: Sat, 14 Sep 2024 15:20:28 +0300 Subject: [PATCH 275/283] Use `#[inline(always)]` on `clone_arc_raw` (#2865) --- futures-task/src/waker.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs index ef5abfbad2..ce90aa83d9 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -37,6 +37,7 @@ unsafe fn increase_refcount(data: *const ()) { } // used by `waker_ref` +#[inline(always)] unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { unsafe { increase_refcount::(data) } RawWaker::new(data, waker_vtable::()) From 7bf5a72826c0daed633ec48e8ddaecd8a32f0d11 Mon Sep 17 00:00:00 2001 From: Hans Kratz Date: Wed, 18 Sep 2024 20:58:39 +0200 Subject: [PATCH 276/283] Fix issues with `AsyncBufRead::read_line` and `AsyncBufReadExt::lines` (#2884) Fixes the following issues in `AsyncBufRead::read_line`: * When the future is dropped the previous string contents are not restored so the string is empty. * If invalid UTF-8 is encountered the previous string contents are not restored. * If an IO error occurs after `read_until_internal` already read a couple of bytes a debug assertion fails. * Performance: The whole string to which read contents are appended is check for UTF-8 validity instead of just the added bytes. Fixes the following issues in `AsyncBufRead::read_line`: * If an IO error occurs after `read_until_internal` already read a couple of bytes a debug assertion fails. (#2862) Fixes #2862 --- futures-util/src/io/lines.rs | 1 + futures-util/src/io/read_line.rs | 47 +++++++++++++++++++++---------- futures-util/src/io/read_until.rs | 3 +- futures/tests/io_lines.rs | 26 ++++++++++++++++- futures/tests/io_read_line.rs | 43 ++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 18 deletions(-) diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index 8c4d17c584..0a1abf4bf6 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -35,6 +35,7 @@ impl Stream for Lines { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); let n = ready!(read_line_internal(this.reader, cx, this.buf, this.bytes, this.read))?; + *this.read = 0; if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index b4483223dc..43942add54 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -18,13 +18,14 @@ pub struct ReadLine<'a, R: ?Sized> { buf: &'a mut String, bytes: Vec, read: usize, + finished: bool, } impl Unpin for ReadLine<'_, R> {} 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: mem::take(buf).into_bytes(), buf, read: 0 } + Self { reader, bytes: mem::take(buf).into_bytes(), buf, read: 0, finished: false } } } @@ -35,26 +36,42 @@ pub(super) fn read_line_internal( bytes: &mut Vec, read: &mut usize, ) -> Poll> { - let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); - if str::from_utf8(bytes).is_err() { - bytes.clear(); - Poll::Ready(ret.and_then(|_| { - Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")) - })) - } else { - debug_assert!(buf.is_empty()); - debug_assert_eq!(*read, 0); - // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. - mem::swap(unsafe { buf.as_mut_vec() }, bytes); - Poll::Ready(ret) + let mut ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + if str::from_utf8(&bytes[bytes.len() - *read..bytes.len()]).is_err() { + bytes.truncate(bytes.len() - *read); + if ret.is_ok() { + ret = Err(io::Error::new( + io::ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + )); + } } + *read = 0; + // Safety: `bytes` is valid UTF-8 because it was taken from a String + // and the newly read bytes are either valid UTF-8 or have been removed. + mem::swap(unsafe { buf.as_mut_vec() }, bytes); + Poll::Ready(ret) } impl Future for ReadLine<'_, R> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { reader, buf, bytes, read } = &mut *self; - read_line_internal(Pin::new(reader), cx, buf, bytes, read) + let Self { reader, buf, bytes, read, finished: _ } = &mut *self; + let ret = ready!(read_line_internal(Pin::new(reader), cx, buf, bytes, read)); + self.finished = true; + Poll::Ready(ret) + } +} + +impl Drop for ReadLine<'_, R> { + fn drop(&mut self) { + // restore old string contents + if !self.finished { + self.bytes.truncate(self.bytes.len() - self.read); + // Safety: `bytes` is valid UTF-8 because it was taken from a String + // and the newly read bytes have been removed. + mem::swap(unsafe { self.buf.as_mut_vec() }, &mut self.bytes); + } } } diff --git a/futures-util/src/io/read_until.rs b/futures-util/src/io/read_until.rs index d6121d6f05..adc359db51 100644 --- a/futures-util/src/io/read_until.rs +++ b/futures-util/src/io/read_until.rs @@ -3,7 +3,6 @@ use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; use std::io; -use std::mem; use std::pin::Pin; use std::vec::Vec; @@ -46,7 +45,7 @@ pub(super) fn read_until_internal( reader.as_mut().consume(used); *read += used; if done || used == 0 { - return Poll::Ready(Ok(mem::replace(read, 0))); + return Poll::Ready(Ok(*read)); } } } diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs index 5ce01a6945..62afef326a 100644 --- a/futures/tests/io_lines.rs +++ b/futures/tests/io_lines.rs @@ -1,6 +1,6 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::io::{AsyncBufReadExt, Cursor}; +use futures::io::{AsyncBufReadExt, AsyncRead, Cursor}; use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; use futures_test::io::AsyncReadTestExt; @@ -27,6 +27,24 @@ macro_rules! run_next { }; } +struct IOErrorRead(bool); + +impl AsyncRead for IOErrorRead { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + b: &mut [u8], + ) -> Poll> { + if self.0 { + Poll::Ready(Err(std::io::ErrorKind::InvalidInput.into())) + } else { + self.0 = true; + b[..16].fill(b'x'); + Ok(16).into() + } + } +} + #[test] fn lines() { let buf = Cursor::new(&b"12\r"[..]); @@ -58,3 +76,9 @@ fn maybe_pending() { assert_eq!(run_next!(s), "".to_string()); assert!(run(s.next()).is_none()); } + +#[test] +fn issue2862() { + let mut lines = futures::io::BufReader::new(IOErrorRead(false)).lines(); + assert!(block_on(lines.next()).unwrap().is_err()) +} diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs index 88a877928a..c7559c593b 100644 --- a/futures/tests/io_read_line.rs +++ b/futures/tests/io_read_line.rs @@ -3,6 +3,7 @@ use futures::future::{Future, FutureExt}; use futures::io::{AsyncBufReadExt, Cursor}; use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; +use futures::AsyncRead; use futures_test::io::AsyncReadTestExt; use futures_test::task::noop_context; @@ -15,6 +16,24 @@ fn run(mut f: F) -> F::Output { } } +struct IOErrorRead(bool); + +impl AsyncRead for IOErrorRead { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + b: &mut [u8], + ) -> Poll> { + if self.0 { + Poll::Ready(Err(std::io::ErrorKind::InvalidInput.into())) + } else { + self.0 = true; + b[..16].fill(b'x'); + Ok(16).into() + } + } +} + #[test] fn read_line() { let mut buf = Cursor::new(b"12"); @@ -34,6 +53,30 @@ fn read_line() { assert_eq!(v, ""); } +#[test] +fn read_line_drop() { + // string contents should be preserved if the future is dropped + let mut buf = Cursor::new(b"12\n\n"); + let mut v = String::from("abc"); + drop(buf.read_line(&mut v)); + assert_eq!(v, "abc"); +} + +#[test] +fn read_line_io_error() { + let mut r = futures::io::BufReader::new(IOErrorRead(false)); + let _ = block_on(r.read_line(&mut String::new())); +} + +#[test] +fn read_line_utf8_error() { + let mut buf = Cursor::new(b"12\xFF\n\n"); + let mut v = String::from("abc"); + let res = block_on(buf.read_line(&mut v)); + assert_eq!(res.unwrap_err().kind(), std::io::ErrorKind::InvalidData); + assert_eq!(v, "abc"); +} + #[test] fn maybe_pending() { let mut buf = b"12".interleave_pending(); From 33c46b3dc65e151e0e794636ad62872330733557 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 18 Mar 2024 02:07:00 +0900 Subject: [PATCH 277/283] ci: Work around sanitizer issue on latest Linux kernel --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 436d14a9f6..5a48f53405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -266,6 +266,8 @@ jobs: - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain install nightly --component rust-src && rustup default nightly + # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491 + - run: sudo sysctl vm.mmap_rnd_bits=28 - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests env: # TODO: Once `cfg(sanitize = "..")` is stable, replace From f00e7afb467e5f9b3c81724cc3cac1f0687fff7c Mon Sep 17 00:00:00 2001 From: Imbris <2002109+Imberflur@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:11:15 -0400 Subject: [PATCH 278/283] Fix use after free of task in FuturesUnordered when dropped future panics (#2886) --- .github/workflows/ci.yml | 10 +++- .../src/stream/futures_unordered/mod.rs | 51 +++++++++++++------ .../futures_unordered/ready_to_run_queue.rs | 2 + futures/tests/stream_futures_unordered.rs | 23 +++++++++ 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a48f53405..aafcfee053 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -246,10 +246,16 @@ jobs: - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain install nightly --component miri && rustup default nightly - - run: cargo miri test --workspace --all-features + - run: cargo miri test --workspace --all-features -- --skip panic_on_drop_fut env: MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout + # This test is expected to leak. + - run: cargo miri test --workspace --all-features --test stream_futures_unordered -- panic_on_drop_fut + env: + MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks + RUSTDOCFLAGS: ${{ env.RUSTDOCFLAGS }} -Z randomize-layout + RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} @@ -268,7 +274,7 @@ jobs: run: rustup toolchain install nightly --component rust-src && rustup default nightly # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491 - run: sudo sysctl vm.mmap_rnd_bits=28 - - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests + - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests -- --skip panic_on_drop_fut env: # TODO: Once `cfg(sanitize = "..")` is stable, replace # `cfg(futures_sanitizer)` with `cfg(sanitize = "..")` and remove diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 5d4b3f295e..ca62b10b91 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -256,16 +256,6 @@ impl FuturesUnordered { // `wake` from doing any work in the future let prev = task.queued.swap(true, SeqCst); - // Drop the future, even if it hasn't finished yet. This is safe - // because we're dropping the future on the thread that owns - // `FuturesUnordered`, which correctly tracks `Fut`'s lifetimes and - // such. - unsafe { - // Set to `None` rather than `take()`ing to prevent moving the - // future. - *task.future.get() = None; - } - // If the queued flag was previously set, then it means that this task // is still in our internal ready to run queue. We then transfer // ownership of our reference count to the ready to run queue, and it'll @@ -277,8 +267,25 @@ impl FuturesUnordered { // enqueue the task, so our task will never see the ready to run queue // again. The task itself will be deallocated once all reference counts // have been dropped elsewhere by the various wakers that contain it. - if prev { - mem::forget(task); + // + // Use ManuallyDrop to transfer the reference count ownership before + // dropping the future so unwinding won't release the reference count. + let md_slot; + let task = if prev { + md_slot = mem::ManuallyDrop::new(task); + &*md_slot + } else { + &task + }; + + // Drop the future, even if it hasn't finished yet. This is safe + // because we're dropping the future on the thread that owns + // `FuturesUnordered`, which correctly tracks `Fut`'s lifetimes and + // such. + unsafe { + // Set to `None` rather than `take()`ing to prevent moving the + // future. + *task.future.get() = None; } } @@ -567,15 +574,27 @@ impl FuturesUnordered { impl Drop for FuturesUnordered { fn drop(&mut self) { + // Before the strong reference to the queue is dropped we need all + // futures to be dropped. See note at the bottom of this method. + // + // If there is a panic before this completes, we leak the queue. + struct LeakQueueOnDrop<'a, Fut>(&'a mut FuturesUnordered); + impl Drop for LeakQueueOnDrop<'_, Fut> { + fn drop(&mut self) { + mem::forget(Arc::clone(&self.0.ready_to_run_queue)); + } + } + let guard = LeakQueueOnDrop(self); // When a `FuturesUnordered` is dropped we want to drop all futures // associated with it. At the same time though there may be tons of // wakers flying around which contain `Task` references // inside them. We'll let those naturally get deallocated. - while !self.head_all.get_mut().is_null() { - let head = *self.head_all.get_mut(); - let task = unsafe { self.unlink(head) }; - self.release_task(task); + while !guard.0.head_all.get_mut().is_null() { + let head = *guard.0.head_all.get_mut(); + let task = unsafe { guard.0.unlink(head) }; + guard.0.release_task(task); } + mem::forget(guard); // safe to release strong reference to queue // Note that at this point we could still have a bunch of tasks in the // ready to run queue. None of those tasks, however, have futures diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 77abdf4ea4..6ffaf554dd 100644 --- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs +++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs @@ -27,6 +27,8 @@ pub(super) struct ReadyToRunQueue { /// An MPSC queue into which the tasks containing the futures are inserted /// whenever the future inside is scheduled for polling. impl ReadyToRunQueue { + // FIXME: this takes raw pointer without safety conditions. + /// The enqueue function from the 1024cores intrusive MPSC queue algorithm. pub(super) fn enqueue(&self, task: *const Task) { unsafe { diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs index 9243c437c0..e25e755cde 100644 --- a/futures/tests/stream_futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -406,3 +406,26 @@ fn clear_in_loop() { } }); } + +// https://github.com/rust-lang/futures-rs/issues/2863#issuecomment-2219441515 +#[test] +#[should_panic] +fn panic_on_drop_fut() { + struct BadFuture; + + impl Drop for BadFuture { + fn drop(&mut self) { + panic!() + } + } + + impl Future for BadFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } + } + + FuturesUnordered::default().push(BadFuture); +} From f3fb74df310d070d91df7995c942f223cba6720a Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Thu, 3 Oct 2024 21:12:50 -0400 Subject: [PATCH 279/283] Document how `BoxFuture`s / `BoxStream`s are often made (#2887) --- futures-core/src/future.rs | 10 ++++++++++ futures-core/src/stream.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/futures-core/src/future.rs b/futures-core/src/future.rs index 7540cd027e..30c0323a6b 100644 --- a/futures-core/src/future.rs +++ b/futures-core/src/future.rs @@ -9,10 +9,20 @@ pub use core::future::Future; /// An owned dynamically typed [`Future`] for use in cases where you can't /// statically type your result or need to add some indirection. +/// +/// This type is often created by the [`boxed`] method on [`FutureExt`]. See its documentation for more. +/// +/// [`boxed`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.boxed +/// [`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html #[cfg(feature = "alloc")] pub type BoxFuture<'a, T> = Pin + Send + 'a>>; /// `BoxFuture`, but without the `Send` requirement. +/// +/// This type is often created by the [`boxed_local`] method on [`FutureExt`]. See its documentation for more. +/// +/// [`boxed_local`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.boxed_local +/// [`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html #[cfg(feature = "alloc")] pub type LocalBoxFuture<'a, T> = Pin + 'a>>; diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs index 19114e7dab..dd07d5aa01 100644 --- a/futures-core/src/stream.rs +++ b/futures-core/src/stream.rs @@ -6,10 +6,20 @@ use core::task::{Context, Poll}; /// An owned dynamically typed [`Stream`] for use in cases where you can't /// statically type your result or need to add some indirection. +/// +/// This type is often created by the [`boxed`] method on [`StreamExt`]. See its documentation for more. +/// +/// [`boxed`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.boxed +/// [`StreamExt`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html #[cfg(feature = "alloc")] pub type BoxStream<'a, T> = Pin + Send + 'a>>; /// `BoxStream`, but without the `Send` requirement. +/// +/// This type is often created by the [`boxed_local`] method on [`StreamExt`]. See its documentation for more. +/// +/// [`boxed_local`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.boxed_local +/// [`StreamExt`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html #[cfg(feature = "alloc")] pub type LocalBoxStream<'a, T> = Pin + 'a>>; From 8a8b085a8c1a7396173a104c67e0cf2f5b74fc74 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 26 Oct 2021 22:50:24 +0900 Subject: [PATCH 280/283] Fix clippy::uninit_vec warning error: calling `set_len()` immediately after reserving a buffer creates uninitialized values --> futures-util/src/io/buf_reader.rs:52:13 | 52 | let mut buffer = Vec::with_capacity(capacity); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 53 | buffer.set_len(capacity); | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[deny(clippy::uninit_vec)]` on by default = help: initialize the buffer or wrap the content in `MaybeUninit` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec --- futures-util/src/io/buf_reader.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index ec6a8e154e..9a919f7183 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -48,12 +48,9 @@ impl BufReader { /// Creates a new `BufReader` with the specified buffer capacity. pub fn with_capacity(capacity: usize, inner: R) -> Self { - unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - super::initialize(&inner, &mut buffer); - Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 } - } + // TODO: consider using Box<[u8]>::new_uninit_slice once it stabilized + let buffer = vec![0; capacity]; + Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 } } } From 1e052816b09890925cfdfcbe8d390cdaae5e4c38 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 5 Oct 2024 15:45:19 +0900 Subject: [PATCH 281/283] Release 0.3.31 --- CHANGELOG.md | 13 +++++++++++++ examples/functional/Cargo.toml | 2 +- examples/imperative/Cargo.toml | 2 +- futures-channel/Cargo.toml | 6 +++--- futures-core/Cargo.toml | 2 +- futures-executor/Cargo.toml | 8 ++++---- futures-io/Cargo.toml | 2 +- futures-macro/Cargo.toml | 2 +- futures-sink/Cargo.toml | 2 +- futures-task/Cargo.toml | 2 +- futures-test/Cargo.toml | 16 ++++++++-------- futures-util/Cargo.toml | 14 +++++++------- futures/Cargo.toml | 16 ++++++++-------- futures/tests/macro-reexport/Cargo.toml | 2 +- futures/tests/macro-tests/Cargo.toml | 2 +- futures/tests/no-std/Cargo.toml | 2 +- 16 files changed, 53 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1b633b2bc..992a457a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 0.3.31 - 2024-10-05 + +* Fix use after free of task in `FuturesUnordered` when dropped future panics (#2886) +* Fix soundness bug in `task::waker_ref` (#2830) + This is a breaking change but allowed because it is soundness bug fix. +* Fix bugs in `AsyncBufRead::read_line` and `AsyncBufReadExt::lines` (#2884) +* Fix parsing issue in `select!`/`select_biased!` (#2832) + This is technically a breaking change as it will now reject a very odd undocumented syntax that was previously accidentally accepted. +* Work around issue due to upstream `Waker::will_wake` change (#2865) +* Add `stream::Iter::{get_ref,get_mut,into_inner}` (#2875) +* Add `future::AlwaysReady` (#2825) +* Relax trait bound on non-constructor methods of `io::{BufReader,BufWriter}` (#2848) + # 0.3.30 - 2023-12-24 * Add `{BiLock,SplitStream,SplitSink,ReadHalf,WriteHalf}::is_pair_of` (#2797) diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml index a1618d312d..bb95a6f6a3 100644 --- a/examples/functional/Cargo.toml +++ b/examples/functional/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-example-functional" +version = "0.0.0" edition = "2018" -version = "0.1.0" publish = false [dependencies] diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml index c8076e4b91..afd7a20428 100644 --- a/examples/imperative/Cargo.toml +++ b/examples/imperative/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "futures-example-imperative" +version = "0.0.0" edition = "2018" -version = "0.1.0" publish = false [dependencies] diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index 81ebc42de8..4d430b78d7 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -22,8 +22,8 @@ unstable = [] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.31", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.31", default-features = false, optional = true } [dev-dependencies] futures = { path = "../futures", default-features = true } diff --git a/futures-core/Cargo.toml b/futures-core/Cargo.toml index 137385c413..a7d710e6a5 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-core" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 87201d3844..978f8ae018 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -16,9 +16,9 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.30", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.31", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.31", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.31", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index d51c4c9d65..e5320d4281 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-io" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index b154f125b0..fdd7be3dd8 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index dda3504a3c..21f8259155 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.36" license = "MIT OR Apache-2.0" diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index 40f07a835a..f1848dacbd 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-task" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 6b5a3045d0..46d933415f 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-test" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -11,13 +11,13 @@ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.30", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.30", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.30", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.30", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.30", path = "../futures-executor", default-features = false } -futures-sink = { version = "0.3.30", path = "../futures-sink", default-features = false } -futures-macro = { version = "=0.3.30", path = "../futures-macro", default-features = false } +futures-core = { version = "0.3.31", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.31", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.31", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.31", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.31", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.31", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.31", path = "../futures-macro", default-features = false } pin-project = "1.0.11" [dev-dependencies] diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 1b0bcb7888..825a204125 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures-util" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -35,12 +35,12 @@ write-all-vectored = ["io"] cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.30", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.30", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "=0.3.30", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.31", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.31", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.31", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.31", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.31", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.31", default-features = false, 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" } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index cd8cf82708..102f9215cc 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "futures" -version = "0.3.30" +version = "0.3.31" edition = "2018" rust-version = "1.56" license = "MIT OR Apache-2.0" @@ -15,13 +15,13 @@ composability, and iterator-like interfaces. categories = ["asynchronous"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.30", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.30", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.30", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.30", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.30", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.30", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.30", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.31", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.31", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.31", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.31", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.31", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.31", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.31", default-features = false, features = ["sink"] } [dev-dependencies] futures-executor = { path = "../futures-executor", features = ["thread-pool"] } diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml index 731ad5f455..2ddd1587a0 100644 --- a/futures/tests/macro-reexport/Cargo.toml +++ b/futures/tests/macro-reexport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro-reexport" -version = "0.1.0" +version = "0.0.0" edition = "2018" publish = false diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml index 10430ddad3..db72bd41b2 100644 --- a/futures/tests/macro-tests/Cargo.toml +++ b/futures/tests/macro-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro-tests" -version = "0.1.0" +version = "0.0.0" edition = "2018" publish = false diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml index f638b8a4af..ec7d4c7871 100644 --- a/futures/tests/no-std/Cargo.toml +++ b/futures/tests/no-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "no-std" -version = "0.1.0" +version = "0.0.0" edition = "2018" publish = false From bf1cc19c941943d4152ec042b4531f9a84041ff1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 13 Oct 2024 15:06:26 +0900 Subject: [PATCH 282/283] Set warn_non_default_branch = true in triagebot.toml --- triagebot.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 triagebot.toml diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 0000000000..e4e7a2d785 --- /dev/null +++ b/triagebot.toml @@ -0,0 +1,2 @@ +[assign] +warn_non_default_branch = true From 63f068260630edbefd82260f1e7f9f07509bd55f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 8 Oct 2024 03:02:13 +0900 Subject: [PATCH 283/283] ci: Work around sanitizer-related regression ``` Running unittests src/lib.rs (target/debug/deps/futures_macro-cb16371840b70586) /home/runner/work/futures-rs/futures-rs/target/debug/deps/futures_macro-cb16371840b70586: error while loading shared libraries: libstd-f157c25fb2dbfbe0.so: cannot open shared object file: No such file or directory error: test failed, to rerun pass `-p futures-macro --lib` Caused by: process didn't exit successfully: `/home/runner/work/futures-rs/futures-rs/target/debug/deps/futures_macro-cb16371840b70586 --skip panic_on_drop_fut` (exit status: 127) ``` --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aafcfee053..ae34fc33a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -274,7 +274,8 @@ jobs: run: rustup toolchain install nightly --component rust-src && rustup default nightly # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491 - run: sudo sysctl vm.mmap_rnd_bits=28 - - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests -- --skip panic_on_drop_fut + # Exclude futures-macro to work around upstream bug since nightly-2024-10-06. + - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests --exclude futures-macro -- --skip panic_on_drop_fut env: # TODO: Once `cfg(sanitize = "..")` is stable, replace # `cfg(futures_sanitizer)` with `cfg(sanitize = "..")` and remove