diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c516e3138..c204ef6670 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,30 +19,48 @@ 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 + # 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, 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 +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-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 @@ -100,6 +118,7 @@ jobs: build: name: cargo +${{ matrix.rust }} build strategy: + fail-fast: false matrix: rust: - stable @@ -130,10 +149,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 +232,7 @@ jobs: san: name: cargo test -Z sanitizer=${{ matrix.sanitizer }} strategy: + fail-fast: false matrix: sanitizer: - address @@ -235,12 +258,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: @@ -248,8 +267,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/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/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/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/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..4cd936d552 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -12,27 +12,31 @@ //! 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) + ) +))] -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-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index 28612da84d..44834b7c95 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -510,9 +510,8 @@ 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, + // 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-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-core/src/lib.rs b/futures-core/src/lib.rs index e363ff777d..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; @@ -20,9 +25,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, } }; } 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-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-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/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..c72460744c 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,41 +1,43 @@ //! 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; -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-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-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/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/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/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/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); } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 16871cb4b5..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")))] @@ -47,13 +56,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 +338,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/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-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/mod.rs b/futures-util/src/stream/mod.rs index 0b2fc90532..8cf9f80a9d 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; @@ -88,33 +91,44 @@ 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}; -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")] +#[doc(inline)] +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/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..bd86990cdb --- /dev/null +++ b/futures-util/src/stream/select_with_strategy.rs @@ -0,0 +1,229 @@ +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); +/// +/// 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() + } +} 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 9089e6e3df..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; @@ -169,35 +177,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; @@ -621,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 diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 11cd9c0d31..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; @@ -110,25 +118,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")] @@ -572,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..07d4425a81 --- /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 occurred, 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 {} 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/futures/src/lib.rs b/futures/src/lib.rs index d15c16c377..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); //! @@ -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")))] 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 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 { 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", 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