From 8603348459c709305ab80ba62a9169b498559d5e Mon Sep 17 00:00:00 2001 From: Kevin Cox Date: Thu, 30 Dec 2021 04:09:18 -0500 Subject: [PATCH 01/15] 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 fe86d0aee17ec44af320e0c8ba2a161b3e926917 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 12 Jan 2022 23:46:28 +0900 Subject: [PATCH 02/15] 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 66d76886fc3b9e31f55aa8a637e20835aa4d143e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:06:22 +0900 Subject: [PATCH 03/15] 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 2850fe53ee9375c6be7bd85072c16b8fafba4b5f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:04:38 +0900 Subject: [PATCH 04/15] 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 5c13f53db4366af3a13dfe5fc2b6d38e79f8c3a6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:05:35 +0900 Subject: [PATCH 05/15] 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 c79b674996c1bf709432858b80336d343c3c33ec Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 13 Jan 2022 05:23:35 +0900 Subject: [PATCH 06/15] 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 93925781b5d407c319b04dd207fcd7d08f9446a3 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 07/15] 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 4f3b98e2afa0aa4b45894120da21ed66b6f370fb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 13:19:46 +0900 Subject: [PATCH 08/15] 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 e62f534f3aabfff71baa5f2e9cd690716689ee36 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 13:39:01 +0900 Subject: [PATCH 09/15] 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 0d38c6e845ba8a3140d84da2657a96ce251c93ed Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:01:41 +0900 Subject: [PATCH 10/15] 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 7ab95604f6024c32a2f6ec547feffbe3cc3a11fc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:23:43 +0900 Subject: [PATCH 11/15] 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 8a6fd2eb4b990900f75ae2b0952c3b9f255a2a85 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:29:21 +0900 Subject: [PATCH 12/15] 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 13/15] 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 0fabecd81bffd83f94dd9219bf1355b0635e8fdc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 14:46:19 +0900 Subject: [PATCH 14/15] 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 9ed1e4478e8eaffedfc16ab53a42dc9ec3b25d94 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 6 Feb 2022 15:49:16 +0900 Subject: [PATCH 15/15] 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