diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000000..992016c29a --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.36" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..79a8d7d53f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*.rs] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..dfb7eea4ff --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,330 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - master + - '[0-9]+.[0-9]+' + schedule: + - cron: '0 1 * * *' + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + test: + name: cargo test (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: + - 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 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 + - name: Install cross + uses: taiki-e/install-action@cross + - run: cross test --target ${{ matrix.target }} --workspace --all-features + - run: cross test --target ${{ matrix.target }} --workspace --all-features --release + # TODO: https://github.com/rust-lang/futures-rs/issues/2451 + if: matrix.target != 'aarch64-unknown-linux-gnu' + + core-msrv: + 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. + # When updating this, the reminder to update the minimum required version in README.md, Cargo.toml, and .clippy.toml. + - 1.36 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead. + # Refs: cargo#3620, cargo#4106, cargo#4463, cargo#4753, cargo#5015, cargo#5364, cargo#6195 + - 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 + - run: | + cargo hack build --workspace --ignore-private --no-default-features \ + --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-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-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-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 + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - 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 + - run: cargo hack build --workspace --ignore-private + # Check no-default-features + - run: cargo hack build --workspace --exclude futures-test --ignore-private --no-default-features + # Check alloc feature + - run: cargo hack build --workspace --exclude futures-test --ignore-private --no-default-features --features alloc --ignore-unknown-features + # Check std feature + - run: cargo hack build --workspace --ignore-private --no-default-features --features std --ignore-unknown-features + # Check compat feature (futures, futures-util) + - run: cargo hack build -p futures -p futures-util --no-default-features --features std,io-compat + # Check thread-pool feature (futures, futures-executor) + - run: cargo hack build -p futures -p futures-executor --no-default-features --features std,thread-pool + + build: + name: cargo +${{ matrix.rust }} build + strategy: + fail-fast: false + matrix: + rust: + - stable + - beta + - nightly + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - 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 + + minimal-versions: + name: cargo build -Z minimal-versions + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 + - run: cargo hack --remove-dev-deps --workspace + - run: cargo update -Z minimal-versions + - run: cargo build --workspace --all-features + + 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: + - thumbv7m-none-eabi + - thumbv6m-none-eabi + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: rustup target add ${{ matrix.target }} + - 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 hack build --manifest-path futures/tests/no-std/Cargo.toml \ + --each-feature --optional-deps \ + --target ${{ matrix.target }} + - run: | + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ + --no-default-features \ + --target ${{ matrix.target }} + - run: | + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ + --no-default-features --features alloc --ignore-unknown-features \ + --target ${{ matrix.target }} + - run: | + cargo hack build --workspace --ignore-private \ + --exclude futures-test --exclude futures-macro \ + --no-default-features --features async-await,alloc --ignore-unknown-features \ + --target ${{ matrix.target }} + + bench: + name: cargo bench + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: cargo bench --workspace + - run: cargo bench --manifest-path futures-util/Cargo.toml --features=bilock,unstable + + features: + name: cargo hack check --feature-powerset + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - 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` + # * `--no-dev-deps` - build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 + # * `--exclude futures-test` - futures-test cannot be compiled with no-default features + # * `--features unstable` - some features cannot be compiled without this feature + # * `--ignore-unknown-features` - some crates doesn't have 'unstable' feature + - run: | + cargo hack check \ + --feature-powerset --depth 2 --no-dev-deps \ + --workspace --exclude futures-test \ + --features unstable --ignore-unknown-features + + # When this job failed, run ci/no_atomic_cas.sh and commit result changes. + codegen: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: ci/no_atomic_cas.sh + - run: git add -N . && git diff --exit-code + if: github.repository_owner != 'rust-lang' || github.event_name != 'schedule' + - id: diff + run: | + git config user.name "Taiki Endo" + git config user.email "te316e89@gmail.com" + git add -N . + if ! git diff --exit-code; then + git add . + git commit -m "Update no_atomic_cas.rs" + echo "::set-output name=success::false" + fi + if: github.repository_owner == 'rust-lang' && github.event_name == 'schedule' + - uses: peter-evans/create-pull-request@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.repository_owner == 'rust-lang' && github.event_name == 'schedule' && steps.diff.outputs.success == 'false' + + 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-check-number-validity -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation + + san: + name: cargo test -Z sanitizer=${{ matrix.sanitizer }} + strategy: + fail-fast: false + matrix: + sanitizer: + - address + - memory + - thread + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: rustup component add rust-src + - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests + env: + # TODO: Once `cfg(sanitize = "..")` is stable, replace + # `cfg(futures_sanitizer)` with `cfg(sanitize = "..")` and remove + # `--cfg futures_sanitizer`. + RUSTFLAGS: -D warnings -Z sanitizer=${{ matrix.sanitizer }} --cfg futures_sanitizer + + # This branch no longer actively developed. Most commits to this + # branch are backporting and should not be blocked by clippy. + # clippy: + # name: cargo clippy + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - name: Install Rust + # run: rustup toolchain install nightly --component clippy && rustup default nightly + # - run: cargo clippy --workspace --all-features --all-targets + + fmt: + name: cargo fmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable + - run: cargo fmt --all -- --check + + docs: + name: cargo doc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --workspace --no-deps --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..dc9b65bf6e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + push: + tags: + - '[0-9]+.*' + +env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + +jobs: + create-release: + if: github.repository_owner == 'rust-lang' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable + - run: cargo build --all + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + branch: 'master|[0-9]+\.[0-9]+' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000000..2a79d9274a --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,2 @@ +use_small_heuristics = "Max" +edition = "2018" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 93526353b8..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,159 +0,0 @@ -language: rust -sudo: false - -# Refs: https://levans.fr/rust_travis_cache.html -cache: - directories: - - /home/travis/.cargo -before_cache: - - rm -rf /home/travis/.cargo/registry - -matrix: - include: - # This is the minimum Rust version supported by futures-rs. - # When updating this, the reminder to update the minimum required version in README.md. - - name: cargo check (minimum required version) - rust: 1.36.0 - install: - # 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 - - if ! cargo hack -V 2>/dev/null; then - cargo install cargo-hack; - fi - script: - # remove dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - - cargo hack --remove-dev-deps --workspace - # Check no-default-features - - cargo hack check --workspace --exclude futures-test --ignore-private --no-default-features - # Check alloc feature - - cargo hack check --workspace --exclude futures-test --ignore-private --no-default-features --features alloc --ignore-unknown-features - # Check std feature - - cargo hack check --workspace --ignore-private --no-default-features --features std --ignore-unknown-features - # Check compat feature (futures, futures-util) - - cargo hack check -p futures -p futures-util --no-default-features --features std,io-compat - # Check thread-pool feature (futures, futures-executor) - - cargo hack check -p futures -p futures-executor --no-default-features --features std,thread-pool - - # This is the minimum Rust version supported by `async-await` feature. - # When updating this, the reminder to update the minimum required version of `async-await` feature in README.md. - - name: cargo build --features async-await (minimum required version) - rust: 1.39.0 - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - # async-await feature is activated by default. - - cargo build --workspace - - - name: cargo +stable build - rust: stable - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - - cargo build --workspace - - - name: cargo +beta build - rust: beta - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - - cargo build --workspace - - - name: cargo test - rust: nightly - os: osx - - - name: cargo test - rust: nightly - os: linux - - - name: cargo build (with minimal versions) - rust: nightly - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - - cargo update -Zminimal-versions - - cargo build --workspace --all-features - - - name: cargo clippy - rust: nightly - install: - - if ! rustup component add clippy 2>/dev/null; then - target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy`; - echo "'clippy' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead"; - rustup toolchain install nightly-$target; - rustup default nightly-$target; - rustup component add clippy; - fi - script: - - cargo clippy --workspace --all-features --all-targets - - - name: cargo bench - rust: nightly - script: - - cargo bench --workspace - - cargo bench --manifest-path futures-util/Cargo.toml --features=bilock,unstable - - - name: cargo build --target=thumbv6m-none-eabi - rust: nightly - install: - - rustup target add thumbv6m-none-eabi - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - - cargo build --manifest-path futures/Cargo.toml - --target thumbv6m-none-eabi - --no-default-features - --features unstable,cfg-target-has-atomic - - cargo build --manifest-path futures/Cargo.toml - --target thumbv6m-none-eabi - --no-default-features - --features unstable,cfg-target-has-atomic,alloc - - cargo build --manifest-path futures/Cargo.toml - --target thumbv6m-none-eabi - --no-default-features - --features unstable,cfg-target-has-atomic,async-await - - - name: cargo build --target=thumbv7m-none-eabi - rust: nightly - install: - - rustup target add thumbv7m-none-eabi - script: - - cargo run --manifest-path ci/remove-dev-dependencies/Cargo.toml */Cargo.toml - - cargo build --manifest-path futures/Cargo.toml - --target thumbv7m-none-eabi - --no-default-features - - cargo build --manifest-path futures/Cargo.toml - --target thumbv7m-none-eabi - --no-default-features - --features alloc - - cargo build --manifest-path futures/Cargo.toml - --target thumbv7m-none-eabi - --no-default-features - --features async-await - - - name: cargo check (features) - rust: nightly - install: - - cargo install cargo-hack - script: - # Check each specified feature works properly - # * `--each-feature` - run for each feature which includes --no-default-features and default features of package - # * `--no-dev-deps` - build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866 - # * `--exclude futures-test` - futures-test cannot be compiled with no-default features - # * `--features unstable` - some features cannot be compiled without this feature - # * `--ignore-unknown-features` - some crates doesn't have 'unstable' feature - - cargo hack check - --each-feature --no-dev-deps - --workspace --exclude futures-test - --features unstable --ignore-unknown-features - - - name: cargo doc - rust: nightly - script: - - RUSTDOCFLAGS=-Dwarnings cargo doc --workspace --no-deps --all-features - -script: - - cargo test --workspace --all-features - - cargo test --workspace --all-features --release - -env: - - RUSTFLAGS=-Dwarnings - -notifications: - email: - on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md index b93b0ae557..8f76048681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,176 @@ +# 0.3.21 - 2022-02-06 + +* Fix potential data race in `FlattenUnordered` that introduced in 0.3.20 (#2566) + +# 0.3.20 - 2022-02-06 + +NOTE: This release has been yanked due to a bug fixed in 0.3.21. + +* Fix stacked borrows violations when `-Zmiri-tag-raw-pointers` is enabled. This raises MSRV of `futures-task` to 1.45. (#2548, #2550) +* Change `FuturesUnordered` to respect yielding from future (#2551) +* Add `StreamExt::{flatten_unordered, flat_map_unordered}` (#2083) + +# 0.3.19 - 2021-12-18 + +* Remove unstable `read-initializer` feature (#2534) +* Fix panic in `FuturesUnordered` (#2535) +* Fix compatibility issue with `FuturesUnordered` and tokio's cooperative scheduling (#2527) +* Add `StreamExt::count` (#2495) + +# 0.3.18 - 2021-11-23 + +NOTE: This release has been yanked. See #2529 for details. + +* Fix unusable `Sink` implementation on `stream::Scan` (#2499) +* Make `task::noop_waker_ref` available without `std` feature (#2505) +* Add async `LineWriter` (#2477) +* Remove dependency on `proc-macro-hack`. This raises MSRV of utility crates to 1.45. (#2520) + +# 0.3.17 - 2021-08-30 + +* Use `FuturesOrdered` in `join_all` (#2412) +* Add `{future, stream}::poll_immediate` (#2452) +* Add `stream_select!` macro (#2262) +* Implement `Default` for `OptionFuture` (#2471) +* Add `Peekable::{peek_mut, poll_peek_mut}` (#2488) +* Add `BufReader::seek_relative` (#2489) + +# 0.3.16 - 2021-07-23 + +* Add `TryStreamExt::try_chunks` (#2438) +* Add `StreamExt::{all, any}` (#2460) +* Add `stream::select_with_strategy` (#2450) +* Update to new `io_slice_advance` interface (#2454) + +# 0.3.15 - 2021-05-11 + +* Use `#[proc_macro]` at Rust 1.45+ to fix an issue where proc macros don't work with rust-analyzer (#2407) +* Support targets that do not have atomic CAS on stable Rust (#2400) +* futures-test: Add async `#[test]` function attribute (#2409) +* Add `stream::abortable` (#2410) +* Add `FuturesUnordered::clear` (#2415) +* Implement `IntoIterator` for `FuturesUnordered` (#2423) +* Implement `Send` and `Sync` for `FuturesUnordered` iterators (#2416) +* Make `FuturesUnordered::iter_pin_ref` public (#2423) +* Add `SelectAll::clear` (#2430) +* Add `SelectAll::{iter, iter_mut}` (#2428) +* Implement `IntoIterator` for `SelectAll` (#2428) +* Implement `Clone` for `WeakShared` (#2396) + +# 0.3.14 - 2021-04-10 + +* Add `future::SelectAll::into_inner` (#2363) +* Allow calling `UnboundedReceiver::try_next` after `None` (#2369) +* Reexport non-Ext traits from the root of `futures_util` (#2377) +* Add `AsyncSeekExt::stream_position` (#2380) +* Add `stream::Peekable::{next_if, next_if_eq}` (#2379) + +# 0.3.13 - 2021-02-23 + +* Mitigated starvation issues in `FuturesUnordered` (#2333) +* Fixed race with dropping `mpsc::Receiver` (#2304) +* Added `Shared::{strong_count, weak_count}` (#2346) +* Added `no_std` support for `task::noop_waker_ref` (#2332) +* Implemented `Stream::size_hint` for `Either` (#2325) + +# 0.3.12 - 2021-01-15 + +* Fixed `Unpin` impl of `future::{MaybeDone, TryMaybeDone}` where trait bounds were accidentally added in 0.3.9. (#2317) + +# 0.3.11 - 2021-01-14 + +* Fixed heap buffer overflow in `AsyncReadExt::{read_to_end, read_to_string}` (#2314) + +# 0.3.10 - 2021-01-13 + +NOTE: This release has been yanked. See #2310 for details. + +* Fixed type-inference in `sink::unfold` by specifying more of its types (breaking change -- see #2311) + +# 0.3.9 - 2021-01-08 + +NOTE: This release has been yanked. See #2310 for details. + +* Significantly improved compile time when `async-await` crate feature is disabled (#2273) +* Added `stream::repeat_with` (#2279) +* Added `StreamExt::unzip` (#2263) +* Added `sink::unfold` (#2268) +* Added `SinkExt::feed` (#2155) +* Implemented `FusedFuture` for `oneshot::Receiver` (#2300) +* Implemented `Clone` for `sink::With` (#2290) +* Re-exported `MapOkOrElse`, `MapInto`, `OkInto`, `TryFlatten`, `WriteAllVectored` (#2275) + +# 0.3.8 - 2020-11-04 + +NOTE: This release has been yanked. See #2310 for details. + +* Switched proc-macros to use native `#[proc_macro]` at Rust 1.45+ (#2243) +* Added `WeakShared` (#2169) +* Added `TryStreamExt::try_buffered` (#2245) +* Added `StreamExt::cycle` (#2252) +* Implemented `Clone` for `stream::{Empty, Pending, Repeat, Iter}` (#2248, #2252) +* Fixed panic in some `TryStreamExt` combinators (#2250) + +# 0.3.7 - 2020-10-23 + +NOTE: This release has been yanked. See #2310 for details. + +* Fixed unsoundness in `MappedMutexGuard` (#2240) +* Re-exported `TakeUntil` (#2235) +* futures-test: Prevent double panic in `panic_waker` (#2236) + +# 0.3.6 - 2020-10-06 + +NOTE: This release has been yanked. See #2310 for details. + +* Fixed UB due to missing 'static on `task::waker` (#2206) +* Added `AsyncBufReadExt::fill_buf` (#2225) +* Added `TryStreamExt::try_take_while` (#2212) +* Added `is_connected_to` method to `mpsc::{Sender, UnboundedSender}` (#2179) +* Added `is_connected_to` method to `oneshot::Sender` (#2158) +* Implement `FusedStream` for `FuturesOrdered` (#2205) +* Fixed documentation links +* Improved documentation +* futures-test: Added `track_closed` method to `AsyncWriteTestExt` and `SinkTestExt` (#2159) +* futures-test: Implemented more traits for `InterleavePending` (#2208) +* futures-test: Implemented more traits for `AssertUnmoved` (#2208) + +# 0.3.5 - 2020-05-08 + +NOTE: This release has been yanked. See #2310 for details. + +* Added `StreamExt::flat_map`. +* Added `StreamExt::ready_chunks`. +* Added `*_unpin` methods to `SinkExt`. +* Added a `cancellation()` future to `oneshot::Sender`. +* Added `reunite` method to `ReadHalf` and `WriteHalf`. +* Added `Extend` implementations for `Futures(Un)Ordered` and `SelectAll`. +* Added support for reexporting the `join!` and `select!` macros. +* Added `no_std` support for the `pending!` and `poll!` macros. +* Added `Send` and `Sync` support for `AssertUnmoved`. +* Fixed a bug where `Shared` wasn't relinquishing control to the executor. +* Removed the `Send` bound on the output of `RemoteHandle`. +* Relaxed bounds on `FuturesUnordered`. +* Reorganized internal tests to work under different `--feature`s. +* Reorganized the bounds on `StreamExt::forward`. +* Removed and replaced a large amount of internal `unsafe`. + # 0.3.4 - 2020-02-06 + +NOTE: This release has been yanked. See #2310 for details. + * Fixed missing `Drop` for `UnboundedReceiver` (#2064) # 0.3.3 - 2020-02-04 + +NOTE: This release has been yanked. See #2310 for details. + * Fixed compatibility issue with pinned facade (#2062) # 0.3.2 - 2020-02-03 + +NOTE: This release has been yanked. See #2310 for details. + * Improved buffering performance of `SplitSink` (#1969) * Added `select_biased!` macro (#1976) * Added `hash_receiver` method to mpsc channel (#1962) @@ -12,7 +178,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) @@ -22,10 +188,16 @@ * Mitigated starvation issues in `FuturesUnordered` (#2049) * Added `TryFutureExt::map_ok_or_else` (#2058) -# 0.3.1 - 2019-11-7 +# 0.3.1 - 2019-11-07 + +NOTE: This release has been yanked. See #2310 for details. + * Fix signature of `LocalSpawn` trait (breaking change -- see #1959) -# 0.3.0 - 2019-11-5 +# 0.3.0 - 2019-11-05 + +NOTE: This release has been yanked. See #2310 for details. + * Stable release along with stable async/await! * Added async/await to default features (#1953) * Changed `Spawn` trait and `FuturesUnordered::push` to take `&self` (#1950) @@ -46,7 +218,8 @@ * Added some missing `Clone` implementations * Documentation fixes -# 0.3.0-alpha.19 - 2019-9-25 +# 0.3.0-alpha.19 - 2019-09-25 + * Stabilized the `async-await` feature (#1816) * Made `async-await` feature no longer require `std` feature (#1815) * Updated `proc-macro2`, `syn`, and `quote` to 1.0 (#1798) @@ -69,7 +242,8 @@ * Removed dependency on `rand` by using our own PRNG (#1837) * Removed `futures-core` dependency from `futures-sink` (#1832) -# 0.3.0-alpha.18 - 2019-8-9 +# 0.3.0-alpha.18 - 2019-08-09 + * Rewrote `join!` and `try_join!` as procedural macros to allow passing expressions (#1783) * Banned manual implementation of `TryFuture` and `TryStream` for forward compatibility. See #1776 for more details. (#1777) * Changed `AsyncReadExt::read_to_end` to return the total number of bytes read (#1721) @@ -88,7 +262,8 @@ * Added `TryStreamExt::try_flatten` (#1731) * Added `FutureExt::now_or_never` (#1747) -# 0.3.0-alpha.17 - 2019-7-3 +# 0.3.0-alpha.17 - 2019-07-03 + * Removed `try_ready!` macro in favor of `ready!(..)?`. (#1602) * Removed `io::Window::{set_start, set_end}` in favor of `io::Window::set`. (#1667) * Re-exported `pin_utils::pin_mut!` macro. (#1686) @@ -121,7 +296,8 @@ * Renamed `Sink::SinkError` to `Sink::Error`. * Made a number of dependencies of `futures-util` optional. -# 0.3.0-alpha.16 - 2019-5-10 +# 0.3.0-alpha.16 - 2019-05-10 + * Updated to new nightly `async_await`. * Changed `AsyncRead::poll_vectored_read` and `AsyncWrite::poll_vectored_write` to use stabilized `std::io::{IoSlice, IoSliceMut}` instead of `iovec::IoVec`, and renamed to @@ -132,7 +308,8 @@ * Added `AsyncBufReadExt::{read_line, lines}`. * Added `io::BufReader`. -# 0.3.0-alpha.15 - 2019-4-26 +# 0.3.0-alpha.15 - 2019-04-26 + * Updated to stabilized `futures_api`. * Removed `StreamObj`, cautioned against usage of `FutureObj`. * Changed `StreamExt::select` to a function. @@ -145,10 +322,11 @@ * Added functions to partially progress a local pool. * Changed to use our own `Either` type rather than the one from the `either` crate. -# 0.3.0-alpha.14 - 2019-4-15 +# 0.3.0-alpha.14 - 2019-04-15 + * Updated to new nightly `futures_api`. * Changed `Forward` combinator to drop sink after completion, and allow `!Unpin` `Sink`s. -* Added 0.1 <-> 0.3 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`. @@ -156,7 +334,8 @@ * Changed `join` and `try_join` combinators to functions. * Fixed propagation of `cfg-target-has-atomic` feature. -# 0.3.0-alpha.13 - 2019-2-20 +# 0.3.0-alpha.13 - 2019-02-20 + * Updated to new nightly with stabilization candidate API. * Removed `LocalWaker`. * Added `#[must_use]` to `Stream` and `Sink` traits. @@ -166,7 +345,8 @@ * Removed `TokioDefaultSpawner` and `tokio-compat`. * Moved intra-crate dependencies to exact versions. -# 0.3.0-alpha.12 - 2019-1-14 +# 0.3.0-alpha.12 - 2019-01-14 + * Updated to new nightly with a modification to `Pin::set`. * Expose `AssertUnmoved` and `PendingOnce`. * Prevent double-panic in `AssertUnmoved`. @@ -174,6 +354,7 @@ * Implement `Default` for `Mutex` and `SelectAll`. # 0.3.0-alpha.11 - 2018-12-27 + * Updated to newly stabilized versions of the `pin` and `arbitrary_self_types` features. * Re-added `select_all` for streams. * Added `TryStream::into_async_read` for converting from a stream of bytes into @@ -183,6 +364,7 @@ * Exposed `join_all` from the facade # 0.3.0-alpha.10 - 2018-11-27 + * Revamped `select!` macro * Added `select_next_some` method for getting only the `Some` elements of a stream from `select!` * Added `futures::lock::Mutex` for async-aware synchronization. @@ -195,27 +377,33 @@ * Added `try_concat` # 0.3.0-alpha.9 - 2018-10-18 + * Fixed in response to new nightly handling of 2018 edition + `#![no_std]` # 0.3.0-alpha.8 - 2018-10-16 + * Fixed stack overflow in 0.1 compatibility layer * Added AsyncRead / AsyncWrite compatibility layer * Added Spawn -> 0.1 Executor compatibility * Made 0.1 futures usable on 0.3 executors without an additional global `Task`, accomplished by wrapping 0.1 futures in an 0.1 `Spawn` when using them as 0.3 futures. -* Cleanups and improvments to the `AtomicWaker` implementation. +* Cleanups and improvements to the `AtomicWaker` implementation. # 0.3.0-alpha.7 - 2018-10-01 + * Update to new nightly which removes `Spawn` from `task::Context` and replaces `Context` with `LocalWaker`. * Add `Spawn` and `LocalSpawn` traits and `FutureObj` and `LocalFutureObj` types to `futures-core`. # 0.3.0-alpha.6 - 2018-09-10 + * Replace usage of `crate` visibility with `pub(crate)` now that `crate` visibility is no longer included in the 2018 edition * Remove newly-stabilized "edition" feature in Cargo.toml files # 0.3.0-alpha.5 - 2018-09-03 + * Revert usage of cargo crate renaming feature # 0.3.0-alpha.4 - 2018-09-02 + **Note: This release does not work, use `0.3.0-alpha.5` instead** * `future::ok` and `future:err` to create result wrapping futures (similar to `future::ready`) @@ -223,13 +411,14 @@ * `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 # 0.3.0-alpha.3 - 2018-08-15 -* Compatibilty with newest nightly + +* Compatibility with newest nightly * Futures 0.1 compatibility layer including Tokio compatibility * Added `spawn!` and `spawn_with_handle!` macros * Added `SpawnExt` methods `spawn` and `spawn_with_handle` @@ -239,7 +428,7 @@ * Improvements to `select!` and `join!` macros * Added `try_join!` macro * Added `StreamExt` combinator methods `try_join` and `for_each_concurrent` -* Added `TryStreamExt` combinator methdos `into_stream`, `try_filter_map`, `try_skip_while`, `try_for_each_concurrent` and `try_buffer_unordered` +* Added `TryStreamExt` combinator methods `into_stream`, `try_filter_map`, `try_skip_while`, `try_for_each_concurrent` and `try_buffer_unordered` * Fix stream termination bug in `StreamExt::buffered` and `StreamExt::buffer_unordered` * Added docs for `StreamExt::buffered`, `StreamExt::buffer_unordered` * Added `task::local_waker_ref_from_nonlocal` and `task::local_waker_ref` functions @@ -247,16 +436,17 @@ * Doc improvements to `StreamExt::select` # 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: - - Methods of trait `TryFutureExt` - - Free functions `empty`, `lazy`, `maybe_done`, `poll_fn` and `ready` - - Type `FutureOption` - - Macros `join!`, `select!` and `pending!` - - Stream: Methods of trait `TryStreamExt` + * IO: Methods of traits `AsyncReadExt`, `AsyncWriteExt` + * Future: + * Methods of trait `TryFutureExt` + * Free functions `empty`, `lazy`, `maybe_done`, `poll_fn` and `ready` + * Type `FutureOption` + * Macros `join!`, `select!` and `pending!` + * Stream: Methods of trait `TryStreamExt` * Added `TryStreamExt` combinators `map_ok`, `map_err`, `err_into`, `try_next` and `try_for_each` * Added `Drain`, a sink that will discard all items given to it. Can be created using the `drain` function * Bugfix for the `write_all` combinator @@ -270,10 +460,11 @@ * 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 + * Major changes: See [the announcement](https://rust-lang-nursery.github.io/futures-rs/blog/2018/07/19/futures-0.3.0-alpha.1.html) on our new blog for details. The changes are too numerous to be covered in this changelog because nearly every line of code was modified. # 0.1.17 - 2017-10-31 diff --git a/Cargo.toml b/Cargo.toml index 56d24c2e90..d27a9f2885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,10 @@ members = [ "futures-util", "futures-test", + "futures/tests/macro-tests", + "futures/tests/macro-reexport", + "futures/tests/no-std", + "examples/functional", "examples/imperative", ] diff --git a/README.md b/README.md index 5c1bdcbc98..e6127fd6f8 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,13 @@

- - Build Status + + Build Status Crates.io - - - Rustc Version -

@@ -42,13 +38,7 @@ Add this to your `Cargo.toml`: futures = "0.3" ``` -Now, you can use futures-rs: - -```rust -use futures::future::Future; -``` - -The current futures-rs requires Rust 1.39 or later. +The current `futures` requires Rust 1.45 or later. ### Feature `std` @@ -58,22 +48,14 @@ a `#[no_std]` environment, use: ```toml [dependencies] -futures = { version = "0.3.4", default-features = false } +futures = { version = "0.3", default-features = false } ``` -# License - -This project is licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - http://opensource.org/licenses/MIT) - -at your option. +## License -### Contribution +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in futures-rs by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh new file mode 100755 index 0000000000..ba0200d698 --- /dev/null +++ b/ci/no_atomic_cas.sh @@ -0,0 +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 + +file="no_atomic_cas.rs" + +no_atomic_cas=() +for target in $(rustc --print target-list); do + target_spec=$(rustc --print target-spec-json -Z unstable-options --target "${target}") + res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)') + [[ -z "${res}" ]] || no_atomic_cas+=("${target}") +done + +cat >"${file}" <>"${file}" +done +cat >>"${file}" <"] -edition = "2018" -publish = false - -[workspace] - -[dependencies] -toml_edit = "0.1.3" diff --git a/ci/remove-dev-dependencies/src/main.rs b/ci/remove-dev-dependencies/src/main.rs deleted file mode 100644 index 0b5b0452fe..0000000000 --- a/ci/remove-dev-dependencies/src/main.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::{env, error::Error, fs}; - -fn main() -> Result<(), Box> { - for file in env::args().skip(1) { - let content = fs::read_to_string(&file)?; - let mut doc: toml_edit::Document = content.parse()?; - doc.as_table_mut().remove("dev-dependencies"); - fs::write(file, doc.to_string())?; - } - - Ok(()) -} diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml index 468a45432d..7b8b494d98 100644 --- a/examples/functional/Cargo.toml +++ b/examples/functional/Cargo.toml @@ -1,20 +1,8 @@ [package] name = "futures-example-functional" edition = "2018" -version = "0.3.0" -authors = ["Alex Crichton "] -license = "MIT OR Apache-2.0" -readme = "../README.md" -keywords = ["futures", "async", "future"] -repository = "https://github.com/rust-lang/futures-rs" -homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" -description = """ -An implementation of futures and streams featuring zero allocations, -composability, and iterator-like interfaces. -""" -categories = ["asynchronous"] +version = "0.1.0" publish = false [dependencies] -futures = { path = "../../futures", version = "0.3.0", features = ["thread-pool"] } +futures = { path = "../../futures", features = ["thread-pool"] } diff --git a/examples/functional/src/main.rs b/examples/functional/src/main.rs index 3ce65de66a..2ed8b37c58 100644 --- a/examples/functional/src/main.rs +++ b/examples/functional/src/main.rs @@ -30,9 +30,7 @@ fn main() { // responsible for transmission pool.spawn_ok(fut_tx_result); - let fut_values = rx - .map(|v| v * 2) - .collect(); + let fut_values = rx.map(|v| v * 2).collect(); // Use the executor provided to this async block to wait for the // future to complete. @@ -40,9 +38,9 @@ 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); println!("Values={:?}", values); -} \ No newline at end of file +} diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml index b5d47c0076..3405451f00 100644 --- a/examples/imperative/Cargo.toml +++ b/examples/imperative/Cargo.toml @@ -1,20 +1,8 @@ [package] name = "futures-example-imperative" edition = "2018" -version = "0.3.0" -authors = ["Alex Crichton "] -license = "MIT OR Apache-2.0" -readme = "../README.md" -keywords = ["futures", "async", "future"] -repository = "https://github.com/rust-lang/futures-rs" -homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" -description = """ -An implementation of futures and streams featuring zero allocations, -composability, and iterator-like interfaces. -""" -categories = ["asynchronous"] +version = "0.1.0" publish = false [dependencies] -futures = { path = "../../futures", version = "0.3.0", features = ["thread-pool"] } +futures = { path = "../../futures", features = ["thread-pool"] } diff --git a/examples/imperative/src/main.rs b/examples/imperative/src/main.rs index ff1afffe27..44f4153cd9 100644 --- a/examples/imperative/src/main.rs +++ b/examples/imperative/src/main.rs @@ -34,15 +34,15 @@ fn main() { // of the stream to be available. while let Some(v) = rx.next().await { pending.push(v * 2); - }; + } pending }; // 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); println!("Values={:?}", values); -} \ No newline at end of file +} diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml index a15c98abca..f356eabd98 100644 --- a/futures-channel/Cargo.toml +++ b/futures-channel/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-channel" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-channel/0.3.0" description = """ Channels for asynchronous communication using futures-rs. """ @@ -17,19 +16,19 @@ std = ["alloc", "futures-core/std"] alloc = ["futures-core/alloc"] sink = ["futures-sink"] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. -unstable = ["futures-core/unstable"] -cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic"] +# These features are no longer used. +# TODO: remove in the next major version. +unstable = [] +cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false, optional = true } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } [dev-dependencies] -futures = { path = "../futures", version = "0.3.4", default-features = true } -futures-test = { path = "../futures-test", version = "0.3.4", default-features = true } +futures = { path = "../futures", default-features = true } +futures-test = { path = "../futures-test", default-features = true } [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures-channel/README.md b/futures-channel/README.md new file mode 100644 index 0000000000..3287be924c --- /dev/null +++ b/futures-channel/README.md @@ -0,0 +1,23 @@ +# futures-channel + +Channels for asynchronous communication using futures-rs. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-channel = "0.3" +``` + +The current `futures-channel` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-channel/benches/sync_mpsc.rs b/futures-channel/benches/sync_mpsc.rs index e22fe60666..7c3c3d3a80 100644 --- a/futures-channel/benches/sync_mpsc.rs +++ b/futures-channel/benches/sync_mpsc.rs @@ -7,8 +7,8 @@ use { futures::{ channel::mpsc::{self, Sender, UnboundedSender}, ready, - stream::{Stream, StreamExt}, sink::Sink, + stream::{Stream, StreamExt}, task::{Context, Poll}, }, futures_test::task::noop_context, @@ -25,7 +25,6 @@ fn unbounded_1_tx(b: &mut Bencher) { // 1000 iterations to avoid measuring overhead of initialization // Result should be divided by 1000 for i in 0..1000 { - // Poll, not ready, park assert_eq!(Poll::Pending, rx.poll_next_unpin(&mut cx)); @@ -73,7 +72,6 @@ fn unbounded_uncontended(b: &mut Bencher) { }) } - /// A Stream that continuously sends incrementing number of the queue struct TestSender { tx: Sender, @@ -84,9 +82,7 @@ struct TestSender { impl Stream for TestSender { type Item = u32; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = &mut *self; let mut tx = Pin::new(&mut this.tx); @@ -123,12 +119,7 @@ fn bounded_100_tx(b: &mut Bencher) { // Each sender can send one item after specified capacity let (tx, mut rx) = mpsc::channel(0); - let mut tx: Vec<_> = (0..100).map(|_| { - TestSender { - tx: tx.clone(), - last: 0 - } - }).collect(); + let mut tx: Vec<_> = (0..100).map(|_| TestSender { tx: tx.clone(), last: 0 }).collect(); for i in 0..10 { for x in &mut tx { diff --git a/futures-channel/build.rs b/futures-channel/build.rs new file mode 100644 index 0000000000..05e0496d94 --- /dev/null +++ b/futures-channel/build.rs @@ -0,0 +1,41 @@ +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does *not* support atomic CAS operations. +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-channel/no_atomic_cas.rs b/futures-channel/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-channel/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-channel/src/lib.rs b/futures-channel/src/lib.rs index d33c919b75..4cd936d552 100644 --- a/futures-channel/src/lib.rs +++ b/futures-channel/src/lib.rs @@ -1,42 +1,42 @@ //! Asynchronous channels. //! -//! This crate provides channels that can be used to communicate between -//! asynchronous tasks. +//! Like threads, concurrent tasks sometimes need to communicate with each +//! other. This module contains two basic abstractions for doing so: //! -//! All items of this library are only available when the `std` or `alloc` feature of this +//! - [oneshot], a way of sending a single value from one task to another. +//! - [mpsc], a multi-producer, single-consumer channel for sending values +//! between tasks, analogous to the similarly-named structure in the standard +//! library. +//! +//! All items are only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] - -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] - -#![doc(html_root_url = "https://docs.rs/futures-channel/0.3.0")] - -#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] -compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); - -macro_rules! cfg_target_has_atomic { - ($($item:item)*) => {$( - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - $item - )*}; -} - -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; -} +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + single_use_lifetimes, + unreachable_pub +)] +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_assignments, unused_variables) + ) +))] + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +extern crate alloc; + +#[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/lock.rs b/futures-channel/src/lock.rs index e477369d53..b328d0f7dd 100644 --- a/futures-channel/src/lock.rs +++ b/futures-channel/src/lock.rs @@ -6,8 +6,8 @@ use core::cell::UnsafeCell; use core::ops::{Deref, DerefMut}; -use core::sync::atomic::Ordering::SeqCst; use core::sync::atomic::AtomicBool; +use core::sync::atomic::Ordering::SeqCst; /// A "mutex" around a value, similar to `std::sync::Mutex`. /// @@ -36,11 +36,8 @@ unsafe impl Sync for Lock {} impl Lock { /// Creates a new lock around the given value. - pub(crate) fn new(t: T) -> Lock { - Lock { - locked: AtomicBool::new(false), - data: UnsafeCell::new(t), - } + pub(crate) fn new(t: T) -> Self { + Self { locked: AtomicBool::new(false), data: UnsafeCell::new(t) } } /// Attempts to acquire this lock, returning whether the lock was acquired or diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs index f69f440439..44834b7c95 100644 --- a/futures-channel/src/mpsc/mod.rs +++ b/futures-channel/src/mpsc/mod.rs @@ -79,13 +79,14 @@ // by the queue structure. use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll, Waker}; use futures_core::task::__internal::AtomicWaker; +use futures_core::task::{Context, Poll, Waker}; use std::fmt; use std::pin::Pin; -use std::sync::{Arc, Mutex}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; +use std::sync::{Arc, Mutex}; +use std::thread; use crate::mpsc::queue::Queue; @@ -109,7 +110,7 @@ struct BoundedSenderInner { // unblocked. sender_task: Arc>, - // True if the sender might be blocked. This is an optimization to avoid + // `true` if the sender might be blocked. This is an optimization to avoid // having to lock the mutex most of the time. maybe_parked: bool, } @@ -189,7 +190,7 @@ impl fmt::Display for SendError { impl std::error::Error for SendError {} impl SendError { - /// Returns true if this error is a result of the channel being full. + /// Returns `true` if this error is a result of the channel being full. pub fn is_full(&self) -> bool { match self.kind { SendErrorKind::Full => true, @@ -197,7 +198,7 @@ impl SendError { } } - /// Returns true if this error is a result of the receiver being dropped. + /// Returns `true` if this error is a result of the receiver being dropped. pub fn is_disconnected(&self) -> bool { match self.kind { SendErrorKind::Disconnected => true, @@ -208,9 +209,7 @@ impl SendError { impl fmt::Debug for TrySendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TrySendError") - .field("kind", &self.err.kind) - .finish() + f.debug_struct("TrySendError").field("kind", &self.err.kind).finish() } } @@ -227,12 +226,12 @@ impl fmt::Display for TrySendError { impl std::error::Error for TrySendError {} impl TrySendError { - /// Returns true if this error is a result of the channel being full. + /// Returns `true` if this error is a result of the channel being full. pub fn is_full(&self) -> bool { self.err.is_full() } - /// Returns true if this error is a result of the receiver being dropped. + /// Returns `true` if this error is a result of the receiver being dropped. pub fn is_disconnected(&self) -> bool { self.err.is_disconnected() } @@ -250,8 +249,7 @@ impl TrySendError { impl fmt::Debug for TryRecvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TryRecvError") - .finish() + f.debug_tuple("TryRecvError").finish() } } @@ -334,10 +332,7 @@ struct SenderTask { impl SenderTask { fn new() -> Self { - SenderTask { - task: None, - is_parked: false, - } + Self { task: None, is_parked: false } } fn notify(&mut self) { @@ -380,9 +375,7 @@ pub fn channel(buffer: usize) -> (Sender, Receiver) { maybe_parked: false, }; - let rx = Receiver { - inner: Some(inner), - }; + let rx = Receiver { inner: Some(inner) }; (Sender(Some(tx)), rx) } @@ -398,7 +391,6 @@ pub fn channel(buffer: usize) -> (Sender, Receiver) { /// the channel. Using an `unbounded` channel has the ability of causing the /// process to run out of memory. In this case, the process will be aborted. pub fn unbounded() -> (UnboundedSender, UnboundedReceiver) { - let inner = Arc::new(UnboundedInner { state: AtomicUsize::new(INIT_STATE), message_queue: Queue::new(), @@ -406,13 +398,9 @@ pub fn unbounded() -> (UnboundedSender, UnboundedReceiver) { recv_task: AtomicWaker::new(), }); - let tx = UnboundedSenderInner { - inner: inner.clone(), - }; + let tx = UnboundedSenderInner { inner: inner.clone() }; - let rx = UnboundedReceiver { - inner: Some(inner), - }; + let rx = UnboundedReceiver { inner: Some(inner) }; (UnboundedSender(Some(tx)), rx) } @@ -429,13 +417,10 @@ impl UnboundedSenderInner { if state.is_open { Poll::Ready(Ok(())) } else { - Poll::Ready(Err(SendError { - kind: SendErrorKind::Disconnected, - })) + Poll::Ready(Err(SendError { kind: SendErrorKind::Disconnected })) } } - // Push message to the queue and signal to the receiver fn queue_push_and_signal(&self, msg: T) { // Push the message onto the message queue @@ -461,16 +446,17 @@ impl UnboundedSenderInner { // This probably is never hit? Odds are the process will run out of // memory first. It may be worth to return something else in this // case? - assert!(state.num_messages < MAX_CAPACITY, "buffer space \ - exhausted; sending this messages would overflow the state"); + assert!( + state.num_messages < MAX_CAPACITY, + "buffer space \ + exhausted; sending this messages would overflow the state" + ); state.num_messages += 1; let next = encode_state(&state); match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) { - Ok(_) => { - return Some(state.num_messages) - } + Ok(_) => return Some(state.num_messages), Err(actual) => curr = actual, } } @@ -481,6 +467,11 @@ impl UnboundedSenderInner { Arc::ptr_eq(&self.inner, &other.inner) } + /// Returns whether the sender send to this receiver. + fn is_connected_to(&self, inner: &Arc>) -> bool { + Arc::ptr_eq(&self.inner, inner) + } + /// Returns pointer to the Arc containing sender /// /// The returned pointer is not referenced and should be only used for hashing! @@ -510,12 +501,7 @@ impl BoundedSenderInner { fn try_send(&mut self, msg: T) -> Result<(), TrySendError> { // If the sender is currently blocked, reject the message if !self.poll_unparked(None).is_ready() { - return Err(TrySendError { - err: SendError { - kind: SendErrorKind::Full, - }, - val: msg, - }); + return Err(TrySendError { err: SendError { kind: SendErrorKind::Full }, val: msg }); } // The channel has capacity to accept the message, so send it @@ -524,11 +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, + fn do_send_b(&mut self, msg: T) -> Result<(), TrySendError> { + // 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()); @@ -536,7 +519,7 @@ impl BoundedSenderInner { // This operation will also atomically determine if the sender task // should be parked. // - // None is returned in the case that the channel has been closed by the + // `None` is returned in the case that the channel has been closed by the // receiver. This happens when `Receiver::close` is called or the // receiver is dropped. let park_self = match self.inc_num_messages() { @@ -545,12 +528,12 @@ impl BoundedSenderInner { // the configured buffer size num_messages > self.inner.buffer } - None => return Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }), + None => { + return Err(TrySendError { + err: SendError { kind: SendErrorKind::Disconnected }, + val: msg, + }) + } }; // If the channel has reached capacity, then the sender task needs to @@ -594,16 +577,17 @@ impl BoundedSenderInner { // This probably is never hit? Odds are the process will run out of // memory first. It may be worth to return something else in this // case? - assert!(state.num_messages < MAX_CAPACITY, "buffer space \ - exhausted; sending this messages would overflow the state"); + assert!( + state.num_messages < MAX_CAPACITY, + "buffer space \ + exhausted; sending this messages would overflow the state" + ); state.num_messages += 1; let next = encode_state(&state); match self.inner.state.compare_exchange(curr, next, SeqCst, SeqCst) { - Ok(_) => { - return Some(state.num_messages) - } + Ok(_) => return Some(state.num_messages), Err(actual) => curr = actual, } } @@ -638,15 +622,10 @@ impl BoundedSenderInner { /// capacity, in which case the current task is queued to be notified once /// capacity is available; /// - `Poll::Ready(Err(SendError))` if the receiver has been dropped. - fn poll_ready( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let state = decode_state(self.inner.state.load(SeqCst)); if !state.is_open { - return Poll::Ready(Err(SendError { - kind: SendErrorKind::Disconnected, - })); + return Poll::Ready(Err(SendError { kind: SendErrorKind::Disconnected })); } self.poll_unparked(Some(cx)).map(Ok) @@ -657,6 +636,11 @@ impl BoundedSenderInner { Arc::ptr_eq(&self.inner, &other.inner) } + /// Returns whether the sender send to this receiver. + fn is_connected_to(&self, receiver: &Arc>) -> bool { + Arc::ptr_eq(&self.inner, receiver) + } + /// Returns pointer to the Arc containing sender /// /// The returned pointer is not referenced and should be only used for hashing! @@ -688,7 +672,7 @@ impl BoundedSenderInner { if !task.is_parked { self.maybe_parked = false; - return Poll::Ready(()) + return Poll::Ready(()); } // At this point, an unpark request is pending, so there will be an @@ -713,12 +697,7 @@ impl Sender { if let Some(inner) = &mut self.0 { inner.try_send(msg) } else { - Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }) + Err(TrySendError { err: SendError { kind: SendErrorKind::Disconnected }, val: msg }) } } @@ -728,8 +707,7 @@ impl Sender { /// [`poll_ready`](Sender::poll_ready) has reported that the channel is /// ready to receive a message. pub fn start_send(&mut self, msg: T) -> Result<(), SendError> { - self.try_send(msg) - .map_err(|e| e.err) + self.try_send(msg).map_err(|e| e.err) } /// Polls the channel to determine if there is guaranteed capacity to send @@ -744,13 +722,8 @@ impl Sender { /// capacity, in which case the current task is queued to be notified once /// capacity is available; /// - `Poll::Ready(Err(SendError))` if the receiver has been dropped. - pub fn poll_ready( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { - let inner = self.0.as_mut().ok_or(SendError { - kind: SendErrorKind::Disconnected, - })?; + pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let inner = self.0.as_mut().ok_or(SendError { kind: SendErrorKind::Disconnected })?; inner.poll_ready(cx) } @@ -779,8 +752,19 @@ impl Sender { } } + /// Returns whether the sender send to this receiver. + pub fn is_connected_to(&self, receiver: &Receiver) -> bool { + match (&self.0, &receiver.inner) { + (Some(inner), Some(receiver)) => inner.is_connected_to(receiver), + _ => false, + } + } + /// Hashes the receiver into the provided hasher - pub fn hash_receiver(&self, hasher: &mut H) where H: std::hash::Hasher { + pub fn hash_receiver(&self, hasher: &mut H) + where + H: std::hash::Hasher, + { use std::hash::Hash; let ptr = self.0.as_ref().map(|inner| inner.ptr()); @@ -790,13 +774,8 @@ impl Sender { impl UnboundedSender { /// Check if the channel is ready to receive a message. - pub fn poll_ready( - &self, - _: &mut Context<'_>, - ) -> Poll> { - let inner = self.0.as_ref().ok_or(SendError { - kind: SendErrorKind::Disconnected, - })?; + pub fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { + let inner = self.0.as_ref().ok_or(SendError { kind: SendErrorKind::Disconnected })?; inner.poll_ready_nb() } @@ -826,12 +805,7 @@ impl UnboundedSender { } } - Err(TrySendError { - err: SendError { - kind: SendErrorKind::Disconnected, - }, - val: msg, - }) + Err(TrySendError { err: SendError { kind: SendErrorKind::Disconnected }, val: msg }) } /// Send a message on the channel. @@ -839,8 +813,7 @@ impl UnboundedSender { /// This method should only be called after `poll_ready` has been used to /// verify that the channel is ready to receive a message. pub fn start_send(&mut self, msg: T) -> Result<(), SendError> { - self.do_send_nb(msg) - .map_err(|e| e.err) + self.do_send_nb(msg).map_err(|e| e.err) } /// Sends a message along this channel. @@ -860,8 +833,19 @@ impl UnboundedSender { } } + /// Returns whether the sender send to this receiver. + pub fn is_connected_to(&self, receiver: &UnboundedReceiver) -> bool { + match (&self.0, &receiver.inner) { + (Some(inner), Some(receiver)) => inner.is_connected_to(receiver), + _ => false, + } + } + /// Hashes the receiver into the provided hasher - pub fn hash_receiver(&self, hasher: &mut H) where H: std::hash::Hasher { + pub fn hash_receiver(&self, hasher: &mut H) + where + H: std::hash::Hasher, + { use std::hash::Hash; let ptr = self.0.as_ref().map(|inner| inner.ptr()); @@ -870,19 +854,19 @@ impl UnboundedSender { } impl Clone for Sender { - fn clone(&self) -> Sender { - Sender(self.0.clone()) + fn clone(&self) -> Self { + Self(self.0.clone()) } } impl Clone for UnboundedSender { - fn clone(&self) -> UnboundedSender { - UnboundedSender(self.0.clone()) + fn clone(&self) -> Self { + Self(self.0.clone()) } } impl Clone for UnboundedSenderInner { - fn clone(&self) -> UnboundedSenderInner { + fn clone(&self) -> Self { // Since this atomic op isn't actually guarding any memory and we don't // care about any orderings besides the ordering on the single atomic // variable, a relaxed ordering is acceptable. @@ -897,23 +881,20 @@ impl Clone for UnboundedSenderInner { debug_assert!(curr < MAX_BUFFER); let next = curr + 1; - let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst); - - // The ABA problem doesn't matter here. We only care that the - // number of senders never exceeds the maximum. - if actual == curr { - return UnboundedSenderInner { - inner: self.inner.clone(), - }; + match self.inner.num_senders.compare_exchange(curr, next, SeqCst, SeqCst) { + Ok(_) => { + // The ABA problem doesn't matter here. We only care that the + // number of senders never exceeds the maximum. + return Self { inner: self.inner.clone() }; + } + Err(actual) => curr = actual, } - - curr = actual; } } } impl Clone for BoundedSenderInner { - fn clone(&self) -> BoundedSenderInner { + fn clone(&self) -> Self { // Since this atomic op isn't actually guarding any memory and we don't // care about any orderings besides the ordering on the single atomic // variable, a relaxed ordering is acceptable. @@ -928,19 +909,18 @@ impl Clone for BoundedSenderInner { debug_assert!(curr < self.inner.max_senders()); let next = curr + 1; - let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst); - - // The ABA problem doesn't matter here. We only care that the - // number of senders never exceeds the maximum. - if actual == curr { - return BoundedSenderInner { - inner: self.inner.clone(), - sender_task: Arc::new(Mutex::new(SenderTask::new())), - maybe_parked: false, - }; + match self.inner.num_senders.compare_exchange(curr, next, SeqCst, SeqCst) { + Ok(_) => { + // The ABA problem doesn't matter here. We only care that the + // number of senders never exceeds the maximum. + return Self { + inner: self.inner.clone(), + sender_task: Arc::new(Mutex::new(SenderTask::new())), + maybe_parked: false, + }; + } + Err(actual) => curr = actual, } - - curr = actual; } } } @@ -996,19 +976,22 @@ impl Receiver { /// only when you've otherwise arranged to be notified when the channel is /// no longer empty. /// - /// This function will panic if called after `try_next` or `poll_next` has - /// returned None. + /// This function returns: + /// * `Ok(Some(t))` when message is fetched + /// * `Ok(None)` when channel is closed and no messages left in the queue + /// * `Err(e)` when there are no messages available, but channel is not yet closed pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { - Poll::Ready(msg) => { - Ok(msg) - }, + Poll::Ready(msg) => Ok(msg), Poll::Pending => Err(TryRecvError { _priv: () }), } } fn next_message(&mut self) -> Poll> { - let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`"); + let inner = match self.inner.as_mut() { + None => return Poll::Ready(None), + Some(inner) => inner, + }; // Pop off a message match unsafe { inner.message_queue.pop_spin() } { Some(msg) => { @@ -1023,7 +1006,12 @@ impl Receiver { } None => { let state = decode_state(inner.state.load(SeqCst)); - if state.is_open || state.num_messages != 0 { + if state.is_closed() { + // If closed flag is set AND there are no pending messages + // it means end of stream + self.inner = None; + Poll::Ready(None) + } else { // If queue is open, we need to return Pending // to be woken up when new messages arrive. // If queue is closed but num_messages is non-zero, @@ -1032,11 +1020,6 @@ impl Receiver { // so we need to park until sender unparks the task // after queueing the message. Poll::Pending - } else { - // If closed flag is set AND there are no pending messages - // it means end of stream - self.inner = None; - Poll::Ready(None) } } } @@ -1073,18 +1056,15 @@ impl FusedStream for Receiver { impl Stream for Receiver { type Item = T; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - // Try to read a message off of the message queue. + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Try to read a message off of the message queue. match self.next_message() { Poll::Ready(msg) => { if msg.is_none() { self.inner = None; } Poll::Ready(msg) - }, + } Poll::Pending => { // There are no messages to read, in this case, park. self.inner.as_ref().unwrap().recv_task.register(cx.waker()); @@ -1102,8 +1082,26 @@ impl Drop for Receiver { // Drain the channel of all pending messages self.close(); if self.inner.is_some() { - while let Poll::Ready(Some(..)) = self.next_message() { - // ... + loop { + match self.next_message() { + Poll::Ready(Some(_)) => {} + Poll::Ready(None) => break, + Poll::Pending => { + let state = decode_state(self.inner.as_ref().unwrap().state.load(SeqCst)); + + // If the channel is closed, then there is no need to park. + if state.is_closed() { + break; + } + + // TODO: Spinning isn't ideal, it might be worth + // investigating using a condvar or some other strategy + // here. That said, if this case is hit, then another thread + // is about to push the value into the queue and this isn't + // the only spinlock in the impl right now. + thread::yield_now(); + } + } } } } @@ -1126,19 +1124,22 @@ impl UnboundedReceiver { /// only when you've otherwise arranged to be notified when the channel is /// no longer empty. /// - /// This function will panic if called after `try_next` or `poll_next` has - /// returned None. + /// This function returns: + /// * `Ok(Some(t))` when message is fetched + /// * `Ok(None)` when channel is closed and no messages left in the queue + /// * `Err(e)` when there are no messages available, but channel is not yet closed pub fn try_next(&mut self) -> Result, TryRecvError> { match self.next_message() { - Poll::Ready(msg) => { - Ok(msg) - }, + Poll::Ready(msg) => Ok(msg), Poll::Pending => Err(TryRecvError { _priv: () }), } } fn next_message(&mut self) -> Poll> { - let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`"); + let inner = match self.inner.as_mut() { + None => return Poll::Ready(None), + Some(inner) => inner, + }; // Pop off a message match unsafe { inner.message_queue.pop_spin() } { Some(msg) => { @@ -1149,7 +1150,12 @@ impl UnboundedReceiver { } None => { let state = decode_state(inner.state.load(SeqCst)); - if state.is_open || state.num_messages != 0 { + if state.is_closed() { + // If closed flag is set AND there are no pending messages + // it means end of stream + self.inner = None; + Poll::Ready(None) + } else { // If queue is open, we need to return Pending // to be woken up when new messages arrive. // If queue is closed but num_messages is non-zero, @@ -1158,11 +1164,6 @@ impl UnboundedReceiver { // so we need to park until sender unparks the task // after queueing the message. Poll::Pending - } else { - // If closed flag is set AND there are no pending messages - // it means end of stream - self.inner = None; - Poll::Ready(None) } } } @@ -1187,10 +1188,7 @@ impl FusedStream for UnboundedReceiver { impl Stream for UnboundedReceiver { type Item = T; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Try to read a message off of the message queue. match self.next_message() { Poll::Ready(msg) => { @@ -1198,7 +1196,7 @@ impl Stream for UnboundedReceiver { self.inner = None; } Poll::Ready(msg) - }, + } Poll::Pending => { // There are no messages to read, in this case, park. self.inner.as_ref().unwrap().recv_task.register(cx.waker()); @@ -1216,8 +1214,26 @@ impl Drop for UnboundedReceiver { // Drain the channel of all pending messages self.close(); if self.inner.is_some() { - while let Poll::Ready(Some(..)) = self.next_message() { - // ... + loop { + match self.next_message() { + Poll::Ready(Some(_)) => {} + Poll::Ready(None) => break, + Poll::Pending => { + let state = decode_state(self.inner.as_ref().unwrap().state.load(SeqCst)); + + // If the channel is closed, then there is no need to park. + if state.is_closed() { + break; + } + + // TODO: Spinning isn't ideal, it might be worth + // investigating using a condvar or some other strategy + // here. That said, if this case is hit, then another thread + // is about to push the value into the queue and this isn't + // the only spinlock in the impl right now. + thread::yield_now(); + } + } } } } @@ -1265,6 +1281,12 @@ unsafe impl Sync for UnboundedInner {} unsafe impl Send for BoundedInner {} unsafe impl Sync for BoundedInner {} +impl State { + fn is_closed(&self) -> bool { + !self.is_open && self.num_messages == 0 + } +} + /* * * ===== Helpers ===== @@ -1272,10 +1294,7 @@ unsafe impl Sync for BoundedInner {} */ fn decode_state(num: usize) -> State { - State { - is_open: num & OPEN_MASK == OPEN_MASK, - num_messages: num & MAX_CAPACITY, - } + State { is_open: num & OPEN_MASK == OPEN_MASK, num_messages: num & MAX_CAPACITY } } fn encode_state(state: &State) -> usize { diff --git a/futures-channel/src/mpsc/queue.rs b/futures-channel/src/mpsc/queue.rs index 353e75e9a1..57dc7f5654 100644 --- a/futures-channel/src/mpsc/queue.rs +++ b/futures-channel/src/mpsc/queue.rs @@ -43,10 +43,10 @@ pub(super) use self::PopResult::*; -use std::thread; use std::cell::UnsafeCell; use std::ptr; use std::sync::atomic::{AtomicPtr, Ordering}; +use std::thread; /// A result of the `pop` function. pub(super) enum PopResult { @@ -63,7 +63,7 @@ pub(super) enum PopResult { #[derive(Debug)] struct Node { - next: AtomicPtr>, + next: AtomicPtr, value: Option, } @@ -76,27 +76,21 @@ pub(super) struct Queue { tail: UnsafeCell<*mut Node>, } -unsafe impl Send for Queue { } -unsafe impl Sync for Queue { } +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} impl Node { - unsafe fn new(v: Option) -> *mut Node { - Box::into_raw(Box::new(Node { - next: AtomicPtr::new(ptr::null_mut()), - value: v, - })) + unsafe fn new(v: Option) -> *mut Self { + Box::into_raw(Box::new(Self { next: AtomicPtr::new(ptr::null_mut()), value: v })) } } impl Queue { /// Creates a new queue that is safe to share among multiple producers and /// one consumer. - pub(super) fn new() -> Queue { + pub(super) fn new() -> Self { let stub = unsafe { Node::new(None) }; - Queue { - head: AtomicPtr::new(stub), - tail: UnsafeCell::new(stub), - } + Self { head: AtomicPtr::new(stub), tail: UnsafeCell::new(stub) } } /// Pushes a new value onto this queue. @@ -133,7 +127,11 @@ impl Queue { return Data(ret); } - if self.head.load(Ordering::Acquire) == tail {Empty} else {Inconsistent} + if self.head.load(Ordering::Acquire) == tail { + Empty + } else { + Inconsistent + } } /// Pop an element similarly to `pop` function, but spin-wait on inconsistent diff --git a/futures-channel/src/mpsc/sink_impl.rs b/futures-channel/src/mpsc/sink_impl.rs index e7f5457338..1be20162c2 100644 --- a/futures-channel/src/mpsc/sink_impl.rs +++ b/futures-channel/src/mpsc/sink_impl.rs @@ -6,24 +6,15 @@ use std::pin::Pin; impl Sink for Sender { type Error = SendError; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { (*self).poll_ready(cx) } - fn start_send( - mut self: Pin<&mut Self>, - msg: T, - ) -> Result<(), Self::Error> { + fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { (*self).start_send(msg) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match (*self).poll_ready(cx) { Poll::Ready(Err(ref e)) if e.is_disconnected() => { // If the receiver disconnected, we consider the sink to be flushed. @@ -33,10 +24,7 @@ impl Sink for Sender { } } - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.disconnect(); Poll::Ready(Ok(())) } @@ -45,31 +33,19 @@ impl Sink for Sender { impl Sink for UnboundedSender { type Error = SendError; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - UnboundedSender::poll_ready(&*self, cx) + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Self::poll_ready(&*self, cx) } - fn start_send( - mut self: Pin<&mut Self>, - msg: T, - ) -> Result<(), Self::Error> { - UnboundedSender::start_send(&mut *self, msg) + fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { + Self::start_send(&mut *self, msg) } - fn poll_flush( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.disconnect(); Poll::Ready(Ok(())) } @@ -78,29 +54,19 @@ impl Sink for UnboundedSender { impl Sink for &UnboundedSender { type Error = SendError; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { UnboundedSender::poll_ready(*self, cx) } fn start_send(self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> { - self.unbounded_send(msg) - .map_err(TrySendError::into_send_error) + self.unbounded_send(msg).map_err(TrySendError::into_send_error) } - fn poll_flush( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn poll_close( - self: Pin<&mut Self>, - _: &mut Context<'_>, - ) -> Poll> { + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.close_channel(); Poll::Ready(Ok(())) } diff --git a/futures-channel/src/oneshot.rs b/futures-channel/src/oneshot.rs index d3be67e71e..5af651b913 100644 --- a/futures-channel/src/oneshot.rs +++ b/futures-channel/src/oneshot.rs @@ -1,28 +1,28 @@ //! A channel for sending a single message between asynchronous tasks. +//! +//! This is a single-producer, single-consumer channel. use alloc::sync::Arc; use core::fmt; use core::pin::Pin; use core::sync::atomic::AtomicBool; use core::sync::atomic::Ordering::SeqCst; -use futures_core::future::Future; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use crate::lock::Lock; /// A future for a value that will be provided by another asynchronous task. /// -/// This is created by the [`channel`] function. +/// This is created by the [`channel`](channel) function. #[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] pub struct Receiver { inner: Arc>, } /// A means of transmitting a single value to another task. /// -/// This is created by the [`channel`] function. -#[derive(Debug)] +/// This is created by the [`channel`](channel) function. pub struct Sender { inner: Arc>, } @@ -33,7 +33,6 @@ impl Unpin for Sender {} /// Internal state of the `Receiver`/`Sender` pair above. This is all used as /// the internal synchronization between the two for send/recv operations. -#[derive(Debug)] struct Inner { /// Indicates whether this oneshot is complete yet. This is filled in both /// by `Sender::drop` and by `Receiver::drop`, and both sides interpret it @@ -68,7 +67,9 @@ struct Inner { tx_task: Lock>, } -/// Creates a new one-shot channel for sending values across asynchronous tasks. +/// Creates a new one-shot channel for sending a single value across asynchronous tasks. +/// +/// The channel works for a spsc (single-producer, single-consumer) scheme. /// /// This function is similar to Rust's channel constructor found in the standard /// library. Two halves are returned, the first of which is a `Sender` handle, @@ -82,37 +83,34 @@ struct Inner { /// /// ``` /// use futures::channel::oneshot; -/// use futures::future::FutureExt; -/// use std::thread; +/// use std::{thread, time::Duration}; /// /// let (sender, receiver) = oneshot::channel::(); /// -/// # let t = /// thread::spawn(|| { -/// let future = receiver.map(|i| { -/// println!("got: {:?}", i); -/// }); -/// // ... -/// # return future; +/// println!("THREAD: sleeping zzz..."); +/// thread::sleep(Duration::from_millis(1000)); +/// println!("THREAD: i'm awake! sending."); +/// sender.send(3).unwrap(); /// }); /// -/// sender.send(3).unwrap(); -/// # futures::executor::block_on(t.join().unwrap()); +/// println!("MAIN: doing some useful stuff"); +/// +/// futures::executor::block_on(async { +/// println!("MAIN: waiting for msg..."); +/// println!("MAIN: got: {:?}", receiver.await) +/// }); /// ``` pub fn channel() -> (Sender, Receiver) { let inner = Arc::new(Inner::new()); - let receiver = Receiver { - inner: inner.clone(), - }; - let sender = Sender { - inner, - }; + let receiver = Receiver { inner: inner.clone() }; + let sender = Sender { inner }; (sender, receiver) } impl Inner { - fn new() -> Inner { - Inner { + fn new() -> Self { + Self { complete: AtomicBool::new(false), data: Lock::new(None), rx_task: Lock::new(None), @@ -122,11 +120,11 @@ impl Inner { fn send(&self, t: T) -> Result<(), T> { if self.complete.load(SeqCst) { - return Err(t) + return Err(t); } // Note that this lock acquisition may fail if the receiver - // is closed and sets the `complete` flag to true, whereupon + // is closed and sets the `complete` flag to `true`, whereupon // the receiver may call `poll()`. if let Some(mut slot) = self.data.try_lock() { assert!(slot.is_none()); @@ -159,7 +157,7 @@ impl Inner { // destructor, but our destructor hasn't run yet so if it's set then the // oneshot is gone. if self.complete.load(SeqCst) { - return Poll::Ready(()) + return Poll::Ready(()); } // If our other half is not gone then we need to park our current task @@ -268,7 +266,10 @@ impl Inner { } else { let task = cx.waker().clone(); match self.rx_task.try_lock() { - Some(mut slot) => { *slot = Some(task); false }, + Some(mut slot) => { + *slot = Some(task); + false + } None => true, } }; @@ -336,14 +337,13 @@ impl Sender { /// /// If the value is successfully enqueued for the remote end to receive, /// then `Ok(())` is returned. If the receiving end was dropped before - /// this function was called, however, then `Err` is returned with the value - /// provided. + /// this function was called, however, then `Err(t)` is returned. pub fn send(self, t: T) -> Result<(), T> { self.inner.send(t) } /// Polls this `Sender` half to detect whether its associated - /// [`Receiver`](Receiver) with has been dropped. + /// [`Receiver`](Receiver) has been dropped. /// /// # Return values /// @@ -358,6 +358,15 @@ impl Sender { self.inner.poll_canceled(cx) } + /// Creates a future that resolves when this `Sender`'s corresponding + /// [`Receiver`](Receiver) half has hung up. + /// + /// This is a utility wrapping [`poll_canceled`](Sender::poll_canceled) + /// to expose a [`Future`](core::future::Future). + pub fn cancellation(&mut self) -> Cancellation<'_, T> { + Cancellation { inner: self } + } + /// Tests to see whether this `Sender`'s corresponding `Receiver` /// has been dropped. /// @@ -367,6 +376,12 @@ impl Sender { pub fn is_canceled(&self) -> bool { self.inner.is_canceled() } + + /// Tests to see whether this `Sender` is connected to the given `Receiver`. That is, whether + /// they were created by the same call to `channel`. + pub fn is_connected_to(&self, receiver: &Receiver) -> bool { + Arc::ptr_eq(&self.inner, &receiver.inner) + } } impl Drop for Sender { @@ -375,6 +390,29 @@ impl Drop for Sender { } } +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").field("complete", &self.inner.complete).finish() + } +} + +/// A future that resolves when the receiving end of a channel has hung up. +/// +/// This is an `.await`-friendly interface around [`poll_canceled`](Sender::poll_canceled). +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[derive(Debug)] +pub struct Cancellation<'a, T> { + inner: &'a mut Sender, +} + +impl Future for Cancellation<'_, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + self.inner.poll_canceled(cx) + } +} + /// Error returned from a [`Receiver`](Receiver) when the corresponding /// [`Sender`](Sender) is dropped. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -417,16 +455,34 @@ impl Receiver { impl Future for Receiver { type Output = Result; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.inner.recv(cx) } } +impl FusedFuture for Receiver { + fn is_terminated(&self) -> bool { + if self.inner.complete.load(SeqCst) { + if let Some(slot) = self.inner.data.try_lock() { + if slot.is_some() { + return false; + } + } + true + } else { + false + } + } +} + impl Drop for Receiver { fn drop(&mut self) { self.inner.drop_rx() } } + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Receiver").field("complete", &self.inner.complete).finish() + } +} diff --git a/futures-channel/tests/channel.rs b/futures-channel/tests/channel.rs index 73dac645b1..5f01a8ef4c 100644 --- a/futures-channel/tests/channel.rs +++ b/futures-channel/tests/channel.rs @@ -1,8 +1,8 @@ use futures::channel::mpsc; use futures::executor::block_on; use futures::future::poll_fn; -use futures::stream::StreamExt; use futures::sink::SinkExt; +use futures::stream::StreamExt; use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; @@ -11,9 +11,7 @@ fn sequence() { let (tx, rx) = mpsc::channel(1); let amt = 20; - let t = thread::spawn(move || { - block_on(send_sequence(amt, tx)) - }); + let t = thread::spawn(move || block_on(send_sequence(amt, tx))); let list: Vec<_> = block_on(rx.collect()); let mut list = list.into_iter(); for i in (1..=amt).rev() { @@ -34,9 +32,7 @@ async fn send_sequence(n: u32, mut sender: mpsc::Sender) { fn drop_sender() { let (tx, mut rx) = mpsc::channel::(1); drop(tx); - let f = poll_fn(|cx| { - rx.poll_next_unpin(cx) - }); + let f = poll_fn(|cx| rx.poll_next_unpin(cx)); assert_eq!(block_on(f), None) } diff --git a/futures-channel/tests/mpsc-close.rs b/futures-channel/tests/mpsc-close.rs index 50852eb71d..1a14067eca 100644 --- a/futures-channel/tests/mpsc-close.rs +++ b/futures-channel/tests/mpsc-close.rs @@ -1,17 +1,19 @@ use futures::channel::mpsc; use futures::executor::block_on; +use futures::future::Future; use futures::sink::SinkExt; use futures::stream::StreamExt; -use std::sync::Arc; +use futures::task::{Context, Poll}; +use std::pin::Pin; +use std::sync::{Arc, Weak}; use std::thread; +use std::time::{Duration, Instant}; #[test] fn smoke() { let (mut sender, receiver) = mpsc::channel(1); - let t = thread::spawn(move || { - while let Ok(()) = block_on(sender.send(42)) {} - }); + let t = thread::spawn(move || while let Ok(()) = block_on(sender.send(42)) {}); // `receiver` needs to be dropped for `sender` to stop sending and therefore before the join. block_on(receiver.take(3).for_each(|_| futures::future::ready(()))); @@ -142,3 +144,156 @@ fn single_receiver_drop_closes_channel_and_drains() { assert!(sender.is_closed()); } } + +// 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; + // To provide variable timing characteristics (in the hopes of + // reproducing the collision that leads to a race), we busy-re-poll + // the test MPSC receiver a variable number of times before actually + // stopping. We vary this countdown between 1 and the following + // value. + const MAX_COUNTDOWN: usize = 20; + // When we detect that a successfully sent item is still in the + // queue after a disconnect, we spin for up to 100ms to confirm that + // it is a persistent condition and not a concurrency illusion. + const SPIN_TIMEOUT_S: u64 = 10; + const SPIN_SLEEP_MS: u64 = 10; + struct TestRx { + rx: mpsc::Receiver>, + // The number of times to query `rx` before dropping it. + poll_count: usize, + } + struct TestTask { + command_rx: mpsc::Receiver, + test_rx: Option>>, + countdown: usize, + } + impl TestTask { + /// Create a new TestTask + fn new() -> (TestTask, mpsc::Sender) { + let (command_tx, command_rx) = mpsc::channel::(0); + ( + TestTask { + command_rx, + test_rx: None, + countdown: 0, // 0 means no countdown is in progress. + }, + command_tx, + ) + } + } + impl Future for TestTask { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Poll the test channel, if one is present. + if let Some(rx) = &mut self.test_rx { + if let Poll::Ready(v) = rx.poll_next_unpin(cx) { + let _ = v.expect("test finished unexpectedly!"); + } + self.countdown -= 1; + // Busy-poll until the countdown is finished. + cx.waker().wake_by_ref(); + } + // Accept any newly submitted MPSC channels for testing. + match self.command_rx.poll_next_unpin(cx) { + Poll::Ready(Some(TestRx { rx, poll_count })) => { + self.test_rx = Some(rx); + self.countdown = poll_count; + cx.waker().wake_by_ref(); + } + Poll::Ready(None) => return Poll::Ready(()), + Poll::Pending => {} + } + if self.countdown == 0 { + // Countdown complete -- drop the Receiver. + self.test_rx = None; + } + Poll::Pending + } + } + let (f, mut cmd_tx) = TestTask::new(); + let bg = thread::spawn(move || block_on(f)); + for i in 0..AMT { + let (mut test_tx, rx) = mpsc::channel(0); + let poll_count = i % MAX_COUNTDOWN; + cmd_tx.try_send(TestRx { rx, poll_count }).unwrap(); + let mut prev_weak: Option> = None; + let mut attempted_sends = 0; + let mut successful_sends = 0; + loop { + // Create a test item. + let item = Arc::new(()); + let weak = Arc::downgrade(&item); + match test_tx.try_send(item) { + Ok(_) => { + prev_weak = Some(weak); + successful_sends += 1; + } + Err(ref e) if e.is_full() => {} + Err(ref e) if e.is_disconnected() => { + // Test for evidence of the race condition. + if let Some(prev_weak) = prev_weak { + if prev_weak.upgrade().is_some() { + // The previously sent item is still allocated. + // However, there appears to be some aspect of the + // concurrency that can legitimately cause the Arc + // to be momentarily valid. Spin for up to 100ms + // waiting for the previously sent item to be + // dropped. + let t0 = Instant::now(); + let mut spins = 0; + loop { + if prev_weak.upgrade().is_none() { + break; + } + assert!( + t0.elapsed() < Duration::from_secs(SPIN_TIMEOUT_S), + "item not dropped on iteration {} after \ + {} sends ({} successful). spin=({})", + i, + attempted_sends, + successful_sends, + spins + ); + spins += 1; + thread::sleep(Duration::from_millis(SPIN_SLEEP_MS)); + } + } + } + break; + } + Err(ref e) => panic!("unexpected error: {}", e), + } + attempted_sends += 1; + } + } + drop(cmd_tx); + bg.join().expect("background thread join"); +} + +#[test] +fn unbounded_try_next_after_none() { + let (tx, mut rx) = mpsc::unbounded::(); + // Drop the sender, close the channel. + drop(tx); + // Receive the end of channel. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); + // None received, check we can call `try_next` again. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); +} + +#[test] +fn bounded_try_next_after_none() { + let (tx, mut rx) = mpsc::channel::(17); + // Drop the sender, close the channel. + drop(tx); + // Receive the end of channel. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); + // None received, check we can call `try_next` again. + assert_eq!(Ok(None), rx.try_next().map_err(|_| ())); +} diff --git a/futures-channel/tests/mpsc.rs b/futures-channel/tests/mpsc.rs index 409fa6e308..da0899d491 100644 --- a/futures-channel/tests/mpsc.rs +++ b/futures-channel/tests/mpsc.rs @@ -1,13 +1,13 @@ use futures::channel::{mpsc, oneshot}; use futures::executor::{block_on, block_on_stream}; -use futures::future::{FutureExt, poll_fn}; -use futures::stream::{Stream, StreamExt}; +use futures::future::{poll_fn, FutureExt}; +use futures::pin_mut; use futures::sink::{Sink, SinkExt}; +use futures::stream::{Stream, StreamExt}; use futures::task::{Context, Poll}; -use futures::pin_mut; use futures_test::task::{new_count_waker, noop_context}; -use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; use std::thread; trait AssertSend: Send {} @@ -77,7 +77,7 @@ fn send_shared_recv() { fn send_recv_threads() { let (mut tx, rx) = mpsc::channel::(16); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { block_on(tx.send(1)).unwrap(); }); @@ -200,11 +200,14 @@ 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::(); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { let result: Vec<_> = block_on(rx.collect()); assert_eq!(result.len(), (AMT * NTHREADS) as usize); for item in result { @@ -215,7 +218,7 @@ fn stress_shared_unbounded() { for _ in 0..NTHREADS { let tx = tx.clone(); - thread::spawn(move|| { + thread::spawn(move || { for _ in 0..AMT { tx.unbounded_send(1).unwrap(); } @@ -229,11 +232,14 @@ 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); - let t = thread::spawn(move|| { + let t = thread::spawn(move || { let result: Vec<_> = block_on(rx.collect()); assert_eq!(result.len(), (AMT * NTHREADS) as usize); for item in result { @@ -256,8 +262,12 @@ fn stress_shared_bounded_hard() { t.join().unwrap(); } +#[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; @@ -296,9 +306,9 @@ fn stress_receiver_multi_task_bounded_hard() { } Poll::Ready(None) => { *rx_opt = None; - break - }, - Poll::Pending => {}, + break; + } + Poll::Pending => {} } } } else { @@ -310,7 +320,6 @@ fn stress_receiver_multi_task_bounded_hard() { th.push(t); } - for i in 0..AMT { block_on(tx.send(i)).unwrap(); } @@ -327,7 +336,12 @@ fn stress_receiver_multi_task_bounded_hard() { /// after sender dropped. #[test] fn stress_drop_sender() { - fn list() -> impl Stream { + #[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 || { block_on(send_one_two_three(tx)); @@ -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(); } } @@ -394,8 +411,12 @@ async fn stress_poll_ready_sender(mut sender: mpsc::Sender, count: u32) { } /// Tests that after `poll_ready` indicates capacity a channel can always send without waiting. +#[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; @@ -405,9 +426,7 @@ fn stress_poll_ready() { let mut threads = Vec::new(); for _ in 0..NTHREADS { let sender = tx.clone(); - threads.push(thread::spawn(move || { - block_on(stress_poll_ready_sender(sender, AMT)) - })); + threads.push(thread::spawn(move || block_on(stress_poll_ready_sender(sender, AMT)))); } drop(tx); @@ -425,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; @@ -434,7 +454,7 @@ fn try_send_1() { for i in 0..N { loop { if tx.try_send(i).is_ok() { - break + break; } } } @@ -527,10 +547,21 @@ fn same_receiver() { assert!(txb1.same_receiver(&txb2)); } +#[test] +fn is_connected_to() { + let (txa, rxa) = mpsc::channel::(1); + let (txb, rxb) = mpsc::channel::(1); + + assert!(txa.is_connected_to(&rxa)); + assert!(txb.is_connected_to(&rxb)); + assert!(!txa.is_connected_to(&rxb)); + assert!(!txb.is_connected_to(&rxa)); +} + #[test] fn hash_receiver() { - use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; + use std::hash::Hasher; let mut hasher_a1 = DefaultHasher::new(); let mut hasher_a2 = DefaultHasher::new(); diff --git a/futures-channel/tests/oneshot.rs b/futures-channel/tests/oneshot.rs index 5194ce468f..c9f5508973 100644 --- a/futures-channel/tests/oneshot.rs +++ b/futures-channel/tests/oneshot.rs @@ -1,9 +1,8 @@ use futures::channel::oneshot::{self, Sender}; use futures::executor::block_on; -use futures::future::{Future, FutureExt, poll_fn}; +use futures::future::{poll_fn, FutureExt}; use futures::task::{Context, Poll}; use futures_test::task::panic_waker_ref; -use std::pin::Pin; use std::sync::mpsc; use std::thread; @@ -25,38 +24,31 @@ fn smoke_poll() { #[test] fn cancel_notifies() { - let (tx, rx) = oneshot::channel::(); + let (mut tx, rx) = oneshot::channel::(); - let t = thread::spawn(|| { - block_on(WaitForCancel { tx }); + let t = thread::spawn(move || { + block_on(tx.cancellation()); }); drop(rx); t.join().unwrap(); } -struct WaitForCancel { - tx: Sender, -} - -impl Future for WaitForCancel { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.tx.poll_canceled(cx) - } -} - #[test] fn cancel_lots() { + #[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 (tx, tx2) in rx { - block_on(WaitForCancel { tx }); + for (mut tx, tx2) in rx { + block_on(tx.cancellation()); tx2.send(()).unwrap(); } }); - for _ in 0..20000 { + for _ in 0..N { let (otx, orx) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); tx.send((otx, tx2)).unwrap(); @@ -83,7 +75,7 @@ fn close() { rx.close(); block_on(poll_fn(|cx| { match rx.poll_unpin(cx) { - Poll::Ready(Err(_)) => {}, + Poll::Ready(Err(_)) => {} _ => panic!(), }; assert!(tx.poll_canceled(cx).is_ready()); @@ -93,13 +85,13 @@ fn close() { #[test] fn close_wakes() { - let (tx, mut rx) = oneshot::channel::(); + let (mut tx, mut rx) = oneshot::channel::(); let (tx2, rx2) = mpsc::channel(); let t = thread::spawn(move || { rx.close(); rx2.recv().unwrap(); }); - block_on(WaitForCancel { tx }); + block_on(tx.cancellation()); tx2.send(()).unwrap(); t.join().unwrap(); } @@ -114,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 { @@ -121,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-core/Cargo.toml b/futures-core/Cargo.toml index f6afa0028f..4a360f8a42 100644 --- a/futures-core/Cargo.toml +++ b/futures-core/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-core" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.36" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-core/0.3.0" description = """ The core traits and types in for the `futures` library. """ @@ -16,16 +15,16 @@ default = ["std"] std = ["alloc"] alloc = [] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. +# These features are no longer used. +# TODO: remove in the next major version. unstable = [] cfg-target-has-atomic = [] [dependencies] [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures" } [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures-core/README.md b/futures-core/README.md new file mode 100644 index 0000000000..96e0e064bc --- /dev/null +++ b/futures-core/README.md @@ -0,0 +1,23 @@ +# futures-core + +The core traits and types in for the `futures` library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-core = "0.3" +``` + +The current `futures-core` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-core/build.rs b/futures-core/build.rs new file mode 100644 index 0000000000..05e0496d94 --- /dev/null +++ b/futures-core/build.rs @@ -0,0 +1,41 @@ +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does *not* support atomic CAS operations. +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-core/no_atomic_cas.rs b/futures-core/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-core/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-core/src/future.rs b/futures-core/src/future.rs index 35a0fd3baa..7540cd027e 100644 --- a/futures-core/src/future.rs +++ b/futures-core/src/future.rs @@ -4,6 +4,7 @@ use core::ops::DerefMut; use core::pin::Pin; use core::task::{Context, Poll}; +#[doc(no_inline)] pub use core::future::Future; /// An owned dynamically typed [`Future`] for use in cases where you can't @@ -66,28 +67,26 @@ pub trait TryFuture: Future + private_try_future::Sealed { /// This method is a stopgap for a compiler limitation that prevents us from /// directly inheriting from the `Future` trait; in the future it won't be /// needed. - fn try_poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>; + fn try_poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; } impl TryFuture for F - where F: ?Sized + Future> +where + F: ?Sized + Future>, { type Ok = T; type Error = E; #[inline] - fn try_poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn try_poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.poll(cx) } } #[cfg(feature = "alloc")] mod if_alloc { - use alloc::boxed::Box; use super::*; + use alloc::boxed::Box; impl FusedFuture for Box { fn is_terminated(&self) -> bool { diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 85eff3e843..9c31d8d90b 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,36 +1,27 @@ //! Core traits and types for asynchronous operations in Rust. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![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(html_root_url = "https://docs.rs/futures-core/0.3.0")] - -#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] -compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#![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; pub mod future; -#[doc(hidden)] pub use self::future::{Future, FusedFuture, TryFuture}; +#[doc(no_inline)] +pub use self::future::{FusedFuture, Future, TryFuture}; pub mod stream; -#[doc(hidden)] pub use self::stream::{Stream, FusedStream, TryStream}; +#[doc(no_inline)] +pub use self::stream::{FusedStream, Stream, TryStream}; #[macro_use] pub mod task; - -// Not public API. -#[doc(hidden)] -pub mod core_reexport { - #[doc(hidden)] - pub use core::*; -} diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs index 1f0f9a6e45..ad5350b795 100644 --- a/futures-core/src/stream.rs +++ b/futures-core/src/stream.rs @@ -50,15 +50,20 @@ pub trait Stream { /// /// # Panics /// - /// Once a stream is finished, i.e. `Ready(None)` has been returned, further - /// calls to `poll_next` may result in a panic or other "bad behavior". If - /// this is difficult to guard against then the `fuse` adapter can be used + /// Once a stream has finished (returned `Ready(None)` from `poll_next`), calling its + /// `poll_next` method again may panic, block forever, or cause other kinds of + /// problems; the `Stream` trait places no requirements on the effects of + /// such a call. However, as the `poll_next` method is not marked `unsafe`, + /// Rust's usual rules apply: calls must never cause undefined behavior + /// (memory corruption, incorrect use of `unsafe` functions, or the like), + /// regardless of the stream's state. + /// + /// If this is difficult to guard against then the [`fuse`] adapter can be used /// to ensure that `poll_next` always returns `Ready(None)` in subsequent /// calls. - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>; + /// + /// [`fuse`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.fuse + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Returns the bounds on the remaining length of the stream. /// @@ -95,10 +100,7 @@ pub trait Stream { impl Stream for &mut S { type Item = S::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { S::poll_next(Pin::new(&mut **self), cx) } @@ -114,10 +116,7 @@ where { type Item = ::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut().as_mut().poll_next(cx) } @@ -177,35 +176,36 @@ pub trait TryStream: Stream + private_try_stream::Sealed { /// This method is a stopgap for a compiler limitation that prevents us from /// directly inheriting from the `Stream` trait; in the future it won't be /// needed. - fn try_poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>>; + fn try_poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>>; } impl TryStream for S - where S: ?Sized + Stream> +where + S: ?Sized + Stream>, { type Ok = T; type Error = E; - fn try_poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>> - { + fn try_poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { self.poll_next(cx) } } #[cfg(feature = "alloc")] mod if_alloc { - use alloc::boxed::Box; use super::*; + use alloc::boxed::Box; impl Stream for Box { type Item = S::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self).poll_next(cx) } @@ -218,10 +218,7 @@ mod if_alloc { impl Stream for std::panic::AssertUnwindSafe { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx) } diff --git a/futures-core/src/task/__internal/atomic_waker.rs b/futures-core/src/task/__internal/atomic_waker.rs index 506737e35b..d49d043619 100644 --- a/futures-core/src/task/__internal/atomic_waker.rs +++ b/futures-core/src/task/__internal/atomic_waker.rs @@ -1,7 +1,7 @@ use core::cell::UnsafeCell; use core::fmt; use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering::{Acquire, Release, AcqRel}; +use core::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use core::task::Waker; /// A synchronization primitive for task wakeup. @@ -53,7 +53,7 @@ use core::task::Waker; /// /// impl Flag { /// pub fn new() -> Self { -/// Flag(Arc::new(Inner { +/// Self(Arc::new(Inner { /// waker: AtomicWaker::new(), /// set: AtomicBool::new(false), /// })) @@ -202,10 +202,7 @@ impl AtomicWaker { trait AssertSync: Sync {} impl AssertSync for Waker {} - AtomicWaker { - state: AtomicUsize::new(WAITING), - waker: UnsafeCell::new(None), - } + Self { state: AtomicUsize::new(WAITING), waker: UnsafeCell::new(None) } } /// Registers the waker to be notified on calls to `wake`. @@ -259,7 +256,11 @@ impl AtomicWaker { /// } /// ``` pub fn register(&self, waker: &Waker) { - match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) { + match self + .state + .compare_exchange(WAITING, REGISTERING, Acquire, Acquire) + .unwrap_or_else(|x| x) + { WAITING => { unsafe { // Locked acquired, update the waker cell @@ -275,8 +276,7 @@ impl AtomicWaker { // nothing to acquire, only release. In case of concurrent // wakers, we need to acquire their releases, so success needs // to do both. - let res = self.state.compare_exchange( - REGISTERING, WAITING, AcqRel, Acquire); + let res = self.state.compare_exchange(REGISTERING, WAITING, AcqRel, Acquire); match res { Ok(_) => { @@ -340,9 +340,7 @@ impl AtomicWaker { // // We just want to maintain memory safety. It is ok to drop the // call to `register`. - debug_assert!( - state == REGISTERING || - state == REGISTERING | WAKING); + debug_assert!(state == REGISTERING || state == REGISTERING | WAKING); } } } @@ -387,9 +385,8 @@ impl AtomicWaker { // not. // debug_assert!( - state == REGISTERING || - state == REGISTERING | WAKING || - state == WAKING); + state == REGISTERING || state == REGISTERING | WAKING || state == WAKING + ); None } } @@ -398,7 +395,7 @@ impl AtomicWaker { impl Default for AtomicWaker { fn default() -> Self { - AtomicWaker::new() + Self::new() } } diff --git a/futures-core/src/task/__internal/mod.rs b/futures-core/src/task/__internal/mod.rs index 77e3678075..c902eb4bfb 100644 --- a/futures-core/src/task/__internal/mod.rs +++ b/futures-core/src/task/__internal/mod.rs @@ -1,4 +1,4 @@ -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] mod atomic_waker; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] pub use self::atomic_waker::AtomicWaker; diff --git a/futures-core/src/task/mod.rs b/futures-core/src/task/mod.rs index 362b376e04..19e4eaecdd 100644 --- a/futures-core/src/task/mod.rs +++ b/futures-core/src/task/mod.rs @@ -6,4 +6,5 @@ mod poll; #[doc(hidden)] pub mod __internal; -pub use core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +#[doc(no_inline)] +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; diff --git a/futures-core/src/task/poll.rs b/futures-core/src/task/poll.rs index fc61683e76..607e78e060 100644 --- a/futures-core/src/task/poll.rs +++ b/futures-core/src/task/poll.rs @@ -3,9 +3,10 @@ /// This macro bakes in propagation of `Pending` signals by returning early. #[macro_export] macro_rules! ready { - ($e:expr $(,)?) => (match $e { - $crate::core_reexport::task::Poll::Ready(t) => t, - $crate::core_reexport::task::Poll::Pending => - return $crate::core_reexport::task::Poll::Pending, - }) + ($e:expr $(,)?) => { + match $e { + $crate::task::Poll::Ready(t) => t, + $crate::task::Poll::Pending => return $crate::task::Poll::Pending, + } + }; } diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml index 1682a8f5a2..dae5f22e77 100644 --- a/futures-executor/Cargo.toml +++ b/futures-executor/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-executor" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-executor/0.3.0" description = """ Executors for asynchronous tasks based on the futures-rs library. """ @@ -17,13 +16,14 @@ std = ["futures-core/std", "futures-task/std", "futures-util/std"] thread-pool = ["std", "num_cpus"] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.4", default-features = false } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.21", default-features = false } num_cpus = { version = "1.8.0", optional = true } [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures" } [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures-executor/README.md b/futures-executor/README.md new file mode 100644 index 0000000000..67086851e4 --- /dev/null +++ b/futures-executor/README.md @@ -0,0 +1,23 @@ +# futures-executor + +Executors for asynchronous tasks based on the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-executor = "0.3" +``` + +The current `futures-executor` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-executor/benches/thread_notify.rs b/futures-executor/benches/thread_notify.rs index d8fbec4555..88d0447cf6 100644 --- a/futures-executor/benches/thread_notify.rs +++ b/futures-executor/benches/thread_notify.rs @@ -102,10 +102,7 @@ fn thread_yield_multi_thread(b: &mut Bencher) { }); b.iter(move || { - let y = Yield { - rem: NUM, - tx: tx.clone(), - }; + let y = Yield { rem: NUM, tx: tx.clone() }; block_on(y); }); diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs index f267876ce5..b1af87545f 100644 --- a/futures-executor/src/lib.rs +++ b/futures-executor/src/lib.rs @@ -1,18 +1,57 @@ //! Built-in executors and related tools. //! -//! All items of this library are only available when the `std` feature of this +//! All asynchronous computation occurs within an executor, which is +//! capable of spawning futures as tasks. This module provides several +//! built-in executors, as well as tools for building your own. +//! +//! All items are only available when the `std` feature of this //! library is activated, and it is activated by default. +//! +//! # Using a thread pool (M:N task scheduling) +//! +//! Most of the time tasks should be executed on a [thread pool](ThreadPool). +//! A small set of worker threads can handle a very large set of spawned tasks +//! (which are much lighter weight than threads). Tasks spawned onto the pool +//! with the [`spawn_ok`](ThreadPool::spawn_ok) function will run ambiently on +//! the created threads. +//! +//! # Spawning additional tasks +//! +//! Tasks can be spawned onto a spawner by calling its [`spawn_obj`] method +//! directly. In the case of `!Send` futures, [`spawn_local_obj`] can be used +//! instead. +//! +//! # Single-threaded execution +//! +//! In addition to thread pools, it's possible to run a task (and the tasks +//! it spawns) entirely within a single thread via the [`LocalPool`] executor. +//! Aside from cutting down on synchronization costs, this executor also makes +//! it possible to spawn non-`Send` tasks, via [`spawn_local_obj`]. The +//! [`LocalPool`] is best suited for running I/O-bound tasks that do relatively +//! little work between I/O operations. +//! +//! There is also a convenience function [`block_on`] for simply running a +//! future to completion on the current thread. +//! +//! [`spawn_obj`]: https://docs.rs/futures/0.3/futures/task/trait.Spawn.html#tymethod.spawn_obj +//! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj #![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))))] - -#![doc(html_root_url = "https://docs.rs/futures-executor/0.3.0")] +#![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")] mod local_pool; @@ -20,12 +59,14 @@ mod local_pool; pub use crate::local_pool::{block_on, block_on_stream, BlockingStream, LocalPool, LocalSpawner}; #[cfg(feature = "thread-pool")] +#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] #[cfg(feature = "std")] -mod unpark_mutex; +mod thread_pool; #[cfg(feature = "thread-pool")] #[cfg(feature = "std")] -mod thread_pool; +mod unpark_mutex; #[cfg(feature = "thread-pool")] +#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] #[cfg(feature = "std")] pub use crate::thread_pool::{ThreadPool, ThreadPoolBuilder}; diff --git a/futures-executor/src/local_pool.rs b/futures-executor/src/local_pool.rs index b089a8094f..bee96d8db9 100644 --- a/futures-executor/src/local_pool.rs +++ b/futures-executor/src/local_pool.rs @@ -10,7 +10,10 @@ use futures_util::stream::StreamExt; use std::cell::RefCell; use std::ops::{Deref, DerefMut}; use std::rc::{Rc, Weak}; -use std::sync::{Arc, atomic::{AtomicBool, Ordering}}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; use std::thread::{self, Thread}; /// A single-threaded task pool for polling futures to completion. @@ -118,18 +121,13 @@ fn poll_executor) -> T>(mut f: F) -> T { impl LocalPool { /// Create a new, empty pool of tasks. - pub fn new() -> LocalPool { - LocalPool { - pool: FuturesUnordered::new(), - incoming: Default::default(), - } + pub fn new() -> Self { + Self { pool: FuturesUnordered::new(), incoming: Default::default() } } /// Get a clonable handle to the pool as a [`Spawn`]. pub fn spawner(&self) -> LocalSpawner { - LocalSpawner { - incoming: Rc::downgrade(&self.incoming), - } + LocalSpawner { incoming: Rc::downgrade(&self.incoming) } } /// Run all tasks in the pool to completion. diff --git a/futures-executor/src/thread_pool.rs b/futures-executor/src/thread_pool.rs index 6a87bc5f08..5e1f586eb8 100644 --- a/futures-executor/src/thread_pool.rs +++ b/futures-executor/src/thread_pool.rs @@ -2,8 +2,8 @@ use crate::enter; use crate::unpark_mutex::UnparkMutex; use futures_core::future::Future; use futures_core::task::{Context, Poll}; +use futures_task::{waker_ref, ArcWake}; use futures_task::{FutureObj, Spawn, SpawnError}; -use futures_task::{ArcWake, waker_ref}; use futures_util::future::FutureExt; use std::cmp; use std::fmt; @@ -24,6 +24,7 @@ use std::thread; /// /// This type is only available when the `thread-pool` feature of this /// library is activated. +#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] pub struct ThreadPool { state: Arc, } @@ -32,6 +33,7 @@ pub struct ThreadPool { /// /// This type is only available when the `thread-pool` feature of this /// library is activated. +#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] pub struct ThreadPoolBuilder { pool_size: usize, stack_size: usize, @@ -52,9 +54,7 @@ struct PoolState { impl fmt::Debug for ThreadPool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ThreadPool") - .field("size", &self.state.size) - .finish() + f.debug_struct("ThreadPool").field("size", &self.state.size).finish() } } @@ -78,7 +78,7 @@ impl ThreadPool { /// See documentation for the methods in /// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default /// configuration. - pub fn new() -> Result { + pub fn new() -> Result { ThreadPoolBuilder::new().create() } @@ -98,10 +98,7 @@ impl ThreadPool { pub fn spawn_obj_ok(&self, future: FutureObj<'static, ()>) { let task = Task { future, - wake_handle: Arc::new(WakeHandle { - exec: self.clone(), - mutex: UnparkMutex::new(), - }), + wake_handle: Arc::new(WakeHandle { exec: self.clone(), mutex: UnparkMutex::new() }), exec: self.clone(), }; self.state.send(Message::Run(task)); @@ -130,10 +127,7 @@ impl ThreadPool { } impl Spawn for ThreadPool { - fn spawn_obj( - &self, - future: FutureObj<'static, ()>, - ) -> Result<(), SpawnError> { + fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { self.spawn_obj_ok(future); Ok(()) } @@ -144,10 +138,12 @@ impl PoolState { self.tx.lock().unwrap().send(msg).unwrap(); } - fn work(&self, - idx: usize, - after_start: Option>, - before_stop: Option>) { + fn work( + &self, + idx: usize, + after_start: Option>, + before_stop: Option>, + ) { let _scope = enter().unwrap(); if let Some(after_start) = after_start { after_start(idx); @@ -166,9 +162,9 @@ impl PoolState { } impl Clone for ThreadPool { - fn clone(&self) -> ThreadPool { + fn clone(&self) -> Self { self.state.cnt.fetch_add(1, Ordering::Relaxed); - ThreadPool { state: self.state.clone() } + Self { state: self.state.clone() } } } @@ -210,7 +206,7 @@ impl ThreadPoolBuilder { self } - /// Set stack size of threads in the pool. + /// Set stack size of threads in the pool, in bytes. /// /// By default, worker threads use Rust's standard stack size. pub fn stack_size(&mut self, stack_size: usize) -> &mut Self { @@ -239,7 +235,8 @@ impl ThreadPoolBuilder { /// The closure provided will receive an index corresponding to the worker /// thread it's running on. pub fn after_start(&mut self, f: F) -> &mut Self - where F: Fn(usize) + Send + Sync + 'static + where + F: Fn(usize) + Send + Sync + 'static, { self.after_start = Some(Arc::new(f)); self @@ -248,13 +245,14 @@ 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 /// thread it's running on. pub fn before_stop(&mut self, f: F) -> &mut Self - where F: Fn(usize) + Send + Sync + 'static + where + F: Fn(usize) + Send + Sync + 'static, { self.before_stop = Some(Arc::new(f)); self @@ -311,7 +309,7 @@ impl Task { /// Actually run the task (invoking `poll` on the future) on the current /// thread. fn run(self) { - let Task { mut future, wake_handle, mut exec } = self; + let Self { mut future, wake_handle, mut exec } = self; let waker = waker_ref(&wake_handle); let mut cx = Context::from_waker(&waker); @@ -326,14 +324,11 @@ impl Task { Poll::Pending => {} Poll::Ready(()) => return wake_handle.mutex.complete(), } - let task = Task { - future, - wake_handle: wake_handle.clone(), - exec, - }; + let task = Self { future, wake_handle: wake_handle.clone(), exec }; match wake_handle.mutex.wait(task) { Ok(()) => return, // we've waited - Err(task) => { // someone's notified us + Err(task) => { + // someone's notified us future = task.future; exec = task.exec; } @@ -345,9 +340,7 @@ impl Task { impl fmt::Debug for Task { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Task") - .field("contents", &"...") - .finish() + f.debug_struct("Task").field("contents", &"...").finish() } } @@ -370,9 +363,11 @@ mod tests { let (tx, rx) = mpsc::sync_channel(2); let _cpu_pool = ThreadPoolBuilder::new() .pool_size(2) - .after_start(move |_| tx.send(1).unwrap()).create().unwrap(); + .after_start(move |_| tx.send(1).unwrap()) + .create() + .unwrap(); - // After ThreadPoolBuilder is deconstructed, the tx should be droped + // 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/src/unpark_mutex.rs b/futures-executor/src/unpark_mutex.rs index 3497faac12..ac5112cfa2 100644 --- a/futures-executor/src/unpark_mutex.rs +++ b/futures-executor/src/unpark_mutex.rs @@ -29,25 +29,22 @@ unsafe impl Sync for UnparkMutex {} // transitions: // The task is blocked, waiting on an event -const WAITING: usize = 0; // --> POLLING +const WAITING: usize = 0; // --> POLLING // The task is actively being polled by a thread; arrival of additional events // of interest should move it to the REPOLL state -const POLLING: usize = 1; // --> WAITING, REPOLL, or COMPLETE +const POLLING: usize = 1; // --> WAITING, REPOLL, or COMPLETE // The task is actively being polled, but will need to be re-polled upon // completion to ensure that all events were observed. -const REPOLL: usize = 2; // --> POLLING +const REPOLL: usize = 2; // --> POLLING // The task has finished executing (either successfully or with an error/panic) -const COMPLETE: usize = 3; // No transitions out +const COMPLETE: usize = 3; // No transitions out impl UnparkMutex { - pub(crate) fn new() -> UnparkMutex { - UnparkMutex { - status: AtomicUsize::new(WAITING), - inner: UnsafeCell::new(None), - } + pub(crate) fn new() -> Self { + Self { status: AtomicUsize::new(WAITING), inner: UnsafeCell::new(None) } } /// Attempt to "notify" the mutex that a poll should occur. @@ -62,8 +59,7 @@ impl UnparkMutex { match status { // The task is idle, so try to run it immediately. WAITING => { - match self.status.compare_exchange(WAITING, POLLING, - SeqCst, SeqCst) { + match self.status.compare_exchange(WAITING, POLLING, SeqCst, SeqCst) { Ok(_) => { let data = unsafe { // SAFETY: we've ensured mutual exclusion via @@ -82,13 +78,10 @@ impl UnparkMutex { // The task is being polled, so we need to record that it should // be *repolled* when complete. - POLLING => { - match self.status.compare_exchange(POLLING, REPOLL, - SeqCst, SeqCst) { - Ok(_) => return Err(()), - Err(cur) => status = cur, - } - } + POLLING => match self.status.compare_exchange(POLLING, REPOLL, SeqCst, SeqCst) { + Ok(_) => return Err(()), + Err(cur) => status = cur, + }, // The task is already scheduled for polling, or is complete, so // we've got nothing to do. @@ -108,7 +101,7 @@ impl UnparkMutex { self.status.store(POLLING, SeqCst); } - /// Alert the mutex that polling completed with NotReady. + /// Alert the mutex that polling completed with `Pending`. /// /// # Safety /// diff --git a/futures-executor/tests/local_pool.rs b/futures-executor/tests/local_pool.rs index b31f1034cb..9b1316b998 100644 --- a/futures-executor/tests/local_pool.rs +++ b/futures-executor/tests/local_pool.rs @@ -1,14 +1,14 @@ use futures::channel::oneshot; use futures::executor::LocalPool; -use futures::future::{self, Future, lazy, poll_fn}; -use futures::task::{Context, Poll, Spawn, LocalSpawn, Waker}; +use futures::future::{self, lazy, poll_fn, Future}; +use futures::task::{Context, LocalSpawn, Poll, Spawn, Waker}; use std::cell::{Cell, RefCell}; use std::pin::Pin; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread; use std::time::Duration; -use std::sync::Arc; -use std::sync::atomic::{Ordering, AtomicBool}; struct Pending(Rc<()>); @@ -52,9 +52,14 @@ fn run_until_executes_spawned() { let (tx, rx) = oneshot::channel(); let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - tx.send(()).unwrap(); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + tx.send(()).unwrap(); + })) + .into(), + ) + .unwrap(); pool.run_until(rx).unwrap(); } @@ -74,18 +79,27 @@ fn run_executes_spawned() { let spawn = pool.spawner(); let spawn2 = pool.spawner(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - spawn2.spawn_local_obj(Box::pin(lazy(move |_| { - cnt2.set(cnt2.get() + 1); - })).into()).unwrap(); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + spawn2 + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt2.set(cnt2.get() + 1); + })) + .into(), + ) + .unwrap(); + })) + .into(), + ) + .unwrap(); pool.run(); assert_eq!(cnt.get(), 1); } - #[test] fn run_spawn_many() { const ITER: usize = 200; @@ -97,9 +111,14 @@ fn run_spawn_many() { for _ in 0..ITER { let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); } pool.run(); @@ -126,9 +145,14 @@ fn try_run_one_executes_one_ready() { spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); } @@ -154,15 +178,20 @@ fn try_run_one_returns_on_no_progress() { { let cnt = cnt.clone(); let waker = waker.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |ctx| { - cnt.set(cnt.get() + 1); - waker.set(Some(ctx.waker().clone())); - if cnt.get() == ITER { - Poll::Ready(()) - } else { - Poll::Pending - } - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |ctx| { + cnt.set(cnt.get() + 1); + waker.set(Some(ctx.waker().clone())); + if cnt.get() == ITER { + Poll::Ready(()) + } else { + Poll::Pending + } + })) + .into(), + ) + .unwrap(); } for i in 0..ITER - 1 { @@ -185,16 +214,21 @@ fn try_run_one_runs_sub_futures() { let inner_spawner = spawn.clone(); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |_| { - cnt1.set(cnt1.get() + 1); - - let cnt2 = cnt1.clone(); - inner_spawner.spawn_local_obj(Box::pin(lazy(move |_|{ - cnt2.set(cnt2.get() + 1) - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |_| { + cnt1.set(cnt1.get() + 1); - Poll::Pending - })).into()).unwrap(); + let cnt2 = cnt1.clone(); + inner_spawner + .spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()) + .unwrap(); + + Poll::Pending + })) + .into(), + ) + .unwrap(); pool.try_run_one(); assert_eq!(cnt.get(), 2); @@ -214,12 +248,12 @@ fn run_until_stalled_returns_multiple_times() { let cnt = Rc::new(Cell::new(0)); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_|{ cnt1.set(cnt1.get() + 1) })).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt1.set(cnt1.get() + 1))).into()).unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 1); let cnt2 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_|{ cnt2.set(cnt2.get() + 1) })).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()).unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 2); } @@ -232,16 +266,21 @@ fn run_until_stalled_runs_spawned_sub_futures() { let inner_spawner = spawn.clone(); let cnt1 = cnt.clone(); - spawn.spawn_local_obj(Box::pin(poll_fn(move |_| { - cnt1.set(cnt1.get() + 1); - - let cnt2 = cnt1.clone(); - inner_spawner.spawn_local_obj(Box::pin(lazy(move |_|{ - cnt2.set(cnt2.get() + 1) - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(poll_fn(move |_| { + cnt1.set(cnt1.get() + 1); - Poll::Pending - })).into()).unwrap(); + let cnt2 = cnt1.clone(); + inner_spawner + .spawn_local_obj(Box::pin(lazy(move |_| cnt2.set(cnt2.get() + 1))).into()) + .unwrap(); + + Poll::Pending + })) + .into(), + ) + .unwrap(); pool.run_until_stalled(); assert_eq!(cnt.get(), 2); @@ -262,9 +301,14 @@ fn run_until_stalled_executes_all_ready() { spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); let cnt = cnt.clone(); - spawn.spawn_local_obj(Box::pin(lazy(move |_| { - cnt.set(cnt.get() + 1); - })).into()).unwrap(); + spawn + .spawn_local_obj( + Box::pin(lazy(move |_| { + cnt.set(cnt.get() + 1); + })) + .into(), + ) + .unwrap(); // also add some pending tasks to test if they are ignored spawn.spawn_local_obj(Box::pin(pending()).into()).unwrap(); @@ -281,10 +325,15 @@ fn nesting_run() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_obj(Box::pin(lazy(|_| { - let mut pool = LocalPool::new(); - pool.run(); - })).into()).unwrap(); + spawn + .spawn_obj( + Box::pin(lazy(|_| { + let mut pool = LocalPool::new(); + pool.run(); + })) + .into(), + ) + .unwrap(); pool.run(); } @@ -295,10 +344,15 @@ fn nesting_run_run_until_stalled() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_obj(Box::pin(lazy(|_| { - let mut pool = LocalPool::new(); - pool.run_until_stalled(); - })).into()).unwrap(); + spawn + .spawn_obj( + Box::pin(lazy(|_| { + let mut pool = LocalPool::new(); + pool.run_until_stalled(); + })) + .into(), + ) + .unwrap(); pool.run(); } @@ -342,32 +396,26 @@ fn tasks_are_scheduled_fairly() { let mut pool = LocalPool::new(); let spawn = pool.spawner(); - spawn.spawn_local_obj(Box::pin(Spin { - state: state.clone(), - idx: 0, - }).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(Spin { state: state.clone(), idx: 0 }).into()).unwrap(); - spawn.spawn_local_obj(Box::pin(Spin { - state, - idx: 1, - }).into()).unwrap(); + spawn.spawn_local_obj(Box::pin(Spin { state, idx: 1 }).into()).unwrap(); pool.run(); } // 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; let future = future::poll_fn(move |cx| { if done { - return Poll::Ready(()) + return Poll::Ready(()); } done = true; cx.waker().clone().wake(); // (*) - // some user-code that temporarily parks the thread + // some user-code that temporarily parks the thread let test = thread::current(); let latch = Arc::new(AtomicBool::new(false)); let signal = latch.clone(); @@ -384,4 +432,3 @@ fn park_unpark_independence() { futures::executor::block_on(future) } - diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml index 76f2565cb2..8d446947b1 100644 --- a/futures-io/Cargo.toml +++ b/futures-io/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-io" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.36" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-io/0.3.0" description = """ The `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` traits for the futures-rs library. """ @@ -19,9 +18,9 @@ std = [] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. unstable = [] -read-initializer = [] [dependencies] [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures-io/README.md b/futures-io/README.md new file mode 100644 index 0000000000..da6eec28ba --- /dev/null +++ b/futures-io/README.md @@ -0,0 +1,23 @@ +# futures-io + +The `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` traits for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-io = "0.3" +``` + +The current `futures-io` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-io/src/lib.rs b/futures-io/src/lib.rs index 34fb24186d..e91eb78492 100644 --- a/futures-io/src/lib.rs +++ b/futures-io/src/lib.rs @@ -8,21 +8,18 @@ //! All items of this library are only available when the `std` feature of this //! library is activated, and it is activated by default. -#![cfg_attr(all(feature = "read-initializer", feature = "std"), feature(read_initializer))] - #![cfg_attr(not(feature = "std"), no_std)] - -#![warn(missing_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(html_root_url = "https://docs.rs/futures-io/0.3.0")] - -#[cfg(all(feature = "read-initializer", not(feature = "unstable")))] -compile_error!("The `read-initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#![doc(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")] mod if_std { @@ -34,18 +31,8 @@ mod if_std { // Re-export some types from `std::io` so that users don't have to deal // with conflicts when `use`ing `futures::io` and `std::io`. #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use io::{ - Error as Error, - ErrorKind as ErrorKind, - Result as Result, - IoSlice as IoSlice, - IoSliceMut as IoSliceMut, - SeekFrom as SeekFrom, - }; - - #[cfg(feature = "read-initializer")] - #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 - pub use io::Initializer as Initializer; + #[doc(no_inline)] + pub use io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; /// Read bytes asynchronously. /// @@ -55,26 +42,6 @@ mod if_std { /// for wakeup and return if data is not yet available, rather than blocking /// the calling thread. pub trait AsyncRead { - /// Determines if this `AsyncRead`er can work with buffers of - /// uninitialized memory. - /// - /// The default implementation returns an initializer which will zero - /// buffers. - /// - /// This method is only available when the `read-initializer` feature of this - /// library is activated. - /// - /// # Safety - /// - /// This method is `unsafe` because an `AsyncRead`er could otherwise - /// return a non-zeroing `Initializer` from another `AsyncRead` type - /// without an `unsafe` block. - #[cfg(feature = "read-initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::zeroing() - } - /// Attempt to read from the `AsyncRead` into `buf`. /// /// On success, returns `Poll::Ready(Ok(num_bytes_read))`. @@ -90,8 +57,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll>; + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll>; /// Attempt to read from the `AsyncRead` into `bufs` using vectored /// IO operations. @@ -115,9 +85,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { for b in bufs { if !b.is_empty() { return self.poll_read(cx, b); @@ -133,7 +105,7 @@ mod if_std { /// This trait is analogous to the `std::io::Write` trait, but integrates /// with the asynchronous task system. In particular, the `poll_write` /// method, unlike `Write::write`, will automatically queue the current task - /// for wakeup and return if data is not yet available, rather than blocking + /// for wakeup and return if the writer cannot take more data, rather than blocking /// the calling thread. pub trait AsyncWrite { /// Attempt to write bytes from `buf` into the object. @@ -151,8 +123,14 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll>; + /// + /// `poll_write` must try to make progress by flushing the underlying object if + /// that is the only way the underlying object can become writable again. + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll>; /// Attempt to write bytes from `bufs` into the object using vectored /// IO operations. @@ -177,9 +155,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { for b in bufs { if !b.is_empty() { return self.poll_write(cx, b); @@ -205,6 +185,8 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. + /// + /// It only makes sense to do anything here if you actually buffer data. fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Attempt to close the object. @@ -252,8 +234,11 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll>; + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll>; } /// Read bytes asynchronously. @@ -292,8 +277,7 @@ mod if_std { /// `Interrupted`. Implementations must convert `WouldBlock` into /// `Poll::Pending` and either internally retry or convert /// `Interrupted` into another error kind. - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll>; + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Tells this buffer that `amt` bytes have been consumed from the buffer, /// so they should no longer be returned in calls to [`poll_read`]. @@ -315,23 +299,22 @@ mod if_std { macro_rules! deref_async_read { () => { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Pin::new(&mut **self).poll_read(cx, buf) } - fn poll_read_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Pin::new(&mut **self).poll_read_vectored(cx, bufs) } - } + }; } impl AsyncRead for Box { @@ -347,43 +330,41 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncRead, { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { self.get_mut().as_mut().poll_read(cx, buf) } - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { self.get_mut().as_mut().poll_read_vectored(cx, bufs) } } macro_rules! delegate_async_read_to_stdio { () => { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - io::Read::initializer(self) - } - - fn poll_read(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Poll::Ready(io::Read::read(&mut *self, buf)) } - fn poll_read_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Poll::Ready(io::Read::read_vectored(&mut *self, bufs)) } - } + }; } impl AsyncRead for &[u8] { @@ -392,15 +373,19 @@ mod if_std { macro_rules! deref_async_write { () => { - fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Pin::new(&mut **self).poll_write(cx, buf) } - fn poll_write_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Pin::new(&mut **self).poll_write_vectored(cx, bufs) } @@ -411,7 +396,7 @@ mod if_std { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self).poll_close(cx) } - } + }; } impl AsyncWrite for Box { @@ -427,15 +412,19 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncWrite, { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { self.get_mut().as_mut().poll_write(cx, buf) } - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { self.get_mut().as_mut().poll_write_vectored(cx, bufs) } @@ -450,15 +439,19 @@ mod if_std { macro_rules! delegate_async_write_to_stdio { () => { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(io::Write::write(&mut *self, buf)) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) } @@ -469,7 +462,7 @@ mod if_std { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } - } + }; } impl AsyncWrite for Vec { @@ -478,12 +471,14 @@ mod if_std { macro_rules! deref_async_seek { () => { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { Pin::new(&mut **self).poll_seek(cx, pos) } - } + }; } impl AsyncSeek for Box { @@ -499,25 +494,25 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncSeek, { - fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { self.get_mut().as_mut().poll_seek(cx, pos) } } macro_rules! deref_async_buf_read { () => { - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self.get_mut()).poll_fill_buf(cx) } fn consume(mut self: Pin<&mut Self>, amt: usize) { Pin::new(&mut **self).consume(amt) } - } + }; } impl AsyncBufRead for Box { @@ -533,9 +528,7 @@ mod if_std { P: DerefMut + Unpin, P::Target: AsyncBufRead, { - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut().as_mut().poll_fill_buf(cx) } @@ -546,16 +539,14 @@ mod if_std { macro_rules! delegate_async_buf_read_to_stdio { () => { - fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { + fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(io::BufRead::fill_buf(self.get_mut())) } fn consume(self: Pin<&mut Self>, amt: usize) { io::BufRead::consume(self.get_mut(), amt) } - } + }; } impl AsyncBufRead for &[u8] { diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index ccc1715ca7..a929d0f198 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-macro" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Taylor Cramer ", "Taiki Endo "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-macro/0.3.0" description = """ The futures-rs procedural macro implementations. """ @@ -18,6 +17,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -proc-macro-hack = "0.5.9" quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.56", features = ["full"] } diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs new file mode 100644 index 0000000000..40a091f94c --- /dev/null +++ b/futures-macro/src/executor.rs @@ -0,0 +1,55 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{quote, quote_spanned, ToTokens}; + +pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { + if !args.is_empty() { + return syn::Error::new_spanned(proc_macro2::TokenStream::from(args), "invalid argument") + .to_compile_error() + .into(); + } + + let mut input = syn::parse_macro_input!(item as syn::ItemFn); + + if input.sig.asyncness.take().is_none() { + return syn::Error::new_spanned(input.sig.fn_token, "Only async functions are supported") + .to_compile_error() + .into(); + } + + // If type mismatch occurs, the current rustc points to the last statement. + let (last_stmt_start_span, last_stmt_end_span) = { + let mut last_stmt = input + .block + .stmts + .last() + .map(ToTokens::into_token_stream) + .unwrap_or_default() + .into_iter(); + // `Span` on stable Rust has a limitation that only points to the first + // token, not the whole tokens. We can work around this limitation by + // using the first/last span of the tokens like + // `syn::Error::new_spanned` does. + let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span()); + let end = last_stmt.last().map_or(start, |t| t.span()); + (start, end) + }; + + let path = quote_spanned! {last_stmt_start_span=> + ::futures_test::__private + }; + let body = &input.block; + input.block.stmts = vec![syn::Stmt::Expr( + syn::parse2(quote_spanned! {last_stmt_end_span=> + #path::block_on(async #body) + }) + .unwrap(), + )]; + + let gen = quote! { + #[::core::prelude::v1::test] + #input + }; + + gen.into() +} diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs index 2a7b11ca6b..d427da27a0 100644 --- a/futures-macro/src/join.rs +++ b/futures-macro/src/join.rs @@ -4,31 +4,16 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; -use syn::{parenthesized, parse_quote, Expr, Ident, Token}; - -mod kw { - syn::custom_keyword!(futures_crate_path); -} +use syn::{Expr, Ident, Token}; #[derive(Default)] struct Join { - futures_crate_path: Option, fut_exprs: Vec, } impl Parse for Join { fn parse(input: ParseStream<'_>) -> syn::Result { - let mut join = Join::default(); - - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - join.futures_crate_path = Some(content.parse()?); - } + let mut join = Self::default(); while !input.is_empty() { join.fut_exprs.push(input.parse::()?); @@ -42,11 +27,7 @@ impl Parse for Join { } } -fn bind_futures( - futures_crate: &syn::Path, - fut_exprs: Vec, - span: Span, -) -> (Vec, Vec) { +fn bind_futures(fut_exprs: Vec, span: Span) -> (Vec, Vec) { let mut future_let_bindings = Vec::with_capacity(fut_exprs.len()); let future_names: Vec<_> = fut_exprs .into_iter() @@ -56,7 +37,7 @@ fn bind_futures( future_let_bindings.push(quote! { // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut #name = #futures_crate::future::maybe_done(#expr); + let mut #name = __futures_crate::future::maybe_done(#expr); }); name }) @@ -69,39 +50,35 @@ fn bind_futures( pub(crate) fn join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - __all_done &= #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); + __all_done &= __futures_crate::future::Future::poll( + unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_ready(); } }); let take_outputs = future_names.iter().map(|fut| { quote! { - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), + unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap(), } }); TokenStream::from(quote! { { #( #future_let_bindings )* - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready(( + __futures_crate::task::Poll::Ready(( #( #take_outputs )* )) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) @@ -111,29 +88,25 @@ pub(crate) fn join(input: TokenStream) -> TokenStream { pub(crate) fn try_join(input: TokenStream) -> TokenStream { let parsed = syn::parse_macro_input!(input as Join); - let futures_crate = parsed - .futures_crate_path - .unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); - let (future_let_bindings, future_names) = bind_futures(&futures_crate, parsed.fut_exprs, span); + let (future_let_bindings, future_names) = bind_futures(parsed.fut_exprs, span); let poll_futures = future_names.iter().map(|fut| { quote! { - if #futures_crate::core_reexport::future::Future::poll( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() + if __futures_crate::future::Future::poll( + unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }, __cx).is_pending() { __all_done = false; - } else if unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { + } else if unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.output_mut().unwrap().is_err() { // `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce // a `T: Debug` bound. // Also, for an error type of ! any code after `err().unwrap()` is unreachable. #[allow(unreachable_code)] - return #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Err( - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() + return __futures_crate::task::Poll::Ready( + __futures_crate::Err( + unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().err().unwrap() ) ); } @@ -145,7 +118,7 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { // an `E: Debug` bound. // Also, for an ok type of ! any code after `ok().unwrap()` is unreachable. #[allow(unreachable_code)] - unsafe { #futures_crate::core_reexport::pin::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), + unsafe { __futures_crate::Pin::new_unchecked(&mut #fut) }.take_output().unwrap().ok().unwrap(), } }); @@ -153,17 +126,17 @@ pub(crate) fn try_join(input: TokenStream) -> TokenStream { #( #future_let_bindings )* #[allow(clippy::diverging_sub_expression)] - #futures_crate::future::poll_fn(move |__cx: &mut #futures_crate::task::Context<'_>| { + __futures_crate::future::poll_fn(move |__cx: &mut __futures_crate::task::Context<'_>| { let mut __all_done = true; #( #poll_futures )* if __all_done { - #futures_crate::core_reexport::task::Poll::Ready( - #futures_crate::core_reexport::result::Result::Ok(( + __futures_crate::task::Poll::Ready( + __futures_crate::Ok(( #( #take_outputs )* )) ) } else { - #futures_crate::core_reexport::task::Poll::Pending + __futures_crate::task::Poll::Pending } }).await } }) diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 932ec2e4f1..0afe34b83b 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -1,14 +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))))] - -#![doc(html_root_url = "https://docs.rs/futures-join-macro/0.3.0")] +#![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`. @@ -16,31 +15,47 @@ extern crate proc_macro; use proc_macro::TokenStream; -use proc_macro_hack::proc_macro_hack; +mod executor; mod join; mod select; +mod stream_select; /// The `join!` macro. -#[proc_macro_hack] -pub fn join(input: TokenStream) -> TokenStream { +#[proc_macro] +pub fn join_internal(input: TokenStream) -> TokenStream { crate::join::join(input) } /// The `try_join!` macro. -#[proc_macro_hack] -pub fn try_join(input: TokenStream) -> TokenStream { +#[proc_macro] +pub fn try_join_internal(input: TokenStream) -> TokenStream { crate::join::try_join(input) } /// The `select!` macro. -#[proc_macro_hack] -pub fn select(input: TokenStream) -> TokenStream { +#[proc_macro] +pub fn select_internal(input: TokenStream) -> TokenStream { crate::select::select(input) } /// The `select_biased!` macro. -#[proc_macro_hack] -pub fn select_biased(input: TokenStream) -> TokenStream { +#[proc_macro] +pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } + +// TODO: Change this to doc comment once rustdoc bug fixed: https://github.com/rust-lang/futures-rs/pull/2435 +// The `test` attribute. +#[proc_macro_attribute] +pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { + crate::executor::test(input, item) +} + +/// The `stream_select!` macro. +#[proc_macro] +pub fn stream_select_internal(input: TokenStream) -> TokenStream { + crate::stream_select::stream_select(input.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs index f7f688b6ae..0c8e5f1ca0 100644 --- a/futures-macro/src/select.rs +++ b/futures-macro/src/select.rs @@ -3,16 +3,14 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{format_ident, quote}; -use syn::{parenthesized, parse_quote, Expr, Ident, Pat, Token}; use syn::parse::{Parse, ParseStream}; +use syn::{parse_quote, Expr, Ident, Pat, Token}; mod kw { syn::custom_keyword!(complete); - syn::custom_keyword!(futures_crate_path); } struct Select { - futures_crate_path: Option, // span of `complete`, then expression after `=> ...` complete: Option, default: Option, @@ -29,24 +27,13 @@ enum CaseKind { impl Parse for Select { fn parse(input: ParseStream<'_>) -> syn::Result { - let mut select = Select { - futures_crate_path: None, + let mut select = Self { complete: None, default: None, normal_fut_exprs: vec![], normal_fut_handlers: vec![], }; - // When `futures_crate_path(::path::to::futures::lib)` is provided, - // it sets the path through which futures library functions will be - // accessed. - if input.peek(kw::futures_crate_path) { - input.parse::()?; - let content; - parenthesized!(content in input); - select.futures_crate_path = Some(content.parse()?); - } - while !input.is_empty() { let case_kind = if input.peek(kw::complete) { // `complete` @@ -76,7 +63,10 @@ impl Parse for Select { // Commas after the expression are only optional if it's a `Block` // or it is the last branch in the `match`. - let is_block = match expr { Expr::Block(_) => true, _ => false }; + let is_block = match expr { + Expr::Block(_) => true, + _ => false, + }; if is_block || input.is_empty() { input.parse::>()?; } else { @@ -89,7 +79,7 @@ impl Parse for Select { CaseKind::Normal(pat, fut_expr) => { select.normal_fut_exprs.push(fut_expr); select.normal_fut_handlers.push((pat, expr)); - }, + } } } @@ -105,22 +95,16 @@ fn declare_result_enum( result_ident: Ident, variants: usize, complete: bool, - span: Span + span: Span, ) -> (Vec, syn::ItemEnum) { // "_0", "_1", "_2" let variant_names: Vec = - (0..variants) - .map(|num| format_ident!("_{}", num, span = span)) - .collect(); + (0..variants).map(|num| format_ident!("_{}", num, span = span)).collect(); let type_parameters = &variant_names; let variants = &variant_names; - let complete_variant = if complete { - Some(quote!(Complete)) - } else { - None - }; + let complete_variant = if complete { Some(quote!(Complete)) } else { None }; let enum_item = parse_quote! { enum #result_ident<#(#type_parameters,)*> { @@ -147,8 +131,6 @@ pub(crate) fn select_biased(input: TokenStream) -> TokenStream { fn select_inner(input: TokenStream, random: bool) -> TokenStream { let parsed = syn::parse_macro_input!(input as Select); - let futures_crate: syn::Path = parsed.futures_crate_path.unwrap_or_else(|| parse_quote!(::futures_util)); - // should be def_site, but that's unstable let span = Span::call_site(); @@ -163,7 +145,9 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // bind non-`Ident` future exprs w/ `let` let mut future_let_bindings = Vec::with_capacity(parsed.normal_fut_exprs.len()); - let bound_future_names: Vec<_> = parsed.normal_fut_exprs.into_iter() + let bound_future_names: Vec<_> = parsed + .normal_fut_exprs + .into_iter() .zip(variant_names.iter()) .map(|(expr, variant_name)| { match expr { @@ -175,11 +159,11 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // We check for this condition here in order to be able to // safely use Pin::new_unchecked(&mut #path) later on. future_let_bindings.push(quote! { - #futures_crate::async_await::assert_fused_future(&#path); - #futures_crate::async_await::assert_unpin(&#path); + __futures_crate::async_await::assert_fused_future(&#path); + __futures_crate::async_await::assert_unpin(&#path); }); path - }, + } _ => { // Bind and pin the resulting Future on the stack. This is // necessary to support direct select! calls on !Unpin @@ -203,8 +187,8 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // For each future, make an `&mut dyn FnMut(&mut Context<'_>) -> Option>` // to use for polling that individual future. These will then be put in an array. - let poll_functions = bound_future_names.iter().zip(variant_names.iter()) - .map(|(bound_future_name, variant_name)| { + let poll_functions = bound_future_names.iter().zip(variant_names.iter()).map( + |(bound_future_name, variant_name)| { // Below we lazily create the Pin on the Future below. // This is done in order to avoid allocating memory in the generator // for the Pin variable. @@ -214,28 +198,29 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // 2. The Future is created in scope of the select! function and will // not be moved for the duration of it. It is thereby stack-pinned quote! { - let mut #variant_name = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut #variant_name = |__cx: &mut __futures_crate::task::Context<'_>| { let mut #bound_future_name = unsafe { - ::core::pin::Pin::new_unchecked(&mut #bound_future_name) + __futures_crate::Pin::new_unchecked(&mut #bound_future_name) }; - if #futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { - None + if __futures_crate::future::FusedFuture::is_terminated(&#bound_future_name) { + __futures_crate::None } else { - Some(#futures_crate::future::FutureExt::poll_unpin( + __futures_crate::Some(__futures_crate::future::FutureExt::poll_unpin( &mut #bound_future_name, __cx, ).map(#enum_ident::#variant_name)) } }; let #variant_name: &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = &mut #variant_name; + &mut __futures_crate::task::Context<'_> + ) -> __futures_crate::Option<__futures_crate::task::Poll<_>> = &mut #variant_name; } - }); + }, + ); let none_polled = if parsed.complete.is_some() { quote! { - #futures_crate::task::Poll::Ready(#enum_ident::Complete) + __futures_crate::task::Poll::Ready(#enum_ident::Complete) } } else { quote! { @@ -244,13 +229,13 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { } }; - let branches = parsed.normal_fut_handlers.into_iter() - .zip(variant_names.iter()) - .map(|((pat, expr), variant_name)| { + let branches = parsed.normal_fut_handlers.into_iter().zip(variant_names.iter()).map( + |((pat, expr), variant_name)| { quote! { #enum_ident::#variant_name(#pat) => { #expr }, } - }); + }, + ); let branches = quote! { #( #branches )* }; let complete_branch = parsed.complete.map(|complete_expr| { @@ -267,13 +252,13 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let await_select_fut = if parsed.default.is_some() { // For select! with default this returns the Poll result quote! { - __poll_fn(&mut #futures_crate::task::Context::from_waker( - #futures_crate::task::noop_waker_ref() + __poll_fn(&mut __futures_crate::task::Context::from_waker( + __futures_crate::task::noop_waker_ref() )) } } else { quote! { - #futures_crate::future::poll_fn(__poll_fn).await + __futures_crate::future::poll_fn(__poll_fn).await } }; @@ -281,7 +266,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { // For select! with default __select_result is a Poll, otherwise not quote! { match __select_result { - #futures_crate::task::Poll::Ready(result) => match result { + __futures_crate::task::Poll::Ready(result) => match result { #branches }, _ => #default_expr @@ -297,7 +282,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let shuffle = if random { quote! { - #futures_crate::async_await::shuffle(&mut __select_arr); + __futures_crate::async_await::shuffle(&mut __select_arr); } } else { quote!() @@ -309,7 +294,7 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { let __select_result = { #( #future_let_bindings )* - let mut __poll_fn = |__cx: &mut #futures_crate::task::Context<'_>| { + let mut __poll_fn = |__cx: &mut __futures_crate::task::Context<'_>| { let mut __any_polled = false; #( #poll_functions )* @@ -318,22 +303,22 @@ fn select_inner(input: TokenStream, random: bool) -> TokenStream { #shuffle for poller in &mut __select_arr { let poller: &mut &mut dyn FnMut( - &mut #futures_crate::task::Context<'_> - ) -> Option<#futures_crate::task::Poll<_>> = poller; + &mut __futures_crate::task::Context<'_> + ) -> __futures_crate::Option<__futures_crate::task::Poll<_>> = poller; match poller(__cx) { - Some(x @ #futures_crate::task::Poll::Ready(_)) => + __futures_crate::Some(x @ __futures_crate::task::Poll::Ready(_)) => return x, - Some(#futures_crate::task::Poll::Pending) => { + __futures_crate::Some(__futures_crate::task::Poll::Pending) => { __any_polled = true; } - None => {} + __futures_crate::None => {} } } if !__any_polled { #none_polled } else { - #futures_crate::task::Poll::Pending + __futures_crate::task::Poll::Pending } }; diff --git a/futures-macro/src/stream_select.rs b/futures-macro/src/stream_select.rs new file mode 100644 index 0000000000..9927b53073 --- /dev/null +++ b/futures-macro/src/stream_select.rs @@ -0,0 +1,113 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{parse::Parser, punctuated::Punctuated, Expr, Index, Token}; + +/// The `stream_select!` macro. +pub(crate) fn stream_select(input: TokenStream) -> Result { + let args = Punctuated::::parse_terminated.parse2(input)?; + if args.len() < 2 { + return Ok(quote! { + compile_error!("stream select macro needs at least two arguments.") + }); + } + let generic_idents = (0..args.len()).map(|i| format_ident!("_{}", i)).collect::>(); + let field_idents = (0..args.len()).map(|i| format_ident!("__{}", i)).collect::>(); + let field_idents_2 = (0..args.len()).map(|i| format_ident!("___{}", i)).collect::>(); + let field_indices = (0..args.len()).map(Index::from).collect::>(); + let args = args.iter().map(|e| e.to_token_stream()); + + Ok(quote! { + { + #[derive(Debug)] + struct StreamSelect<#(#generic_idents),*> (#(Option<#generic_idents>),*); + + enum StreamEnum<#(#generic_idents),*> { + #( + #generic_idents(#generic_idents) + ),*, + None, + } + + impl __futures_crate::stream::Stream for StreamEnum<#(#generic_idents),*> + where #(#generic_idents: __futures_crate::stream::Stream + ::std::marker::Unpin,)* + { + type Item = ITEM; + + fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll> { + match self.get_mut() { + #( + Self::#generic_idents(#generic_idents) => ::std::pin::Pin::new(#generic_idents).poll_next(cx) + ),*, + Self::None => panic!("StreamEnum::None should never be polled!"), + } + } + } + + impl __futures_crate::stream::Stream for StreamSelect<#(#generic_idents),*> + where #(#generic_idents: __futures_crate::stream::Stream + ::std::marker::Unpin,)* + { + type Item = ITEM; + + fn poll_next(mut self: ::std::pin::Pin<&mut Self>, cx: &mut __futures_crate::task::Context<'_>) -> __futures_crate::task::Poll> { + let Self(#(ref mut #field_idents),*) = self.get_mut(); + #( + let mut #field_idents_2 = false; + )* + let mut any_pending = false; + { + let mut stream_array = [#(#field_idents.as_mut().map(|f| StreamEnum::#generic_idents(f)).unwrap_or(StreamEnum::None)),*]; + __futures_crate::async_await::shuffle(&mut stream_array); + + for mut s in stream_array { + if let StreamEnum::None = s { + continue; + } else { + match __futures_crate::stream::Stream::poll_next(::std::pin::Pin::new(&mut s), cx) { + r @ __futures_crate::task::Poll::Ready(Some(_)) => { + return r; + }, + __futures_crate::task::Poll::Pending => { + any_pending = true; + }, + __futures_crate::task::Poll::Ready(None) => { + match s { + #( + StreamEnum::#generic_idents(_) => { #field_idents_2 = true; } + ),*, + StreamEnum::None => panic!("StreamEnum::None should never be polled!"), + } + }, + } + } + } + } + #( + if #field_idents_2 { + *#field_idents = None; + } + )* + if any_pending { + __futures_crate::task::Poll::Pending + } else { + __futures_crate::task::Poll::Ready(None) + } + } + + fn size_hint(&self) -> (usize, Option) { + let mut s = (0, Some(0)); + #( + if let Some(new_hint) = self.#field_indices.as_ref().map(|s| s.size_hint()) { + s.0 += new_hint.0; + // We can change this out for `.zip` when the MSRV is 1.46.0 or higher. + s.1 = s.1.and_then(|a| new_hint.1.map(|b| a + b)); + } + )* + s + } + } + + StreamSelect(#(Some(#args)),*) + + } + }) +} diff --git a/futures-sink/Cargo.toml b/futures-sink/Cargo.toml index fadb6b04f3..2c0685ac6b 100644 --- a/futures-sink/Cargo.toml +++ b/futures-sink/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-sink" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.36" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-sink/0.3.0" description = """ The asynchronous `Sink` trait for the futures-rs library. """ diff --git a/futures-sink/README.md b/futures-sink/README.md new file mode 100644 index 0000000000..1d683e95b5 --- /dev/null +++ b/futures-sink/README.md @@ -0,0 +1,23 @@ +# futures-sink + +The asynchronous `Sink` trait for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-sink = "0.3" +``` + +The current `futures-sink` requires Rust 1.36 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-sink/src/lib.rs b/futures-sink/src/lib.rs index fea98fac97..0328740efd 100644 --- a/futures-sink/src/lib.rs +++ b/futures-sink/src/lib.rs @@ -4,14 +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(html_root_url = "https://docs.rs/futures-sink/0.3.0")] +#![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; @@ -210,7 +212,10 @@ mod if_alloc { impl + Unpin, Item> Sink for alloc::boxed::Box { type Error = S::Error; - fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_ready(cx) } @@ -218,11 +223,17 @@ mod if_alloc { Pin::new(&mut **self).start_send(item) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_flush(cx) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut **self).poll_close(cx) } } diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml index a387026472..77fec6fd92 100644 --- a/futures-task/Cargo.toml +++ b/futures-task/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-task" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://rust-lang.github.io/futures-api-docs/0.3.0-alpha.18/futures_core" description = """ Tools for working with tasks. """ @@ -16,16 +15,15 @@ default = ["std"] std = ["alloc"] alloc = [] -# Unstable features -# These features are outside of the normal semver guarantees and require the -# `unstable` feature as an explicit opt-in to unstable API. +# These features are no longer used. +# TODO: remove in the next major version. unstable = [] cfg-target-has-atomic = [] [dependencies] [dev-dependencies] -futures = { path = "../futures", version = "0.3.4" } +futures = { path = "../futures" } [package.metadata.docs.rs] all-features = true diff --git a/futures-task/README.md b/futures-task/README.md new file mode 100644 index 0000000000..8ceeba9d57 --- /dev/null +++ b/futures-task/README.md @@ -0,0 +1,23 @@ +# futures-task + +Tools for working with tasks. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-task = "0.3" +``` + +The current `futures-task` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-task/build.rs b/futures-task/build.rs new file mode 100644 index 0000000000..05e0496d94 --- /dev/null +++ b/futures-task/build.rs @@ -0,0 +1,41 @@ +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does *not* support atomic CAS operations. +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-task/no_atomic_cas.rs b/futures-task/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-task/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-task/src/future_obj.rs b/futures-task/src/future_obj.rs index 6acdf7c37f..f67494adb6 100644 --- a/futures-task/src/future_obj.rs +++ b/futures-task/src/future_obj.rs @@ -1,8 +1,8 @@ use core::{ - mem, fmt, future::Future, marker::PhantomData, + mem, pin::Pin, task::{Context, Poll}, }; @@ -26,24 +26,24 @@ impl Unpin for LocalFutureObj<'_, T> {} #[allow(single_use_lifetimes)] #[allow(clippy::transmute_ptr_to_ptr)] -unsafe fn remove_future_lifetime<'a, T>(ptr: *mut (dyn Future + 'a)) - -> *mut (dyn Future + 'static) -{ +unsafe fn remove_future_lifetime<'a, T>( + ptr: *mut (dyn Future + 'a), +) -> *mut (dyn Future + 'static) { mem::transmute(ptr) } #[allow(single_use_lifetimes)] -unsafe fn remove_drop_lifetime<'a, T>(ptr: unsafe fn (*mut (dyn Future + 'a))) - -> unsafe fn(*mut (dyn Future + 'static)) -{ +unsafe fn remove_drop_lifetime<'a, T>( + ptr: unsafe fn(*mut (dyn Future + 'a)), +) -> unsafe fn(*mut (dyn Future + 'static)) { mem::transmute(ptr) } impl<'a, T> LocalFutureObj<'a, T> { /// Create a `LocalFutureObj` from a custom trait object representation. #[inline] - pub fn new + 'a>(f: F) -> LocalFutureObj<'a, T> { - LocalFutureObj { + pub fn new + 'a>(f: F) -> Self { + Self { future: unsafe { remove_future_lifetime(f.into_raw()) }, drop_fn: unsafe { remove_drop_lifetime(F::drop) }, _marker: PhantomData, @@ -65,14 +65,13 @@ impl<'a, T> LocalFutureObj<'a, T> { impl fmt::Debug for LocalFutureObj<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LocalFutureObj") - .finish() + f.debug_struct("LocalFutureObj").finish() } } impl<'a, T> From> for LocalFutureObj<'a, T> { #[inline] - fn from(f: FutureObj<'a, T>) -> LocalFutureObj<'a, T> { + fn from(f: FutureObj<'a, T>) -> Self { f.0 } } @@ -82,17 +81,13 @@ impl Future for LocalFutureObj<'_, T> { #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - Pin::new_unchecked(&mut *self.future).poll(cx) - } + unsafe { Pin::new_unchecked(&mut *self.future).poll(cx) } } } impl Drop for LocalFutureObj<'_, T> { fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.future) - } + unsafe { (self.drop_fn)(self.future) } } } @@ -113,15 +108,14 @@ unsafe impl Send for FutureObj<'_, T> {} impl<'a, T> FutureObj<'a, T> { /// Create a `FutureObj` from a custom trait object representation. #[inline] - pub fn new + Send>(f: F) -> FutureObj<'a, T> { - FutureObj(LocalFutureObj::new(f)) + pub fn new + Send>(f: F) -> Self { + Self(LocalFutureObj::new(f)) } } impl fmt::Debug for FutureObj<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureObj") - .finish() + f.debug_struct("FutureObj").finish() } } @@ -130,7 +124,7 @@ impl Future for FutureObj<'_, T> { #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new( &mut self.0 ).poll(cx) + Pin::new(&mut self.0).poll(cx) } } @@ -180,7 +174,7 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F where - F: Future + Unpin + 'a + F: Future + Unpin + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { self as *mut dyn Future @@ -189,8 +183,7 @@ where unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } -unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + Unpin + 'a) -{ +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + Unpin + 'a) { fn into_raw(self) -> *mut (dyn Future + 'a) { self as *mut dyn Future } @@ -200,7 +193,7 @@ unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin<&'a mut F> where - F: Future + 'a + F: Future + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { unsafe { self.get_unchecked_mut() as *mut dyn Future } @@ -209,8 +202,7 @@ where unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } -unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future + 'a)> -{ +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future + 'a)> { fn into_raw(self) -> *mut (dyn Future + 'a) { unsafe { self.get_unchecked_mut() as *mut dyn Future } } @@ -224,14 +216,15 @@ mod if_alloc { use alloc::boxed::Box; unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Box - where F: Future + 'a + where + F: Future + 'a, { fn into_raw(self) -> *mut (dyn Future + 'a) { Box::into_raw(self) } unsafe fn drop(ptr: *mut (dyn Future + 'a)) { - drop(Box::from_raw(ptr as *mut F)) + drop(Box::from_raw(ptr.cast::())) } } @@ -257,12 +250,11 @@ mod if_alloc { unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin> where - F: Future + 'a + F: Future + 'a, { - fn into_raw(mut self) -> *mut (dyn Future + 'a) { - let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ }; - 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)) { @@ -271,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)) { @@ -283,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)) { @@ -296,49 +286,49 @@ mod if_alloc { impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { fn from(boxed: Box) -> Self { - FutureObj::new(boxed) + Self::new(boxed) } } impl<'a> From + Send + 'a>> for FutureObj<'a, ()> { fn from(boxed: Box + Send + 'a>) -> Self { - FutureObj::new(boxed) + Self::new(boxed) } } impl<'a, F: Future + Send + 'a> From>> for FutureObj<'a, ()> { fn from(boxed: Pin>) -> Self { - FutureObj::new(boxed) + Self::new(boxed) } } impl<'a> From + Send + 'a>>> for FutureObj<'a, ()> { fn from(boxed: Pin + Send + 'a>>) -> Self { - FutureObj::new(boxed) + Self::new(boxed) } } impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { fn from(boxed: Box) -> Self { - LocalFutureObj::new(boxed) + Self::new(boxed) } } impl<'a> From + 'a>> for LocalFutureObj<'a, ()> { fn from(boxed: Box + 'a>) -> Self { - LocalFutureObj::new(boxed) + Self::new(boxed) } } impl<'a, F: Future + 'a> From>> for LocalFutureObj<'a, ()> { fn from(boxed: Pin>) -> Self { - LocalFutureObj::new(boxed) + Self::new(boxed) } } impl<'a> From + 'a>>> for LocalFutureObj<'a, ()> { fn from(boxed: Pin + 'a>>) -> Self { - LocalFutureObj::new(boxed) + Self::new(boxed) } } } diff --git a/futures-task/src/lib.rs b/futures-task/src/lib.rs index b18a33a83f..c72460744c 100644 --- a/futures-task/src/lib.rs +++ b/futures-task/src/lib.rs @@ -1,57 +1,50 @@ //! Tools for working with tasks. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] - #![cfg_attr(not(feature = "std"), no_std)] - -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![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(html_root_url = "https://docs.rs/futures-task/0.3.0")] - -#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] -compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#![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_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - $item - )*}; -} - mod spawn; -pub use crate::spawn::{Spawn, SpawnError, LocalSpawn}; +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(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(feature = "alloc")] - mod waker; - #[cfg(feature = "alloc")] - pub use crate::waker::waker; +#[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(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 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}; mod noop_waker; pub use crate::noop_waker::noop_waker; -#[cfg(feature = "std")] pub use crate::noop_waker::noop_waker_ref; -pub use core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +#[doc(no_inline)] +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; diff --git a/futures-task/src/noop_waker.rs b/futures-task/src/noop_waker.rs index a0924ad925..f76a8a2e95 100644 --- a/futures-task/src/noop_waker.rs +++ b/futures-task/src/noop_waker.rs @@ -1,9 +1,7 @@ //! Utilities for creating zero-cost wakers that don't do anything. -use core::task::{RawWaker, RawWakerVTable, Waker}; use core::ptr::null; -#[cfg(feature = "std")] -use core::cell::UnsafeCell; +use core::task::{RawWaker, RawWakerVTable, Waker}; unsafe fn noop_clone(_data: *const ()) -> RawWaker { noop_raw_waker() @@ -13,7 +11,7 @@ unsafe fn noop(_data: *const ()) {} const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); -fn noop_raw_waker() -> RawWaker { +const fn noop_raw_waker() -> RawWaker { RawWaker::new(null(), &NOOP_WAKER_VTABLE) } @@ -29,9 +27,8 @@ fn noop_raw_waker() -> RawWaker { /// ``` #[inline] pub fn noop_waker() -> Waker { - unsafe { - Waker::from_raw(noop_raw_waker()) - } + // FIXME: Since 1.46.0 we can use transmute in consts, allowing this function to be const. + unsafe { Waker::from_raw(noop_raw_waker()) } } /// Get a static reference to a [`Waker`] which @@ -45,11 +42,22 @@ pub fn noop_waker() -> Waker { /// waker.wake_by_ref(); /// ``` #[inline] -#[cfg(feature = "std")] pub fn noop_waker_ref() -> &'static Waker { - thread_local! { - static NOOP_WAKER_INSTANCE: UnsafeCell = - UnsafeCell::new(noop_waker()); + struct SyncRawWaker(RawWaker); + unsafe impl Sync for SyncRawWaker {} + + static NOOP_WAKER_INSTANCE: SyncRawWaker = SyncRawWaker(noop_raw_waker()); + + // SAFETY: `Waker` is #[repr(transparent)] over its `RawWaker`. + unsafe { &*(&NOOP_WAKER_INSTANCE.0 as *const RawWaker as *const Waker) } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "std")] + fn issue_2091_cross_thread_segfault() { + let waker = std::thread::spawn(super::noop_waker_ref).join().unwrap(); + waker.wake_by_ref(); } - NOOP_WAKER_INSTANCE.with(|l| unsafe { &*l.get() }) } diff --git a/futures-task/src/spawn.rs b/futures-task/src/spawn.rs index a515dd4e18..f4e63397bd 100644 --- a/futures-task/src/spawn.rs +++ b/futures-task/src/spawn.rs @@ -126,7 +126,7 @@ impl LocalSpawn for &mut Sp { #[cfg(feature = "alloc")] mod if_alloc { use super::*; - use alloc::{ boxed::Box, rc::Rc }; + use alloc::{boxed::Box, rc::Rc}; impl Spawn for Box { fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { @@ -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-task/src/waker.rs b/futures-task/src/waker.rs index 635bfe8543..79112569c5 100644 --- a/futures-task/src/waker.rs +++ b/futures-task/src/waker.rs @@ -1,7 +1,7 @@ use super::arc_wake::ArcWake; -use core::mem; -use core::task::{Waker, RawWaker, RawWakerVTable}; use alloc::sync::Arc; +use core::mem; +use core::task::{RawWaker, RawWakerVTable, Waker}; pub(super) fn waker_vtable() -> &'static RawWakerVTable { &RawWakerVTable::new( @@ -18,13 +18,11 @@ pub(super) fn waker_vtable() -> &'static RawWakerVTable { /// [`ArcWake.wake()`](ArcWake::wake) if awoken. pub fn waker(wake: Arc) -> Waker where - W: ArcWake, + 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::())) - } + unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) } } // FIXME: panics on Arc::clone / refcount changes could wreak havoc on the @@ -33,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(); } @@ -45,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::())) } diff --git a/futures-task/src/waker_ref.rs b/futures-task/src/waker_ref.rs index 8dda499a62..7fb552fcfd 100644 --- a/futures-task/src/waker_ref.rs +++ b/futures-task/src/waker_ref.rs @@ -1,10 +1,10 @@ -use super::arc_wake::{ArcWake}; +use super::arc_wake::ArcWake; use super::waker::waker_vtable; use alloc::sync::Arc; -use core::mem::ManuallyDrop; use core::marker::PhantomData; +use core::mem::ManuallyDrop; use core::ops::Deref; -use core::task::{Waker, RawWaker}; +use core::task::{RawWaker, Waker}; /// A [`Waker`] that is only valid for a given lifetime. /// @@ -22,10 +22,7 @@ impl<'a> WakerRef<'a> { // copy the underlying (raw) waker without calling a clone, // as we won't call Waker::drop either. let waker = ManuallyDrop::new(unsafe { core::ptr::read(waker) }); - WakerRef { - waker, - _marker: PhantomData, - } + Self { waker, _marker: PhantomData } } /// Create a new [`WakerRef`] from a [`Waker`] that must not be dropped. @@ -35,10 +32,7 @@ impl<'a> WakerRef<'a> { /// by the caller), and the [`Waker`] doesn't need to or must not be /// destroyed. pub fn new_unowned(waker: ManuallyDrop) -> Self { - WakerRef { - waker, - _marker: PhantomData, - } + Self { waker, _marker: PhantomData } } } @@ -57,14 +51,13 @@ impl Deref for WakerRef<'_> { #[inline] pub fn waker_ref(wake: &Arc) -> WakerRef<'_> where - W: ArcWake + W: ArcWake, { // simply copy the pointer instead of using Arc::into_raw, // as we don't actually keep a refcount by using ManuallyDrop.< - let ptr = (&**wake as *const W) as *const (); + let ptr = Arc::as_ptr(wake).cast::<()>(); - let waker = ManuallyDrop::new(unsafe { - Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) - }); + let waker = + ManuallyDrop::new(unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable::())) }); WakerRef::new_unowned(waker) } diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 0267799c04..b5aa8a7dd1 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -1,26 +1,28 @@ [package] name = "futures-test" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Wim Looman "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-test/0.3.0" description = """ Common utilities for testing components built off futures-rs. """ [dependencies] -futures-core = { version = "0.3.4", path = "../futures-core", default-features = false } -futures-task = { version = "0.3.4", path = "../futures-task", default-features = false } -futures-io = { version = "0.3.4", path = "../futures-io", default-features = false } -futures-util = { version = "0.3.4", path = "../futures-util", default-features = false } -futures-executor = { version = "0.3.4", path = "../futures-executor", default-features = false } -pin-utils = { version = "0.1.0-alpha.4", default-features = false } +futures-core = { version = "0.3.21", path = "../futures-core", default-features = false } +futures-task = { version = "0.3.21", path = "../futures-task", default-features = false } +futures-io = { version = "0.3.21", path = "../futures-io", default-features = false } +futures-util = { version = "0.3.21", path = "../futures-util", default-features = false } +futures-executor = { version = "0.3.21", path = "../futures-executor", default-features = false } +futures-sink = { version = "0.3.21", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.3.21", path = "../futures-macro", default-features = false } +pin-utils = { version = "0.1.0", default-features = false } +pin-project = "1.0.1" [dev-dependencies] -futures = { version = "0.3.4", path = "../futures", default-features = false, features = ["std"] } +futures = { path = "../futures", default-features = false, features = ["std", "executor"] } [features] default = ["std"] diff --git a/futures-test/README.md b/futures-test/README.md new file mode 100644 index 0000000000..b3c30e5d6c --- /dev/null +++ b/futures-test/README.md @@ -0,0 +1,23 @@ +# futures-test + +Common utilities for testing components built off futures-rs. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-test = "0.3" +``` + +The current `futures-test` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-test/src/assert.rs b/futures-test/src/assert.rs index 24b4ec8e75..75d7832482 100644 --- a/futures-test/src/assert.rs +++ b/futures-test/src/assert.rs @@ -27,12 +27,10 @@ pub fn assert_is_unpin_stream(_: &mut S) {} macro_rules! assert_stream_pending { ($stream:expr) => {{ let mut stream = &mut $stream; - $crate::assert::assert_is_unpin_stream(stream); - let stream = $crate::std_reexport::pin::Pin::new(stream); + $crate::__private::assert::assert_is_unpin_stream(stream); + let stream = $crate::__private::Pin::new(stream); let mut cx = $crate::task::noop_context(); - let poll = $crate::futures_core_reexport::stream::Stream::poll_next( - stream, &mut cx, - ); + let poll = $crate::__private::stream::Stream::poll_next(stream, &mut cx); if poll.is_ready() { panic!("assertion failed: stream is not pending"); } @@ -63,21 +61,23 @@ macro_rules! assert_stream_pending { macro_rules! assert_stream_next { ($stream:expr, $item:expr) => {{ let mut stream = &mut $stream; - $crate::assert::assert_is_unpin_stream(stream); - let stream = $crate::std_reexport::pin::Pin::new(stream); + $crate::__private::assert::assert_is_unpin_stream(stream); + let stream = $crate::__private::Pin::new(stream); let mut cx = $crate::task::noop_context(); - match $crate::futures_core_reexport::stream::Stream::poll_next(stream, &mut cx) { - $crate::futures_core_reexport::task::Poll::Ready(Some(x)) => { + match $crate::__private::stream::Stream::poll_next(stream, &mut cx) { + $crate::__private::task::Poll::Ready($crate::__private::Some(x)) => { assert_eq!(x, $item); } - $crate::futures_core_reexport::task::Poll::Ready(None) => { - panic!("assertion failed: expected stream to provide item but stream is at its end"); + $crate::__private::task::Poll::Ready($crate::__private::None) => { + panic!( + "assertion failed: expected stream to provide item but stream is at its end" + ); } - $crate::futures_core_reexport::task::Poll::Pending => { + $crate::__private::task::Poll::Pending => { panic!("assertion failed: expected stream to provide item but stream wasn't ready"); } } - }} + }}; } /// Assert that the next poll to the provided stream will return an empty @@ -105,17 +105,17 @@ macro_rules! assert_stream_next { macro_rules! assert_stream_done { ($stream:expr) => {{ let mut stream = &mut $stream; - $crate::assert::assert_is_unpin_stream(stream); - let stream = $crate::std_reexport::pin::Pin::new(stream); + $crate::__private::assert::assert_is_unpin_stream(stream); + let stream = $crate::__private::Pin::new(stream); let mut cx = $crate::task::noop_context(); - match $crate::futures_core_reexport::stream::Stream::poll_next(stream, &mut cx) { - $crate::futures_core_reexport::task::Poll::Ready(Some(_)) => { + match $crate::__private::stream::Stream::poll_next(stream, &mut cx) { + $crate::__private::task::Poll::Ready($crate::__private::Some(_)) => { panic!("assertion failed: expected stream to be done but had more elements"); } - $crate::futures_core_reexport::task::Poll::Ready(None) => {} - $crate::futures_core_reexport::task::Poll::Pending => { + $crate::__private::task::Poll::Ready($crate::__private::None) => {} + $crate::__private::task::Poll::Pending => { panic!("assertion failed: expected stream to be done but was pending"); } } - }} + }}; } diff --git a/futures-test/src/assert_unmoved.rs b/futures-test/src/assert_unmoved.rs new file mode 100644 index 0000000000..95d9a095f2 --- /dev/null +++ b/futures-test/src/assert_unmoved.rs @@ -0,0 +1,218 @@ +use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use futures_io::{ + self as io, AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom, +}; +use futures_sink::Sink; +use pin_project::{pin_project, pinned_drop}; +use std::pin::Pin; +use std::ptr; +use std::thread::panicking; + +/// Combinator that asserts that the underlying type is not moved after being polled. +/// +/// See the `assert_unmoved` methods on: +/// * [`FutureTestExt`](crate::future::FutureTestExt::assert_unmoved) +/// * [`StreamTestExt`](crate::stream::StreamTestExt::assert_unmoved) +/// * [`SinkTestExt`](crate::sink::SinkTestExt::assert_unmoved_sink) +/// * [`AsyncReadTestExt`](crate::io::AsyncReadTestExt::assert_unmoved) +/// * [`AsyncWriteTestExt`](crate::io::AsyncWriteTestExt::assert_unmoved_write) +#[pin_project(PinnedDrop, !Unpin)] +#[derive(Debug, Clone)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct AssertUnmoved { + #[pin] + inner: T, + this_ptr: *const Self, +} + +// Safety: having a raw pointer in a struct makes it `!Send`, however the +// pointer is never dereferenced so this is safe. +unsafe impl Send for AssertUnmoved {} +unsafe impl Sync for AssertUnmoved {} + +impl AssertUnmoved { + pub(crate) fn new(inner: T) -> Self { + Self { inner, this_ptr: ptr::null() } + } + + fn poll_with<'a, U>(mut self: Pin<&'a mut Self>, f: impl FnOnce(Pin<&'a mut T>) -> U) -> U { + let cur_this = &*self as *const Self; + if self.this_ptr.is_null() { + // First time being polled + *self.as_mut().project().this_ptr = cur_this; + } else { + assert_eq!(self.this_ptr, cur_this, "AssertUnmoved moved between poll calls"); + } + f(self.project().inner) + } +} + +impl Future for AssertUnmoved { + type Output = Fut::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.poll_with(|f| f.poll(cx)) + } +} + +impl FusedFuture for AssertUnmoved { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } +} + +impl Stream for AssertUnmoved { + type Item = St::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|s| s.poll_next(cx)) + } +} + +impl FusedStream for AssertUnmoved { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } +} + +impl, Item> Sink for AssertUnmoved { + type Error = Si::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|s| s.poll_ready(cx)) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + self.poll_with(|s| s.start_send(item)) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|s| s.poll_flush(cx)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|s| s.poll_close(cx)) + } +} + +impl AsyncRead for AssertUnmoved { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + self.poll_with(|r| r.poll_read(cx, buf)) + } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + self.poll_with(|r| r.poll_read_vectored(cx, bufs)) + } +} + +impl AsyncWrite for AssertUnmoved { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.poll_with(|w| w.poll_write(cx, buf)) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + self.poll_with(|w| w.poll_write_vectored(cx, bufs)) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|w| w.poll_flush(cx)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|w| w.poll_close(cx)) + } +} + +impl AsyncSeek for AssertUnmoved { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + self.poll_with(|s| s.poll_seek(cx, pos)) + } +} + +impl AsyncBufRead for AssertUnmoved { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(|r| r.poll_fill_buf(cx)) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.poll_with(|r| r.consume(amt)) + } +} + +#[pinned_drop] +impl PinnedDrop for AssertUnmoved { + fn drop(self: Pin<&mut Self>) { + // If the thread is panicking then we can't panic again as that will + // cause the process to be aborted. + if !panicking() && !self.this_ptr.is_null() { + let cur_this = &*self as *const Self; + assert_eq!(self.this_ptr, cur_this, "AssertUnmoved moved before drop"); + } + } +} + +#[cfg(test)] +mod tests { + use futures_core::future::Future; + use futures_core::task::{Context, Poll}; + use futures_util::future::pending; + use futures_util::task::noop_waker; + use std::pin::Pin; + + use super::AssertUnmoved; + + #[test] + fn assert_send_sync() { + fn assert() {} + assert::>(); + } + + #[test] + fn dont_panic_when_not_polled() { + // This shouldn't panic. + let future = AssertUnmoved::new(pending::<()>()); + drop(future); + } + + #[test] + #[should_panic(expected = "AssertUnmoved moved between poll calls")] + fn dont_double_panic() { + // This test should only panic, not abort the process. + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + // First we allocate the future on the stack and poll it. + let mut future = AssertUnmoved::new(pending::<()>()); + let pinned_future = unsafe { Pin::new_unchecked(&mut future) }; + assert_eq!(pinned_future.poll(&mut cx), Poll::Pending); + + // Next we move it back to the heap and poll it again. This second call + // should panic (as the future is moved), but we shouldn't panic again + // whilst dropping `AssertUnmoved`. + let mut future = Box::new(future); + let pinned_boxed_future = unsafe { Pin::new_unchecked(&mut *future) }; + assert_eq!(pinned_boxed_future.poll(&mut cx), Poll::Pending); + } +} diff --git a/futures-test/src/future/assert_unmoved.rs b/futures-test/src/future/assert_unmoved.rs deleted file mode 100644 index 74f92c626b..0000000000 --- a/futures-test/src/future/assert_unmoved.rs +++ /dev/null @@ -1,98 +0,0 @@ -use futures_core::future::Future; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use std::marker::PhantomPinned; -use std::pin::Pin; -use std::ptr; -use std::thread::panicking; - -/// Combinator for the -/// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved) -/// method. -#[derive(Debug, Clone)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct AssertUnmoved { - future: Fut, - this_ptr: *const AssertUnmoved, - _pinned: PhantomPinned, -} - -impl AssertUnmoved { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(this_ptr: *const Self); - - pub(super) fn new(future: Fut) -> Self { - Self { - future, - this_ptr: ptr::null(), - _pinned: PhantomPinned, - } - } -} - -impl Future for AssertUnmoved { - type Output = Fut::Output; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - let cur_this = &*self as *const Self; - if self.this_ptr.is_null() { - // First time being polled - *self.as_mut().this_ptr() = cur_this; - } else { - assert_eq!(self.this_ptr, cur_this, "Future moved between poll calls"); - } - self.as_mut().future().poll(cx) - } -} - -impl Drop for AssertUnmoved { - fn drop(&mut self) { - // If the thread is panicking then we can't panic again as that will - // cause the process to be aborted. - if !panicking() && !self.this_ptr.is_null() { - let cur_this = &*self as *const Self; - assert_eq!(self.this_ptr, cur_this, "Future moved before drop"); - } - } -} - -#[cfg(test)] -mod tests { - use futures_core::future::Future; - use futures_core::task::{Context, Poll}; - use futures_util::future::pending; - use futures_util::task::noop_waker; - use std::pin::Pin; - - use super::AssertUnmoved; - - #[test] - fn dont_panic_when_not_polled() { - // This shouldn't panic. - let future = AssertUnmoved::new(pending::<()>()); - drop(future); - } - - #[test] - #[should_panic(expected = "Future moved between poll calls")] - fn dont_double_panic() { - // This test should only panic, not abort the process. - let waker = noop_waker(); - let mut cx = Context::from_waker(&waker); - - // First we allocate the future on the stack and poll it. - let mut future = AssertUnmoved::new(pending::<()>()); - let pinned_future = unsafe { Pin::new_unchecked(&mut future) }; - assert_eq!(pinned_future.poll(&mut cx), Poll::Pending); - - // Next we move it back to the heap and poll it again. This second call - // should panic (as the future is moved), but we shouldn't panic again - // whilst dropping `AssertUnmoved`. - let mut future = Box::new(future); - let pinned_boxed_future = unsafe { Pin::new_unchecked(&mut *future) }; - assert_eq!(pinned_boxed_future.poll(&mut cx), Poll::Pending); - } -} diff --git a/futures-test/src/future/mod.rs b/futures-test/src/future/mod.rs index caf814d5a8..ee5c6ddd5d 100644 --- a/futures-test/src/future/mod.rs +++ b/futures-test/src/future/mod.rs @@ -1,14 +1,12 @@ //! Additional combinators for testing futures. -mod assert_unmoved; -pub use self::assert_unmoved::AssertUnmoved; - mod pending_once; pub use self::pending_once::PendingOnce; use futures_core::future::Future; use std::thread; +pub use crate::assert_unmoved::AssertUnmoved; pub use crate::interleave_pending::InterleavePending; /// Additional combinators for testing futures. diff --git a/futures-test/src/future/pending_once.rs b/futures-test/src/future/pending_once.rs index f803f3a543..0fc3ef0b34 100644 --- a/futures-test/src/future/pending_once.rs +++ b/futures-test/src/future/pending_once.rs @@ -1,7 +1,7 @@ -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; +use pin_project::pin_project; use std::pin::Pin; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; /// Combinator that guarantees one [`Poll::Pending`] before polling its inner /// future. @@ -9,36 +9,30 @@ use pin_utils::{unsafe_pinned, unsafe_unpinned}; /// This is created by the /// [`FutureTestExt::pending_once`](super::FutureTestExt::pending_once) /// method. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct PendingOnce { + #[pin] future: Fut, polled_before: bool, } impl PendingOnce { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(polled_before: bool); - pub(super) fn new(future: Fut) -> Self { - Self { - future, - polled_before: false, - } + Self { future, polled_before: false } } } impl Future for PendingOnce { type Output = Fut::Output; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - if self.polled_before { - self.as_mut().future().poll(cx) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if *this.polled_before { + this.future.poll(cx) } else { - *self.as_mut().polled_before() = true; + *this.polled_before = true; cx.waker().wake_by_ref(); Poll::Pending } diff --git a/futures-test/src/interleave_pending.rs b/futures-test/src/interleave_pending.rs index 35c599dcdb..91640778b2 100644 --- a/futures-test/src/interleave_pending.rs +++ b/futures-test/src/interleave_pending.rs @@ -1,7 +1,10 @@ -use futures_core::future::{Future, FusedFuture}; -use futures_core::stream::{Stream, FusedStream}; -use futures_io::{self as io, AsyncBufRead, AsyncRead, AsyncWrite}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; +use futures_io::{ + self as io, AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom, +}; +use futures_sink::Sink; +use pin_project::pin_project; use std::{ pin::Pin, task::{Context, Poll}, @@ -12,25 +15,20 @@ use std::{ /// See the `interleave_pending` methods on: /// * [`FutureTestExt`](crate::future::FutureTestExt::interleave_pending) /// * [`StreamTestExt`](crate::stream::StreamTestExt::interleave_pending) +/// * [`SinkTestExt`](crate::sink::SinkTestExt::interleave_pending_sink) /// * [`AsyncReadTestExt`](crate::io::AsyncReadTestExt::interleave_pending) /// * [`AsyncWriteTestExt`](crate::io::AsyncWriteTestExt::interleave_pending_write) +#[pin_project] #[derive(Debug)] pub struct InterleavePending { + #[pin] inner: T, pended: bool, } -impl Unpin for InterleavePending {} - impl InterleavePending { - unsafe_pinned!(inner: T); - unsafe_unpinned!(pended: bool); - pub(crate) fn new(inner: T) -> Self { - Self { - inner, - pended: false, - } + Self { inner, pended: false } } /// Acquires a reference to the underlying I/O object that this adaptor is @@ -48,7 +46,7 @@ impl InterleavePending { /// Acquires a pinned mutable reference to the underlying I/O object that /// this adaptor is wrapping. pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().0 + self.project().inner } /// Consumes this adaptor returning the underlying I/O object. @@ -56,35 +54,34 @@ impl InterleavePending { self.inner } - fn project(self: Pin<&mut Self>) -> (Pin<&mut T>, &mut bool) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.inner), &mut this.pended) - } - } -} - -impl Future for InterleavePending { - type Output = Fut::Output; - - fn poll( - mut self: Pin<&mut Self>, + fn poll_with<'a, U>( + self: Pin<&'a mut Self>, cx: &mut Context<'_>, - ) -> Poll { - if *self.as_mut().pended() { - let next = self.as_mut().inner().poll(cx); + f: impl FnOnce(Pin<&'a mut T>, &mut Context<'_>) -> Poll, + ) -> Poll { + let this = self.project(); + if *this.pended { + let next = f(this.inner, cx); if next.is_ready() { - *self.pended() = false; + *this.pended = false; } next } else { cx.waker().wake_by_ref(); - *self.pended() = true; + *this.pended = true; Poll::Pending } } } +impl Future for InterleavePending { + type Output = Fut::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.poll_with(cx, Fut::poll) + } +} + impl FusedFuture for InterleavePending { fn is_terminated(&self) -> bool { self.inner.is_terminated() @@ -94,21 +91,8 @@ impl FusedFuture for InterleavePending { impl Stream for InterleavePending { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if *self.as_mut().pended() { - let next = self.as_mut().inner().poll_next(cx); - if next.is_ready() { - *self.pended() = false; - } - next - } else { - cx.waker().wake_by_ref(); - *self.pended() = true; - Poll::Pending - } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, St::poll_next) } fn size_hint(&self) -> (usize, Option) { @@ -116,110 +100,92 @@ impl Stream for InterleavePending { } } -impl FusedStream for InterleavePending { +impl FusedStream for InterleavePending { fn is_terminated(&self) -> bool { self.inner.is_terminated() } } -impl AsyncWrite for InterleavePending { - fn poll_write( +impl, Item> Sink for InterleavePending { + type Error = Si::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, Si::poll_ready) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + self.project().inner.start_send(item) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, Si::poll_flush) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, Si::poll_close) + } +} + +impl AsyncRead for InterleavePending { + fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &[u8], + buf: &mut [u8], ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_write(cx, buf); - if next.is_ready() { - *pended = false; - } - next - } else { - cx.waker().wake_by_ref(); - *pended = true; - Poll::Pending - } + self.poll_with(cx, |r, cx| r.poll_read(cx, buf)) } - fn poll_flush( + fn poll_read_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_flush(cx); - if next.is_ready() { - *pended = false; - } - next - } else { - cx.waker().wake_by_ref(); - *pended = true; - Poll::Pending - } + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + self.poll_with(cx, |r, cx| r.poll_read_vectored(cx, bufs)) } +} - fn poll_close( +impl AsyncWrite for InterleavePending { + fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> { - let (writer, pended) = self.project(); - if *pended { - let next = writer.poll_close(cx); - if next.is_ready() { - *pended = false; - } - next - } else { - cx.waker().wake_by_ref(); - *pended = true; - Poll::Pending - } + buf: &[u8], + ) -> Poll> { + self.poll_with(cx, |w, cx| w.poll_write(cx, buf)) } -} -impl AsyncRead for InterleavePending { - fn poll_read( + fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &mut [u8], + bufs: &[IoSlice<'_>], ) -> Poll> { - let (reader, pended) = self.project(); - if *pended { - let next = reader.poll_read(cx, buf); - if next.is_ready() { - *pended = false; - } - next - } else { - cx.waker().wake_by_ref(); - *pended = true; - Poll::Pending - } + self.poll_with(cx, |w, cx| w.poll_write_vectored(cx, bufs)) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, W::poll_flush) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, W::poll_close) } } -impl AsyncBufRead for InterleavePending { - fn poll_fill_buf( +impl AsyncSeek for InterleavePending { + fn poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> { - let (reader, pended) = self.project(); - if *pended { - let next = reader.poll_fill_buf(cx); - if next.is_ready() { - *pended = false; - } - next - } else { - cx.waker().wake_by_ref(); - *pended = true; - Poll::Pending - } + pos: SeekFrom, + ) -> Poll> { + self.poll_with(cx, |s, cx| s.poll_seek(cx, pos)) + } +} + +impl AsyncBufRead for InterleavePending { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_with(cx, R::poll_fill_buf) } fn consume(self: Pin<&mut Self>, amount: usize) { - self.inner().consume(amount) + self.project().inner.consume(amount) } } diff --git a/futures-test/src/io/limited.rs b/futures-test/src/io/limited.rs index 68ba674d4d..34b72a530e 100644 --- a/futures-test/src/io/limited.rs +++ b/futures-test/src/io/limited.rs @@ -1,5 +1,5 @@ use futures_io::{self as io, AsyncBufRead, AsyncRead, AsyncWrite}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; use std::{ cmp, pin::Pin, @@ -12,20 +12,17 @@ use std::{ /// /// [`limited`]: super::AsyncReadTestExt::limited /// [`limited_write`]: super::AsyncWriteTestExt::limited_write +#[pin_project] #[derive(Debug)] pub struct Limited { + #[pin] io: Io, limit: usize, } -impl Unpin for Limited {} - impl Limited { - unsafe_pinned!(io: Io); - unsafe_unpinned!(limit: usize); - - pub(crate) fn new(io: Io, limit: usize) -> Limited { - Limited { io, limit } + pub(crate) fn new(io: Io, limit: usize) -> Self { + Self { io, limit } } /// Acquires a reference to the underlying I/O object that this adaptor is @@ -43,7 +40,7 @@ impl Limited { /// Acquires a pinned mutable reference to the underlying I/O object that /// this adaptor is wrapping. pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Io> { - self.io() + self.project().io } /// Consumes this adaptor returning the underlying I/O object. @@ -54,49 +51,41 @@ impl Limited { impl AsyncWrite for Limited { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let limit = *self.as_mut().limit(); - self.io().poll_write(cx, &buf[..cmp::min(limit, buf.len())]) + let this = self.project(); + this.io.poll_write(cx, &buf[..cmp::min(*this.limit, buf.len())]) } - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.io().poll_flush(cx) + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().io.poll_flush(cx) } - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.io().poll_close(cx) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().io.poll_close(cx) } } impl AsyncRead for Limited { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let limit = cmp::min(*self.as_mut().limit(), buf.len()); - self.io().poll_read(cx, &mut buf[..limit]) + let this = self.project(); + let limit = cmp::min(*this.limit, buf.len()); + this.io.poll_read(cx, &mut buf[..limit]) } } impl AsyncBufRead for Limited { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.io().poll_fill_buf(cx) + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().io.poll_fill_buf(cx) } fn consume(self: Pin<&mut Self>, amount: usize) { - self.io().consume(amount) + self.project().io.consume(amount) } } diff --git a/futures-test/src/io/read/mod.rs b/futures-test/src/io/read/mod.rs index f6c51fd799..cb5f1d3f86 100644 --- a/futures-test/src/io/read/mod.rs +++ b/futures-test/src/io/read/mod.rs @@ -3,10 +3,26 @@ use futures_io::AsyncRead; pub use super::limited::Limited; +pub use crate::assert_unmoved::AssertUnmoved; pub use crate::interleave_pending::InterleavePending; /// Additional combinators for testing async readers. pub trait AsyncReadTestExt: AsyncRead { + /// Asserts that the given is not moved after being polled. + /// + /// A check for movement is performed each time the reader is polled + /// and when `Drop` is called. + /// + /// Aside from keeping track of the location at which the reader was first + /// polled and providing assertions, this reader adds no runtime behavior + /// and simply delegates to the child reader. + fn assert_unmoved(self) -> AssertUnmoved + where + Self: Sized, + { + AssertUnmoved::new(self) + } + /// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending) /// in between each read of the reader. /// diff --git a/futures-test/src/io/write/mod.rs b/futures-test/src/io/write/mod.rs index d228dd6a77..01ca4b2fef 100644 --- a/futures-test/src/io/write/mod.rs +++ b/futures-test/src/io/write/mod.rs @@ -3,10 +3,27 @@ use futures_io::AsyncWrite; pub use super::limited::Limited; +pub use crate::assert_unmoved::AssertUnmoved; pub use crate::interleave_pending::InterleavePending; +pub use crate::track_closed::TrackClosed; /// Additional combinators for testing async writers. pub trait AsyncWriteTestExt: AsyncWrite { + /// Asserts that the given is not moved after being polled. + /// + /// A check for movement is performed each time the writer is polled + /// and when `Drop` is called. + /// + /// Aside from keeping track of the location at which the writer was first + /// polled and providing assertions, this writer adds no runtime behavior + /// and simply delegates to the child writer. + fn assert_unmoved_write(self) -> AssertUnmoved + where + Self: Sized, + { + AssertUnmoved::new(self) + } + /// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending) /// in between each operation on the writer. /// @@ -80,6 +97,45 @@ pub trait AsyncWriteTestExt: AsyncWrite { { Limited::new(self, limit) } + + /// Track whether this stream has been closed and errors if it is used after closing. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::io::{AsyncWriteExt, Cursor}; + /// use futures_test::io::AsyncWriteTestExt; + /// + /// let mut writer = Cursor::new(vec![0u8; 4]).track_closed(); + /// + /// writer.write_all(&[1, 2]).await?; + /// assert!(!writer.is_closed()); + /// writer.close().await?; + /// assert!(writer.is_closed()); + /// + /// # Ok::<(), std::io::Error>(()) })?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::io::{AsyncWriteExt, Cursor}; + /// use futures_test::io::AsyncWriteTestExt; + /// + /// let mut writer = Cursor::new(vec![0u8; 4]).track_closed(); + /// + /// writer.close().await?; + /// assert!(writer.write_all(&[1, 2]).await.is_err()); + /// # Ok::<(), std::io::Error>(()) })?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + fn track_closed(self) -> TrackClosed + where + Self: Sized, + { + TrackClosed::new(self) + } } impl AsyncWriteTestExt for W where W: AsyncWrite {} diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index da018fe5b9..2eb4a1c4cd 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -1,29 +1,45 @@ //! 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))))] - -#![doc(html_root_url = "https://docs.rs/futures-test/0.3.0")] +#![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!("`futures-test` must have the `std` feature activated, this is a default-active feature"); +compile_error!( + "`futures-test` must have the `std` feature activated, this is a default-active feature" +); +// Not public API. #[doc(hidden)] #[cfg(feature = "std")] -pub use std as std_reexport; +pub mod __private { + pub use futures_core::{future, stream, task}; + pub use futures_executor::block_on; + pub use std::{ + option::Option::{None, Some}, + pin::Pin, + result::Result::{Err, Ok}, + }; -#[doc(hidden)] -#[cfg(feature = "std")] -pub extern crate futures_core as futures_core_reexport; + pub mod assert { + pub use crate::assert::*; + } +} #[macro_use] -#[doc(hidden)] #[cfg(feature = "std")] -pub mod assert; +mod assert; #[cfg(feature = "std")] pub mod task; @@ -34,7 +50,37 @@ pub mod future; #[cfg(feature = "std")] pub mod stream; +#[cfg(feature = "std")] +pub mod sink; + #[cfg(feature = "std")] pub mod io; +mod assert_unmoved; mod interleave_pending; +mod track_closed; + +/// Enables an `async` test function. The generated future will be run to completion with +/// [`futures_executor::block_on`](futures_executor::block_on). +/// +/// ``` +/// #[futures_test::test] +/// async fn my_test() { +/// let fut = async { true }; +/// assert!(fut.await); +/// } +/// ``` +/// +/// This is equivalent to the following code: +/// +/// ``` +/// #[test] +/// fn my_test() { +/// futures::executor::block_on(async move { +/// let fut = async { true }; +/// assert!(fut.await); +/// }) +/// } +/// ``` +#[cfg(feature = "std")] +pub use futures_macro::test_internal as test; diff --git a/futures-test/src/sink/mod.rs b/futures-test/src/sink/mod.rs new file mode 100644 index 0000000000..eb5a6efa84 --- /dev/null +++ b/futures-test/src/sink/mod.rs @@ -0,0 +1,82 @@ +//! Additional combinators for testing sinks. + +use futures_sink::Sink; + +pub use crate::assert_unmoved::AssertUnmoved; +pub use crate::interleave_pending::InterleavePending; +pub use crate::track_closed::TrackClosed; + +/// Additional combinators for testing sinks. +pub trait SinkTestExt: Sink { + /// Asserts that the given is not moved after being polled. + /// + /// A check for movement is performed each time the sink is polled + /// and when `Drop` is called. + /// + /// Aside from keeping track of the location at which the sink was first + /// polled and providing assertions, this sink adds no runtime behavior + /// and simply delegates to the child sink. + fn assert_unmoved_sink(self) -> AssertUnmoved + where + Self: Sized, + { + AssertUnmoved::new(self) + } + + /// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending) + /// in between each operation on the sink. + fn interleave_pending_sink(self) -> InterleavePending + where + Self: Sized, + { + InterleavePending::new(self) + } + + /// Track whether this sink has been closed and panics if it is used after closing. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::sink::{SinkExt, drain}; + /// use futures_test::sink::SinkTestExt; + /// + /// let mut sink = drain::().track_closed(); + /// + /// sink.send(1).await?; + /// assert!(!sink.is_closed()); + /// sink.close().await?; + /// assert!(sink.is_closed()); + /// + /// # Ok::<(), std::convert::Infallible>(()) })?; + /// # Ok::<(), std::convert::Infallible>(()) + /// ``` + /// + /// Note: Unlike [`AsyncWriteTestExt::track_closed`] when + /// used as a sink the adaptor will panic if closed too early as there's no easy way to + /// integrate as an error. + /// + /// [`AsyncWriteTestExt::track_closed`]: crate::io::AsyncWriteTestExt::track_closed + /// + /// ``` + /// # futures::executor::block_on(async { + /// use std::panic::AssertUnwindSafe; + /// use futures::{sink::{SinkExt, drain}, future::FutureExt}; + /// use futures_test::sink::SinkTestExt; + /// + /// let mut sink = drain::().track_closed(); + /// + /// sink.close().await?; + /// assert!(AssertUnwindSafe(sink.send(1)).catch_unwind().await.is_err()); + /// # Ok::<(), std::convert::Infallible>(()) })?; + /// # Ok::<(), std::convert::Infallible>(()) + /// ``` + fn track_closed(self) -> TrackClosed + where + Self: Sized, + { + TrackClosed::new(self) + } +} + +impl SinkTestExt for W where W: Sink {} diff --git a/futures-test/src/stream/mod.rs b/futures-test/src/stream/mod.rs index 5c194e71ad..9151a21c89 100644 --- a/futures-test/src/stream/mod.rs +++ b/futures-test/src/stream/mod.rs @@ -2,10 +2,26 @@ use futures_core::stream::Stream; +pub use crate::assert_unmoved::AssertUnmoved; pub use crate::interleave_pending::InterleavePending; /// Additional combinators for testing streams. pub trait StreamTestExt: Stream { + /// Asserts that the given is not moved after being polled. + /// + /// A check for movement is performed each time the stream is polled + /// and when `Drop` is called. + /// + /// Aside from keeping track of the location at which the stream was first + /// polled and providing assertions, this stream adds no runtime behavior + /// and simply delegates to the child stream. + fn assert_unmoved(self) -> AssertUnmoved + where + Self: Sized, + { + AssertUnmoved::new(self) + } + /// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending) /// in between each item of the stream. /// diff --git a/futures-test/src/task/context.rs b/futures-test/src/task/context.rs index 602127c657..b2b0dfe31e 100644 --- a/futures-test/src/task/context.rs +++ b/futures-test/src/task/context.rs @@ -1,4 +1,4 @@ -use crate::task::{panic_waker_ref, noop_waker_ref}; +use crate::task::{noop_waker_ref, panic_waker_ref}; use futures_core::task::Context; /// Create a new [`Context`](core::task::Context) where the diff --git a/futures-test/src/task/mod.rs b/futures-test/src/task/mod.rs index 9d775dd207..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 @@ -23,7 +23,7 @@ //! Test wakers: //! - [`noop_waker`](crate::task::noop_waker) creates a waker that ignores calls to //! [`wake`](futures_core::task::Waker). -//! - [`panic_waker`](crate::task::panic_waker::panic_waker) creates a waker that panics when +//! - [`panic_waker`](crate::task::panic_waker) creates a waker that panics when //! [`wake`](futures_core::task::Waker) is called. //! - [`new_count_waker`](crate::task::new_count_waker) creates a waker that increments a counter whenever //! [`wake`](futures_core::task::Waker) is called. @@ -57,4 +57,4 @@ mod record_spawner; pub use self::record_spawner::RecordSpawner; mod wake_counter; -pub use self::wake_counter::{AwokenCount, new_count_waker}; +pub use self::wake_counter::{new_count_waker, AwokenCount}; diff --git a/futures-test/src/task/panic_waker.rs b/futures-test/src/task/panic_waker.rs index 74476d823f..38e2443156 100644 --- a/futures-test/src/task/panic_waker.rs +++ b/futures-test/src/task/panic_waker.rs @@ -1,6 +1,5 @@ -use futures_core::task::{Waker, RawWaker, RawWakerVTable}; -use core::cell::UnsafeCell; use core::ptr::null; +use futures_core::task::{RawWaker, RawWakerVTable, Waker}; unsafe fn clone_panic_waker(_data: *const ()) -> RawWaker { raw_panic_waker() @@ -9,17 +8,15 @@ unsafe fn clone_panic_waker(_data: *const ()) -> RawWaker { unsafe fn noop(_data: *const ()) {} unsafe fn wake_panic(_data: *const ()) { - panic!("should not be woken"); + if !std::thread::panicking() { + panic!("should not be woken"); + } } -const PANIC_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( - clone_panic_waker, - wake_panic, - wake_panic, - noop, -); +const PANIC_WAKER_VTABLE: RawWakerVTable = + RawWakerVTable::new(clone_panic_waker, wake_panic, wake_panic, noop); -fn raw_panic_waker() -> RawWaker { +const fn raw_panic_waker() -> RawWaker { RawWaker::new(null(), &PANIC_WAKER_VTABLE) } @@ -36,6 +33,7 @@ fn raw_panic_waker() -> RawWaker { /// waker.wake(); // Will panic /// ``` pub fn panic_waker() -> Waker { + // FIXME: Since 1.46.0 we can use transmute in consts, allowing this function to be const. unsafe { Waker::from_raw(raw_panic_waker()) } } @@ -52,9 +50,21 @@ pub fn panic_waker() -> Waker { /// waker.wake_by_ref(); // Will panic /// ``` pub fn panic_waker_ref() -> &'static Waker { - thread_local! { - static PANIC_WAKER_INSTANCE: UnsafeCell = - UnsafeCell::new(panic_waker()); + struct SyncRawWaker(RawWaker); + unsafe impl Sync for SyncRawWaker {} + + static PANIC_WAKER_INSTANCE: SyncRawWaker = SyncRawWaker(raw_panic_waker()); + + // SAFETY: `Waker` is #[repr(transparent)] over its `RawWaker`. + unsafe { &*(&PANIC_WAKER_INSTANCE.0 as *const RawWaker as *const Waker) } +} + +#[cfg(test)] +mod tests { + #[test] + #[should_panic(expected = "should not be woken")] + fn issue_2091_cross_thread_segfault() { + let waker = std::thread::spawn(super::panic_waker_ref).join().unwrap(); + waker.wake_by_ref(); } - PANIC_WAKER_INSTANCE.with(|l| unsafe { &*l.get() }) } diff --git a/futures-test/src/task/wake_counter.rs b/futures-test/src/task/wake_counter.rs index cf496c2ddb..52c63e1cc9 100644 --- a/futures-test/src/task/wake_counter.rs +++ b/futures-test/src/task/wake_counter.rs @@ -1,7 +1,7 @@ use futures_core::task::Waker; use futures_util::task::{self, ArcWake}; -use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; /// Number of times the waker was awoken. /// diff --git a/futures-test/src/track_closed.rs b/futures-test/src/track_closed.rs new file mode 100644 index 0000000000..be883b1491 --- /dev/null +++ b/futures-test/src/track_closed.rs @@ -0,0 +1,143 @@ +use futures_io::AsyncWrite; +use futures_sink::Sink; +use std::{ + io::{self, IoSlice}, + pin::Pin, + task::{Context, Poll}, +}; + +/// Async wrapper that tracks whether it has been closed. +/// +/// See the `track_closed` methods on: +/// * [`SinkTestExt`](crate::sink::SinkTestExt::track_closed) +/// * [`AsyncWriteTestExt`](crate::io::AsyncWriteTestExt::track_closed) +#[pin_project::pin_project] +#[derive(Debug)] +pub struct TrackClosed { + #[pin] + inner: T, + closed: bool, +} + +impl TrackClosed { + pub(crate) fn new(inner: T) -> Self { + Self { inner, closed: false } + } + + /// Check whether this object has been closed. + pub fn is_closed(&self) -> bool { + self.closed + } + + /// Acquires a reference to the underlying object that this adaptor is + /// wrapping. + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Acquires a mutable reference to the underlying object that this + /// adaptor is wrapping. + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + /// Acquires a pinned mutable reference to the underlying object that + /// this adaptor is wrapping. + pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + self.project().inner + } + + /// Consumes this adaptor returning the underlying object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl AsyncWrite for TrackClosed { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if self.is_closed() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "Attempted to write after stream was closed", + ))); + } + self.project().inner.poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.is_closed() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "Attempted to flush after stream was closed", + ))); + } + assert!(!self.is_closed()); + self.project().inner.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.is_closed() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "Attempted to close after stream was closed", + ))); + } + let this = self.project(); + match this.inner.poll_close(cx) { + Poll::Ready(Ok(())) => { + *this.closed = true; + Poll::Ready(Ok(())) + } + other => other, + } + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + if self.is_closed() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "Attempted to write after stream was closed", + ))); + } + self.project().inner.poll_write_vectored(cx, bufs) + } +} + +impl> Sink for TrackClosed { + type Error = T::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + assert!(!self.is_closed()); + self.project().inner.poll_ready(cx) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + assert!(!self.is_closed()); + self.project().inner.start_send(item) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + assert!(!self.is_closed()); + self.project().inner.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + assert!(!self.is_closed()); + let this = self.project(); + match this.inner.poll_close(cx) { + Poll::Ready(Ok(())) => { + *this.closed = true; + Poll::Ready(Ok(())) + } + other => other, + } + } +} diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index fbe10480a2..46ec854b04 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "futures-util" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.45" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures-util/0.3.0" description = """ Common utilities and extension traits for the futures-rs library. """ @@ -16,7 +15,7 @@ default = ["std", "async-await", "async-await-macro"] std = ["alloc", "futures-core/std", "futures-task/std", "slab"] alloc = ["futures-core/alloc", "futures-task/alloc"] async-await = [] -async-await-macro = ["async-await", "futures-macro", "proc-macro-hack", "proc-macro-nested"] +async-await-macro = ["async-await", "futures-macro"] compat = ["std", "futures_01"] io-compat = ["io", "compat", "tokio-io"] sink = ["futures-sink"] @@ -27,29 +26,32 @@ channel = ["std", "futures-channel"] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. unstable = ["futures-core/unstable", "futures-task/unstable"] -cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic", "futures-task/cfg-target-has-atomic"] bilock = [] -read-initializer = ["io", "futures-io/read-initializer", "futures-io/unstable"] +write-all-vectored = ["io"] + +# These features are no longer used. +# TODO: remove in the next major version. +cfg-target-has-atomic = [] [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.4", default-features = false, features = ["std"], optional = true } -futures-io = { path = "../futures-io", version = "0.3.4", default-features = false, features = ["std"], optional = true } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false, optional = true } -futures-macro = { path = "../futures-macro", version = "0.3.4", default-features = false, optional = true } -proc-macro-hack = { version = "0.5.9", optional = true } -proc-macro-nested = { version = "0.1.2", optional = true } -slab = { version = "0.4", optional = true } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["std"], optional = true } +futures-io = { path = "../futures-io", version = "0.3.21", default-features = false, features = ["std"], optional = true } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false, optional = true } +futures-macro = { path = "../futures-macro", version = "=0.3.21", default-features = false, optional = true } +slab = { version = "0.4.2", optional = true } memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } -pin-utils = "0.1.0-alpha.4" +pin-utils = "0.1.0" +pin-project-lite = "0.2.4" [dev-dependencies] -futures = { path = "../futures", version = "0.3.4", features = ["async-await"] } -futures-test = { path = "../futures-test", version = "0.3.4" } +futures = { path = "../futures", features = ["async-await", "thread-pool"] } +futures-test = { path = "../futures-test" } tokio = "0.1.11" [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/futures-util/README.md b/futures-util/README.md new file mode 100644 index 0000000000..6e0aaed847 --- /dev/null +++ b/futures-util/README.md @@ -0,0 +1,23 @@ +# futures-util + +Common utilities and extension traits for the futures-rs library. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +futures-util = "0.3" +``` + +The current `futures-util` requires Rust 1.45 or later. + +## License + +Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or +[MIT license](LICENSE-MIT) at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/futures-util/benches/flatten_unordered.rs b/futures-util/benches/flatten_unordered.rs new file mode 100644 index 0000000000..64d5f9a4e3 --- /dev/null +++ b/futures-util/benches/flatten_unordered.rs @@ -0,0 +1,66 @@ +#![feature(test)] + +extern crate test; +use crate::test::Bencher; + +use futures::channel::oneshot; +use futures::executor::block_on; +use futures::future::{self, FutureExt}; +use futures::stream::{self, StreamExt}; +use futures::task::Poll; +use std::collections::VecDeque; +use std::thread; + +#[bench] +fn oneshot_streams(b: &mut Bencher) { + const STREAM_COUNT: usize = 10_000; + const STREAM_ITEM_COUNT: usize = 1; + + b.iter(|| { + let mut txs = VecDeque::with_capacity(STREAM_COUNT); + let mut rxs = Vec::new(); + + for _ in 0..STREAM_COUNT { + let (tx, rx) = oneshot::channel(); + txs.push_back(tx); + rxs.push(rx); + } + + thread::spawn(move || { + let mut last = 1; + while let Some(tx) = txs.pop_front() { + let _ = tx.send(stream::iter(last..last + STREAM_ITEM_COUNT)); + last += STREAM_ITEM_COUNT; + } + }); + + let mut flatten = stream::unfold(rxs.into_iter(), |mut vals| { + async { + if let Some(next) = vals.next() { + let val = next.await.unwrap(); + Some((val, vals)) + } else { + None + } + } + .boxed() + }) + .flatten_unordered(None); + + block_on(future::poll_fn(move |cx| { + let mut count = 0; + loop { + match flatten.poll_next_unpin(cx) { + Poll::Ready(None) => break, + Poll::Ready(Some(_)) => { + count += 1; + } + _ => {} + } + } + assert_eq!(count, STREAM_COUNT * STREAM_ITEM_COUNT); + + Poll::Ready(()) + })) + }); +} diff --git a/futures-util/benches/futures_unordered.rs b/futures-util/benches/futures_unordered.rs index 05e9eadb79..d5fe7a59de 100644 --- a/futures-util/benches/futures_unordered.rs +++ b/futures-util/benches/futures_unordered.rs @@ -6,7 +6,7 @@ use crate::test::Bencher; use futures::channel::oneshot; use futures::executor::block_on; use futures::future; -use futures::stream::{StreamExt, FuturesUnordered}; +use futures::stream::{FuturesUnordered, StreamExt}; use futures::task::Poll; use std::collections::VecDeque; use std::thread; @@ -34,7 +34,7 @@ fn oneshots(b: &mut Bencher) { block_on(future::poll_fn(move |cx| { loop { if let Poll::Ready(None) = rxs.poll_next_unpin(cx) { - break + break; } } Poll::Ready(()) diff --git a/futures-util/benches_disabled/bilock.rs b/futures-util/benches_disabled/bilock.rs index 78b5edb6bf..417f75d31e 100644 --- a/futures-util/benches_disabled/bilock.rs +++ b/futures-util/benches_disabled/bilock.rs @@ -2,125 +2,121 @@ #[cfg(feature = "bilock")] mod bench { -use futures::task::{Context, Waker}; -use futures::executor::LocalPool; -use futures_util::lock::BiLock; -use futures_util::lock::BiLockAcquire; -use futures_util::lock::BiLockAcquired; -use futures_util::task::ArcWake; + use futures::executor::LocalPool; + use futures::task::{Context, Waker}; + use futures_util::lock::BiLock; + use futures_util::lock::BiLockAcquire; + use futures_util::lock::BiLockAcquired; + use futures_util::task::ArcWake; -use std::sync::Arc; -use test::Bencher; + use std::sync::Arc; + use test::Bencher; -fn notify_noop() -> Waker { - struct Noop; + fn notify_noop() -> Waker { + struct Noop; - impl ArcWake for Noop { - fn wake(_: &Arc) {} - } - - ArcWake::into_waker(Arc::new(Noop)) -} - - -/// Pseudo-stream which simply calls `lock.poll()` on `poll` -struct LockStream { - lock: BiLockAcquire, -} - -impl LockStream { - fn new(lock: BiLock) -> LockStream { - LockStream { - lock: lock.lock() + impl ArcWake for Noop { + fn wake(_: &Arc) {} } - } - /// Release a lock after it was acquired in `poll`, - /// so `poll` could be called again. - fn release_lock(&mut self, guard: BiLockAcquired) { - self.lock = guard.unlock().lock() + ArcWake::into_waker(Arc::new(Noop)) } -} - -impl Stream for LockStream { - type Item = BiLockAcquired; - type Error = (); - fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll, Self::Error> { - self.lock.poll(cx).map(|a| a.map(Some)) + /// Pseudo-stream which simply calls `lock.poll()` on `poll` + struct LockStream { + lock: BiLockAcquire, } -} - - -#[bench] -fn contended(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - // Try poll second lock while first lock still holds the lock - match y.poll_next(&mut waker) { - Ok(Poll::Pending) => (), - _ => panic!(), - }; - - x.release_lock(x_guard); - - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; - - y.release_lock(y_guard); + impl LockStream { + fn new(lock: BiLock) -> Self { + Self { lock: lock.lock() } } - (x, y) - }); -} - -#[bench] -fn lock_unlock(b: &mut Bencher) { - let pool = LocalPool::new(); - let mut exec = pool.executor(); - let waker = notify_noop(); - let mut map = task::LocalMap::new(); - let mut waker = task::Context::new(&mut map, &waker, &mut exec); - b.iter(|| { - let (x, y) = BiLock::new(1); - - let mut x = LockStream::new(x); - let mut y = LockStream::new(y); + /// Release a lock after it was acquired in `poll`, + /// so `poll` could be called again. + fn release_lock(&mut self, guard: BiLockAcquired) { + self.lock = guard.unlock().lock() + } + } - for _ in 0..1000 { - let x_guard = match x.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; + impl Stream for LockStream { + type Item = BiLockAcquired; + type Error = (); - x.release_lock(x_guard); + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll, Self::Error> { + self.lock.poll(cx).map(|a| a.map(Some)) + } + } - let y_guard = match y.poll_next(&mut waker) { - Ok(Poll::Ready(Some(guard))) => guard, - _ => panic!(), - }; + #[bench] + fn contended(b: &mut Bencher) { + let pool = LocalPool::new(); + let mut exec = pool.executor(); + let waker = notify_noop(); + let mut map = task::LocalMap::new(); + let mut waker = task::Context::new(&mut map, &waker, &mut exec); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + let mut x = LockStream::new(x); + let mut y = LockStream::new(y); + + for _ in 0..1000 { + let x_guard = match x.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + // Try poll second lock while first lock still holds the lock + match y.poll_next(&mut waker) { + Ok(Poll::Pending) => (), + _ => panic!(), + }; + + x.release_lock(x_guard); + + let y_guard = match y.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + y.release_lock(y_guard); + } + (x, y) + }); + } - y.release_lock(y_guard); - } - (x, y) - }) -} + #[bench] + fn lock_unlock(b: &mut Bencher) { + let pool = LocalPool::new(); + let mut exec = pool.executor(); + let waker = notify_noop(); + let mut map = task::LocalMap::new(); + let mut waker = task::Context::new(&mut map, &waker, &mut exec); + + b.iter(|| { + let (x, y) = BiLock::new(1); + + let mut x = LockStream::new(x); + let mut y = LockStream::new(y); + + for _ in 0..1000 { + let x_guard = match x.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + x.release_lock(x_guard); + + let y_guard = match y.poll_next(&mut waker) { + Ok(Poll::Ready(Some(guard))) => guard, + _ => panic!(), + }; + + y.release_lock(y_guard); + } + (x, y) + }) + } } diff --git a/futures-util/build.rs b/futures-util/build.rs new file mode 100644 index 0000000000..05e0496d94 --- /dev/null +++ b/futures-util/build.rs @@ -0,0 +1,41 @@ +// The rustc-cfg listed below are considered public API, but it is *unstable* +// and outside of the normal semver guarantees: +// +// - `futures_no_atomic_cas` +// Assume the target does *not* support atomic CAS operations. +// This is usually detected automatically by the build script, but you may +// need to enable it manually when building for custom targets or using +// non-cargo build systems that don't run the build script. +// +// With the exceptions mentioned above, the rustc-cfg emitted by the build +// script are *not* public API. + +#![warn(rust_2018_idioms, single_use_lifetimes)] + +use std::env; + +include!("no_atomic_cas.rs"); + +fn main() { + let target = match env::var("TARGET") { + Ok(target) => target, + Err(e) => { + println!( + "cargo:warning={}: unable to get TARGET environment variable: {}", + env!("CARGO_PKG_NAME"), + e + ); + return; + } + }; + + // Note that this is `no_*`, not `has_*`. This allows treating + // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't + // run. This is needed for compatibility with non-cargo build systems that + // don't run the build script. + if NO_ATOMIC_CAS.contains(&&*target) { + println!("cargo:rustc-cfg=futures_no_atomic_cas"); + } + + println!("cargo:rerun-if-changed=no_atomic_cas.rs"); +} diff --git a/futures-util/no_atomic_cas.rs b/futures-util/no_atomic_cas.rs new file mode 120000 index 0000000000..3d7380fadd --- /dev/null +++ b/futures-util/no_atomic_cas.rs @@ -0,0 +1 @@ +../no_atomic_cas.rs \ No newline at end of file diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs new file mode 100644 index 0000000000..bb82dd0db8 --- /dev/null +++ b/futures-util/src/abortable.rs @@ -0,0 +1,185 @@ +use crate::task::AtomicWaker; +use alloc::sync::Arc; +use core::fmt; +use core::pin::Pin; +use core::sync::atomic::{AtomicBool, Ordering}; +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; +use futures_core::Stream; +use pin_project_lite::pin_project; + +pin_project! { + /// A future/stream which can be remotely short-circuited using an `AbortHandle`. + #[derive(Debug, Clone)] + #[must_use = "futures/streams do nothing unless you poll them"] + pub struct Abortable { + #[pin] + task: T, + inner: Arc, + } +} + +impl Abortable { + /// Creates a new `Abortable` future/stream using an existing `AbortRegistration`. + /// `AbortRegistration`s can be acquired through `AbortHandle::new`. + /// + /// When `abort` is called on the handle tied to `reg` or if `abort` has + /// already been called, the future/stream will complete immediately without making + /// any further progress. + /// + /// # Examples: + /// + /// Usage with futures: + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future::{Abortable, AbortHandle, Aborted}; + /// + /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); + /// let future = Abortable::new(async { 2 }, abort_registration); + /// abort_handle.abort(); + /// assert_eq!(future.await, Err(Aborted)); + /// # }); + /// ``` + /// + /// Usage with streams: + /// + /// ``` + /// # futures::executor::block_on(async { + /// # use futures::future::{Abortable, AbortHandle}; + /// # use futures::stream::{self, StreamExt}; + /// + /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); + /// let mut stream = Abortable::new(stream::iter(vec![1, 2, 3]), abort_registration); + /// abort_handle.abort(); + /// assert_eq!(stream.next().await, None); + /// # }); + /// ``` + pub fn new(task: T, reg: AbortRegistration) -> Self { + Self { task, inner: reg.inner } + } + + /// Checks whether the task has been aborted. Note that all this + /// method indicates is whether [`AbortHandle::abort`] was *called*. + /// This means that it will return `true` even if: + /// * `abort` was called after the task had completed. + /// * `abort` was called while the task was being polled - the task may still be running and + /// will not be stopped until `poll` returns. + pub fn is_aborted(&self) -> bool { + self.inner.aborted.load(Ordering::Relaxed) + } +} + +/// A registration handle for an `Abortable` task. +/// Values of this type can be acquired from `AbortHandle::new` and are used +/// in calls to `Abortable::new`. +#[derive(Debug)] +pub struct AbortRegistration { + inner: Arc, +} + +/// A handle to an `Abortable` task. +#[derive(Debug, Clone)] +pub struct AbortHandle { + inner: Arc, +} + +impl AbortHandle { + /// Creates an (`AbortHandle`, `AbortRegistration`) pair which can be used + /// to abort a running future or stream. + /// + /// This function is usually paired with a call to [`Abortable::new`]. + pub fn new_pair() -> (Self, AbortRegistration) { + let inner = + Arc::new(AbortInner { waker: AtomicWaker::new(), aborted: AtomicBool::new(false) }); + + (Self { inner: inner.clone() }, AbortRegistration { inner }) + } +} + +// Inner type storing the waker to awaken and a bool indicating that it +// should be aborted. +#[derive(Debug)] +struct AbortInner { + waker: AtomicWaker, + aborted: AtomicBool, +} + +/// Indicator that the `Abortable` task was aborted. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Aborted; + +impl fmt::Display for Aborted { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "`Abortable` future has been aborted") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Aborted {} + +impl Abortable { + fn try_poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + poll: impl Fn(Pin<&mut T>, &mut Context<'_>) -> Poll, + ) -> Poll> { + // Check if the task has been aborted + if self.is_aborted() { + return Poll::Ready(Err(Aborted)); + } + + // attempt to complete the task + if let Poll::Ready(x) = poll(self.as_mut().project().task, cx) { + return Poll::Ready(Ok(x)); + } + + // Register to receive a wakeup if the task is aborted in the future + self.inner.waker.register(cx.waker()); + + // Check to see if the task was aborted between the first check and + // registration. + // Checking with `is_aborted` which uses `Relaxed` is sufficient because + // `register` introduces an `AcqRel` barrier. + if self.is_aborted() { + return Poll::Ready(Err(Aborted)); + } + + Poll::Pending + } +} + +impl Future for Abortable +where + Fut: Future, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.try_poll(cx, |fut, cx| fut.poll(cx)) + } +} + +impl Stream for Abortable +where + St: Stream, +{ + type Item = St::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.try_poll(cx, |stream, cx| stream.poll_next(cx)).map(Result::ok).map(Option::flatten) + } +} + +impl AbortHandle { + /// Abort the `Abortable` stream/future associated with this handle. + /// + /// Notifies the Abortable task associated with this handle that it + /// should abort. Note that if the task is currently being polled on + /// another thread, it will not immediately stop running. Instead, it will + /// continue to run until its poll method returns. + pub fn abort(&self) { + self.inner.aborted.store(true, Ordering::Relaxed); + self.inner.waker.wake(); + } +} diff --git a/futures-util/src/async_await/join_mod.rs b/futures-util/src/async_await/join_mod.rs index 2e27f2149a..28f3b232e7 100644 --- a/futures-util/src/async_await/join_mod.rs +++ b/futures-util/src/async_await/join_mod.rs @@ -1,16 +1,12 @@ //! The `join` macro. -use proc_macro_hack::proc_macro_hack; - -#[doc(hidden)] -#[macro_export] macro_rules! document_join_macro { ($join:item $try_join:item) => { /// Polls multiple futures simultaneously, returning a tuple /// of all results once complete. /// /// While `join!(a, b)` is similar to `(a.await, b.await)`, - /// `join!` polls both futures concurrently and therefore is more efficent. + /// `join!` polls both futures concurrently and therefore is more efficient. /// /// This macro is only usable inside of async functions, closures, and blocks. /// It is also gated behind the `async-await` feature of this library, which is @@ -24,8 +20,13 @@ macro_rules! document_join_macro { /// /// let a = async { 1 }; /// let b = async { 2 }; - /// /// assert_eq!(join!(a, b), (1, 2)); + /// + /// // `join!` is variadic, so you can pass any number of futures + /// let c = async { 3 }; + /// let d = async { 4 }; + /// let e = async { 5 }; + /// assert_eq!(join!(c, d, e), (3, 4, 5)); /// # }); /// ``` $join @@ -50,9 +51,14 @@ macro_rules! document_join_macro { /// use futures::try_join; /// /// let a = async { Ok::(1) }; - /// let b = async { Ok::(2) }; - /// + /// let b = async { Ok::(2) }; /// assert_eq!(try_join!(a, b), Ok((1, 2))); + /// + /// // `try_join!` is variadic, so you can pass any number of futures + /// let c = async { Ok::(3) }; + /// let d = async { Ok::(4) }; + /// let e = async { Ok::(5) }; + /// assert_eq!(try_join!(c, d, e), Ok((3, 4, 5))); /// # }); /// ``` /// @@ -73,10 +79,32 @@ macro_rules! document_join_macro { } } +#[allow(unreachable_pub)] +#[doc(hidden)] +pub use futures_macro::join_internal; + +#[allow(unreachable_pub)] +#[doc(hidden)] +pub use futures_macro::try_join_internal; + document_join_macro! { - #[proc_macro_hack(support_nested)] - pub use futures_macro::join; + #[macro_export] + macro_rules! join { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::join_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::try_join; + #[macro_export] + macro_rules! try_join { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::try_join_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 69cae13bed..7276da227a 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -3,37 +3,44 @@ //! This module contains a number of functions and combinators for working //! with `async`/`await` code. -use futures_core::future::Future; -use futures_core::stream::Stream; - -#[doc(hidden)] -pub use futures_core::future::FusedFuture; -#[doc(hidden)] -pub use futures_core::stream::FusedStream; +use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; #[macro_use] mod poll; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 pub use self::poll::*; #[macro_use] mod pending; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 pub use self::pending::*; // Primary export is a macro #[cfg(feature = "async-await-macro")] mod join_mod; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 #[cfg(feature = "async-await-macro")] pub use self::join_mod::*; // Primary export is a macro #[cfg(feature = "async-await-macro")] mod select_mod; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 #[cfg(feature = "async-await-macro")] pub use self::select_mod::*; +// Primary export is a macro +#[cfg(feature = "async-await-macro")] +mod stream_select_mod; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 +#[cfg(feature = "async-await-macro")] +pub use self::stream_select_mod::*; + #[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] mod random; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/64762 #[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] pub use self::random::*; diff --git a/futures-util/src/async_await/pending.rs b/futures-util/src/async_await/pending.rs index b143869e77..5d7a431811 100644 --- a/futures-util/src/async_await/pending.rs +++ b/futures-util/src/async_await/pending.rs @@ -15,8 +15,8 @@ use futures_core::task::{Context, Poll}; #[macro_export] macro_rules! pending { () => { - $crate::async_await::pending_once().await - } + $crate::__private::async_await::pending_once().await + }; } #[doc(hidden)] diff --git a/futures-util/src/async_await/poll.rs b/futures-util/src/async_await/poll.rs index dffa94b918..b62f45a943 100644 --- a/futures-util/src/async_await/poll.rs +++ b/futures-util/src/async_await/poll.rs @@ -9,11 +9,15 @@ use futures_core::task::{Context, Poll}; /// This macro is only usable inside of `async` functions, closures, and blocks. /// It is also gated behind the `async-await` feature of this library, which is /// activated by default. +/// +/// If you need the result of polling a [`Stream`](crate::stream::Stream), +/// you can use this macro with the [`next`](crate::stream::StreamExt::next) method: +/// `poll!(stream.next())`. #[macro_export] macro_rules! poll { ($x:expr $(,)?) => { - $crate::async_await::poll($x).await - } + $crate::__private::async_await::poll($x).await + }; } #[doc(hidden)] diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 38153c7f7a..1d13067d38 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -1,9 +1,5 @@ //! The `select` macro. -use proc_macro_hack::proc_macro_hack; - -#[doc(hidden)] -#[macro_export] macro_rules! document_select_macro { // This branch is required for `futures 0.3.1`, from before select_biased was introduced ($select:item) => { @@ -16,7 +12,7 @@ macro_rules! document_select_macro { /// (e.g. an `async fn` call) instead of a `Future` by name the `Unpin` /// requirement is relaxed, since the macro will pin the resulting `Future` /// on the stack. However the `Future` returned by the expression must - /// still implement `FusedFuture`. This difference is presented + /// still implement `FusedFuture`. /// /// Futures and streams which are not already fused can be fused using the /// `.fuse()` method. Note, though, that fusing a future or stream directly @@ -33,9 +29,6 @@ macro_rules! document_select_macro { /// It is also gated behind the `async-await` feature of this library, which is /// activated by default. /// - /// Note that `select!` relies on `proc-macro-hack`, and may require to set the - /// compiler's recursion limit very high, e.g. `#![recursion_limit="1024"]`. - /// /// # Examples /// /// ``` @@ -86,7 +79,7 @@ macro_rules! document_select_macro { /// a_res = async_identity_fn(62).fuse() => a_res + 1, /// b_res = async_identity_fn(13).fuse() => b_res, /// }; - /// assert!(res == 63 || res == 12); + /// assert!(res == 63 || res == 13); /// # }); /// ``` /// @@ -158,7 +151,7 @@ macro_rules! document_select_macro { }; ($select:item $select_biased:item) => { - $crate::document_select_macro!($select); + document_select_macro!($select); /// Polls multiple futures and streams simultaneously, executing the branch /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready, @@ -169,7 +162,7 @@ macro_rules! document_select_macro { /// (e.g. an `async fn` call) instead of a `Future` by name the `Unpin` /// requirement is relaxed, since the macro will pin the resulting `Future` /// on the stack. However the `Future` returned by the expression must - /// still implement `FusedFuture`. This difference is presented + /// still implement `FusedFuture`. /// /// Futures and streams which are not already fused can be fused using the /// `.fuse()` method. Note, though, that fusing a future or stream directly @@ -310,11 +303,34 @@ macro_rules! document_select_macro { }; } +#[cfg(feature = "std")] +#[allow(unreachable_pub)] +#[doc(hidden)] +pub use futures_macro::select_internal; + +#[allow(unreachable_pub)] +#[doc(hidden)] +pub use futures_macro::select_biased_internal; + document_select_macro! { #[cfg(feature = "std")] - #[proc_macro_hack(support_nested)] - pub use futures_macro::select; + #[macro_export] + macro_rules! select { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::select_internal! { + $( $tokens )* + } + }} + } - #[proc_macro_hack(support_nested)] - pub use futures_macro::select_biased; + #[macro_export] + macro_rules! select_biased { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::select_biased_internal! { + $( $tokens )* + } + }} + } } diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs new file mode 100644 index 0000000000..1c8002fff3 --- /dev/null +++ b/futures-util/src/async_await/stream_select_mod.rs @@ -0,0 +1,40 @@ +//! The `stream_select` macro. + +#[cfg(feature = "std")] +#[allow(unreachable_pub)] +#[doc(hidden)] +pub use futures_macro::stream_select_internal; + +/// Combines several streams, all producing the same `Item` type, into one stream. +/// This is similar to `select_all` but does not require the streams to all be the same type. +/// It also keeps the streams inline, and does not require `Box`s to be allocated. +/// Streams passed to this macro must be `Unpin`. +/// +/// If multiple streams are ready, one will be pseudo randomly selected at runtime. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::{stream, StreamExt, stream_select}; +/// let endless_ints = |i| stream::iter(vec![i].into_iter().cycle()).fuse(); +/// +/// let mut endless_numbers = stream_select!(endless_ints(1i32), endless_ints(2), endless_ints(3)); +/// match endless_numbers.next().await { +/// Some(1) => println!("Got a 1"), +/// Some(2) => println!("Got a 2"), +/// Some(3) => println!("Got a 3"), +/// _ => unreachable!(), +/// } +/// # }); +/// ``` +#[cfg(feature = "std")] +#[macro_export] +macro_rules! stream_select { + ($($tokens:tt)*) => {{ + use $crate::__private as __futures_crate; + $crate::stream_select_internal! { + $( $tokens )* + } + }} +} diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index 9bb00bf00c..36de1da98d 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -1,20 +1,18 @@ use futures_01::executor::{ - spawn as spawn01, Notify as Notify01, NotifyHandle as NotifyHandle01, - Spawn as Spawn01, UnsafeNotify as UnsafeNotify01, -}; -use futures_01::{ - Async as Async01, Future as Future01, - Stream as Stream01, + spawn as spawn01, Notify as Notify01, NotifyHandle as NotifyHandle01, Spawn as Spawn01, + UnsafeNotify as UnsafeNotify01, }; +use futures_01::{Async as Async01, Future as Future01, Stream as Stream01}; #[cfg(feature = "sink")] use futures_01::{AsyncSink as AsyncSink01, Sink as Sink01}; -use futures_core::{task as task03, future::Future as Future03, stream::Stream as Stream03}; -use std::pin::Pin; -use std::task::Context; +use futures_core::{future::Future as Future03, stream::Stream as Stream03, task as task03}; #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; +use std::pin::Pin; +use std::task::Context; #[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use io::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; @@ -31,10 +29,8 @@ impl Unpin for Compat01As03 {} impl Compat01As03 { /// Wraps a futures 0.1 Future, Stream, AsyncRead, or AsyncWrite /// object in a futures 0.3-compatible wrapper. - pub fn new(object: T) -> Compat01As03 { - Compat01As03 { - inner: spawn01(object), - } + pub fn new(object: T) -> Self { + Self { inner: spawn01(object) } } fn in_notify(&mut self, cx: &mut Context<'_>, f: impl FnOnce(&mut T) -> R) -> R { @@ -68,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 @@ -94,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; @@ -115,6 +113,7 @@ impl Stream01CompatExt for St {} /// Extension trait for futures 0.1 [`Sink`](futures_01::sink::Sink) #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub trait Sink01CompatExt: Sink01 { /// Converts a futures 0.1 /// [`Sink`](futures_01::sink::Sink) @@ -122,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}; @@ -155,10 +155,7 @@ fn poll_01_to_03(x: Result, E>) -> task03::Poll> { impl Future03 for Compat01As03 { type Output = Result; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> task03::Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> task03::Poll { poll_01_to_03(self.in_notify(cx, Future01::poll)) } } @@ -180,6 +177,7 @@ impl Stream03 for Compat01As03 { /// Converts a futures 0.1 Sink object to a futures 0.3-compatible version #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct Compat01As03Sink { @@ -194,19 +192,11 @@ impl Unpin for Compat01As03Sink {} #[cfg(feature = "sink")] impl Compat01As03Sink { /// Wraps a futures 0.1 Sink object in a futures 0.3-compatible wrapper. - pub fn new(inner: S) -> Compat01As03Sink { - Compat01As03Sink { - inner: spawn01(inner), - buffer: None, - close_started: false - } + pub fn new(inner: S) -> Self { + Self { inner: spawn01(inner), buffer: None, close_started: false } } - fn in_notify( - &mut self, - cx: &mut Context<'_>, - f: impl FnOnce(&mut S) -> R, - ) -> R { + fn in_notify(&mut self, cx: &mut Context<'_>, f: impl FnOnce(&mut S) -> R) -> R { let notify = &WakerToHandle(cx.waker()); self.inner.poll_fn_notify(notify, 0, f) } @@ -253,10 +243,7 @@ where { type Error = S::SinkError; - fn start_send( - mut self: Pin<&mut Self>, - item: SinkItem, - ) -> Result<(), Self::Error> { + fn start_send(mut self: Pin<&mut Self>, item: SinkItem) -> Result<(), Self::Error> { debug_assert!(self.buffer.is_none()); self.buffer = Some(item); Ok(()) @@ -286,9 +273,7 @@ where match self.in_notify(cx, |f| match item { Some(i) => match f.start_send(i)? { AsyncSink01::Ready => f.poll_complete().map(|i| (i, None)), - AsyncSink01::NotReady(t) => { - Ok((Async01::NotReady, Some(t))) - } + AsyncSink01::NotReady(t) => Ok((Async01::NotReady, Some(t))), }, None => f.poll_complete().map(|i| (i, None)), })? { @@ -341,10 +326,10 @@ struct NotifyWaker(task03::Waker); struct WakerToHandle<'a>(&'a task03::Waker); impl From> for NotifyHandle01 { - fn from(handle: WakerToHandle<'_>) -> NotifyHandle01 { + fn from(handle: WakerToHandle<'_>) -> Self { let ptr = Box::new(NotifyWaker(handle.0.clone())); - unsafe { NotifyHandle01::new(Box::into_raw(ptr)) } + unsafe { Self::new(Box::into_raw(ptr)) } } } @@ -366,29 +351,28 @@ unsafe impl UnsafeNotify01 for NotifyWaker { } #[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] mod io { use super::*; - #[cfg(feature = "read-initializer")] - use futures_io::Initializer; use futures_io::{AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03}; use std::io::Error; use tokio_io::{AsyncRead as AsyncRead01, AsyncWrite as AsyncWrite01}; /// Extension trait for tokio-io [`AsyncRead`](tokio_io::AsyncRead) + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] pub trait AsyncRead01CompatExt: AsyncRead01 { /// Converts a tokio-io [`AsyncRead`](tokio_io::AsyncRead) into a futures-io 0.3 /// [`AsyncRead`](futures_io::AsyncRead). /// /// ``` - /// #![feature(impl_trait_in_bindings)] - /// # #![allow(incomplete_features)] + /// # 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; /// /// let input = b"Hello World!"; - /// let reader: impl tokio_io::AsyncRead = std::io::Cursor::new(input); - /// let mut reader: impl futures::io::AsyncRead + Unpin = reader.compat(); + /// let reader /* : impl tokio_io::AsyncRead */ = std::io::Cursor::new(input); + /// let mut reader /* : impl futures::io::AsyncRead + Unpin */ = reader.compat(); /// /// let mut output = Vec::with_capacity(12); /// reader.read_to_end(&mut output).await.unwrap(); @@ -405,11 +389,13 @@ mod io { impl AsyncRead01CompatExt for R {} /// Extension trait for tokio-io [`AsyncWrite`](tokio_io::AsyncWrite) + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] pub trait AsyncWrite01CompatExt: AsyncWrite01 { /// Converts a tokio-io [`AsyncWrite`](tokio_io::AsyncWrite) into a futures-io 0.3 /// [`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; @@ -433,39 +419,35 @@ mod io { impl AsyncWrite01CompatExt for W {} impl AsyncRead03 for Compat01As03 { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - // check if `prepare_uninitialized_buffer` needs zeroing - if self.inner.get_ref().prepare_uninitialized_buffer(&mut [1]) { - Initializer::zeroing() - } else { - Initializer::nop() - } - } - - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> task03::Poll> - { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, |x| x.poll_read(buf))) } } impl AsyncWrite03 for Compat01As03 { - fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> task03::Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, |x| x.poll_write(buf))) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> task03::Poll> - { + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, AsyncWrite01::poll_flush)) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> task03::Poll> - { + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> task03::Poll> { poll_01_to_03(self.in_notify(cx, AsyncWrite01::shutdown)) } } diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs index 3fd2ae0616..5d3a6e920b 100644 --- a/futures-util/src/compat/compat03as01.rs +++ b/futures-util/src/compat/compat03as01.rs @@ -1,31 +1,19 @@ +use crate::task::{self as task03, ArcWake as ArcWake03, WakerRef}; use futures_01::{ - task as task01, Async as Async01, Future as Future01, Poll as Poll01, - Stream as Stream01, + task as task01, Async as Async01, Future as Future01, Poll as Poll01, Stream as Stream01, }; #[cfg(feature = "sink")] -use futures_01::{ - AsyncSink as AsyncSink01, Sink as Sink01, StartSend as StartSend01, -}; +use futures_01::{AsyncSink as AsyncSink01, Sink as Sink01, StartSend as StartSend01}; use futures_core::{ - task::{RawWaker, RawWakerVTable}, future::TryFuture as TryFuture03, stream::TryStream as TryStream03, + task::{RawWaker, RawWakerVTable}, }; #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; -use crate::task::{ - self as task03, - ArcWake as ArcWake03, - WakerRef, -}; #[cfg(feature = "sink")] use std::marker::PhantomData; -use std::{ - mem, - pin::Pin, - sync::Arc, - task::Context, -}; +use std::{mem, pin::Pin, sync::Arc, task::Context}; /// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture) or /// [`TryStream`](futures_core::stream::TryStream) into a futures 0.1 @@ -40,6 +28,7 @@ pub struct Compat { /// Converts a futures 0.3 [`Sink`](futures_sink::Sink) into a futures 0.1 /// [`Sink`](futures_01::sink::Sink). #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct CompatSink { @@ -53,8 +42,8 @@ impl Compat { /// For types which implement appropriate futures `0.3` /// traits, the result will be a type which implements /// the corresponding futures 0.1 type. - pub fn new(inner: T) -> Compat { - Compat { inner } + pub fn new(inner: T) -> Self { + Self { inner } } /// Get a reference to 0.3 Future, Stream, AsyncRead, or AsyncWrite object @@ -79,10 +68,7 @@ impl Compat { impl CompatSink { /// Creates a new [`CompatSink`]. pub fn new(inner: T) -> Self { - CompatSink { - inner, - _phantom: PhantomData, - } + Self { inner, _phantom: PhantomData } } /// Get a reference to 0.3 Sink contained within. @@ -101,9 +87,7 @@ impl CompatSink { } } -fn poll_03_to_01(x: task03::Poll>) - -> Result, E> -{ +fn poll_03_to_01(x: task03::Poll>) -> Result, E> { match x? { task03::Poll::Ready(t) => Ok(Async01::Ready(t)), task03::Poll::Pending => Ok(Async01::NotReady), @@ -146,17 +130,10 @@ where type SinkItem = Item; type SinkError = T::Error; - fn start_send( - &mut self, - item: Self::SinkItem, - ) -> StartSend01 { - with_sink_context(self, |mut inner, cx| { - match inner.as_mut().poll_ready(cx)? { - task03::Poll::Ready(()) => { - inner.start_send(item).map(|()| AsyncSink01::Ready) - } - task03::Poll::Pending => Ok(AsyncSink01::NotReady(item)), - } + fn start_send(&mut self, item: Self::SinkItem) -> StartSend01 { + with_sink_context(self, |mut inner, cx| match inner.as_mut().poll_ready(cx)? { + task03::Poll::Ready(()) => inner.start_send(item).map(|()| AsyncSink01::Ready), + task03::Poll::Pending => Ok(AsyncSink01::NotReady(item)), }) } @@ -173,8 +150,8 @@ where struct Current(task01::Task); impl Current { - fn new() -> Current { - Current(task01::current()) + fn new() -> Self { + Self(task01::current()) } fn as_waker(&self) -> WakerRef<'_> { @@ -189,9 +166,9 @@ impl Current { // Lazily create the `Arc` only when the waker is actually cloned. // FIXME: remove `transmute` when a `Waker` -> `RawWaker` conversion // function is landed in `core`. - mem::transmute::( - task03::waker(Arc::new(ptr_to_current(ptr).clone())) - ) + mem::transmute::(task03::waker(Arc::new( + ptr_to_current(ptr).clone(), + ))) } unsafe fn drop(_: *const ()) {} unsafe fn wake(ptr: *const ()) { @@ -236,14 +213,13 @@ where } #[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] mod io { use super::*; use futures_io::{AsyncRead as AsyncRead03, AsyncWrite as AsyncWrite03}; use tokio_io::{AsyncRead as AsyncRead01, AsyncWrite as AsyncWrite01}; - fn poll_03_to_io(x: task03::Poll>) - -> Result - { + fn poll_03_to_io(x: task03::Poll>) -> Result { match x { task03::Poll::Ready(Ok(t)) => Ok(t), task03::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), @@ -260,17 +236,7 @@ mod io { } } - impl AsyncRead01 for Compat { - #[cfg(feature = "read-initializer")] - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - let initializer = self.inner.initializer(); - let does_init = initializer.should_initialize(); - if does_init { - initializer.initialize(buf); - } - does_init - } - } + impl AsyncRead01 for Compat {} impl std::io::Write for Compat { fn write(&mut self, buf: &[u8]) -> std::io::Result { diff --git a/futures-util/src/compat/executor.rs b/futures-util/src/compat/executor.rs index 82cb496a70..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; @@ -66,9 +67,7 @@ where fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError03> { let future = future.unit_error().compat(); - self.executor01 - .execute(future) - .map_err(|_| SpawnError03::shutdown()) + self.executor01.execute(future).map_err(|_| SpawnError03::shutdown()) } } diff --git a/futures-util/src/compat/mod.rs b/futures-util/src/compat/mod.rs index 18268364ec..4812803eb6 100644 --- a/futures-util/src/compat/mod.rs +++ b/futures-util/src/compat/mod.rs @@ -1,19 +1,22 @@ -//! Futures 0.1 / 0.3 shims +//! Interop between `futures` 0.1 and 0.3. //! //! This module is only available when the `compat` feature of this //! library is activated. mod executor; -pub use self::executor::{Executor01CompatExt, Executor01Future, Executor01As03}; +pub use self::executor::{Executor01As03, Executor01CompatExt, Executor01Future}; mod compat01as03; +#[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] +pub use self::compat01as03::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; pub use self::compat01as03::{Compat01As03, Future01CompatExt, Stream01CompatExt}; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::compat01as03::{Compat01As03Sink, Sink01CompatExt}; -#[cfg(feature = "io-compat")] -pub use self::compat01as03::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; mod compat03as01; pub use self::compat03as01::Compat; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::compat03as01::CompatSink; diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs new file mode 100644 index 0000000000..37ee03e6df --- /dev/null +++ b/futures-util/src/fns.rs @@ -0,0 +1,372 @@ +use core::fmt::{self, Debug}; +use core::marker::PhantomData; + +pub trait FnOnce1 { + type Output; + fn call_once(self, arg: A) -> Self::Output; +} + +impl FnOnce1 for T +where + T: FnOnce(A) -> R, +{ + type Output = R; + fn call_once(self, arg: A) -> R { + self(arg) + } +} + +pub trait FnMut1: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output; +} + +impl FnMut1 for T +where + T: FnMut(A) -> R, +{ + fn call_mut(&mut self, arg: A) -> R { + self(arg) + } +} + +// Not used, but present for completeness +#[allow(unreachable_pub)] +pub trait Fn1: FnMut1 { + fn call(&self, arg: A) -> Self::Output; +} + +impl Fn1 for T +where + T: Fn(A) -> R, +{ + fn call(&self, arg: A) -> R { + self(arg) + } +} + +macro_rules! trivial_fn_impls { + ($name:ident <$($arg:ident),*> $t:ty = $debug:literal) => { + impl<$($arg),*> Copy for $t {} + impl<$($arg),*> Clone for $t { + fn clone(&self) -> Self { *self } + } + impl<$($arg),*> Debug for $t { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str($debug) + } + } + impl<$($arg,)* A> FnMut1 for $t where Self: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + impl<$($arg,)* A> Fn1 for $t where Self: FnOnce1 { + fn call(&self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + pub(crate) fn $name<$($arg),*>() -> $t { + Default::default() + } + } +} + +pub struct OkFn(PhantomData); + +impl Default for OkFn { + fn default() -> Self { + Self(PhantomData) + } +} + +impl FnOnce1 for OkFn { + type Output = Result; + fn call_once(self, arg: A) -> Self::Output { + Ok(arg) + } +} + +trivial_fn_impls!(ok_fn OkFn = "Ok"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct ChainFn(F, G); + +impl FnOnce1 for ChainFn +where + F: FnOnce1, + G: FnOnce1, +{ + type Output = G::Output; + fn call_once(self, arg: A) -> Self::Output { + self.1.call_once(self.0.call_once(arg)) + } +} +impl FnMut1 for ChainFn +where + F: FnMut1, + G: FnMut1, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.1.call_mut(self.0.call_mut(arg)) + } +} +impl Fn1 for ChainFn +where + F: Fn1, + G: Fn1, +{ + fn call(&self, arg: A) -> Self::Output { + self.1.call(self.0.call(arg)) + } +} +pub(crate) fn chain_fn(f: F, g: G) -> ChainFn { + ChainFn(f, g) +} + +#[derive(Default)] +pub struct MergeResultFn; + +impl FnOnce1> for MergeResultFn { + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + match arg { + Ok(x) => x, + Err(x) => x, + } + } +} +trivial_fn_impls!(merge_result_fn <> MergeResultFn = "merge_result"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct InspectFn(F); + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl FnOnce1 for InspectFn +where + F: for<'a> FnOnce1<&'a A, Output = ()>, +{ + type Output = A; + fn call_once(self, arg: A) -> Self::Output { + self.0.call_once(&arg); + arg + } +} +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl FnMut1 for InspectFn +where + F: for<'a> FnMut1<&'a A, Output = ()>, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.0.call_mut(&arg); + arg + } +} +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl Fn1 for InspectFn +where + F: for<'a> Fn1<&'a A, Output = ()>, +{ + fn call(&self, arg: A) -> Self::Output { + self.0.call(&arg); + arg + } +} +pub(crate) fn inspect_fn(f: F) -> InspectFn { + InspectFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapOkFn(F); + +impl FnOnce1> for MapOkFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapOkFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapOkFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call(x)) + } +} +pub(crate) fn map_ok_fn(f: F) -> MapOkFn { + MapOkFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapErrFn(F); + +impl FnOnce1> for MapErrFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapErrFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapErrFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call(x)) + } +} +pub(crate) fn map_err_fn(f: F) -> MapErrFn { + MapErrFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectOkFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectOkFn +where + F: FnOnce1<&'a T, Output = ()>, +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { + self.0.call_once(x) + } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectOkFn +where + F: FnMut1<&'a T, Output = ()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { + self.0.call_mut(x) + } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectOkFn +where + F: Fn1<&'a T, Output = ()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { + self.0.call(x) + } + } +} +pub(crate) fn inspect_ok_fn(f: F) -> InspectOkFn { + InspectOkFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectErrFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectErrFn +where + F: FnOnce1<&'a E, Output = ()>, +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { + self.0.call_once(x) + } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectErrFn +where + F: FnMut1<&'a E, Output = ()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { + self.0.call_mut(x) + } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectErrFn +where + F: Fn1<&'a E, Output = ()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { + self.0.call(x) + } + } +} +pub(crate) fn inspect_err_fn(f: F) -> InspectErrFn { + InspectErrFn(f) +} + +pub(crate) type MapOkOrElseFn = ChainFn, ChainFn, MergeResultFn>>; +pub(crate) fn map_ok_or_else_fn(f: F, g: G) -> MapOkOrElseFn { + chain_fn(map_ok_fn(f), chain_fn(map_err_fn(g), merge_result_fn())) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct UnwrapOrElseFn(F); + +impl FnOnce1> for UnwrapOrElseFn +where + F: FnOnce1, +{ + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_once(x)) + } +} +impl FnMut1> for UnwrapOrElseFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_mut(x)) + } +} +impl Fn1> for UnwrapOrElseFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call(x)) + } +} +pub(crate) fn unwrap_or_else_fn(f: F) -> UnwrapOrElseFn { + UnwrapOrElseFn(f) +} + +pub struct IntoFn(PhantomData T>); + +impl Default for IntoFn { + fn default() -> Self { + Self(PhantomData) + } +} +impl FnOnce1 for IntoFn +where + A: Into, +{ + type Output = T; + fn call_once(self, arg: A) -> Self::Output { + arg.into() + } +} + +trivial_fn_impls!(into_fn IntoFn = "Into::into"); diff --git a/futures-util/src/future/abortable.rs b/futures-util/src/future/abortable.rs index 281cf6b481..d017ab7340 100644 --- a/futures-util/src/future/abortable.rs +++ b/futures-util/src/future/abortable.rs @@ -1,110 +1,8 @@ -use crate::task::AtomicWaker; +use super::assert_future; +use crate::future::{AbortHandle, Abortable, Aborted}; use futures_core::future::Future; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use core::fmt; -use core::pin::Pin; -use core::sync::atomic::{AtomicBool, Ordering}; -use alloc::sync::Arc; -/// A future which can be remotely short-circuited using an `AbortHandle`. -#[derive(Debug, Clone)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Abortable { - future: Fut, - inner: Arc, -} - -impl Unpin for Abortable {} - -impl Abortable where Fut: Future { - unsafe_pinned!(future: Fut); - - /// Creates a new `Abortable` future using an existing `AbortRegistration`. - /// `AbortRegistration`s can be acquired through `AbortHandle::new`. - /// - /// When `abort` is called on the handle tied to `reg` or if `abort` has - /// already been called, the future will complete immediately without making - /// any further progress. - /// - /// Example: - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::future::{Abortable, AbortHandle, Aborted}; - /// - /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); - /// let future = Abortable::new(async { 2 }, abort_registration); - /// abort_handle.abort(); - /// assert_eq!(future.await, Err(Aborted)); - /// # }); - /// ``` - pub fn new(future: Fut, reg: AbortRegistration) -> Self { - Abortable { - future, - inner: reg.inner, - } - } -} - -/// A registration handle for a `Abortable` future. -/// Values of this type can be acquired from `AbortHandle::new` and are used -/// in calls to `Abortable::new`. -#[derive(Debug)] -pub struct AbortRegistration { - inner: Arc, -} - -/// A handle to a `Abortable` future. -#[derive(Debug, Clone)] -pub struct AbortHandle { - inner: Arc, -} - -impl AbortHandle { - /// Creates an (`AbortHandle`, `AbortRegistration`) pair which can be used - /// to abort a running future. - /// - /// This function is usually paired with a call to `Abortable::new`. - /// - /// Example: - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::future::{Abortable, AbortHandle, Aborted}; - /// - /// let (abort_handle, abort_registration) = AbortHandle::new_pair(); - /// let future = Abortable::new(async { 2 }, abort_registration); - /// abort_handle.abort(); - /// assert_eq!(future.await, Err(Aborted)); - /// # }); - /// ``` - pub fn new_pair() -> (Self, AbortRegistration) { - let inner = Arc::new(AbortInner { - waker: AtomicWaker::new(), - cancel: AtomicBool::new(false), - }); - - ( - AbortHandle { - inner: inner.clone(), - }, - AbortRegistration { - inner, - }, - ) - } -} - -// Inner type storing the waker to awaken and a bool indicating that it -// should be cancelled. -#[derive(Debug)] -struct AbortInner { - waker: AtomicWaker, - cancel: AtomicBool, -} - -/// Creates a new `Abortable` future and a `AbortHandle` which can be used to stop it. +/// Creates a new `Abortable` future and an `AbortHandle` which can be used to stop it. /// /// This function is a convenient (but less flexible) alternative to calling /// `AbortHandle::new` and `Abortable::new` manually. @@ -112,66 +10,10 @@ struct AbortInner { /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. pub fn abortable(future: Fut) -> (Abortable, AbortHandle) - where Fut: Future +where + Fut: Future, { let (handle, reg) = AbortHandle::new_pair(); - ( - Abortable::new(future, reg), - handle, - ) -} - -/// Indicator that the `Abortable` future was aborted. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Aborted; - -impl fmt::Display for Aborted { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "`Abortable` future has been aborted") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Aborted {} - -impl Future for Abortable where Fut: Future { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Check if the future has been aborted - if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)) - } - - // attempt to complete the future - if let Poll::Ready(x) = self.as_mut().future().poll(cx) { - return Poll::Ready(Ok(x)) - } - - // Register to receive a wakeup if the future is aborted in the... future - self.inner.waker.register(cx.waker()); - - // Check to see if the future was aborted between the first check and - // registration. - // Checking with `Relaxed` is sufficient because `register` introduces an - // `AcqRel` barrier. - if self.inner.cancel.load(Ordering::Relaxed) { - return Poll::Ready(Err(Aborted)) - } - - Poll::Pending - } -} - -impl AbortHandle { - /// Abort the `Abortable` future associated with this handle. - /// - /// Notifies the Abortable future associated with this handle that it - /// should abort. Note that if the future is currently being polled on - /// another thread, it will not immediately stop running. Instead, it will - /// continue to run until its poll method returns. - pub fn abort(&self) { - self.inner.cancel.store(true, Ordering::Relaxed); - self.inner.waker.wake(); - } + let abortable = assert_future::, _>(Abortable::new(future, reg)); + (abortable, handle) } diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 24fbbe79d8..9602de7a42 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -5,14 +5,42 @@ use futures_core::stream::{FusedStream, Stream}; #[cfg(feature = "sink")] use futures_sink::Sink; -/// Combines two different futures, streams, or sinks having the same associated types into a single -/// type. +/// Combines two different futures, streams, or sinks having the same associated types into a single type. +/// +/// This is useful when conditionally choosing between two distinct future types: +/// +/// ```rust +/// use futures::future::Either; +/// +/// # futures::executor::block_on(async { +/// let cond = true; +/// +/// let fut = if cond { +/// Either::Left(async move { 12 }) +/// } else { +/// Either::Right(async move { 44 }) +/// }; +/// +/// assert_eq!(fut.await, 12); +/// # }) +/// ``` #[derive(Debug, Clone)] pub enum Either { /// First branch of the type - Left(A), + Left(/* #[pin] */ A), /// Second branch of the type - Right(B), + Right(/* #[pin] */ B), +} + +impl Either { + fn project(self: Pin<&mut Self>) -> Either, Pin<&mut B>> { + unsafe { + match self.get_unchecked_mut() { + Either::Left(a) => Either::Left(Pin::new_unchecked(a)), + Either::Right(b) => Either::Right(Pin::new_unchecked(b)), + } + } + } } impl Either<(T, A), (T, B)> { @@ -56,12 +84,10 @@ where { type Output = A::Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll(cx), - Either::Right(x) => Pin::new_unchecked(x).poll(cx), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project() { + Either::Left(x) => x.poll(cx), + Either::Right(x) => x.poll(cx), } } } @@ -86,12 +112,17 @@ where { type Item = A::Item; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_next(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_next(cx), - } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.project() { + Either::Left(x) => x.poll_next(cx), + Either::Right(x) => x.poll_next(cx), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + Either::Left(x) => x.size_hint(), + Either::Right(x) => x.size_hint(), } } } @@ -118,38 +149,30 @@ where type Error = A::Error; fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_ready(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_ready(cx), - } + match self.project() { + Either::Left(x) => x.poll_ready(cx), + Either::Right(x) => x.poll_ready(cx), } } fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).start_send(item), - Either::Right(x) => Pin::new_unchecked(x).start_send(item), - } + match self.project() { + Either::Left(x) => x.start_send(item), + Either::Right(x) => x.start_send(item), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -157,11 +180,10 @@ where #[cfg(feature = "io")] #[cfg(feature = "std")] mod if_std { - use super::Either; + use super::*; + use core::pin::Pin; use core::task::{Context, Poll}; - #[cfg(feature = "read-initializer")] - use futures_io::Initializer; use futures_io::{ AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, Result, SeekFrom, }; @@ -171,24 +193,14 @@ mod if_std { A: AsyncRead, B: AsyncRead, { - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - match self { - Either::Left(x) => x.initializer(), - Either::Right(x) => x.initializer(), - } - } - fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_read(cx, buf), - } + match self.project() { + Either::Left(x) => x.poll_read(cx, buf), + Either::Right(x) => x.poll_read(cx, buf), } } @@ -197,11 +209,9 @@ mod if_std { cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - } + match self.project() { + Either::Left(x) => x.poll_read_vectored(cx, bufs), + Either::Right(x) => x.poll_read_vectored(cx, bufs), } } } @@ -216,11 +226,9 @@ mod if_std { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_write(cx, buf), - } + match self.project() { + Either::Left(x) => x.poll_write(cx, buf), + Either::Right(x) => x.poll_write(cx, buf), } } @@ -229,29 +237,23 @@ mod if_std { cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - } + match self.project() { + Either::Left(x) => x.poll_write_vectored(cx, bufs), + Either::Right(x) => x.poll_write_vectored(cx, bufs), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -266,11 +268,9 @@ mod if_std { cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - Either::Right(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - } + match self.project() { + Either::Left(x) => x.poll_seek(cx, pos), + Either::Right(x) => x.poll_seek(cx, pos), } } } @@ -280,24 +280,17 @@ mod if_std { A: AsyncBufRead, B: AsyncBufRead, { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - } + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.project() { + Either::Left(x) => x.poll_fill_buf(cx), + Either::Right(x) => x.poll_fill_buf(cx), } } fn consume(self: Pin<&mut Self>, amt: usize) { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).consume(amt), - Either::Right(x) => Pin::new_unchecked(x).consume(amt), - } + match self.project() { + Either::Left(x) => x.consume(amt), + Either::Right(x) => x.consume(amt), } } } diff --git a/futures-util/src/future/future/catch_unwind.rs b/futures-util/src/future/future/catch_unwind.rs index e88cce7e9d..0e09d6eeb0 100644 --- a/futures-util/src/future/future/catch_unwind.rs +++ b/futures-util/src/future/future/catch_unwind.rs @@ -1,31 +1,38 @@ +use core::any::Any; +use core::pin::Pin; +use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; + use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use std::any::Any; -use std::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; +use pin_project_lite::pin_project; -/// Future for the [`catch_unwind`](super::FutureExt::catch_unwind) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CatchUnwind { - future: Fut, +pin_project! { + /// Future for the [`catch_unwind`](super::FutureExt::catch_unwind) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct CatchUnwind { + #[pin] + future: Fut, + } } -impl CatchUnwind where Fut: Future + UnwindSafe { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> CatchUnwind { - CatchUnwind { future } +impl CatchUnwind +where + Fut: Future + UnwindSafe, +{ + pub(super) fn new(future: Fut) -> Self { + Self { future } } } impl Future for CatchUnwind - where Fut: Future + UnwindSafe, +where + Fut: Future + UnwindSafe, { type Output = Result>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + let f = self.project().future; + catch_unwind(AssertUnwindSafe(|| f.poll(cx)))?.map(Ok) } } diff --git a/futures-util/src/future/future/chain.rs b/futures-util/src/future/future/chain.rs deleted file mode 100644 index 3f248e80fe..0000000000 --- a/futures-util/src/future/future/chain.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum Chain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for Chain {} - -impl Chain { - pub(crate)fn is_terminated(&self) -> bool { - if let Chain::Empty = *self { true } else { false } - } -} - -impl Chain - where Fut1: Future, - Fut2: Future, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> Chain { - Chain::First(fut1, Some(data)) - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll - where F: FnOnce(Fut1::Output, Data) -> Fut2, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - Chain::First(fut1, data) => { - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)); - (output, data.take().unwrap()) - } - Chain::Second(fut2) => { - return unsafe { Pin::new_unchecked(fut2) }.poll(cx); - } - Chain::Empty => unreachable!() - }; - - *this = Chain::Empty; // Drop fut1 - let fut2 = (f.take().unwrap())(output, data); - *this = Chain::Second(fut2) - } - } -} diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 16b3a19de9..bd767af344 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -1,56 +1,153 @@ -use super::chain::Chain; -use core::fmt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; -/// Future for the [`flatten`](super::FutureExt::flatten) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Flatten - where Fut: Future, +pin_project! { + #[project = FlattenProj] + #[derive(Debug)] + pub enum Flatten { + First { #[pin] f: Fut1 }, + Second { #[pin] f: Fut2 }, + Empty, + } +} + +impl Flatten { + pub(crate) fn new(future: Fut1) -> Self { + Self::First { f: future } + } +} + +impl FusedFuture for Flatten +where + Fut: Future, + Fut::Output: Future, { - state: Chain, + fn is_terminated(&self) -> bool { + match self { + Self::Empty => true, + _ => false, + } + } } -impl Flatten - where Fut: Future, - Fut::Output: Future, +impl Future for Flatten +where + Fut: Future, + Fut::Output: Future, { - unsafe_pinned!(state: Chain); + type Output = ::Output; - pub(super) fn new(future: Fut) -> Flatten { - Flatten { - state: Chain::new(future, ()), - } + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + match self.as_mut().project() { + FlattenProj::First { f } => { + let f = ready!(f.poll(cx)); + self.set(Self::Second { f }); + } + FlattenProj::Second { f } => { + let output = ready!(f.poll(cx)); + self.set(Self::Empty); + break output; + } + FlattenProj::Empty => panic!("Flatten polled after completion"), + } + }) } } -impl fmt::Debug for Flatten - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, +impl FusedStream for Flatten +where + Fut: Future, + Fut::Output: Stream, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Flatten") - .field("state", &self.state) - .finish() + fn is_terminated(&self) -> bool { + match self { + Self::Empty => true, + _ => false, + } } } -impl FusedFuture for Flatten - where Fut: Future, - Fut::Output: Future, +impl Stream for Flatten +where + Fut: Future, + Fut::Output: Stream, { - fn is_terminated(&self) -> bool { self.state.is_terminated() } + type Item = ::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + match self.as_mut().project() { + FlattenProj::First { f } => { + let f = ready!(f.poll(cx)); + self.set(Self::Second { f }); + } + FlattenProj::Second { f } => { + let output = ready!(f.poll_next(cx)); + if output.is_none() { + self.set(Self::Empty); + } + break output; + } + FlattenProj::Empty => break None, + } + }) + } } -impl Future for Flatten - where Fut: Future, - Fut::Output: Future, +#[cfg(feature = "sink")] +impl Sink for Flatten +where + Fut: Future, + Fut::Output: Sink, { - type Output = ::Output; + type Error = >::Error; + + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + match self.as_mut().project() { + FlattenProj::First { f } => { + let f = ready!(f.poll(cx)); + self.set(Self::Second { f }); + } + FlattenProj::Second { f } => { + break ready!(f.poll_ready(cx)); + } + FlattenProj::Empty => panic!("poll_ready called after eof"), + } + }) + } - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.state().poll(cx, |a, ()| a) + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + match self.project() { + FlattenProj::First { .. } => panic!("poll_ready not called first"), + FlattenProj::Second { f } => f.start_send(item), + FlattenProj::Empty => panic!("start_send called after eof"), + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.project() { + FlattenProj::First { .. } => Poll::Ready(Ok(())), + FlattenProj::Second { f } => f.poll_flush(cx), + FlattenProj::Empty => panic!("poll_flush called after eof"), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = match self.as_mut().project() { + FlattenProj::Second { f } => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(Self::Empty); + } + res } } diff --git a/futures-util/src/future/future/flatten_stream.rs b/futures-util/src/future/future/flatten_stream.rs deleted file mode 100644 index d1108866ca..0000000000 --- a/futures-util/src/future/future/flatten_stream.rs +++ /dev/null @@ -1,89 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`flatten_stream`](super::FutureExt::flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct FlattenStream { - state: State, -} - -impl FlattenStream { - unsafe_pinned!(state: State); - - pub(super) fn new(future: Fut) -> FlattenStream { - FlattenStream { - state: State::Future(future) - } - } -} - -impl fmt::Debug for FlattenStream - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStream") - .field("state", &self.state) - .finish() - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream - Stream(St), -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut St>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::Stream(s) => State::Stream(unsafe { Pin::new_unchecked(s) }), - } - } -} - -impl FusedStream for FlattenStream - where Fut: Future, - Fut::Output: Stream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::Stream(stream) => stream.is_terminated(), - } - } -} - -impl Stream for FlattenStream - where Fut: Future, - Fut::Output: Stream, -{ - type Item = ::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match self.as_mut().state().get_pin_mut() { - State::Future(f) => { - let stream = ready!(f.poll(cx)); - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.as_mut().state().set(State::Stream(stream)); - } - State::Stream(s) => return s.poll_next(cx), - } - } - } -} diff --git a/futures-util/src/future/future/fuse.rs b/futures-util/src/future/future/fuse.rs index b5ef913034..597aec1a40 100644 --- a/futures-util/src/future/future/fuse.rs +++ b/futures-util/src/future/future/fuse.rs @@ -1,24 +1,26 @@ use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Future for the [`fuse`](super::FutureExt::fuse) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Fuse { - future: Option, +pin_project! { + /// Future for the [`fuse`](super::FutureExt::fuse) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Fuse { + #[pin] + inner: Option, + } } -impl Fuse { - unsafe_pinned!(future: Option); - - pub(super) fn new(f: Fut) -> Fuse { - Fuse { - future: Some(f), - } +impl Fuse { + pub(super) fn new(f: Fut) -> Self { + Self { inner: Some(f) } } +} +impl Fuse { /// Creates a new `Fuse`-wrapped future which is already terminated. /// /// This can be useful in combination with looping and the `select!` @@ -41,7 +43,7 @@ impl Fuse { /// sender.unbounded_send(()).unwrap(); /// drop(sender); /// - /// // Use `Fuse::termianted()` to create an already-terminated future + /// // Use `Fuse::terminated()` to create an already-terminated future /// // which may be instantiated later. /// let foo_printer = Fuse::terminated(); /// pin_mut!(foo_printer); @@ -64,14 +66,14 @@ impl Fuse { /// } /// # }); /// ``` - pub fn terminated() -> Fuse { - Fuse { future: None } + pub fn terminated() -> Self { + Self { inner: None } } } impl FusedFuture for Fuse { fn is_terminated(&self) -> bool { - self.future.is_none() + self.inner.is_none() } } @@ -79,12 +81,13 @@ impl Future for Fuse { type Output = Fut::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let v = match self.as_mut().future().as_pin_mut() { - Some(fut) => ready!(fut.poll(cx)), + Poll::Ready(match self.as_mut().project().inner.as_pin_mut() { + Some(fut) => { + let output = ready!(fut.poll(cx)); + self.project().inner.set(None); + output + } None => return Poll::Pending, - }; - - self.as_mut().future().set(None); - Poll::Ready(v) + }) } } diff --git a/futures-util/src/future/future/inspect.rs b/futures-util/src/future/future/inspect.rs deleted file mode 100644 index d67455aa6d..0000000000 --- a/futures-util/src/future/future/inspect.rs +++ /dev/null @@ -1,47 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect`](super::FutureExt::inspect) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Inspect { - future: Fut, - f: Option, -} - -impl Inspect { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Inspect { - Inspect { - future, - f: Some(f), - } - } -} - -impl Unpin for Inspect {} - -impl FusedFuture for Inspect - where Fut: FusedFuture, - F: FnOnce(&Fut::Output), -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for Inspect - where Fut: Future, - F: FnOnce(&Fut::Output), -{ - type Output = Fut::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().poll(cx)); - let f = self.as_mut().f().take().expect("cannot poll Inspect twice"); - f(&e); - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/future/into_stream.rs b/futures-util/src/future/future/into_stream.rs deleted file mode 100644 index 616c4cbb57..0000000000 --- a/futures-util/src/future/future/into_stream.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::stream::{self, Once}; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`into_stream`](super::FutureExt::into_stream) method. -#[must_use = "streams do nothing unless polled"] -#[derive(Debug)] -pub struct IntoStream { - inner: Once -} - -impl IntoStream { - unsafe_pinned!(inner: Once); - - pub(super) fn new(future: Fut) -> IntoStream { - IntoStream { - inner: stream::once(future) - } - } -} - -impl Stream for IntoStream { - type Item = Fut::Output; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -impl FusedStream for IntoStream { - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index b5fbfb1384..7471aba000 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,49 +1,66 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`map`](super::FutureExt::map) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Map { - future: Fut, - f: Option, +use crate::fns::FnOnce1; + +pin_project! { + /// Internal Map future + #[project = MapProj] + #[project_replace = MapProjReplace] + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub enum Map { + Incomplete { + #[pin] + future: Fut, + f: F, + }, + Complete, + } } impl Map { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - /// Creates a new Map. - pub(super) fn new(future: Fut, f: F) -> Map { - Map { future, f: Some(f) } + pub(crate) fn new(future: Fut, f: F) -> Self { + Self::Incomplete { future, f } } } -impl Unpin for Map {} - impl FusedFuture for Map - where Fut: Future, - F: FnOnce(Fut::Output) -> T, +where + Fut: Future, + F: FnOnce1, { - fn is_terminated(&self) -> bool { self.f.is_none() } + fn is_terminated(&self) -> bool { + match self { + Self::Incomplete { .. } => false, + Self::Complete => true, + } + } } impl Future for Map - where Fut: Future, - F: FnOnce(Fut::Output) -> T, +where + Fut: Future, + F: FnOnce1, { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut() - .future() - .poll(cx) - .map(|output| { - let f = self.f().take() - .expect("Map must not be polled after it returned `Poll::Ready`"); - f(output) - }) + match self.as_mut().project() { + MapProj::Incomplete { future, .. } => { + let output = ready!(future.poll(cx)); + match self.project_replace(Map::Complete) { + MapProjReplace::Incomplete { f, .. } => Poll::Ready(f.call_once(output)), + MapProjReplace::Complete => unreachable!(), + } + } + MapProj::Complete => { + panic!("Map must not be polled after it returned `Poll::Ready`") + } + } } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index e58cafc8c0..c11d108207 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -3,10 +3,14 @@ //! This module contains a number of functions for working with `Future`s, //! including the `FutureExt` trait which adds methods to `Future` types. -use super::{assert_future, Either}; #[cfg(feature = "alloc")] use alloc::boxed::Box; use core::pin::Pin; + +use crate::fns::{inspect_fn, into_fn, ok_fn, InspectFn, IntoFn, OkFn}; +use crate::future::{assert_future, Either}; +use crate::never::Never; +use crate::stream::assert_stream; #[cfg(feature = "alloc")] use futures_core::future::{BoxFuture, LocalBoxFuture}; use futures_core::{ @@ -14,44 +18,81 @@ use futures_core::{ stream::Stream, task::{Context, Poll}, }; +use pin_utils::pin_mut; // Combinators mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; - -mod flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_stream::FlattenStream; - mod fuse; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::fuse::Fuse; - -mod into_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::into_stream::IntoStream; - mod map; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map::Map; - -mod then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::then::Then; - -mod inspect; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; -mod unit_error; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unit_error::UnitError; +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Flatten( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); + +delegate_all!( + /// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method. + FlattenStream( + flatten::Flatten::Output> + ): Debug + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); -mod never_error; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::never_error::NeverError; +pub use fuse::Fuse; + +delegate_all!( + /// Future for the [`map`](super::FutureExt::map) method. + Map( + map::Map + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)] +); + +delegate_all!( + /// Stream for the [`into_stream`](FutureExt::into_stream) method. + IntoStream( + crate::stream::Once + ): Debug + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] +); + +delegate_all!( + /// Future for the [`map_into`](FutureExt::map_into) combinator. + MapInto( + Map> + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`then`](FutureExt::then) method. + Then( + flatten::Flatten, Fut2> + ): Debug + Future + FusedFuture + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] +); + +delegate_all!( + /// Future for the [`inspect`](FutureExt::inspect) method. + Inspect( + map::Map> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] +); + +delegate_all!( + /// Future for the [`never_error`](super::FutureExt::never_error) combinator. + NeverError( + Map> + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] +); + +delegate_all!( + /// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. + UnitError( + Map> + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] +); #[cfg(feature = "std")] mod catch_unwind; @@ -60,9 +101,11 @@ mod catch_unwind; pub use self::catch_unwind::CatchUnwind; #[cfg(feature = "channel")] +#[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] mod remote_handle; #[cfg(feature = "channel")] +#[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::remote_handle::{Remote, RemoteHandle}; @@ -71,12 +114,7 @@ pub use self::remote_handle::{Remote, RemoteHandle}; mod shared; #[cfg(feature = "std")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::shared::Shared; - -// Implementation details - -mod chain; -pub(crate) use self::chain::Chain; +pub use self::shared::{Shared, WeakShared}; impl FutureExt for T where T: Future {} @@ -113,6 +151,19 @@ pub trait FutureExt: Future { assert_future::(Map::new(self, f)) } + /// Map this future's output to a different type, returning a new future of + /// the resulting type. + /// + /// This function is equivalent to calling `map(Into::into)` but allows naming + /// the return type. + fn map_into(self) -> MapInto + where + Self::Output: Into, + Self: Sized, + { + assert_future::(MapInto::new(self)) + } + /// Chain on a computation for when a future finished, passing the result of /// the future to the provided closure `f`. /// @@ -173,7 +224,7 @@ pub trait FutureExt: Future { B: Future, Self: Sized, { - Either::Left(self) + assert_future::(Either::Left(self)) } /// Wrap this future in an `Either` future, making it the right-hand variant @@ -203,7 +254,7 @@ pub trait FutureExt: Future { A: Future, Self: Sized, { - Either::Right(self) + assert_future::(Either::Right(self)) } /// Convert this future into a single element stream. @@ -228,7 +279,7 @@ pub trait FutureExt: Future { where Self: Sized, { - IntoStream::new(self) + assert_stream::(IntoStream::new(self)) } /// Flatten the execution of this future when the output of this @@ -292,7 +343,7 @@ pub trait FutureExt: Future { Self::Output: Stream, Self: Sized, { - FlattenStream::new(self) + assert_stream::<::Item, _>(FlattenStream::new(self)) } /// Fuse a future such that `poll` will never again be called once it has @@ -381,7 +432,9 @@ pub trait FutureExt: Future { where Self: Sized + ::std::panic::UnwindSafe, { - CatchUnwind::new(self) + assert_future::>, _>(CatchUnwind::new( + self, + )) } /// Create a cloneable handle to this future where all handles will resolve @@ -435,7 +488,7 @@ pub trait FutureExt: Future { Self: Sized, Self::Output: Clone, { - Shared::new(self) + assert_future::(Shared::new(self)) } /// Turn this future into a future that yields `()` on completion and sends @@ -447,12 +500,14 @@ pub trait FutureExt: Future { /// This method is only available when the `std` feature of this /// library is activated, and it is activated by default. #[cfg(feature = "channel")] + #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] fn remote_handle(self) -> (Remote, RemoteHandle) where Self: Sized, { - remote_handle::remote_handle(self) + let (wrapped, handle) = remote_handle::remote_handle(self); + (assert_future::<(), _>(wrapped), handle) } /// Wrap the future in a Box, pinning it. @@ -464,7 +519,7 @@ pub trait FutureExt: Future { where Self: Sized + Send + 'a, { - Box::pin(self) + assert_future::(Box::pin(self)) } /// Wrap the future in a Box, pinning it. @@ -478,7 +533,7 @@ pub trait FutureExt: Future { where Self: Sized + 'a, { - Box::pin(self) + assert_future::(Box::pin(self)) } /// Turns a [`Future`](Future) into a @@ -487,7 +542,7 @@ pub trait FutureExt: Future { where Self: Sized, { - UnitError::new(self) + assert_future::, _>(UnitError::new(self)) } /// Turns a [`Future`](Future) into a @@ -496,7 +551,7 @@ pub trait FutureExt: Future { where Self: Sized, { - NeverError::new(self) + assert_future::, _>(NeverError::new(self)) } /// A convenience for calling `Future::poll` on `Unpin` future types. @@ -538,19 +593,16 @@ pub trait FutureExt: Future { /// /// assert_eq!(future_ready.now_or_never().expect("Future not ready"), "foobar"); /// ``` - fn now_or_never(mut self) -> Option + fn now_or_never(self) -> Option where Self: Sized, { let noop_waker = crate::task::noop_waker(); let mut cx = Context::from_waker(&noop_waker); - // SAFETY: This is safe because this method consumes the future, so `poll` is - // only going to be called once. Thus it doesn't matter to us if the - // future is `Unpin` or not. - let pinned = unsafe { Pin::new_unchecked(&mut self) }; - - match pinned.poll(&mut cx) { + let this = self; + pin_mut!(this); + match this.poll(&mut cx) { Poll::Ready(x) => Some(x), _ => None, } diff --git a/futures-util/src/future/future/never_error.rs b/futures-util/src/future/future/never_error.rs deleted file mode 100644 index 5a68e6f952..0000000000 --- a/futures-util/src/future/future/never_error.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::never::Never; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{self, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`never_error`](super::FutureExt::never_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct NeverError { - future: Fut, -} - -impl NeverError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> NeverError { - NeverError { future } - } -} - -impl Unpin for NeverError {} - -impl FusedFuture for NeverError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for NeverError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index 11b2a65af7..1358902cab 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -1,19 +1,20 @@ use { crate::future::{CatchUnwind, FutureExt}, - futures_channel::oneshot::{self, Sender, Receiver}, + futures_channel::oneshot::{self, Receiver, Sender}, futures_core::{ future::Future, + ready, task::{Context, Poll}, }, - pin_utils::{unsafe_pinned, unsafe_unpinned}, + pin_project_lite::pin_project, std::{ any::Any, fmt, panic::{self, AssertUnwindSafe}, pin::Pin, sync::{ - Arc, atomic::{AtomicBool, Ordering}, + Arc, }, thread, }, @@ -22,8 +23,22 @@ use { /// The handle to a remote future returned by /// [`remote_handle`](crate::future::FutureExt::remote_handle). When you drop this, /// the remote future will be woken up to be dropped by the executor. -#[must_use = "futures do nothing unless you `.await` or poll them"] +/// +/// ## Unwind safety +/// +/// When the remote future panics, [Remote] will catch the unwind and transfer it to +/// the thread where `RemoteHandle` is being awaited. This is good for the common +/// case where [Remote] is spawned on a threadpool. It is unlikely that other code +/// in the executor working thread shares mutable data with the spawned future and we +/// preserve the executor from losing its working threads. +/// +/// If you run the future locally and send the handle of to be awaited elsewhere, you +/// must be careful with regard to unwind safety because the thread in which the future +/// is polled will keep running after the panic and the thread running the [RemoteHandle] +/// will unwind. +#[must_use = "dropping a remote handle cancels the underlying future"] #[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "channel")))] pub struct RemoteHandle { rx: Receiver>, keep_running: Arc, @@ -39,13 +54,15 @@ impl RemoteHandle { } } -impl Future for RemoteHandle { +impl Future for RemoteHandle { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(self.rx.poll_unpin(cx)) { Ok(Ok(output)) => Poll::Ready(output), + // the remote future panicked. Ok(Err(e)) => panic::resume_unwind(e), + // The oneshot sender was dropped. Err(e) => panic::resume_unwind(Box::new(e)), } } @@ -53,46 +70,43 @@ impl Future for RemoteHandle { type SendMsg = Result<::Output, Box<(dyn Any + Send + 'static)>>; -/// A future which sends its output to the corresponding `RemoteHandle`. -/// Created by [`remote_handle`](crate::future::FutureExt::remote_handle). -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Remote { - tx: Option>>, - keep_running: Arc, - future: CatchUnwind>, +pin_project! { + /// A future which sends its output to the corresponding `RemoteHandle`. + /// Created by [`remote_handle`](crate::future::FutureExt::remote_handle). + #[must_use = "futures do nothing unless you `.await` or poll them"] + #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] + pub struct Remote { + tx: Option>>, + keep_running: Arc, + #[pin] + future: CatchUnwind>, + } } impl fmt::Debug for Remote { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Remote") - .field(&self.future) - .finish() + f.debug_tuple("Remote").field(&self.future).finish() } } -impl Unpin for Remote {} - -impl Remote { - unsafe_pinned!(future: CatchUnwind>); - unsafe_unpinned!(tx: Option>>); -} - impl Future for Remote { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if let Poll::Ready(_) = self.as_mut().tx().as_mut().unwrap().poll_canceled(cx) { - if !self.keep_running.load(Ordering::SeqCst) { - // Cancelled, bail out - return Poll::Ready(()) - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + let this = self.project(); + + if this.tx.as_mut().unwrap().poll_canceled(cx).is_ready() + && !this.keep_running.load(Ordering::SeqCst) + { + // Cancelled, bail out + return Poll::Ready(()); } - let output = ready!(self.as_mut().future().poll(cx)); + let output = ready!(this.future.poll(cx)); // if the receiving end has gone away then that's ok, we just ignore the // send error here. - drop(self.as_mut().tx().take().unwrap().send(output)); + drop(this.tx.take().unwrap().send(output)); Poll::Ready(()) } } @@ -101,9 +115,7 @@ pub(super) fn remote_handle(future: Fut) -> (Remote, RemoteHan let (tx, rx) = oneshot::channel(); let keep_running = Arc::new(AtomicBool::new(false)); - // AssertUnwindSafe is used here because `Send + 'static` is basically - // an alias for an implementation of the `UnwindSafe` trait but we can't - // express that in the standard library right now. + // Unwind Safety: See the docs for RemoteHandle. let wrapped = Remote { future: AssertUnwindSafe(future).catch_unwind(), tx: Some(tx), diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs index 816f5dd007..9b31932fe3 100644 --- a/futures-util/src/future/future/shared.rs +++ b/futures-util/src/future/future/shared.rs @@ -1,4 +1,4 @@ -use crate::task::{ArcWake, waker_ref}; +use crate::task::{waker_ref, ArcWake}; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use slab::Slab; @@ -6,8 +6,8 @@ use std::cell::UnsafeCell; use std::fmt; use std::pin::Pin; use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::SeqCst; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::Ordering::{Acquire, SeqCst}; +use std::sync::{Arc, Mutex, Weak}; /// Future for the [`shared`](super::FutureExt::shared) method. #[must_use = "futures do nothing unless you `.await` or poll them"] @@ -26,6 +26,15 @@ struct Notifier { wakers: Mutex>>>, } +/// A weak reference to a [`Shared`] that can be upgraded much like an `Arc`. +pub struct WeakShared(Weak>); + +impl Clone for WeakShared { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + // The future itself is polled behind the `Arc`, so it won't be moved // when `Shared` is moved. impl Unpin for Shared {} @@ -45,6 +54,12 @@ impl fmt::Debug for Inner { } } +impl fmt::Debug for WeakShared { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WeakShared").finish() + } +} + enum FutureOrOutput { Future(Fut), Output(Fut::Output), @@ -54,24 +69,25 @@ unsafe impl Send for Inner where Fut: Future + Send, Fut::Output: Send + Sync, -{} +{ +} unsafe impl Sync for Inner where Fut: Future + Send, Fut::Output: Send + Sync, -{} +{ +} const IDLE: usize = 0; const POLLING: usize = 1; -const REPOLL: usize = 2; -const COMPLETE: usize = 3; -const POISONED: usize = 4; +const COMPLETE: usize = 2; +const POISONED: usize = 3; const NULL_WAKER_KEY: usize = usize::max_value(); impl Shared { - pub(super) fn new(future: Fut) -> Shared { + pub(super) fn new(future: Fut) -> Self { let inner = Inner { future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)), notifier: Arc::new(Notifier { @@ -80,10 +96,7 @@ impl Shared { }), }; - Shared { - inner: Some(Arc::new(inner)), - waker_key: NULL_WAKER_KEY, - } + Self { inner: Some(Arc::new(inner)), waker_key: NULL_WAKER_KEY } } } @@ -107,54 +120,40 @@ where None } - /// Registers the current task to receive a wakeup when `Inner` is awoken. - fn set_waker(&mut self, cx: &mut Context<'_>) { - // Acquire the lock first before checking COMPLETE to ensure there - // isn't a race. - let mut wakers_guard = if let Some(inner) = self.inner.as_ref() { - inner.notifier.wakers.lock().unwrap() - } else { - return; - }; - - let wakers = if let Some(wakers) = wakers_guard.as_mut() { - wakers - } else { - return; - }; - - if self.waker_key == NULL_WAKER_KEY { - self.waker_key = wakers.insert(Some(cx.waker().clone())); - } else { - let waker_slot = &mut wakers[self.waker_key]; - let needs_replacement = if let Some(_old_waker) = waker_slot { - // If there's still an unwoken waker in the slot, only replace - // if the current one wouldn't wake the same task. - // TODO: This API is currently not available, so replace always - // !waker.will_wake_nonlocal(old_waker) - true - } else { - true - }; - if needs_replacement { - *waker_slot = Some(cx.waker().clone()); - } + /// Creates a new [`WeakShared`] for this [`Shared`]. + /// + /// Returns [`None`] if it has already been polled to completion. + pub fn downgrade(&self) -> Option> { + if let Some(inner) = self.inner.as_ref() { + return Some(WeakShared(Arc::downgrade(inner))); } - debug_assert!(self.waker_key != NULL_WAKER_KEY); + None } - /// Safety: callers must first ensure that `self.inner.state` - /// is `COMPLETE` - unsafe fn take_or_clone_output(&mut self) -> Fut::Output { - let inner = self.inner.take().unwrap(); + /// Gets the number of strong pointers to this allocation. + /// + /// Returns [`None`] if it has already been polled to completion. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. Another thread + /// can change the strong count at any time, including potentially between calling this method + /// and acting on the result. + pub fn strong_count(&self) -> Option { + self.inner.as_ref().map(|arc| Arc::strong_count(arc)) + } - match Arc::try_unwrap(inner) { - Ok(inner) => match inner.future_or_output.into_inner() { - FutureOrOutput::Output(item) => item, - FutureOrOutput::Future(_) => unreachable!(), - }, - Err(inner) => inner.output().clone(), - } + /// Gets the number of weak pointers to this allocation. + /// + /// Returns [`None`] if it has already been polled to completion. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. Another thread + /// can change the weak count at any time, including potentially between calling this method + /// and acting on the result. + pub fn weak_count(&self) -> Option { + self.inner.as_ref().map(|arc| Arc::weak_count(arc)) } } @@ -167,10 +166,44 @@ where /// is `COMPLETE` unsafe fn output(&self) -> &Fut::Output { match &*self.future_or_output.get() { - FutureOrOutput::Output(ref item) => &item, + FutureOrOutput::Output(ref item) => item, FutureOrOutput::Future(_) => unreachable!(), } } + /// Registers the current task to receive a wakeup when we are awoken. + fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) { + let mut wakers_guard = self.notifier.wakers.lock().unwrap(); + + let wakers = match wakers_guard.as_mut() { + Some(wakers) => wakers, + None => return, + }; + + let new_waker = cx.waker(); + + if *waker_key == NULL_WAKER_KEY { + *waker_key = wakers.insert(Some(new_waker.clone())); + } else { + match wakers[*waker_key] { + Some(ref old_waker) if new_waker.will_wake(old_waker) => {} + // Could use clone_from here, but Waker doesn't specialize it. + ref mut slot => *slot = Some(new_waker.clone()), + } + } + debug_assert!(*waker_key != NULL_WAKER_KEY); + } + + /// Safety: callers must first ensure that `inner.state` + /// is `COMPLETE` + unsafe fn take_or_clone_output(self: Arc) -> Fut::Output { + match Arc::try_unwrap(self) { + Ok(inner) => match inner.future_or_output.into_inner() { + FutureOrOutput::Output(item) => item, + FutureOrOutput::Future(_) => unreachable!(), + }, + Err(inner) => inner.output().clone(), + } + } } impl FusedFuture for Shared @@ -193,27 +226,34 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; - this.set_waker(cx); + let inner = this.inner.take().expect("Shared future polled again after completion"); - let inner = if let Some(inner) = this.inner.as_ref() { - inner - } else { - panic!("Shared future polled again after completion"); - }; + // Fast path for when the wrapped future has already completed + if inner.notifier.state.load(Acquire) == COMPLETE { + // Safety: We're in the COMPLETE state + return unsafe { Poll::Ready(inner.take_or_clone_output()) }; + } - match inner.notifier.state.compare_and_swap(IDLE, POLLING, SeqCst) { + inner.record_waker(&mut this.waker_key, cx); + + match inner + .notifier + .state + .compare_exchange(IDLE, POLLING, SeqCst, SeqCst) + .unwrap_or_else(|x| x) + { IDLE => { // Lock acquired, fall through } - POLLING | REPOLL => { + POLLING => { // Another task is currently polling, at this point we just want // to ensure that the waker for this task is registered - + this.inner = Some(inner); return Poll::Pending; } COMPLETE => { // Safety: We're in the COMPLETE state - return unsafe { Poll::Ready(this.take_or_clone_output()) }; + return unsafe { Poll::Ready(inner.take_or_clone_output()) }; } POISONED => panic!("inner future panicked during poll"), _ => unreachable!(), @@ -236,7 +276,7 @@ where let _reset = Reset(&inner.notifier.state); - let output = loop { + let output = { let future = unsafe { match &mut *inner.future_or_output.get() { FutureOrOutput::Future(fut) => Pin::new_unchecked(fut), @@ -244,49 +284,40 @@ where } }; - let poll = future.poll(&mut cx); - - match poll { + match future.poll(&mut cx) { Poll::Pending => { - let state = &inner.notifier.state; - match state.compare_and_swap(POLLING, IDLE, SeqCst) { - POLLING => { - // Success - return Poll::Pending; - } - REPOLL => { - // Was woken since: Gotta poll again! - let prev = state.swap(POLLING, SeqCst); - assert_eq!(prev, REPOLL); - } - _ => unreachable!(), + if inner.notifier.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok() + { + // Success + drop(_reset); + this.inner = Some(inner); + return Poll::Pending; + } else { + unreachable!() } } - Poll::Ready(output) => break output, + Poll::Ready(output) => output, } }; unsafe { - *inner.future_or_output.get() = - FutureOrOutput::Output(output); + *inner.future_or_output.get() = FutureOrOutput::Output(output); } inner.notifier.state.store(COMPLETE, SeqCst); // Wake all tasks and drop the slab let mut wakers_guard = inner.notifier.wakers.lock().unwrap(); - let wakers = &mut wakers_guard.take().unwrap(); - for (_key, opt_waker) in wakers { - if let Some(waker) = opt_waker.take() { - waker.wake(); - } + let mut wakers = wakers_guard.take().unwrap(); + for waker in wakers.drain().flatten() { + waker.wake(); } drop(_reset); // Make borrow checker happy drop(wakers_guard); // Safety: We're in the COMPLETE state - unsafe { Poll::Ready(this.take_or_clone_output()) } + unsafe { Poll::Ready(inner.take_or_clone_output()) } } } @@ -295,10 +326,7 @@ where Fut: Future, { fn clone(&self) -> Self { - Shared { - inner: self.inner.clone(), - waker_key: NULL_WAKER_KEY, - } + Self { inner: self.inner.clone(), waker_key: NULL_WAKER_KEY } } } @@ -321,8 +349,6 @@ where impl ArcWake for Notifier { fn wake_by_ref(arc_self: &Arc) { - arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst); - let wakers = &mut *arc_self.wakers.lock().unwrap(); if let Some(wakers) = wakers.as_mut() { for (_key, opt_waker) in wakers { @@ -333,3 +359,13 @@ impl ArcWake for Notifier { } } } + +impl WeakShared { + /// Attempts to upgrade this [`WeakShared`] into a [`Shared`]. + /// + /// Returns [`None`] if all clones of the [`Shared`] have been dropped or polled + /// to completion. + pub fn upgrade(&self) -> Option> { + Some(Shared { inner: Some(self.0.upgrade()?), waker_key: NULL_WAKER_KEY }) + } +} diff --git a/futures-util/src/future/future/then.rs b/futures-util/src/future/future/then.rs deleted file mode 100644 index 9f30f09864..0000000000 --- a/futures-util/src/future/future/then.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::Chain; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`then`](super::FutureExt::then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Then { - chain: Chain, -} - -impl Then - where Fut1: Future, - Fut2: Future, -{ - unsafe_pinned!(chain: Chain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> Then { - Then { - chain: Chain::new(future, f), - } - } -} - -impl FusedFuture for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - fn is_terminated(&self) -> bool { self.chain.is_terminated() } -} - -impl Future for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - type Output = Fut2::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut().chain().poll(cx, |output, f| f(output)) - } -} diff --git a/futures-util/src/future/future/unit_error.rs b/futures-util/src/future/future/unit_error.rs deleted file mode 100644 index 679e988b16..0000000000 --- a/futures-util/src/future/future/unit_error.rs +++ /dev/null @@ -1,35 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnitError { - future: Fut, -} - -impl UnitError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> UnitError { - UnitError { future } - } -} - -impl Unpin for UnitError {} - -impl FusedFuture for UnitError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for UnitError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/join.rs b/futures-util/src/future/join.rs index 5af5b408e9..740ffbc988 100644 --- a/futures-util/src/future/join.rs +++ b/futures-util/src/future/join.rs @@ -1,22 +1,24 @@ #![allow(non_snake_case)] -use crate::future::{MaybeDone, maybe_done}; +use super::assert_future; +use crate::future::{maybe_done, MaybeDone}; use core::fmt; use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use super::assert_future; +use pin_project_lite::pin_project; macro_rules! generate { ($( $(#[$doc:meta])* ($Join:ident, <$($Fut:ident),*>), )*) => ($( - $(#[$doc])* - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub struct $Join<$($Fut: Future),*> { - $($Fut: MaybeDone<$Fut>,)* + pin_project! { + $(#[$doc])* + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct $Join<$($Fut: Future),*> { + $(#[pin] $Fut: MaybeDone<$Fut>,)* + } } impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*> @@ -34,29 +36,27 @@ macro_rules! generate { } impl<$($Fut: Future),*> $Join<$($Fut),*> { - fn new($($Fut: $Fut),*) -> $Join<$($Fut),*> { - $Join { + fn new($($Fut: $Fut),*) -> Self { + Self { $($Fut: maybe_done($Fut)),* } } - $( - unsafe_pinned!($Fut: MaybeDone<$Fut>); - )* } impl<$($Fut: Future),*> Future for $Join<$($Fut),*> { type Output = ($($Fut::Output),*); fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; + let mut futures = self.project(); $( - all_done &= self.as_mut().$Fut().poll(cx).is_ready(); + all_done &= futures.$Fut.as_mut().poll(cx).is_ready(); )* if all_done { - Poll::Ready(($(self.as_mut().$Fut().take_output().unwrap()), *)) + Poll::Ready(($(futures.$Fut.take_output().unwrap()), *)) } else { Poll::Pending } @@ -143,7 +143,8 @@ where Fut2: Future, Fut3: Future, { - Join3::new(future1, future2, future3) + let f = Join3::new(future1, future2, future3); + assert_future::<(Fut1::Output, Fut2::Output, Fut3::Output), _>(f) } /// Same as [`join`](join()), but with more futures. @@ -175,7 +176,8 @@ where Fut3: Future, Fut4: Future, { - Join4::new(future1, future2, future3, future4) + let f = Join4::new(future1, future2, future3, future4); + assert_future::<(Fut1::Output, Fut2::Output, Fut3::Output, Fut4::Output), _>(f) } /// Same as [`join`](join()), but with more futures. @@ -210,5 +212,6 @@ where Fut4: Future, Fut5: Future, { - Join5::new(future1, future2, future3, future4, future5) + let f = Join5::new(future1, future2, future3, future4, future5); + assert_future::<(Fut1::Output, Fut2::Output, Fut3::Output, Fut4::Output, Fut5::Output), _>(f) } diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 07408856a4..2e52ac17f4 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -1,68 +1,50 @@ //! Definition of the `JoinAll` combinator, waiting for all of a list of futures //! to finish. +use alloc::boxed::Box; +use alloc::vec::Vec; use core::fmt; use core::future::Future; use core::iter::FromIterator; use core::mem; use core::pin::Pin; use core::task::{Context, Poll}; -use alloc::boxed::Box; -use alloc::vec::Vec; -#[derive(Debug)] -enum ElemState -where - F: Future, -{ - Pending(F), - Done(Option), -} +use super::{assert_future, MaybeDone}; -impl ElemState -where - F: Future, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState -where - F: Future + Unpin, -{ -} +#[cfg(not(futures_no_atomic_cas))] +use crate::stream::{Collect, FuturesOrdered, StreamExt}; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has // the same safety as a normal field pin projection. - unsafe { slice.get_unchecked_mut() } - .iter_mut() - .map(|t| unsafe { Pin::new_unchecked(t) }) + unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) } -/// Future for the [`join_all`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] +/// Future for the [`join_all`] function. pub struct JoinAll where F: Future, { - elems: Pin]>>, + kind: JoinAllKind, +} + +#[cfg(not(futures_no_atomic_cas))] +const SMALL: usize = 30; + +pub(crate) enum JoinAllKind +where + F: Future, +{ + Small { + elems: Pin]>>, + }, + #[cfg(not(futures_no_atomic_cas))] + Big { + fut: Collect, Vec>, + }, } impl fmt::Debug for JoinAll @@ -71,9 +53,13 @@ where F::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinAll") - .field("elems", &self.elems) - .finish() + match self.kind { + JoinAllKind::Small { ref elems } => { + f.debug_struct("JoinAll").field("elems", elems).finish() + } + #[cfg(not(futures_no_atomic_cas))] + JoinAllKind::Big { ref fut, .. } => fmt::Debug::fmt(fut, f), + } } } @@ -89,10 +75,11 @@ where /// /// # See Also /// -/// This is purposefully a very simple API for basic use-cases. In a lot of -/// cases you will want to use the more powerful -/// [`FuturesUnordered`][crate::stream::FuturesUnordered] APIs, some -/// examples of additional functionality that provides: +/// `join_all` will switch to the more powerful [`FuturesOrdered`] for performance +/// reasons if the number of futures is large. You may want to look into using it or +/// it's counterpart [`FuturesUnordered`][crate::stream::FuturesUnordered] directly. +/// +/// Some examples for additional functionality provided by these are: /// /// * Adding new futures to the set even after it has been started. /// @@ -112,13 +99,33 @@ where /// assert_eq!(join_all(futures).await, [1, 2, 3]); /// # }); /// ``` -pub fn join_all(i: I) -> JoinAll +pub fn join_all(iter: I) -> JoinAll where I: IntoIterator, I::Item: Future, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); - JoinAll { elems: elems.into() } + #[cfg(futures_no_atomic_cas)] + { + let elems = iter.into_iter().map(MaybeDone::Future).collect::>().into(); + let kind = JoinAllKind::Small { elems }; + assert_future::::Output>, _>(JoinAll { kind }) + } + #[cfg(not(futures_no_atomic_cas))] + { + let iter = iter.into_iter(); + let kind = match iter.size_hint().1 { + None => JoinAllKind::Big { fut: iter.collect::>().collect() }, + Some(max) => { + if max <= SMALL { + let elems = iter.map(MaybeDone::Future).collect::>().into(); + JoinAllKind::Small { elems } + } else { + JoinAllKind::Big { fut: iter.collect::>().collect() } + } + } + }; + assert_future::::Output>, _>(JoinAll { kind }) + } } impl Future for JoinAll @@ -128,26 +135,27 @@ where type Output = Vec; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut all_done = true; + match &mut self.kind { + JoinAllKind::Small { elems } => { + let mut all_done = true; + + for elem in iter_pin_mut(elems.as_mut()) { + if elem.poll(cx).is_pending() { + all_done = false; + } + } - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - if let Poll::Ready(output) = pending.poll(cx) { - elem.set(ElemState::Done(Some(output))); + if all_done { + let mut elems = mem::replace(elems, Box::pin([])); + let result = + iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); + Poll::Ready(result) } else { - all_done = false; + Poll::Pending } } - } - - if all_done { - let mut elems = mem::replace(&mut self.elems, Box::pin([])); - let result = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) - .collect(); - Poll::Ready(result) - } else { - Poll::Pending + #[cfg(not(futures_no_atomic_cas))] + JoinAllKind::Big { fut } => Pin::new(fut).poll(cx), } } } diff --git a/futures-util/src/future/lazy.rs b/futures-util/src/future/lazy.rs index 5e72218d1f..e9a8cf2fa9 100644 --- a/futures-util/src/future/lazy.rs +++ b/futures-util/src/future/lazy.rs @@ -1,3 +1,4 @@ +use super::assert_future; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; @@ -6,7 +7,7 @@ use futures_core::task::{Context, Poll}; #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Lazy { - f: Option + f: Option, } // safe because we never generate `Pin<&mut F>` @@ -32,23 +33,28 @@ impl Unpin for Lazy {} /// # }); /// ``` pub fn lazy(f: F) -> Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { - Lazy { f: Some(f) } + assert_future::(Lazy { f: Some(f) }) } impl FusedFuture for Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { - fn is_terminated(&self) -> bool { self.f.is_none() } + fn is_terminated(&self) -> bool { + self.f.is_none() + } } impl Future for Lazy - where F: FnOnce(&mut Context<'_>) -> R, +where + F: FnOnce(&mut Context<'_>) -> R, { type Output = R; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Poll::Ready((self.f.take().unwrap())(cx)) + Poll::Ready((self.f.take().expect("Lazy polled after completion"))(cx)) } } diff --git a/futures-util/src/future/maybe_done.rs b/futures-util/src/future/maybe_done.rs index f16f889781..26e6c27588 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -1,8 +1,10 @@ //! Definition of the MaybeDone combinator +use super::assert_future; use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::task::{Context, Poll}; /// A future that may have completed. @@ -11,7 +13,7 @@ use futures_core::task::{Context, Poll}; #[derive(Debug)] pub enum MaybeDone { /// A not-yet-completed future - Future(Fut), + Future(/* #[pin] */ Fut), /// The output of the completed future Done(Fut::Output), /// The empty variant after the result of a [`MaybeDone`] has been @@ -19,7 +21,6 @@ pub enum MaybeDone { Gone, } -// Safe because we never generate `Pin<&mut Fut::Output>` impl Unpin for MaybeDone {} /// Wraps a future into a `MaybeDone` @@ -40,7 +41,7 @@ impl Unpin for MaybeDone {} /// # }); /// ``` pub fn maybe_done(future: Fut) -> MaybeDone { - MaybeDone::Future(future) + assert_future::<(), _>(MaybeDone::Future(future)) } impl MaybeDone { @@ -51,8 +52,7 @@ impl MaybeDone { #[inline] pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { unsafe { - let this = self.get_unchecked_mut(); - match this { + match self.get_unchecked_mut() { MaybeDone::Done(res) => Some(res), _ => None, } @@ -63,16 +63,14 @@ impl MaybeDone { /// towards completion. #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { + match &*self { + Self::Done(_) => {} + Self::Future(_) | Self::Gone => return None, + } unsafe { - let this = self.get_unchecked_mut(); - match this { - MaybeDone::Done(_) => {}, - MaybeDone::Future(_) | MaybeDone::Gone => return None, - }; - if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) { - Some(output) - } else { - unreachable!() + match mem::replace(self.get_unchecked_mut(), Self::Gone) { + MaybeDone::Done(output) => Some(output), + _ => unreachable!(), } } } @@ -81,8 +79,8 @@ impl MaybeDone { impl FusedFuture for MaybeDone { fn is_terminated(&self) -> bool { match self { - MaybeDone::Future(_) => false, - MaybeDone::Done(_) | MaybeDone::Gone => true, + Self::Future(_) => false, + Self::Done(_) | Self::Gone => true, } } } @@ -91,14 +89,16 @@ impl Future for MaybeDone { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let res = unsafe { + unsafe { match self.as_mut().get_unchecked_mut() { - MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)), - MaybeDone::Done(_) => return Poll::Ready(()), + MaybeDone::Future(f) => { + let res = ready!(Pin::new_unchecked(f).poll(cx)); + self.set(Self::Done(res)); + } + MaybeDone::Done(_) => {} MaybeDone::Gone => panic!("MaybeDone polled after value taken"), } - }; - self.set(MaybeDone::Done(res)); + } Poll::Ready(()) } } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 3f4bb01436..374e36512f 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -1,39 +1,51 @@ -//! Futures +//! Asynchronous values. //! -//! This module contains a number of functions for working with `Future`s, -//! including the [`FutureExt`] trait and the [`TryFutureExt`] trait which add -//! methods to `Future` types. +//! This module contains: +//! +//! - The [`Future`] trait. +//! - The [`FutureExt`] and [`TryFutureExt`] trait, which provides adapters for +//! chaining and composing futures. +//! - Top-level future combinators like [`lazy`](lazy()) which creates a future +//! from a closure that defines its return value, and [`ready`](ready()), +//! which constructs a future with an immediate defined value. + +#[doc(no_inline)] +pub use core::future::Future; #[cfg(feature = "alloc")] pub use futures_core::future::{BoxFuture, LocalBoxFuture}; -pub use futures_core::future::{FusedFuture, Future, TryFuture}; +pub use futures_core::future::{FusedFuture, TryFuture}; pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj}; // Extension traits and combinators - #[allow(clippy::module_inception)] mod future; pub use self::future::{ - Flatten, FlattenStream, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, + Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, MapInto, NeverError, Then, UnitError, }; +#[deprecated(note = "This is now an alias for [Flatten](Flatten)")] +pub use self::future::FlattenStream; + #[cfg(feature = "std")] pub use self::future::CatchUnwind; #[cfg(feature = "channel")] +#[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] pub use self::future::{Remote, RemoteHandle}; #[cfg(feature = "std")] -pub use self::future::Shared; +pub use self::future::{Shared, WeakShared}; mod try_future; pub use self::try_future::{ - AndThen, ErrInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, - TryFutureExt, UnwrapOrElse, + AndThen, ErrInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, MapOkOrElse, OkInto, + OrElse, TryFlatten, TryFlattenStream, TryFutureExt, UnwrapOrElse, }; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::try_future::FlattenSink; // Primitive futures @@ -47,12 +59,18 @@ pub use self::pending::{pending, Pending}; mod maybe_done; pub use self::maybe_done::{maybe_done, MaybeDone}; +mod try_maybe_done; +pub use self::try_maybe_done::{try_maybe_done, TryMaybeDone}; + mod option; pub use self::option::OptionFuture; mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; +mod poll_immediate; +pub use self::poll_immediate::{poll_immediate, PollImmediate}; + mod ready; pub use self::ready::{err, ok, ready, Ready}; @@ -93,16 +111,19 @@ 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 self::abortable::{abortable, Abortable, AbortHandle, AbortRegistration, Aborted}; -} +#[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. -fn assert_future(future: F) -> F +pub(crate) fn assert_future(future: F) -> F where F: Future, { diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 21413525d0..0bc377758a 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -1,45 +1,47 @@ //! Definition of the `Option` (optional step) combinator use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// A future representing a value which may or may not be present. -/// -/// Created by the [`From`] implementation for [`Option`](std::option::Option). -/// -/// # Examples -/// -/// ``` -/// # futures::executor::block_on(async { -/// use futures::future::OptionFuture; -/// -/// let mut a: OptionFuture<_> = Some(async { 123 }).into(); -/// assert_eq!(a.await, Some(123)); -/// -/// a = None.into(); -/// assert_eq!(a.await, None); -/// # }); -/// ``` -#[derive(Debug, Clone)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OptionFuture { - option: Option, +pin_project! { + /// A future representing a value which may or may not be present. + /// + /// Created by the [`From`] implementation for [`Option`](std::option::Option). + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future::OptionFuture; + /// + /// let mut a: OptionFuture<_> = Some(async { 123 }).into(); + /// assert_eq!(a.await, Some(123)); + /// + /// a = None.into(); + /// assert_eq!(a.await, None); + /// # }); + /// ``` + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct OptionFuture { + #[pin] + inner: Option, + } } -impl OptionFuture { - unsafe_pinned!(option: Option); +impl Default for OptionFuture { + fn default() -> Self { + Self { inner: None } + } } impl Future for OptionFuture { type Output = Option; - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - match self.option().as_pin_mut() { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project().inner.as_pin_mut() { Some(x) => x.poll(cx).map(Some), None => Poll::Ready(None), } @@ -48,7 +50,7 @@ impl Future for OptionFuture { impl FusedFuture for OptionFuture { fn is_terminated(&self) -> bool { - match &self.option { + match &self.inner { Some(x) => x.is_terminated(), None => true, } @@ -57,6 +59,6 @@ impl FusedFuture for OptionFuture { impl From> for OptionFuture { fn from(option: Option) -> Self { - OptionFuture { option } + Self { inner: option } } } diff --git a/futures-util/src/future/pending.rs b/futures-util/src/future/pending.rs index 5a7bbb8d59..92c78d52b8 100644 --- a/futures-util/src/future/pending.rs +++ b/futures-util/src/future/pending.rs @@ -1,3 +1,4 @@ +use super::assert_future; use core::marker; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; @@ -33,9 +34,7 @@ impl FusedFuture for Pending { /// # }); /// ``` pub fn pending() -> Pending { - Pending { - _data: marker::PhantomData, - } + assert_future::(Pending { _data: marker::PhantomData }) } impl Future for Pending { @@ -46,8 +45,7 @@ impl Future for Pending { } } -impl Unpin for Pending { -} +impl Unpin for Pending {} impl Clone for Pending { fn clone(&self) -> Self { diff --git a/futures-util/src/future/poll_fn.rs b/futures-util/src/future/poll_fn.rs index b7b10be85d..19311570b5 100644 --- a/futures-util/src/future/poll_fn.rs +++ b/futures-util/src/future/poll_fn.rs @@ -1,5 +1,6 @@ //! Definition of the `PollFn` adapter combinator +use super::assert_future; use core::fmt; use core::pin::Pin; use futures_core::future::Future; @@ -34,9 +35,9 @@ impl Unpin for PollFn {} /// ``` pub fn poll_fn(f: F) -> PollFn where - F: FnMut(&mut Context<'_>) -> Poll + F: FnMut(&mut Context<'_>) -> Poll, { - PollFn { f } + assert_future::(PollFn { f }) } impl fmt::Debug for PollFn { @@ -46,7 +47,8 @@ impl fmt::Debug for PollFn { } impl Future for PollFn - where F: FnMut(&mut Context<'_>) -> Poll, +where + F: FnMut(&mut Context<'_>) -> Poll, { type Output = T; diff --git a/futures-util/src/future/poll_immediate.rs b/futures-util/src/future/poll_immediate.rs new file mode 100644 index 0000000000..5ae555c73e --- /dev/null +++ b/futures-util/src/future/poll_immediate.rs @@ -0,0 +1,126 @@ +use super::assert_future; +use core::pin::Pin; +use futures_core::task::{Context, Poll}; +use futures_core::{FusedFuture, Future, Stream}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`poll_immediate`](poll_immediate()) function. + /// + /// It will never return [Poll::Pending](core::task::Poll::Pending) + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct PollImmediate { + #[pin] + future: Option + } +} + +impl Future for PollImmediate +where + F: Future, +{ + type Output = Option; + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let inner = + this.future.as_mut().as_pin_mut().expect("PollImmediate polled after completion"); + match inner.poll(cx) { + Poll::Ready(t) => { + this.future.set(None); + Poll::Ready(Some(t)) + } + Poll::Pending => Poll::Ready(None), + } + } +} + +impl FusedFuture for PollImmediate { + fn is_terminated(&self) -> bool { + self.future.is_none() + } +} + +/// A [Stream](crate::stream::Stream) implementation that can be polled repeatedly until the future is done. +/// The stream will never return [Poll::Pending](core::task::Poll::Pending) +/// so polling it in a tight loop is worse than using a blocking synchronous function. +/// ``` +/// # futures::executor::block_on(async { +/// use futures::task::Poll; +/// use futures::{StreamExt, future, pin_mut}; +/// use future::FusedFuture; +/// +/// let f = async { 1_u32 }; +/// pin_mut!(f); +/// let mut r = future::poll_immediate(f); +/// assert_eq!(r.next().await, Some(Poll::Ready(1))); +/// +/// let f = async {futures::pending!(); 42_u8}; +/// pin_mut!(f); +/// let mut p = future::poll_immediate(f); +/// assert_eq!(p.next().await, Some(Poll::Pending)); +/// assert!(!p.is_terminated()); +/// assert_eq!(p.next().await, Some(Poll::Ready(42))); +/// assert!(p.is_terminated()); +/// assert_eq!(p.next().await, None); +/// # }); +/// ``` +impl Stream for PollImmediate +where + F: Future, +{ + type Item = Poll; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + match this.future.as_mut().as_pin_mut() { + // inner is gone, so we can signal that the stream is closed. + None => Poll::Ready(None), + Some(fut) => Poll::Ready(Some(fut.poll(cx).map(|t| { + this.future.set(None); + t + }))), + } + } +} + +/// Creates a future that is immediately ready with an Option of a value. +/// Specifically this means that [poll](core::future::Future::poll()) always returns [Poll::Ready](core::task::Poll::Ready). +/// +/// # Caution +/// +/// When consuming the future by this function, note the following: +/// +/// - This function does not guarantee that the future will run to completion, so it is generally incompatible with passing the non-cancellation-safe future by value. +/// - Even if the future is cancellation-safe, creating and dropping new futures frequently may lead to performance problems. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::future; +/// +/// let r = future::poll_immediate(async { 1_u32 }); +/// assert_eq!(r.await, Some(1)); +/// +/// let p = future::poll_immediate(future::pending::()); +/// assert_eq!(p.await, None); +/// # }); +/// ``` +/// +/// ### Reusing a future +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::{future, pin_mut}; +/// let f = async {futures::pending!(); 42_u8}; +/// pin_mut!(f); +/// assert_eq!(None, future::poll_immediate(&mut f).await); +/// assert_eq!(42, f.await); +/// # }); +/// ``` +pub fn poll_immediate(f: F) -> PollImmediate { + assert_future::, PollImmediate>(PollImmediate { future: Some(f) }) +} diff --git a/futures-util/src/future/ready.rs b/futures-util/src/future/ready.rs index 48661b3d84..e3d791b3cf 100644 --- a/futures-util/src/future/ready.rs +++ b/futures-util/src/future/ready.rs @@ -1,3 +1,4 @@ +use super::assert_future; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; @@ -28,7 +29,7 @@ impl Future for Ready { #[inline] fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - Poll::Ready(self.0.take().unwrap()) + Poll::Ready(self.0.take().expect("Ready polled after completion")) } } @@ -45,7 +46,7 @@ impl Future for Ready { /// # }); /// ``` pub fn ready(t: T) -> Ready { - Ready(Some(t)) + assert_future::(Ready(Some(t))) } /// Create a future that is immediately ready with a success value. diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index 91b467dca6..bd44f20f77 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -1,7 +1,8 @@ +use super::assert_future; +use crate::future::{Either, FutureExt}; use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; +use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use crate::future::{Either, FutureExt}; /// Future for the [`select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] @@ -27,12 +28,48 @@ impl Unpin for Select {} /// /// # Examples /// +/// A simple example +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::{ +/// pin_mut, +/// future::Either, +/// future::self, +/// }; +/// +/// // These two futures have different types even though their outputs have the same type. +/// let future1 = async { +/// future::pending::<()>().await; // will never finish +/// 1 +/// }; +/// let future2 = async { +/// future::ready(2).await +/// }; +/// +/// // 'select' requires Future + Unpin bounds +/// pin_mut!(future1); +/// pin_mut!(future2); +/// +/// let value = match future::select(future1, future2).await { +/// Either::Left((value1, _)) => value1, // `value1` is resolved from `future1` +/// // `_` represents `future2` +/// Either::Right((value2, _)) => value2, // `value2` is resolved from `future2` +/// // `_` represents `future1` +/// }; +/// +/// assert!(value == 2); +/// # }); +/// ``` +/// +/// A more complex example +/// /// ``` /// use futures::future::{self, Either, Future, FutureExt}; /// /// // A poor-man's join implemented on top of select /// -/// fn join(a: A, b: B) -> impl Future +/// fn join(a: A, b: B) -> impl Future /// where A: Future + Unpin, /// B: Future + Unpin, /// { @@ -45,9 +82,13 @@ impl Unpin for Select {} /// } /// ``` pub fn select(future1: A, future2: B) -> Select - where A: Future + Unpin, B: Future + Unpin +where + A: Future + Unpin, + B: Future + Unpin, { - Select { inner: Some((future1, future2)) } + assert_future::, _>(Select { + inner: Some((future1, future2)), + }) } impl Future for Select @@ -67,7 +108,7 @@ where self.inner = Some((a, b)); Poll::Pending } - } + }, } } } diff --git a/futures-util/src/future/select_all.rs b/futures-util/src/future/select_all.rs index 9f7fb245bf..106e50844c 100644 --- a/futures-util/src/future/select_all.rs +++ b/futures-util/src/future/select_all.rs @@ -1,8 +1,9 @@ +use super::assert_future; use crate::future::FutureExt; +use alloc::vec::Vec; use core::iter::FromIterator; use core::mem; use core::pin::Pin; -use alloc::vec::Vec; use futures_core::future::Future; use futures_core::task::{Context, Poll}; @@ -31,25 +32,29 @@ impl Unpin for SelectAll {} /// /// This function will panic if the iterator specified contains no items. pub fn select_all(iter: I) -> SelectAll - where I: IntoIterator, - I::Item: Future + Unpin, +where + I: IntoIterator, + I::Item: Future + Unpin, { - let ret = SelectAll { - inner: iter.into_iter().collect() - }; + let ret = SelectAll { inner: iter.into_iter().collect() }; assert!(!ret.inner.is_empty()); - ret + assert_future::<(::Output, usize, Vec), _>(ret) +} + +impl SelectAll { + /// Consumes this combinator, returning the underlying futures. + pub fn into_inner(self) -> Vec { + self.inner + } } impl Future for SelectAll { type Output = (Fut::Output, usize, Vec); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| { - match f.poll_unpin(cx) { - Poll::Pending => None, - Poll::Ready(e) => Some((i, e)), - } + let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| match f.poll_unpin(cx) { + Poll::Pending => None, + Poll::Ready(e) => Some((i, e)), }); match item { Some((idx, res)) => { diff --git a/futures-util/src/future/select_ok.rs b/futures-util/src/future/select_ok.rs index 7f4f4d65f4..0ad83c6db6 100644 --- a/futures-util/src/future/select_ok.rs +++ b/futures-util/src/future/select_ok.rs @@ -1,8 +1,9 @@ +use super::assert_future; use crate::future::TryFutureExt; +use alloc::vec::Vec; use core::iter::FromIterator; use core::mem; use core::pin::Pin; -use alloc::vec::Vec; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; @@ -29,14 +30,16 @@ impl Unpin for SelectOk {} /// /// This function will panic if the iterator specified contains no items. pub fn select_ok(iter: I) -> SelectOk - where I: IntoIterator, - I::Item: TryFuture + Unpin, +where + I: IntoIterator, + I::Item: TryFuture + Unpin, { - let ret = SelectOk { - inner: iter.into_iter().collect() - }; + let ret = SelectOk { inner: iter.into_iter().collect() }; assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty"); - ret + assert_future::< + Result<(::Ok, Vec), ::Error>, + _, + >(ret) } impl Future for SelectOk { @@ -45,12 +48,11 @@ impl Future for SelectOk { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // loop until we've either exhausted all errors, a success was hit, or nothing is ready loop { - let item = self.inner.iter_mut().enumerate().find_map(|(i, f)| { - match f.try_poll_unpin(cx) { + let item = + self.inner.iter_mut().enumerate().find_map(|(i, f)| match f.try_poll_unpin(cx) { Poll::Pending => None, Poll::Ready(e) => Some((i, e)), - } - }); + }); match item { Some((idx, res)) => { // always remove Ok or Err, if it's not the last Err continue looping @@ -58,18 +60,18 @@ impl Future for SelectOk { match res { Ok(e) => { let rest = mem::replace(&mut self.inner, Vec::new()); - return Poll::Ready(Ok((e, rest))) + return Poll::Ready(Ok((e, rest))); } Err(e) => { if self.inner.is_empty() { - return Poll::Ready(Err(e)) + return Poll::Ready(Err(e)); } } } } None => { // based on the filter above, nothing is ready, return - return Poll::Pending + return Poll::Pending; } } } diff --git a/futures-util/src/future/try_future/and_then.rs b/futures-util/src/future/try_future/and_then.rs deleted file mode 100644 index 37333e0503..0000000000 --- a/futures-util/src/future/try_future/and_then.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`and_then`](super::TryFutureExt::and_then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct AndThen { - try_chain: TryChain, -} - -impl AndThen - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> AndThen { - AndThen { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Future(async_op(ok)), - Err(err) => TryChainAction::Output(Err(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/err_into.rs b/futures-util/src/future/try_future/err_into.rs deleted file mode 100644 index 731fcae39e..0000000000 --- a/futures-util/src/future/try_future/err_into.rs +++ /dev/null @@ -1,48 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`err_into`](super::TryFutureExt::err_into) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ErrInto { - future: Fut, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> ErrInto { - ErrInto { - future, - _marker: PhantomData, - } - } -} - -impl FusedFuture for ErrInto - where Fut: TryFuture + FusedFuture, - Fut::Error: Into, -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for ErrInto - where Fut: TryFuture, - Fut::Error: Into, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.future().try_poll(cx) - .map(|res| res.map_err(Into::into)) - } -} diff --git a/futures-util/src/future/try_future/flatten_sink.rs b/futures-util/src/future/try_future/flatten_sink.rs deleted file mode 100644 index d6863dd2cd..0000000000 --- a/futures-util/src/future/try_future/flatten_sink.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::FlattenStreamSink; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Sink for the [`flatten_sink`](super::TryFutureExt::flatten_sink) method. -#[derive(Debug)] -#[must_use = "sinks do nothing unless polled"] -pub struct FlattenSink -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl FlattenSink -where - Fut: TryFuture, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl FusedStream for FlattenSink -where - Fut: TryFuture, - S: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for FlattenSink -where - Fut: TryFuture, - S: TryStream, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -impl Sink for FlattenSink -where - Fut: TryFuture, - Si: Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/flatten_stream_sink.rs b/futures-util/src/future/try_future/flatten_stream_sink.rs deleted file mode 100644 index 5a56bf708d..0000000000 --- a/futures-util/src/future/try_future/flatten_stream_sink.rs +++ /dev/null @@ -1,181 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -#[must_use = "streams do nothing unless polled"] -pub(crate) struct FlattenStreamSink -where - Fut: TryFuture, -{ - state: State, -} - -impl Unpin for FlattenStreamSink -where - Fut: TryFuture + Unpin, - Fut::Ok: Unpin, -{ -} - -impl fmt::Debug for FlattenStreamSink -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStreamSink") - .field("state", &self.state) - .finish() - } -} - -impl FlattenStreamSink -where - Fut: TryFuture, -{ - unsafe_pinned!(state: State); - - pub(crate) fn new(future: Fut) -> Self { - Self { - state: State::Future(future), - } - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream or Sink - StreamOrSink(S), - // future resolved to error - Done, -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut S>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::StreamOrSink(s) => State::StreamOrSink(unsafe { Pin::new_unchecked(s) }), - State::Done => State::Done, - } - } -} - -impl State -where - Fut: TryFuture, -{ - fn poll_future(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let State::Future(f) = self.as_mut().get_pin_mut() { - match ready!(f.try_poll(cx)) { - Ok(s) => { - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.set(State::StreamOrSink(s)); - } - Err(e) => { - // Future resolved to error. - // We have neither a pollable stream nor a future. - self.set(State::Done); - return Poll::Ready(Err(e)); - } - } - } - Poll::Ready(Ok(())) - } -} - -impl FusedStream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::StreamOrSink(stream) => stream.is_terminated(), - State::Done => true, - } - } -} - -impl Stream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.try_poll_next(cx), - State::Done => Poll::Ready(None), - State::Future(_) => unreachable!(), - } - } -} - -#[cfg(feature = "sink")] -impl Sink for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: Sink, -{ - type Error = Fut::Error; - - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_ready(cx), - State::Done => panic!("poll_ready called after eof"), - State::Future(_) => unreachable!(), - } - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.start_send(item), - State::Future(_) => panic!("poll_ready not called first"), - State::Done => panic!("start_send called after eof"), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_flush(cx), - // if sink not yet resolved, nothing written ==> everything flushed - State::Future(_) => Poll::Ready(Ok(())), - State::Done => panic!("poll_flush called after eof"), - } - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let res = match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_close(cx), - State::Future(_) | State::Done => Poll::Ready(Ok(())), - }; - if res.is_ready() { - self.as_mut().state().set(State::Done); - } - res - } -} diff --git a/futures-util/src/future/try_future/inspect_err.rs b/futures-util/src/future/try_future/inspect_err.rs deleted file mode 100644 index 8700337bb2..0000000000 --- a/futures-util/src/future/try_future/inspect_err.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectErr { - future: Fut, - f: Option, -} - -impl Unpin for InspectErr {} - -impl InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectErr -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Error), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Err(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectErr twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/inspect_ok.rs b/futures-util/src/future/try_future/inspect_ok.rs deleted file mode 100644 index 3d0a972226..0000000000 --- a/futures-util/src/future/try_future/inspect_ok.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectOk { - future: Fut, - f: Option, -} - -impl Unpin for InspectOk {} - -impl InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectOk -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Ok), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Ok(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectOk twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/into_future.rs b/futures-util/src/future/try_future/into_future.rs index a766d5b66d..9f093d0e2e 100644 --- a/futures-util/src/future/try_future/into_future.rs +++ b/futures-util/src/future/try_future/into_future.rs @@ -1,36 +1,36 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Future for the [`into_future`](super::TryFutureExt::into_future) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IntoFuture { - future: Fut, +pin_project! { + /// Future for the [`into_future`](super::TryFutureExt::into_future) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct IntoFuture { + #[pin] + future: Fut, + } } impl IntoFuture { - unsafe_pinned!(future: Fut); - #[inline] - pub(super) fn new(future: Fut) -> IntoFuture { - IntoFuture { future } + pub(crate) fn new(future: Fut) -> Self { + Self { future } } } impl FusedFuture for IntoFuture { - fn is_terminated(&self) -> bool { self.future.is_terminated() } + fn is_terminated(&self) -> bool { + self.future.is_terminated() + } } impl Future for IntoFuture { type Output = Result; #[inline] - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.future().try_poll(cx) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().future.try_poll(cx) } } diff --git a/futures-util/src/future/try_future/map_err.rs b/futures-util/src/future/try_future/map_err.rs deleted file mode 100644 index 8edebad86d..0000000000 --- a/futures-util/src/future/try_future/map_err.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_err`](super::TryFutureExt::map_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapErr { - future: Fut, - f: Option, -} - -impl MapErr { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapErr. - pub(super) fn new(future: Fut, f: F) -> MapErr { - MapErr { future, f: Some(f) } - } -} - -impl Unpin for MapErr {} - -impl FusedFuture for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - fn is_terminated(&self) -> bool { self.f.is_none() } -} - -impl Future for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let f = self.as_mut().f().take() - .expect("MapErr must not be polled after it returned `Poll::Ready`"); - result.map_err(f) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok.rs b/futures-util/src/future/try_future/map_ok.rs deleted file mode 100644 index ab28f1443f..0000000000 --- a/futures-util/src/future/try_future/map_ok.rs +++ /dev/null @@ -1,54 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok`](super::TryFutureExt::map_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOk { - future: Fut, - f: Option, -} - -impl MapOk { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapOk. - pub(super) fn new(future: Fut, f: F) -> MapOk { - MapOk { future, f: Some(f) } - } -} - -impl Unpin for MapOk {} - -impl FusedFuture for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("MapOk must not be polled after it returned `Poll::Ready`"); - result.map(op) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok_or_else.rs b/futures-util/src/future/try_future/map_ok_or_else.rs deleted file mode 100644 index 730b67922c..0000000000 --- a/futures-util/src/future/try_future/map_ok_or_else.rs +++ /dev/null @@ -1,59 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok_or_else`](super::TryFutureExt::map_ok_or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOkOrElse { - future: Fut, - f: Option, - e: Option, -} - -impl MapOkOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - unsafe_unpinned!(e: Option); - - /// Creates a new MapOkOrElse. - pub(super) fn new(future: Fut, e: E, f: F) -> Self { - Self { future, f: Some(f), e: Some(e) } - } -} - -impl Unpin for MapOkOrElse {} - -impl FusedFuture for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() || self.e.is_none() - } -} - -impl Future for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - type Output = T; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - match result { - Ok(i) => (self.as_mut().f().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(i), - Err(e) => (self.as_mut().e().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(e), - } - }) - } -} diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index e8e059e373..fb3bdd8a02 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -14,65 +14,122 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; -// Combinators - -mod and_then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::and_then::AndThen; +use crate::fns::{ + inspect_err_fn, inspect_ok_fn, into_fn, map_err_fn, map_ok_fn, map_ok_or_else_fn, + unwrap_or_else_fn, InspectErrFn, InspectOkFn, IntoFn, MapErrFn, MapOkFn, MapOkOrElseFn, + UnwrapOrElseFn, +}; +use crate::future::{assert_future, Inspect, Map}; +use crate::stream::assert_stream; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; +// Combinators +mod into_future; +mod try_flatten; +mod try_flatten_err; + +delegate_all!( + /// Future for the [`try_flatten`](TryFutureExt::try_flatten) method. + TryFlatten( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method. + TryFlattenErr( + try_flatten_err::TryFlattenErr + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method. + TryFlattenStream( + try_flatten::TryFlatten + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + where Fut: TryFuture +); #[cfg(feature = "sink")] -mod flatten_sink; -#[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_sink::FlattenSink; - -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; +delegate_all!( + /// Sink for the [`flatten_sink`](TryFutureExt::flatten_sink) method. + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] + FlattenSink( + try_flatten::TryFlatten + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`and_then`](TryFutureExt::and_then) method. + AndThen( + TryFlatten, Fut2> + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] +); + +delegate_all!( + /// Future for the [`or_else`](TryFutureExt::or_else) method. + OrElse( + TryFlattenErr, Fut2> + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] +); + +delegate_all!( + /// Future for the [`err_into`](TryFutureExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Future + FusedFuture + New[|x: Fut| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`ok_into`](TryFutureExt::ok_into) method. + OkInto( + MapOk> + ): Debug + Future + FusedFuture + New[|x: Fut| MapOk::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] +); -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; - -mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::IntoFuture; -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; - -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; - -mod map_ok_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok_or_else::MapOkOrElse; - -mod or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::or_else::OrElse; - -mod try_flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::try_flatten_stream::TryFlattenStream; - -mod unwrap_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unwrap_or_else::UnwrapOrElse; - -// Implementation details - -mod flatten_stream_sink; -pub(crate) use self::flatten_stream_sink::FlattenStreamSink; - -mod try_chain; -pub(crate) use self::try_chain::{TryChain, TryChainAction}; +delegate_all!( + /// Future for the [`map_ok`](TryFutureExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`map_err`](TryFutureExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] +); + +delegate_all!( + /// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method. + MapOkOrElse( + Map, MapOkOrElseFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] +); + +delegate_all!( + /// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method. + UnwrapOrElse( + Map, UnwrapOrElseFn> + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] +); impl TryFutureExt for Fut {} @@ -110,12 +167,13 @@ pub trait TryFutureExt: TryFuture { /// take_sink(fut.flatten_sink()) /// ``` #[cfg(feature = "sink")] + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] fn flatten_sink(self) -> FlattenSink where Self::Ok: Sink, Self: Sized, { - FlattenSink::new(self) + crate::sink::assert_sink::(FlattenSink::new(self)) } /// Maps this future's success value to a different value. @@ -161,7 +219,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Ok) -> T, Self: Sized, { - MapOk::new(self, f) + assert_future::, _>(MapOk::new(self, f)) } /// Maps this future's success value to a different value, and permits for error handling resulting in the same type. @@ -172,7 +230,7 @@ pub trait TryFutureExt: TryFuture { /// The provided closure `f` will only be called if this future is resolved /// to an [`Ok`]. If it resolves to an [`Err`], panics, or is dropped, then /// the provided closure will never be invoked. - /// + /// /// The provided closure `e` will only be called if this future is resolved /// to an [`Err`]. If it resolves to an [`Ok`], panics, or is dropped, then /// the provided closure will never be invoked. @@ -189,20 +247,20 @@ pub trait TryFutureExt: TryFuture { /// let future = async { Ok::(5) }; /// let future = future.map_ok_or_else(|x| x * 2, |x| x + 3); /// assert_eq!(future.await, 8); - /// + /// /// let future = async { Err::(5) }; /// let future = future.map_ok_or_else(|x| x * 2, |x| x + 3); /// assert_eq!(future.await, 10); /// # }); /// ``` - /// + /// fn map_ok_or_else(self, e: E, f: F) -> MapOkOrElse where F: FnOnce(Self::Ok) -> T, E: FnOnce(Self::Error) -> T, Self: Sized, { - MapOkOrElse::new(self, e, f) + assert_future::(MapOkOrElse::new(self, f, e)) } /// Maps this future's error value to a different value. @@ -249,7 +307,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Error) -> E, Self: Sized, { - MapErr::new(self, f) + assert_future::, _>(MapErr::new(self, f)) } /// Maps this future's [`Error`](TryFuture::Error) to a new error type @@ -279,7 +337,17 @@ pub trait TryFutureExt: TryFuture { Self: Sized, Self::Error: Into, { - ErrInto::new(self) + assert_future::, _>(ErrInto::new(self)) + } + + /// Maps this future's [`Ok`](TryFuture::Ok) to a new type + /// using the [`Into`](std::convert::Into) trait. + fn ok_into(self) -> OkInto + where + Self: Sized, + Self::Ok: Into, + { + assert_future::, _>(OkInto::new(self)) } /// Executes another future after this one resolves successfully. The @@ -324,7 +392,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - AndThen::new(self, f) + assert_future::, _>(AndThen::new(self, f)) } /// Executes another future if this one resolves to an error. The @@ -369,7 +437,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - OrElse::new(self, f) + assert_future::, _>(OrElse::new(self, f)) } /// Do something with the success value of a future before passing it on. @@ -395,7 +463,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Ok), Self: Sized, { - InspectOk::new(self, f) + assert_future::, _>(InspectOk::new(self, f)) } /// Do something with the error value of a future before passing it on. @@ -421,7 +489,19 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Error), Self: Sized, { - InspectErr::new(self, f) + assert_future::, _>(InspectErr::new(self, f)) + } + + /// Flatten the execution of this future when the successful result of this + /// future is another future. + /// + /// This is equivalent to `future.and_then(|x| x)`. + fn try_flatten(self) -> TryFlatten + where + Self::Ok: TryFuture, + Self: Sized, + { + assert_future::::Ok, Self::Error>, _>(TryFlatten::new(self)) } /// Flatten the execution of this future when the successful result of this @@ -454,10 +534,12 @@ pub trait TryFutureExt: TryFuture { Self::Ok: TryStream, Self: Sized, { - TryFlattenStream::new(self) + assert_stream::::Ok, Self::Error>, _>(TryFlattenStream::new( + self, + )) } - /// Unwraps this future's ouput, producing a future with this future's + /// Unwraps this future's output, producing a future with this future's /// [`Ok`](TryFuture::Ok) type as its /// [`Output`](std::future::Future::Output) type. /// @@ -484,12 +566,13 @@ pub trait TryFutureExt: TryFuture { Self: Sized, F: FnOnce(Self::Error) -> Self::Ok, { - UnwrapOrElse::new(self, f) + assert_future::(UnwrapOrElse::new(self, f)) } - /// Wraps a [`TryFuture`] into a future compatable with libraries using - /// futures 0.1 future definitons. Requires the `compat` feature to enable. + /// Wraps a [`TryFuture`] into a future compatible with libraries using + /// futures 0.1 future definitions. Requires the `compat` feature to enable. #[cfg(feature = "compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "compat")))] fn compat(self) -> Compat where Self: Sized + Unpin, @@ -522,7 +605,7 @@ pub trait TryFutureExt: TryFuture { where Self: Sized, { - IntoFuture::new(self) + assert_future::, _>(IntoFuture::new(self)) } /// A convenience method for calling [`TryFuture::try_poll`] on [`Unpin`] diff --git a/futures-util/src/future/try_future/or_else.rs b/futures-util/src/future/try_future/or_else.rs deleted file mode 100644 index a9c006fa9f..0000000000 --- a/futures-util/src/future/try_future/or_else.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`or_else`](super::TryFutureExt::or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OrElse { - try_chain: TryChain, -} - -impl OrElse - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> OrElse { - OrElse { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Output(Ok(ok)), - Err(err) => TryChainAction::Future(async_op(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/try_chain.rs b/futures-util/src/future/try_future/try_chain.rs deleted file mode 100644 index 662bdf2d26..0000000000 --- a/futures-util/src/future/try_future/try_chain.rs +++ /dev/null @@ -1,108 +0,0 @@ -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum TryChain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for TryChain {} - -pub(crate) enum TryChainAction - where Fut2: TryFuture, -{ - Future(Fut2), - Output(Result), -} - -impl TryChain - where Fut1: TryFuture, - Fut2: TryFuture, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> TryChain { - TryChain::First(fut1, Some(data)) - } - - pub(crate) fn is_terminated(&self) -> bool { - match self { - TryChain::First(..) | TryChain::Second(_) => false, - TryChain::Empty => true, - } - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll> - where F: FnOnce(Result, Data) -> TryChainAction, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - TryChain::First(fut1, data) => { - // Poll the first future - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.try_poll(cx)); - (output, data.take().unwrap()) - } - TryChain::Second(fut2) => { - // Poll the second future - return unsafe { Pin::new_unchecked(fut2) } - .try_poll(cx) - .map(|res| { - *this = TryChain::Empty; // Drop fut2. - res - }); - } - TryChain::Empty => { - panic!("future must not be polled after it returned `Poll::Ready`"); - } - }; - - *this = TryChain::Empty; // Drop fut1 - let f = f.take().unwrap(); - match f(output, data) { - TryChainAction::Future(fut2) => *this = TryChain::Second(fut2), - TryChainAction::Output(output) => return Poll::Ready(output), - } - } - } -} - -#[cfg(test)] -mod tests { - use std::pin::Pin; - use std::task::Poll; - - use futures_test::task::noop_context; - - use crate::future::ready; - - use super::{TryChain, TryChainAction}; - - #[test] - fn try_chain_is_terminated() { - let mut cx = noop_context(); - - let mut future = TryChain::new(ready(Ok(1)), ()); - assert!(!future.is_terminated()); - - let res = Pin::new(&mut future).poll( - &mut cx, - |res: Result, ()| { - assert!(res.is_ok()); - TryChainAction::Future(ready(Ok(2))) - }, - ); - assert_eq!(res, Poll::Ready::>(Ok(2))); - assert!(future.is_terminated()); - } -} diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs new file mode 100644 index 0000000000..1ce4559ac2 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -0,0 +1,162 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream, TryStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + #[project = TryFlattenProj] + #[derive(Debug)] + pub enum TryFlatten { + First { #[pin] f: Fut1 }, + Second { #[pin] f: Fut2 }, + Empty, + } +} + +impl TryFlatten { + pub(crate) fn new(future: Fut1) -> Self { + Self::First { f: future } + } +} + +impl FusedFuture for TryFlatten +where + Fut: TryFuture, + Fut::Ok: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + Self::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlatten +where + Fut: TryFuture, + Fut::Ok: TryFuture, +{ + type Output = Result<::Ok, Fut::Error>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + match self.as_mut().project() { + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Err(e); + } + }, + TryFlattenProj::Second { f } => { + let output = ready!(f.try_poll(cx)); + self.set(Self::Empty); + break output; + } + TryFlattenProj::Empty => panic!("TryFlatten polled after completion"), + } + }) + } +} + +impl FusedStream for TryFlatten +where + Fut: TryFuture, + Fut::Ok: TryStream, +{ + fn is_terminated(&self) -> bool { + match self { + Self::Empty => true, + _ => false, + } + } +} + +impl Stream for TryFlatten +where + Fut: TryFuture, + Fut::Ok: TryStream, +{ + type Item = Result<::Ok, Fut::Error>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + match self.as_mut().project() { + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Some(Err(e)); + } + }, + TryFlattenProj::Second { f } => { + let output = ready!(f.try_poll_next(cx)); + if output.is_none() { + self.set(Self::Empty); + } + break output; + } + TryFlattenProj::Empty => break None, + } + }) + } +} + +#[cfg(feature = "sink")] +impl Sink for TryFlatten +where + Fut: TryFuture, + Fut::Ok: Sink, +{ + type Error = Fut::Error; + + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + match self.as_mut().project() { + TryFlattenProj::First { f } => match ready!(f.try_poll(cx)) { + Ok(f) => self.set(Self::Second { f }), + Err(e) => { + self.set(Self::Empty); + break Err(e); + } + }, + TryFlattenProj::Second { f } => { + break ready!(f.poll_ready(cx)); + } + TryFlattenProj::Empty => panic!("poll_ready called after eof"), + } + }) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + match self.project() { + TryFlattenProj::First { .. } => panic!("poll_ready not called first"), + TryFlattenProj::Second { f } => f.start_send(item), + TryFlattenProj::Empty => panic!("start_send called after eof"), + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.project() { + TryFlattenProj::First { .. } => Poll::Ready(Ok(())), + TryFlattenProj::Second { f } => f.poll_flush(cx), + TryFlattenProj::Empty => panic!("poll_flush called after eof"), + } + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = match self.as_mut().project() { + TryFlattenProj::Second { f } => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(Self::Empty); + } + res + } +} diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs new file mode 100644 index 0000000000..39b7d9f5f6 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -0,0 +1,62 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + #[project = TryFlattenErrProj] + #[derive(Debug)] + pub enum TryFlattenErr { + First { #[pin] f: Fut1 }, + Second { #[pin] f: Fut2 }, + Empty, + } +} + +impl TryFlattenErr { + pub(crate) fn new(future: Fut1) -> Self { + Self::First { f: future } + } +} + +impl FusedFuture for TryFlattenErr +where + Fut: TryFuture, + Fut::Error: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + Self::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlattenErr +where + Fut: TryFuture, + Fut::Error: TryFuture, +{ + type Output = Result::Error>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + match self.as_mut().project() { + TryFlattenErrProj::First { f } => match ready!(f.try_poll(cx)) { + Err(f) => self.set(Self::Second { f }), + Ok(e) => { + self.set(Self::Empty); + break Ok(e); + } + }, + TryFlattenErrProj::Second { f } => { + let output = ready!(f.try_poll(cx)); + self.set(Self::Empty); + break output; + } + TryFlattenErrProj::Empty => panic!("TryFlattenErr polled after completion"), + } + }) + } +} diff --git a/futures-util/src/future/try_future/try_flatten_stream.rs b/futures-util/src/future/try_future/try_flatten_stream.rs deleted file mode 100644 index 24624314c0..0000000000 --- a/futures-util/src/future/try_future/try_flatten_stream.rs +++ /dev/null @@ -1,91 +0,0 @@ -use super::FlattenStreamSink; -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`try_flatten_stream`](super::TryFutureExt::try_flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct TryFlattenStream -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl fmt::Debug for TryFlattenStream -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryFlattenStream") - .field("inner", &self.inner) - .finish() - } -} - -impl FusedStream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -#[cfg(feature = "sink")] -impl Sink for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/unwrap_or_else.rs b/futures-util/src/future/try_future/unwrap_or_else.rs deleted file mode 100644 index 286cc009fb..0000000000 --- a/futures-util/src/future/try_future/unwrap_or_else.rs +++ /dev/null @@ -1,55 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`unwrap_or_else`](super::TryFutureExt::unwrap_or_else) -/// method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnwrapOrElse { - future: Fut, - f: Option, -} - -impl UnwrapOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new UnwrapOrElse. - pub(super) fn new(future: Fut, f: F) -> UnwrapOrElse { - UnwrapOrElse { future, f: Some(f) } - } -} - -impl Unpin for UnwrapOrElse {} - -impl FusedFuture for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - type Output = Fut::Ok; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("UnwrapOrElse already returned `Poll::Ready` before"); - result.unwrap_or_else(op) - }) - } -} diff --git a/futures-util/src/future/try_join.rs b/futures-util/src/future/try_join.rs index da85eff91d..6af1f0ccbf 100644 --- a/futures-util/src/future/try_join.rs +++ b/futures-util/src/future/try_join.rs @@ -1,22 +1,24 @@ #![allow(non_snake_case)] -use crate::future::{MaybeDone, maybe_done, TryFutureExt, IntoFuture}; +use crate::future::{assert_future, try_maybe_done, TryMaybeDone}; use core::fmt; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; macro_rules! generate { ($( $(#[$doc:meta])* ($Join:ident, ), )*) => ($( - $(#[$doc])* - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub struct $Join { - Fut1: MaybeDone>, - $($Fut: MaybeDone>,)* + pin_project! { + $(#[$doc])* + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct $Join { + #[pin] Fut1: TryMaybeDone, + $(#[pin] $Fut: TryMaybeDone<$Fut>,)* + } } impl fmt::Debug for $Join @@ -45,17 +47,12 @@ macro_rules! generate { $Fut: TryFuture ),* { - fn new(Fut1: Fut1, $($Fut: $Fut),*) -> $Join { - $Join { - Fut1: maybe_done(TryFutureExt::into_future(Fut1)), - $($Fut: maybe_done(TryFutureExt::into_future($Fut))),* + fn new(Fut1: Fut1, $($Fut: $Fut),*) -> Self { + Self { + Fut1: try_maybe_done(Fut1), + $($Fut: try_maybe_done($Fut)),* } } - - unsafe_pinned!(Fut1: MaybeDone>); - $( - unsafe_pinned!($Fut: MaybeDone>); - )* } impl Future for $Join @@ -68,29 +65,20 @@ macro_rules! generate { type Output = Result<(Fut1::Ok, $($Fut::Ok),*), Fut1::Error>; fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; - if self.as_mut().Fut1().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().Fut1().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().Fut1().take_output().unwrap().err().unwrap())); - } + let mut futures = self.project(); + all_done &= futures.Fut1.as_mut().poll(cx)?.is_ready(); $( - if self.as_mut().$Fut().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().$Fut().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().$Fut().take_output().unwrap().err().unwrap())); - } + all_done &= futures.$Fut.as_mut().poll(cx)?.is_ready(); )* if all_done { Poll::Ready(Ok(( - self.as_mut().Fut1().take_output().unwrap().ok().unwrap(), + futures.Fut1.take_output().unwrap(), $( - self.as_mut().$Fut().take_output().unwrap().ok().unwrap() + futures.$Fut.take_output().unwrap() ),* ))) } else { @@ -120,7 +108,7 @@ generate! { /// /// This function will return a new future which awaits both futures to /// complete. If successful, the returned future will finish with a tuple of -/// both results. If unsuccesful, it will complete with the first error +/// both results. If unsuccessful, it will complete with the first error /// encountered. /// /// Note that this function consumes the passed futures and returns a @@ -162,7 +150,7 @@ where Fut1: TryFuture, Fut2: TryFuture, { - TryJoin::new(future1, future2) + assert_future::, _>(TryJoin::new(future1, future2)) } /// Same as [`try_join`](try_join()), but with more futures. @@ -191,7 +179,9 @@ where Fut2: TryFuture, Fut3: TryFuture, { - TryJoin3::new(future1, future2, future3) + assert_future::, _>(TryJoin3::new( + future1, future2, future3, + )) } /// Same as [`try_join`](try_join()), but with more futures. @@ -223,7 +213,9 @@ where Fut3: TryFuture, Fut4: TryFuture, { - TryJoin4::new(future1, future2, future3, future4) + assert_future::, _>( + TryJoin4::new(future1, future2, future3, future4), + ) } /// Same as [`try_join`](try_join()), but with more futures. @@ -258,5 +250,7 @@ where Fut4: TryFuture, Fut5: TryFuture, { - TryJoin5::new(future1, future2, future3, future4, future5) + assert_future::, _>( + TryJoin5::new(future1, future2, future3, future4, future5), + ) } diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 30300e4e3e..29244af837 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -1,63 +1,28 @@ //! Definition of the `TryJoinAll` combinator, waiting for all of a list of //! futures to finish with either success or error. +use alloc::boxed::Box; +use alloc::vec::Vec; use core::fmt; use core::future::Future; use core::iter::FromIterator; use core::mem; use core::pin::Pin; use core::task::{Context, Poll}; -use alloc::boxed::Box; -use alloc::vec::Vec; - -use super::TryFuture; -#[derive(Debug)] -enum ElemState -where - F: TryFuture, -{ - Pending(F), - Done(Option), -} - -impl ElemState -where - F: TryFuture, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState where F: TryFuture + Unpin {} +use super::{assert_future, TryFuture, TryMaybeDone}; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's // invariants aren't required to transmit through slices. Otherwise this has // the same safety as a normal field pin projection. - unsafe { slice.get_unchecked_mut() } - .iter_mut() - .map(|t| unsafe { Pin::new_unchecked(t) }) + unsafe { slice.get_unchecked_mut() }.iter_mut().map(|t| unsafe { Pin::new_unchecked(t) }) } enum FinalState { Pending, AllDone, - Error(E) + Error(E), } /// Future for the [`try_join_all`] function. @@ -66,7 +31,7 @@ pub struct TryJoinAll where F: TryFuture, { - elems: Pin]>>, + elems: Pin]>>, } impl fmt::Debug for TryJoinAll @@ -76,9 +41,7 @@ where F::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryJoinAll") - .field("elems", &self.elems) - .finish() + f.debug_struct("TryJoinAll").field("elems", &self.elems).finish() } } @@ -125,10 +88,10 @@ where I: IntoIterator, I::Item: TryFuture, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); - TryJoinAll { - elems: elems.into(), - } + let elems: Box<[_]> = i.into_iter().map(TryMaybeDone::Future).collect(); + assert_future::::Ok>, ::Error>, _>( + TryJoinAll { elems: elems.into() }, + ) } impl Future for TryJoinAll @@ -140,17 +103,13 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut state = FinalState::AllDone; - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - match pending.try_poll(cx) { - Poll::Pending => state = FinalState::Pending, - Poll::Ready(output) => match output { - Ok(item) => elem.set(ElemState::Done(Some(item))), - Err(e) => { - state = FinalState::Error(e); - break; - } - } + for elem in iter_pin_mut(self.elems.as_mut()) { + match elem.try_poll(cx) { + Poll::Pending => state = FinalState::Pending, + Poll::Ready(Ok(())) => {} + Poll::Ready(Err(e)) => { + state = FinalState::Error(e); + break; } } } @@ -159,15 +118,14 @@ where FinalState::Pending => Poll::Pending, FinalState::AllDone => { let mut elems = mem::replace(&mut self.elems, Box::pin([])); - let results = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) - .collect(); + let results = + iter_pin_mut(elems.as_mut()).map(|e| e.take_output().unwrap()).collect(); Poll::Ready(Ok(results)) - }, + } FinalState::Error(e) => { let _ = mem::replace(&mut self.elems, Box::pin([])); Poll::Ready(Err(e)) - }, + } } } } diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs new file mode 100644 index 0000000000..24044d2c27 --- /dev/null +++ b/futures-util/src/future/try_maybe_done.rs @@ -0,0 +1,92 @@ +//! Definition of the TryMaybeDone combinator + +use super::assert_future; +use core::mem; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::ready; +use futures_core::task::{Context, Poll}; + +/// A future that may have completed with an error. +/// +/// This is created by the [`try_maybe_done()`] function. +#[derive(Debug)] +pub enum TryMaybeDone { + /// A not-yet-completed future + Future(/* #[pin] */ Fut), + /// The output of the completed future + Done(Fut::Ok), + /// The empty variant after the result of a [`TryMaybeDone`] has been + /// taken using the [`take_output`](TryMaybeDone::take_output) method, + /// or if the future returned an error. + Gone, +} + +impl Unpin for TryMaybeDone {} + +/// Wraps a future into a `TryMaybeDone` +pub fn try_maybe_done(future: Fut) -> TryMaybeDone { + assert_future::, _>(TryMaybeDone::Future(future)) +} + +impl TryMaybeDone { + /// Returns an [`Option`] containing a mutable reference to the output of the future. + /// The output of this method will be [`Some`] if and only if the inner + /// future has completed successfully and [`take_output`](TryMaybeDone::take_output) + /// has not yet been called. + #[inline] + pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Ok> { + unsafe { + match self.get_unchecked_mut() { + TryMaybeDone::Done(res) => Some(res), + _ => None, + } + } + } + + /// Attempt to take the output of a `TryMaybeDone` without driving it + /// towards completion. + #[inline] + pub fn take_output(self: Pin<&mut Self>) -> Option { + match &*self { + Self::Done(_) => {} + Self::Future(_) | Self::Gone => return None, + } + unsafe { + match mem::replace(self.get_unchecked_mut(), Self::Gone) { + TryMaybeDone::Done(output) => Some(output), + _ => unreachable!(), + } + } + } +} + +impl FusedFuture for TryMaybeDone { + fn is_terminated(&self) -> bool { + match self { + Self::Future(_) => false, + Self::Done(_) | Self::Gone => true, + } + } +} + +impl Future for TryMaybeDone { + type Output = Result<(), Fut::Error>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { + match self.as_mut().get_unchecked_mut() { + TryMaybeDone::Future(f) => match ready!(Pin::new_unchecked(f).try_poll(cx)) { + Ok(res) => self.set(Self::Done(res)), + Err(e) => { + self.set(Self::Gone); + return Poll::Ready(Err(e)); + } + }, + TryMaybeDone::Done(_) => {} + TryMaybeDone::Gone => panic!("TryMaybeDone polled after value taken"), + } + } + Poll::Ready(Ok(())) + } +} diff --git a/futures-util/src/future/try_select.rs b/futures-util/src/future/try_select.rs index 56564f5b5c..4d0b7ff135 100644 --- a/futures-util/src/future/try_select.rs +++ b/futures-util/src/future/try_select.rs @@ -1,7 +1,7 @@ +use crate::future::{Either, TryFutureExt}; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; -use crate::future::{Either, TryFutureExt}; /// Future for the [`try_select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] @@ -48,19 +48,23 @@ impl Unpin for TrySelect {} /// } /// ``` pub fn try_select(future1: A, future2: B) -> TrySelect - where A: TryFuture + Unpin, B: TryFuture + Unpin +where + A: TryFuture + Unpin, + B: TryFuture + Unpin, { - TrySelect { inner: Some((future1, future2)) } + super::assert_future::< + Result, Either<(A::Error, B), (B::Error, A)>>, + _, + >(TrySelect { inner: Some((future1, future2)) }) } impl Future for TrySelect - where A: TryFuture, B: TryFuture +where + A: TryFuture, + B: TryFuture, { #[allow(clippy::type_complexity)] - type Output = Result< - Either<(A::Ok, B), (B::Ok, A)>, - Either<(A::Error, B), (B::Error, A)>, - >; + type Output = Result, Either<(A::Error, B), (B::Error, A)>>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let (mut a, mut b) = self.inner.take().expect("cannot poll Select twice"); @@ -74,7 +78,7 @@ impl Future for TrySelect self.inner = Some((a, b)); Poll::Pending } - } + }, } } } diff --git a/futures-util/src/io/allow_std.rs b/futures-util/src/io/allow_std.rs index 346e9babea..ec30ee31e5 100644 --- a/futures-util/src/io/allow_std.rs +++ b/futures-util/src/io/allow_std.rs @@ -1,9 +1,7 @@ use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, IoSlice, IoSliceMut, SeekFrom}; -use std::{fmt, io}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; use std::pin::Pin; +use std::{fmt, io}; /// A simple wrapper type which allows types which implement only /// implement `std::io::Read` or `std::io::Write` @@ -35,13 +33,13 @@ macro_rules! try_with_interrupt { } } } - } + }; } impl AllowStdIo { /// Creates a new `AllowStdIo` from an existing IO object. pub fn new(io: T) -> Self { - AllowStdIo(io) + Self(io) } /// Returns a reference to the contained IO object. @@ -60,7 +58,10 @@ impl AllowStdIo { } } -impl io::Write for AllowStdIo where T: io::Write { +impl io::Write for AllowStdIo +where + T: io::Write, +{ fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -78,16 +79,23 @@ impl io::Write for AllowStdIo where T: io::Write { } } -impl AsyncWrite for AllowStdIo where T: io::Write { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { +impl AsyncWrite for AllowStdIo +where + T: io::Write, +{ + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.write(buf)))) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.write_vectored(bufs)))) } @@ -101,17 +109,16 @@ impl AsyncWrite for AllowStdIo where T: io::Write { } } -impl io::Read for AllowStdIo where T: io::Read { +impl io::Read for AllowStdIo +where + T: io::Read, +{ fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.0.read_vectored(bufs) } - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.0.initializer() - } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { self.0.read_to_end(buf) } @@ -123,40 +130,53 @@ impl io::Read for AllowStdIo where T: io::Read { } } -impl AsyncRead for AllowStdIo where T: io::Read { - fn poll_read(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { +impl AsyncRead for AllowStdIo +where + T: io::Read, +{ + fn poll_read( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.read(buf)))) } - fn poll_read_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.read_vectored(bufs)))) } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.0.initializer() - } } -impl io::Seek for AllowStdIo where T: io::Seek { +impl io::Seek for AllowStdIo +where + T: io::Seek, +{ fn seek(&mut self, pos: SeekFrom) -> io::Result { self.0.seek(pos) } } -impl AsyncSeek for AllowStdIo where T: io::Seek { - fn poll_seek(mut self: Pin<&mut Self>, _: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { +impl AsyncSeek for AllowStdIo +where + T: io::Seek, +{ + fn poll_seek( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { Poll::Ready(Ok(try_with_interrupt!(self.0.seek(pos)))) } } -impl io::BufRead for AllowStdIo where T: io::BufRead { +impl io::BufRead for AllowStdIo +where + T: io::BufRead, +{ fn fill_buf(&mut self) -> io::Result<&[u8]> { self.0.fill_buf() } @@ -165,10 +185,11 @@ impl io::BufRead for AllowStdIo where T: io::BufRead { } } -impl AsyncBufRead for AllowStdIo where T: io::BufRead { - fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { +impl AsyncBufRead for AllowStdIo +where + T: io::BufRead, +{ + fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { let this: *mut Self = &mut *self as *mut _; Poll::Ready(Ok(try_with_interrupt!(unsafe { &mut *this }.0.fill_buf()))) } diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 96d3f2815e..0334a9f081 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,43 +1,40 @@ +use super::DEFAULT_BUF_SIZE; +use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSliceMut, SeekFrom}; +use pin_project_lite::pin_project; use std::io::{self, Read}; use std::pin::Pin; use std::{cmp, fmt}; -use super::DEFAULT_BUF_SIZE; - -/// The `BufReader` struct adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`AsyncRead`] -/// instance. A `BufReader` performs large, infrequent reads on the underlying -/// [`AsyncRead`] and maintains an in-memory buffer of the results. -/// -/// `BufReader` can improve the speed of programs that make *small* and -/// *repeated* read calls to the same file or network socket. It does not -/// help when reading very large amounts at once, or reading just one or a few -/// times. It also provides no advantage when reading from a source that is -/// already in memory, like a `Vec`. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be -/// discarded. Creating multiple instances of a `BufReader` on the same -/// stream can cause data loss. -/// -/// [`AsyncRead`]: futures_io::AsyncRead -/// -// TODO: Examples -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, -} -impl BufReader { - unsafe_pinned!(inner: R); - unsafe_unpinned!(pos: usize); - unsafe_unpinned!(cap: usize); +pin_project! { + /// The `BufReader` struct adds buffering to any reader. + /// + /// It can be excessively inefficient to work directly with a [`AsyncRead`] + /// instance. A `BufReader` performs large, infrequent reads on the underlying + /// [`AsyncRead`] and maintains an in-memory buffer of the results. + /// + /// `BufReader` can improve the speed of programs that make *small* and + /// *repeated* read calls to the same file or network socket. It does not + /// help when reading very large amounts at once, or reading just one or a few + /// times. It also provides no advantage when reading from a source that is + /// already in memory, like a `Vec`. + /// + /// When the `BufReader` is dropped, the contents of its buffer will be + /// discarded. Creating multiple instances of a `BufReader` on the same + /// stream can cause data loss. + /// + /// [`AsyncRead`]: futures_io::AsyncRead + /// + // TODO: Examples + pub struct BufReader { + #[pin] + inner: R, + buffer: Box<[u8]>, + pos: usize, + cap: usize, + } } impl BufReader { @@ -53,55 +50,59 @@ impl BufReader { let mut buffer = Vec::with_capacity(capacity); buffer.set_len(capacity); super::initialize(&inner, &mut buffer); - Self { - inner, - buf: buffer.into_boxed_slice(), - pos: 0, - cap: 0, - } + Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 } } } - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes this `BufWriter`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); /// Returns a reference to the internally buffered data. /// /// Unlike `fill_buf`, this will not attempt to fill the buffer if it is empty. pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] + &self.buffer[self.pos..self.cap] } /// Invalidates all data in the internal buffer. #[inline] - fn discard_buffer(mut self: Pin<&mut Self>) { - *self.as_mut().pos() = 0; - *self.cap() = 0; + fn discard_buffer(self: Pin<&mut Self>) { + let this = self.project(); + *this.pos = 0; + *this.cap = 0; + } +} + +impl BufReader { + /// Seeks relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + pub fn seek_relative(self: Pin<&mut Self>, offset: i64) -> SeeKRelative<'_, R> { + SeeKRelative { inner: self, offset, first: true } + } + + /// Attempts to seek relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + pub fn poll_seek_relative( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + offset: i64, + ) -> Poll> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + *self.project().pos = new_pos as usize; + return Poll::Ready(Ok(())); + } + } else if let Some(new_pos) = pos.checked_add(offset as u64) { + if new_pos <= self.cap as u64 { + *self.project().pos = new_pos as usize; + return Poll::Ready(Ok(())); + } + } + self.poll_seek(cx, SeekFrom::Current(offset)).map(|res| res.map(|_| ())) } } @@ -114,8 +115,8 @@ impl AsyncRead for BufReader { // If we don't have any buffered data and we're doing a massive read // (larger than our internal buffer), bypass our internal buffer // entirely. - if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read(cx, buf)); + if self.pos == self.cap && buf.len() >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -131,8 +132,8 @@ impl AsyncRead for BufReader { bufs: &mut [IoSliceMut<'_>], ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.pos == self.cap && total_len >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + if self.pos == self.cap && total_len >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -141,70 +142,38 @@ impl AsyncRead for BufReader { self.consume(nread); Poll::Ready(Ok(nread)) } - - // we can't skip unconditionally because of the large buffer case in read. - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } } impl AsyncBufRead for BufReader { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let Self { inner, buf, cap, pos } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. - if *pos >= *cap { - debug_assert!(*pos == *cap); - *cap = ready!(inner.as_mut().poll_read(cx, buf))?; - *pos = 0; + if *this.pos >= *this.cap { + debug_assert!(*this.pos == *this.cap); + *this.cap = ready!(this.inner.poll_read(cx, this.buffer))?; + *this.pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&this.buffer[*this.pos..*this.cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - *self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); + fn consume(self: Pin<&mut Self>, amt: usize) { + *self.project().pos = cmp::min(self.pos + amt, self.cap); } } impl AsyncWrite for BufReader { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.inner().poll_write(cx, buf) - } - - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - self.inner().poll_write_vectored(cx, bufs) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } + delegate_async_write!(inner); } impl fmt::Debug for BufReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BufReader") .field("reader", &self.inner) - .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) + .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buffer.len())) .finish() } } @@ -221,6 +190,10 @@ impl AsyncSeek for BufReader { /// `.into_inner()` immediately after a seek yields the underlying reader /// at the same position. /// + /// To seek without discarding the internal buffer, use + /// [`BufReader::seek_relative`](BufReader::seek_relative) or + /// [`BufReader::poll_seek_relative`](BufReader::poll_seek_relative). + /// /// See [`AsyncSeek`](futures_io::AsyncSeek) for more details. /// /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)` @@ -242,18 +215,49 @@ impl AsyncSeek for BufReader { // support seeking by i64::min_value() so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(offset)))?; + result = + ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(offset)))?; } else { // seek backwards by our remainder, and then by the offset - ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(-remainder)))?; + ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(-remainder)))?; self.as_mut().discard_buffer(); - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(n)))?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) } } + +/// Future for the [`BufReader::seek_relative`](self::BufReader::seek_relative) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless polled"] +pub struct SeeKRelative<'a, R> { + inner: Pin<&'a mut BufReader>, + offset: i64, + first: bool, +} + +impl Future for SeeKRelative<'_, R> +where + R: AsyncRead + AsyncSeek, +{ + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let offset = self.offset; + if self.first { + self.first = false; + self.inner.as_mut().poll_seek_relative(cx, offset) + } else { + self.inner + .as_mut() + .as_mut() + .poll_seek(cx, SeekFrom::Current(offset)) + .map(|res| res.map(|_| ())) + } + } +} diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index b0afbd81f2..cb74863ad0 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -1,43 +1,41 @@ +use super::DEFAULT_BUF_SIZE; +use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, SeekFrom}; +use pin_project_lite::pin_project; use std::fmt; use std::io::{self, Write}; use std::pin::Pin; -use super::DEFAULT_BUF_SIZE; - -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`AsyncWrite`]. A `BufWriter` keeps an in-memory buffer of data and -/// writes it to an underlying writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a `Vec`. -/// -/// When the `BufWriter` is dropped, the contents of its buffer will be -/// discarded. Creating multiple instances of a `BufWriter` on the same -/// stream can cause data loss. If you need to write out the contents of its -/// buffer, you must manually call flush before the writer is dropped. -/// -/// [`AsyncWrite`]: futures_io::AsyncWrite -/// [`flush`]: super::AsyncWriteExt::flush -/// -// TODO: Examples -pub struct BufWriter { - inner: W, - buf: Vec, - written: usize, -} +use std::ptr; -impl BufWriter { - unsafe_pinned!(inner: W); - unsafe_unpinned!(buf: Vec); +pin_project! { + /// Wraps a writer and buffers its output. + /// + /// It can be excessively inefficient to work directly with something that + /// implements [`AsyncWrite`]. A `BufWriter` keeps an in-memory buffer of data and + /// writes it to an underlying writer in large, infrequent batches. + /// + /// `BufWriter` can improve the speed of programs that make *small* and + /// *repeated* write calls to the same file or network socket. It does not + /// help when writing very large amounts at once, or writing just one or a few + /// times. It also provides no advantage when writing to a destination that is + /// in memory, like a `Vec`. + /// + /// When the `BufWriter` is dropped, the contents of its buffer will be + /// discarded. Creating multiple instances of a `BufWriter` on the same + /// stream can cause data loss. If you need to write out the contents of its + /// buffer, you must manually call flush before the writer is dropped. + /// + /// [`AsyncWrite`]: futures_io::AsyncWrite + /// [`flush`]: super::AsyncWriteExt::flush + /// + // TODO: Examples + pub struct BufWriter { + #[pin] + inner: W, + buf: Vec, + written: usize, + } } impl BufWriter { @@ -49,21 +47,16 @@ impl BufWriter { /// Creates a new `BufWriter` with the specified buffer capacity. pub fn with_capacity(cap: usize, inner: W) -> Self { - Self { - inner, - buf: Vec::with_capacity(cap), - written: 0, - } + Self { inner, buf: Vec::with_capacity(cap), written: 0 } } - fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, buf, written } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + pub(super) fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - let len = buf.len(); + let len = this.buf.len(); let mut ret = Ok(()); - while *written < len { - match ready!(inner.as_mut().poll_write(cx, &buf[*written..])) { + while *this.written < len { + match ready!(this.inner.as_mut().poll_write(cx, &this.buf[*this.written..])) { Ok(0) => { ret = Err(io::Error::new( io::ErrorKind::WriteZero, @@ -71,49 +64,87 @@ impl BufWriter { )); break; } - Ok(n) => *written += n, + Ok(n) => *this.written += n, Err(e) => { ret = Err(e); break; } } } - if *written > 0 { - buf.drain(..*written); + if *this.written > 0 { + this.buf.drain(..*this.written); } - *written = 0; + *this.written = 0; Poll::Ready(ret) } - /// Gets a reference to the underlying writer. - pub fn get_ref(&self) -> &W { - &self.inner + delegate_access_inner!(inner, W, ()); + + /// Returns a reference to the internally buffered data. + pub fn buffer(&self) -> &[u8] { + &self.buf + } + + /// Capacity of `buf`. how many chars can be held in buffer + pub(super) fn capacity(&self) -> usize { + self.buf.capacity() } - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_mut(&mut self) -> &mut W { - &mut self.inner + /// Remaining number of bytes to reach `buf` 's capacity + #[inline] + pub(super) fn spare_capacity(&self) -> usize { + self.buf.capacity() - self.buf.len() } - /// Gets a pinned mutable reference to the underlying writer. + /// Write a byte slice directly into buffer /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { - self.inner() + /// Will truncate the number of bytes written to `spare_capacity()` so you want to + /// calculate the size of your slice to avoid losing bytes + /// + /// Based on `std::io::BufWriter` + pub(super) fn write_to_buf(self: Pin<&mut Self>, buf: &[u8]) -> usize { + let available = self.spare_capacity(); + let amt_to_buffer = available.min(buf.len()); + + // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. + unsafe { + self.write_to_buffer_unchecked(&buf[..amt_to_buffer]); + } + + amt_to_buffer } - /// Consumes this `BufWriter`, returning the underlying writer. + /// Write byte slice directly into `self.buf` /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> W { - self.inner + /// Based on `std::io::BufWriter` + #[inline] + unsafe fn write_to_buffer_unchecked(self: Pin<&mut Self>, buf: &[u8]) { + debug_assert!(buf.len() <= self.spare_capacity()); + let this = self.project(); + let old_len = this.buf.len(); + let buf_len = buf.len(); + let src = buf.as_ptr(); + let dst = this.buf.as_mut_ptr().add(old_len); + ptr::copy_nonoverlapping(src, dst, buf_len); + this.buf.set_len(old_len + buf_len); + } + + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.project().inner.poll_write(cx, buf) } - /// Returns a reference to the internally buffered data. - pub fn buffer(&self) -> &[u8] { - &self.buf + /// Write directly using `inner`, bypassing buffering + pub(super) fn inner_poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + self.project().inner.poll_write_vectored(cx, bufs) } } @@ -127,9 +158,9 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.project().inner.poll_write(cx, buf) } else { - Poll::Ready(self.buf().write(buf)) + Poll::Ready(self.project().buf.write(buf)) } } @@ -143,58 +174,29 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if total_len >= self.buf.capacity() { - self.inner().poll_write_vectored(cx, bufs) + self.project().inner.poll_write_vectored(cx, bufs) } else { - Poll::Ready(self.buf().write_vectored(bufs)) + Poll::Ready(self.project().buf.write_vectored(bufs)) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_flush(cx) + self.project().inner.poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_close(cx) + self.project().inner.poll_close(cx) } } impl AsyncRead for BufWriter { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - self.inner().poll_read(cx, buf) - } - - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - self.inner().poll_read_vectored(cx, bufs) - } - - // we can't skip unconditionally because of the large buffer case in read. - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } + delegate_async_read!(inner); } impl AsyncBufRead for BufWriter { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.inner().poll_fill_buf(cx) - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - self.inner().consume(amt) - } + delegate_async_buf_read!(inner); } impl fmt::Debug for BufWriter { @@ -217,6 +219,6 @@ impl AsyncSeek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.project().inner.poll_seek(cx, pos) } } diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 64bbdec87a..728a3d2dc0 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -1,25 +1,21 @@ +use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead, IoSliceMut}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; use std::fmt; use std::io; use std::pin::Pin; -/// Reader for the [`chain`](super::AsyncReadExt::chain) method. -#[must_use = "readers do nothing unless polled"] -pub struct Chain { - first: T, - second: U, - done_first: bool, -} - -impl Unpin for Chain -where - T: Unpin, - U: Unpin, -{ +pin_project! { + /// Reader for the [`chain`](super::AsyncReadExt::chain) method. + #[must_use = "readers do nothing unless polled"] + pub struct Chain { + #[pin] + first: T, + #[pin] + second: U, + done_first: bool, + } } impl Chain @@ -27,16 +23,8 @@ where T: AsyncRead, U: AsyncRead, { - unsafe_pinned!(first: T); - unsafe_pinned!(second: U); - unsafe_unpinned!(done_first: bool); - pub(super) fn new(first: T, second: U) -> Self { - Self { - first, - second, - done_first: false, - } + Self { first, second, done_first: false } } /// Gets references to the underlying readers in this `Chain`. @@ -59,10 +47,8 @@ where /// underlying readers as doing so may corrupt the internal state of this /// `Chain`. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut T>, Pin<&mut U>) { - unsafe { - let Self { first, second, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(first), Pin::new_unchecked(second)) - } + let this = self.project(); + (this.first, this.second) } /// Consumes the `Chain`, returning the wrapped readers. @@ -91,43 +77,37 @@ where U: AsyncRead, { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - match ready!(self.as_mut().first().poll_read(cx, buf)?) { - 0 if !buf.is_empty() => *self.as_mut().done_first() = true, + let this = self.project(); + + if !*this.done_first { + match ready!(this.first.poll_read(cx, buf)?) { + 0 if !buf.is_empty() => *this.done_first = true, n => return Poll::Ready(Ok(n)), } } - self.second().poll_read(cx, buf) + this.second.poll_read(cx, buf) } fn poll_read_vectored( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - if !self.done_first { - let n = ready!(self.as_mut().first().poll_read_vectored(cx, bufs)?); + let this = self.project(); + + if !*this.done_first { + let n = ready!(this.first.poll_read_vectored(cx, bufs)?); if n == 0 && bufs.iter().any(|b| !b.is_empty()) { - *self.as_mut().done_first() = true + *this.done_first = true } else { return Poll::Ready(Ok(n)); } } - self.second().poll_read_vectored(cx, bufs) - } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - let initializer = self.first.initializer(); - if initializer.should_initialize() { - initializer - } else { - self.second.initializer() - } + this.second.poll_read_vectored(cx, bufs) } } @@ -137,30 +117,26 @@ where U: AsyncBufRead, { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - let first = unsafe { Pin::new_unchecked(first) }; - let second = unsafe { Pin::new_unchecked(second) }; + let this = self.project(); - if !*done_first { - match ready!(first.poll_fill_buf(cx)?) { + if !*this.done_first { + match ready!(this.first.poll_fill_buf(cx)?) { buf if buf.is_empty() => { - *done_first = true; + *this.done_first = true; } buf => return Poll::Ready(Ok(buf)), } } - second.poll_fill_buf(cx) + this.second.poll_fill_buf(cx) } fn consume(self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - self.first().consume(amt) + let this = self.project(); + + if !*this.done_first { + this.first.consume(amt) } else { - self.second().consume(amt) + this.second.consume(amt) } } } diff --git a/futures-util/src/io/close.rs b/futures-util/src/io/close.rs index 4d5669680b..b94459279a 100644 --- a/futures-util/src/io/close.rs +++ b/futures-util/src/io/close.rs @@ -15,7 +15,7 @@ impl Unpin for Close<'_, W> {} impl<'a, W: AsyncWrite + ?Sized + Unpin> Close<'a, W> { pub(super) fn new(writer: &'a mut W) -> Self { - Close { writer } + Self { writer } } } diff --git a/futures-util/src/io/copy.rs b/futures-util/src/io/copy.rs index 9531aab996..c80add271b 100644 --- a/futures-util/src/io/copy.rs +++ b/futures-util/src/io/copy.rs @@ -1,10 +1,10 @@ +use super::{copy_buf, BufReader, CopyBuf}; use futures_core::future::Future; use futures_core::task::{Context, Poll}; use futures_io::{AsyncRead, AsyncWrite}; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; -use super::{BufReader, copy_buf, CopyBuf}; -use pin_utils::unsafe_pinned; /// Creates a future which copies all the bytes from one object to another. /// @@ -36,28 +36,23 @@ where R: AsyncRead, W: AsyncWrite + Unpin + ?Sized, { - Copy { - inner: copy_buf(BufReader::new(reader), writer), - } -} - -/// Future for the [`copy()`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Copy<'a, R, W: ?Sized> { - inner: CopyBuf<'a, BufReader, W>, + Copy { inner: copy_buf(BufReader::new(reader), writer) } } -impl<'a, R: AsyncRead, W: ?Sized> Unpin for Copy<'a, R, W> where CopyBuf<'a, BufReader, W>: Unpin {} - -impl<'a, R: AsyncRead, W: ?Sized> Copy<'a, R, W> { - unsafe_pinned!(inner: CopyBuf<'a, BufReader, W>); +pin_project! { + /// Future for the [`copy()`] function. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Copy<'a, R, W: ?Sized> { + #[pin] + inner: CopyBuf<'a, BufReader, W>, + } } impl Future for Copy<'_, R, W> { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.inner().poll(cx) + self.project().inner.poll(cx) } } diff --git a/futures-util/src/io/copy_buf.rs b/futures-util/src/io/copy_buf.rs index 98811825e0..50f7abdca9 100644 --- a/futures-util/src/io/copy_buf.rs +++ b/futures-util/src/io/copy_buf.rs @@ -1,6 +1,8 @@ use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncWrite}; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; @@ -34,54 +36,43 @@ where R: AsyncBufRead, W: AsyncWrite + Unpin + ?Sized, { - CopyBuf { - reader, - writer, - amt: 0, - } -} - -/// Future for the [`copy_buf()`] function. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CopyBuf<'a, R, W: ?Sized> { - reader: R, - writer: &'a mut W, - amt: u64, + CopyBuf { reader, writer, amt: 0 } } -impl Unpin for CopyBuf<'_, R, W> {} - -impl CopyBuf<'_, R, W> { - fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.reader), Pin::new(&mut *this.writer), &mut this.amt) - } +pin_project! { + /// Future for the [`copy_buf()`] function. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct CopyBuf<'a, R, W: ?Sized> { + #[pin] + reader: R, + writer: &'a mut W, + amt: u64, } } impl Future for CopyBuf<'_, R, W> - where R: AsyncBufRead, - W: AsyncWrite + Unpin + ?Sized, +where + R: AsyncBufRead, + W: AsyncWrite + Unpin + ?Sized, { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut reader, mut writer, amt) = self.project(); + let mut this = self.project(); loop { - let buffer = ready!(reader.as_mut().poll_fill_buf(cx))?; + let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?; if buffer.is_empty() { - ready!(writer.as_mut().poll_flush(cx))?; - return Poll::Ready(Ok(*amt)); + ready!(Pin::new(&mut this.writer).poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); } - let i = ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?; if i == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } - *amt += i as u64; - reader.as_mut().consume(i); + *this.amt += i as u64; + this.reader.as_mut().consume(i); } } } diff --git a/futures-util/src/io/cursor.rs b/futures-util/src/io/cursor.rs index d1359231c5..b6fb3724c7 100644 --- a/futures-util/src/io/cursor.rs +++ b/futures-util/src/io/cursor.rs @@ -13,7 +13,7 @@ use std::pin::Pin; /// allowing these buffers to be used anywhere you might use a reader or writer /// that does actual I/O. /// -/// The standard library implements some I/O traits on various types which +/// This library implements some I/O traits on various types which /// are commonly used as a buffer, like `Cursor<`[`Vec`]`>` and /// `Cursor<`[`&[u8]`][bytes]`>`. /// @@ -42,10 +42,8 @@ impl Cursor { /// # fn force_inference(_: &Cursor>) {} /// # force_inference(&buff); /// ``` - pub fn new(inner: T) -> Cursor { - Cursor { - inner: io::Cursor::new(inner), - } + pub fn new(inner: T) -> Self { + Self { inner: io::Cursor::new(inner) } } /// Consumes this cursor, returning the underlying value. @@ -199,15 +197,19 @@ where macro_rules! delegate_async_write_to_stdio { () => { - fn poll_write(mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { Poll::Ready(io::Write::write(&mut self.inner, buf)) } - fn poll_write_vectored(mut self: Pin<&mut Self>, _: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { Poll::Ready(io::Write::write_vectored(&mut self.inner, bufs)) } @@ -218,7 +220,7 @@ macro_rules! delegate_async_write_to_stdio { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } - } + }; } impl AsyncWrite for Cursor<&mut [u8]> { diff --git a/futures-util/src/io/empty.rs b/futures-util/src/io/empty.rs index ab2395a8af..02f6103f54 100644 --- a/futures-util/src/io/empty.rs +++ b/futures-util/src/io/empty.rs @@ -1,6 +1,4 @@ use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead}; use std::fmt; use std::io; @@ -43,12 +41,6 @@ impl AsyncRead for Empty { ) -> Poll> { Poll::Ready(Ok(0)) } - - #[cfg(feature = "read-initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl AsyncBufRead for Empty { diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs new file mode 100644 index 0000000000..a1484c0322 --- /dev/null +++ b/futures-util/src/io/fill_buf.rs @@ -0,0 +1,51 @@ +use futures_core::future::Future; +use futures_core::task::{Context, Poll}; +use futures_io::AsyncBufRead; +use std::io; +use std::pin::Pin; + +/// Future for the [`fill_buf`](super::AsyncBufReadExt::fill_buf) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct FillBuf<'a, R: ?Sized> { + reader: Option<&'a mut R>, +} + +impl Unpin for FillBuf<'_, R> {} + +impl<'a, R: AsyncBufRead + ?Sized + Unpin> FillBuf<'a, R> { + pub(super) fn new(reader: &'a mut R) -> Self { + Self { reader: Some(reader) } + } +} + +impl<'a, R> Future for FillBuf<'a, R> +where + R: AsyncBufRead + ?Sized + Unpin, +{ + type Output = io::Result<&'a [u8]>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = &mut *self; + let reader = this.reader.take().expect("Polled FillBuf after completion"); + + match Pin::new(&mut *reader).poll_fill_buf(cx) { + // With polonius it is possible to remove this inner match and just have the correct + // lifetime of the reference inferred based on which branch is taken + Poll::Ready(Ok(_)) => match Pin::new(reader).poll_fill_buf(cx) { + Poll::Ready(Ok(slice)) => Poll::Ready(Ok(slice)), + Poll::Ready(Err(err)) => { + unreachable!("reader indicated readiness but then returned an error: {:?}", err) + } + Poll::Pending => { + unreachable!("reader indicated readiness but then returned pending") + } + }, + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), + Poll::Pending => { + this.reader = Some(reader); + Poll::Pending + } + } + } +} diff --git a/futures-util/src/io/flush.rs b/futures-util/src/io/flush.rs index 70b867a207..b75d14c5d3 100644 --- a/futures-util/src/io/flush.rs +++ b/futures-util/src/io/flush.rs @@ -15,12 +15,13 @@ impl Unpin for Flush<'_, W> {} impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> { pub(super) fn new(writer: &'a mut W) -> Self { - Flush { writer } + Self { writer } } } impl Future for Flush<'_, W> - where W: AsyncWrite + ?Sized + Unpin, +where + W: AsyncWrite + ?Sized + Unpin, { type Output = io::Result<()>; diff --git a/futures-util/src/io/into_sink.rs b/futures-util/src/io/into_sink.rs index bdc6b34140..6a41ee2269 100644 --- a/futures-util/src/io/into_sink.rs +++ b/futures-util/src/io/into_sink.rs @@ -1,10 +1,10 @@ +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncWrite; use futures_sink::Sink; +use pin_project_lite::pin_project; use std::io; use std::pin::Pin; -use std::marker::Unpin; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; #[derive(Debug)] struct Block { @@ -12,31 +12,23 @@ struct Block { bytes: Item, } -/// Sink for the [`into_sink`](super::AsyncWriteExt::into_sink) method. -#[must_use = "sinks do nothing unless polled"] -#[derive(Debug)] -pub struct IntoSink { - writer: W, - /// An outstanding block for us to push into the underlying writer, along with an offset of how - /// far into this block we have written already. - buffer: Option>, +pin_project! { + /// Sink for the [`into_sink`](super::AsyncWriteExt::into_sink) method. + #[must_use = "sinks do nothing unless polled"] + #[derive(Debug)] + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] + pub struct IntoSink { + #[pin] + writer: W, + // An outstanding block for us to push into the underlying writer, along with an offset of how + // far into this block we have written already. + buffer: Option>, + } } -impl Unpin for IntoSink {} - impl> IntoSink { - unsafe_pinned!(writer: W); - unsafe_unpinned!(buffer: Option>); - pub(super) fn new(writer: W) -> Self { - IntoSink { writer, buffer: None } - } - - fn project(self: Pin<&mut Self>) -> (Pin<&mut W>, &mut Option>) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.writer), &mut this.buffer) - } + Self { writer, buffer: None } } /// If we have an outstanding block in `buffer` attempt to push it into the writer, does _not_ @@ -44,65 +36,47 @@ impl> IntoSink { fn poll_flush_buffer( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> - { - let (mut writer, buffer) = self.project(); - if let Some(buffer) = buffer { + ) -> Poll> { + let mut this = self.project(); + + if let Some(buffer) = this.buffer { loop { let bytes = buffer.bytes.as_ref(); - let written = ready!(writer.as_mut().poll_write(cx, &bytes[buffer.offset..]))?; + let written = ready!(this.writer.as_mut().poll_write(cx, &bytes[buffer.offset..]))?; buffer.offset += written; if buffer.offset == bytes.len() { break; } } } - *buffer = None; + *this.buffer = None; Poll::Ready(Ok(())) } - } impl> Sink for IntoSink { type Error = io::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { - ready!(self.as_mut().poll_flush_buffer(cx))?; + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + ready!(self.poll_flush_buffer(cx))?; Poll::Ready(Ok(())) } - #[allow(clippy::debug_assert_with_mut_call)] - fn start_send( - mut self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> - { - debug_assert!(self.as_mut().buffer().is_none()); - *self.as_mut().buffer() = Some(Block { offset: 0, bytes: item }); + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + debug_assert!(self.buffer.is_none()); + *self.project().buffer = Some(Block { offset: 0, bytes: item }); Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_flush(cx))?; + ready!(self.project().writer.poll_flush(cx))?; Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> - { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_close(cx))?; + ready!(self.project().writer.poll_close(cx))?; Poll::Ready(Ok(())) } } diff --git a/futures-util/src/io/line_writer.rs b/futures-util/src/io/line_writer.rs new file mode 100644 index 0000000000..71cd668325 --- /dev/null +++ b/futures-util/src/io/line_writer.rs @@ -0,0 +1,155 @@ +use super::buf_writer::BufWriter; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use futures_io::AsyncWrite; +use futures_io::IoSlice; +use pin_project_lite::pin_project; +use std::io; +use std::pin::Pin; + +pin_project! { +/// Wrap a writer, like [`BufWriter`] does, but prioritizes buffering lines +/// +/// This was written based on `std::io::LineWriter` which goes into further details +/// explaining the code. +/// +/// Buffering is actually done using `BufWriter`. This class will leverage `BufWriter` +/// to write on-each-line. +#[derive(Debug)] +pub struct LineWriter { + #[pin] + buf_writer: BufWriter, +} +} + +impl LineWriter { + /// Create a new `LineWriter` with default buffer capacity. The default is currently 1KB + /// which was taken from `std::io::LineWriter` + pub fn new(inner: W) -> LineWriter { + LineWriter::with_capacity(1024, inner) + } + + /// Creates a new `LineWriter` with the specified buffer capacity. + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { + LineWriter { buf_writer: BufWriter::with_capacity(capacity, inner) } + } + + /// Flush `buf_writer` if last char is "new line" + fn flush_if_completed_line(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.buf_writer.buffer().last().copied() { + Some(b'\n') => this.buf_writer.flush_buf(cx), + _ => Poll::Ready(Ok(())), + } + } + + /// Returns a reference to `buf_writer`'s internally buffered data. + pub fn buffer(&self) -> &[u8] { + self.buf_writer.buffer() + } + + /// Acquires a reference to the underlying sink or stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &W { + self.buf_writer.get_ref() + } +} + +impl AsyncWrite for LineWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.as_mut().project(); + let newline_index = match memchr::memrchr(b'\n', buf) { + None => { + ready!(self.as_mut().flush_if_completed_line(cx)?); + return self.project().buf_writer.poll_write(cx, buf); + } + Some(newline_index) => newline_index + 1, + }; + + ready!(this.buf_writer.as_mut().poll_flush(cx)?); + + let lines = &buf[..newline_index]; + + let flushed = { ready!(this.buf_writer.as_mut().inner_poll_write(cx, lines))? }; + + if flushed == 0 { + return Poll::Ready(Ok(0)); + } + + let tail = if flushed >= newline_index { + &buf[flushed..] + } else if newline_index - flushed <= this.buf_writer.capacity() { + &buf[flushed..newline_index] + } else { + let scan_area = &buf[flushed..]; + let scan_area = &scan_area[..this.buf_writer.capacity()]; + match memchr::memrchr(b'\n', scan_area) { + Some(newline_index) => &scan_area[..newline_index + 1], + None => scan_area, + } + }; + + let buffered = this.buf_writer.as_mut().write_to_buf(tail); + Poll::Ready(Ok(flushed + buffered)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + let mut this = self.as_mut().project(); + // `is_write_vectored()` is handled in original code, but not in this crate + // see https://github.com/rust-lang/rust/issues/70436 + + let last_newline_buf_idx = bufs + .iter() + .enumerate() + .rev() + .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); + let last_newline_buf_idx = match last_newline_buf_idx { + None => { + ready!(self.as_mut().flush_if_completed_line(cx)?); + return self.project().buf_writer.poll_write_vectored(cx, bufs); + } + Some(i) => i, + }; + + ready!(this.buf_writer.as_mut().poll_flush(cx)?); + + let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); + + let flushed = { ready!(this.buf_writer.as_mut().inner_poll_write_vectored(cx, lines))? }; + if flushed == 0 { + return Poll::Ready(Ok(0)); + } + + let lines_len = lines.iter().map(|buf| buf.len()).sum(); + if flushed < lines_len { + return Poll::Ready(Ok(flushed)); + } + + let buffered: usize = tail + .iter() + .filter(|buf| !buf.is_empty()) + .map(|buf| this.buf_writer.as_mut().write_to_buf(buf)) + .take_while(|&n| n > 0) + .sum(); + + Poll::Ready(Ok(flushed + buffered)) + } + + /// Forward to `buf_writer` 's `BufWriter::poll_flush()` + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().buf_writer.poll_flush(cx) + } + + /// Forward to `buf_writer` 's `BufWriter::poll_close()` + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().buf_writer.poll_close(cx) + } +} diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index 2e1261689b..13e70df238 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -1,31 +1,29 @@ +use super::read_line::read_line_internal; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; +use pin_project_lite::pin_project; use std::io; use std::mem; use std::pin::Pin; -use super::read_line::read_line_internal; -/// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Lines { - reader: R, - buf: String, - bytes: Vec, - read: usize, +pin_project! { + /// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Lines { + #[pin] + reader: R, + buf: String, + bytes: Vec, + read: usize, + } } -impl Unpin for Lines {} - impl Lines { pub(super) fn new(reader: R) -> Self { - Self { - reader, - buf: String::new(), - bytes: Vec::new(), - read: 0, - } + Self { reader, buf: String::new(), bytes: Vec::new(), read: 0 } } } @@ -33,18 +31,17 @@ impl Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { reader, buf, bytes, read } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = ready!(read_line_internal(reader, cx, buf, bytes, read))?; - if n == 0 && buf.is_empty() { - return Poll::Ready(None) + let this = self.project(); + let n = ready!(read_line_internal(this.reader, cx, this.buf, this.bytes, this.read))?; + if n == 0 && this.buf.is_empty() { + return Poll::Ready(None); } - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); + if this.buf.ends_with('\n') { + this.buf.pop(); + if this.buf.ends_with('\r') { + this.buf.pop(); } } - Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) + Poll::Ready(Some(Ok(mem::replace(this.buf, String::new())))) } } diff --git a/futures-util/src/io/mod.rs b/futures-util/src/io/mod.rs index 43f183f424..4dd2e029bf 100644 --- a/futures-util/src/io/mod.rs +++ b/futures-util/src/io/mod.rs @@ -1,24 +1,34 @@ -//! IO +//! Asynchronous I/O. //! -//! This module contains a number of functions for working with -//! `AsyncRead`, `AsyncWrite`, `AsyncSeek`, and `AsyncBufRead` types, including -//! the `AsyncReadExt`, `AsyncWriteExt`, `AsyncSeekExt`, and `AsyncBufReadExt` -//! traits which add methods to the `AsyncRead`, `AsyncWrite`, `AsyncSeek`, -//! and `AsyncBufRead` types. +//! This module is the asynchronous version of `std::io`. It defines four +//! traits, [`AsyncRead`], [`AsyncWrite`], [`AsyncSeek`], and [`AsyncBufRead`], +//! which mirror the `Read`, `Write`, `Seek`, and `BufRead` traits of the +//! standard library. However, these traits integrate with the asynchronous +//! task system, so that if an I/O object isn't ready for reading (or writing), +//! the thread is not blocked, and instead the current task is queued to be +//! woken when I/O is ready. //! -//! This module is only available when the `io` and `std` features of this +//! In addition, the [`AsyncReadExt`], [`AsyncWriteExt`], [`AsyncSeekExt`], and +//! [`AsyncBufReadExt`] extension traits offer a variety of useful combinators +//! for operating with asynchronous I/O objects, including ways to work with +//! them using futures, streams and sinks. +//! +//! This module is only available when the `std` feature of this //! library is activated, and it is activated by default. #[cfg(feature = "io-compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] use crate::compat::Compat; -use std::ptr; +use crate::future::assert_future; +use crate::stream::assert_stream; +use std::{pin::Pin, ptr}; + +// Re-export some types from `std::io` so that users don't have to deal +// with conflicts when `use`ing `futures::io` and `std::io`. +#[doc(no_inline)] +pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -pub use futures_io::{ - AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, Error, ErrorKind, - IoSlice, IoSliceMut, Result, SeekFrom, -}; -#[cfg(feature = "read-initializer")] -pub use futures_io::Initializer; +pub use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; // used by `BufReader` and `BufWriter` // https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/io.rs#L1 @@ -26,15 +36,9 @@ const DEFAULT_BUF_SIZE: usize = 8 * 1024; /// Initializes a buffer if necessary. /// -/// A buffer is always initialized if `read-initializer` feature is disabled. +/// A buffer is currently always initialized. #[inline] unsafe fn initialize(_reader: &R, buf: &mut [u8]) { - #[cfg(feature = "read-initializer")] - { - if !_reader.initializer().should_initialize() { - return; - } - } ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } @@ -42,11 +46,14 @@ mod allow_std; pub use self::allow_std::AllowStdIo; mod buf_reader; -pub use self::buf_reader::BufReader; +pub use self::buf_reader::{BufReader, SeeKRelative}; mod buf_writer; pub use self::buf_writer::BufWriter; +mod line_writer; +pub use self::line_writer::LineWriter; + mod chain; pub use self::chain::Chain; @@ -65,12 +72,17 @@ pub use self::cursor::Cursor; mod empty; pub use self::empty::{empty, Empty}; +mod fill_buf; +pub use self::fill_buf::FillBuf; + mod flush; pub use self::flush::Flush; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] mod into_sink; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::into_sink::IntoSink; mod lines; @@ -107,7 +119,7 @@ mod sink; pub use self::sink::{sink, Sink}; mod split; -pub use self::split::{ReadHalf, WriteHalf}; +pub use self::split::{ReadHalf, ReuniteError, WriteHalf}; mod take; pub use self::take::Take; @@ -124,6 +136,11 @@ pub use self::write_vectored::WriteVectored; mod write_all; pub use self::write_all::WriteAll; +#[cfg(feature = "write-all-vectored")] +mod write_all_vectored; +#[cfg(feature = "write-all-vectored")] +pub use self::write_all_vectored::WriteAllVectored; + /// An extension trait which adds utility methods to `AsyncRead` types. pub trait AsyncReadExt: AsyncRead { /// Creates an adaptor which will chain this stream with another. @@ -154,7 +171,7 @@ pub trait AsyncReadExt: AsyncRead { Self: Sized, R: AsyncRead, { - Chain::new(self, next) + assert_read(Chain::new(self, next)) } /// Tries to read some bytes directly into the given `buf` in asynchronous @@ -182,9 +199,10 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> - where Self: Unpin, + where + Self: Unpin, { - Read::new(self, buf) + assert_future::, _>(Read::new(self, buf)) } /// Creates a future which will read from the `AsyncRead` into `bufs` using vectored @@ -193,9 +211,10 @@ pub trait AsyncReadExt: AsyncRead { /// The returned future will resolve to the number of bytes read once the read /// operation is completed. fn read_vectored<'a>(&'a mut self, bufs: &'a mut [IoSliceMut<'a>]) -> ReadVectored<'a, Self> - where Self: Unpin, + where + Self: Unpin, { - ReadVectored::new(self, bufs) + assert_future::, _>(ReadVectored::new(self, bufs)) } /// Creates a future which will read exactly enough bytes to fill `buf`, @@ -235,13 +254,11 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); /// # }); /// ``` - fn read_exact<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> ReadExact<'a, Self> - where Self: Unpin, + fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> + where + Self: Unpin, { - ReadExact::new(self, buf) + assert_future::, _>(ReadExact::new(self, buf)) } /// Creates a future which will read all the bytes from this `AsyncRead`. @@ -263,13 +280,11 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_to_end<'a>( - &'a mut self, - buf: &'a mut Vec, - ) -> ReadToEnd<'a, Self> - where Self: Unpin, + fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec) -> ReadToEnd<'a, Self> + where + Self: Unpin, { - ReadToEnd::new(self, buf) + assert_future::, _>(ReadToEnd::new(self, buf)) } /// Creates a future which will read all the bytes from this `AsyncRead`. @@ -291,13 +306,11 @@ pub trait AsyncReadExt: AsyncRead { /// assert_eq!(buffer, String::from("1234")); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_to_string<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ReadToString<'a, Self> - where Self: Unpin, + fn read_to_string<'a>(&'a mut self, buf: &'a mut String) -> ReadToString<'a, Self> + where + Self: Unpin, { - ReadToString::new(self, buf) + assert_future::, _>(ReadToString::new(self, buf)) } /// Helper method for splitting this read/write object into two halves. @@ -330,9 +343,11 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn split(self) -> (ReadHalf, WriteHalf) - where Self: AsyncWrite + Sized, + where + Self: AsyncWrite + Sized, { - split::split(self) + let (r, w) = split::split(self); + (assert_read(r), assert_write(w)) } /// Creates an AsyncRead adapter which will read at most `limit` bytes @@ -355,9 +370,10 @@ pub trait AsyncReadExt: AsyncRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn take(self, limit: u64) -> Take - where Self: Sized + where + Self: Sized, { - Take::new(self, limit) + assert_read(Take::new(self, limit)) } /// Wraps an [`AsyncRead`] in a compatibility wrapper that allows it to be @@ -367,8 +383,10 @@ pub trait AsyncReadExt: AsyncRead { /// /// Requires the `io-compat` feature to enable. #[cfg(feature = "io-compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] fn compat(self) -> Compat - where Self: Sized + Unpin, + where + Self: Sized + Unpin, { Compat::new(self) } @@ -401,16 +419,18 @@ pub trait AsyncWriteExt: AsyncWrite { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn flush(&mut self) -> Flush<'_, Self> - where Self: Unpin, + where + Self: Unpin, { - Flush::new(self) + assert_future::, _>(Flush::new(self)) } /// Creates a future which will entirely close this `AsyncWrite`. fn close(&mut self) -> Close<'_, Self> - where Self: Unpin, + where + Self: Unpin, { - Close::new(self) + assert_future::, _>(Close::new(self)) } /// Creates a future which will write bytes from `buf` into the object. @@ -418,9 +438,10 @@ pub trait AsyncWriteExt: AsyncWrite { /// The returned future will resolve to the number of bytes written once the write /// operation is completed. fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> - where Self: Unpin, + where + Self: Unpin, { - Write::new(self, buf) + assert_future::, _>(Write::new(self, buf)) } /// Creates a future which will write bytes from `bufs` into the object using vectored @@ -429,9 +450,10 @@ pub trait AsyncWriteExt: AsyncWrite { /// The returned future will resolve to the number of bytes written once the write /// operation is completed. fn write_vectored<'a>(&'a mut self, bufs: &'a [IoSlice<'a>]) -> WriteVectored<'a, Self> - where Self: Unpin, + where + Self: Unpin, { - WriteVectored::new(self, bufs) + assert_future::, _>(WriteVectored::new(self, bufs)) } /// Write data into this object. @@ -455,22 +477,79 @@ pub trait AsyncWriteExt: AsyncWrite { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> - where Self: Unpin, + where + Self: Unpin, + { + assert_future::, _>(WriteAll::new(self, buf)) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// Creates a future that will write the entire contents of `bufs` into this + /// `AsyncWrite` using [vectored writes]. + /// + /// The returned future will not complete until all the data has been + /// written. + /// + /// [vectored writes]: std::io::Write::write_vectored + /// + /// # Notes + /// + /// Unlike `io::Write::write_vectored`, this takes a *mutable* reference to + /// a slice of `IoSlice`s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this futures returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to `write_vectored` were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and + /// can be reused. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::io::AsyncWriteExt; + /// use futures_util::io::Cursor; + /// use std::io::IoSlice; + /// + /// let mut writer = Cursor::new(Vec::new()); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs).await?; + /// // Note: the contents of `bufs` is now unspecified, see the Notes section. + /// + /// assert_eq!(writer.into_inner(), &[1, 2, 3, 4, 5, 6]); + /// # Ok::<(), Box>(()) }).unwrap(); + /// ``` + #[cfg(feature = "write-all-vectored")] + fn write_all_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSlice<'a>], + ) -> WriteAllVectored<'a, Self> + where + Self: Unpin, { - WriteAll::new(self, buf) + assert_future::, _>(WriteAllVectored::new(self, bufs)) } /// Wraps an [`AsyncWrite`] in a compatibility wrapper that allows it to be /// used as a futures 0.1 / tokio-io 0.1 `AsyncWrite`. /// Requires the `io-compat` feature to enable. #[cfg(feature = "io-compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] fn compat_write(self) -> Compat - where Self: Sized + Unpin, + where + Self: Sized + Unpin, { Compat::new(self) } - /// Allow using an [`AsyncWrite`] as a [`Sink`](futures_sink::Sink)`>`. /// /// This adapter produces a sink that will write each value passed to it @@ -498,10 +577,12 @@ pub trait AsyncWriteExt: AsyncWrite { /// # Ok::<(), Box>(()) /// ``` #[cfg(feature = "sink")] + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] fn into_sink>(self) -> IntoSink - where Self: Sized, + where + Self: Sized, { - IntoSink::new(self) + crate::sink::assert_sink::(IntoSink::new(self)) } } @@ -515,9 +596,21 @@ pub trait AsyncSeekExt: AsyncSeek { /// In the case of an error the buffer and the object will be discarded, with /// the error yielded. fn seek(&mut self, pos: SeekFrom) -> Seek<'_, Self> - where Self: Unpin, + where + Self: Unpin, { - Seek::new(self, pos) + assert_future::, _>(Seek::new(self, pos)) + } + + /// Creates a future which will return the current seek position from the + /// start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + fn stream_position(&mut self) -> Seek<'_, Self> + where + Self: Unpin, + { + self.seek(SeekFrom::Current(0)) } } @@ -525,6 +618,60 @@ impl AsyncSeekExt for S {} /// An extension trait which adds utility methods to `AsyncBufRead` types. pub trait AsyncBufReadExt: AsyncBufRead { + /// Creates a future which will wait for a non-empty buffer to be available from this I/O + /// object or EOF to be reached. + /// + /// This method is the async equivalent to [`BufRead::fill_buf`](std::io::BufRead::fill_buf). + /// + /// ```rust + /// # futures::executor::block_on(async { + /// use futures::{io::AsyncBufReadExt as _, stream::{iter, TryStreamExt as _}}; + /// + /// let mut stream = iter(vec![Ok(vec![1, 2, 3]), Ok(vec![4, 5, 6])]).into_async_read(); + /// + /// assert_eq!(stream.fill_buf().await?, vec![1, 2, 3]); + /// stream.consume_unpin(2); + /// + /// assert_eq!(stream.fill_buf().await?, vec![3]); + /// stream.consume_unpin(1); + /// + /// assert_eq!(stream.fill_buf().await?, vec![4, 5, 6]); + /// stream.consume_unpin(3); + /// + /// assert_eq!(stream.fill_buf().await?, vec![]); + /// # Ok::<(), Box>(()) }).unwrap(); + /// ``` + fn fill_buf(&mut self) -> FillBuf<'_, Self> + where + Self: Unpin, + { + assert_future::, _>(FillBuf::new(self)) + } + + /// A convenience for calling [`AsyncBufRead::consume`] on [`Unpin`] IO types. + /// + /// ```rust + /// # futures::executor::block_on(async { + /// use futures::{io::AsyncBufReadExt as _, stream::{iter, TryStreamExt as _}}; + /// + /// let mut stream = iter(vec![Ok(vec![1, 2, 3])]).into_async_read(); + /// + /// assert_eq!(stream.fill_buf().await?, vec![1, 2, 3]); + /// stream.consume_unpin(2); + /// + /// assert_eq!(stream.fill_buf().await?, vec![3]); + /// stream.consume_unpin(1); + /// + /// assert_eq!(stream.fill_buf().await?, vec![]); + /// # Ok::<(), Box>(()) }).unwrap(); + /// ``` + fn consume_unpin(&mut self, amt: usize) + where + Self: Unpin, + { + Pin::new(self).consume(amt) + } + /// Creates a future which will read all the bytes associated with this I/O /// object into `buf` until the delimiter `byte` or EOF is reached. /// This method is the async equivalent to [`BufRead::read_until`](std::io::BufRead::read_until). @@ -566,14 +713,11 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// assert_eq!(buf, b""); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - fn read_until<'a>( - &'a mut self, - byte: u8, - buf: &'a mut Vec, - ) -> ReadUntil<'a, Self> - where Self: Unpin, + fn read_until<'a>(&'a mut self, byte: u8, buf: &'a mut Vec) -> ReadUntil<'a, Self> + where + Self: Unpin, { - ReadUntil::new(self, byte, buf) + assert_future::, _>(ReadUntil::new(self, byte, buf)) } /// Creates a future which will read all the bytes associated with this I/O @@ -628,9 +772,10 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn read_line<'a>(&'a mut self, buf: &'a mut String) -> ReadLine<'a, Self> - where Self: Unpin, + where + Self: Unpin, { - ReadLine::new(self, buf) + assert_future::, _>(ReadLine::new(self, buf)) } /// Returns a stream over the lines of this reader. @@ -666,10 +811,28 @@ pub trait AsyncBufReadExt: AsyncBufRead { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` fn lines(self) -> Lines - where Self: Sized, + where + Self: Sized, { - Lines::new(self) + assert_stream::, _>(Lines::new(self)) } } impl AsyncBufReadExt for R {} + +// Just a helper function to ensure the reader we're returning all have the +// right implementations. +pub(crate) fn assert_read(reader: R) -> R +where + R: AsyncRead, +{ + reader +} +// Just a helper function to ensure the writer we're returning all have the +// right implementations. +pub(crate) fn assert_write(writer: W) -> W +where + W: AsyncWrite, +{ + writer +} diff --git a/futures-util/src/io/read.rs b/futures-util/src/io/read.rs index ea25959056..677ba818d9 100644 --- a/futures-util/src/io/read.rs +++ b/futures-util/src/io/read.rs @@ -16,7 +16,7 @@ impl Unpin for Read<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> Read<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { - Read { reader, buf } + Self { reader, buf } } } diff --git a/futures-util/src/io/read_exact.rs b/futures-util/src/io/read_exact.rs index a2bbd400ea..02e38c35be 100644 --- a/futures-util/src/io/read_exact.rs +++ b/futures-util/src/io/read_exact.rs @@ -1,5 +1,6 @@ use crate::io::AsyncRead; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use std::io; use std::mem; @@ -17,7 +18,7 @@ impl Unpin for ReadExact<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadExact<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { - ReadExact { reader, buf } + Self { reader, buf } } } @@ -33,7 +34,7 @@ impl Future for ReadExact<'_, R> { this.buf = rest; } if n == 0 { - return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())) + return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())); } } Poll::Ready(Ok(())) diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index d830514b92..c75af9471f 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -1,11 +1,12 @@ +use super::read_until::read_until_internal; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; use std::io; use std::mem; use std::pin::Pin; use std::str; -use super::read_until::read_until_internal; /// Future for the [`read_line`](super::AsyncBufReadExt::read_line) method. #[derive(Debug)] @@ -21,12 +22,7 @@ impl Unpin for ReadLine<'_, R> {} impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadLine<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { - Self { - reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - read: 0, - } + Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, read: 0 } } } @@ -38,7 +34,7 @@ pub(super) fn read_line_internal( read: &mut usize, ) -> Poll> { let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read)); - if str::from_utf8(&bytes).is_err() { + if str::from_utf8(bytes).is_err() { Poll::Ready(ret.and_then(|_| { Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")) })) diff --git a/futures-util/src/io/read_to_end.rs b/futures-util/src/io/read_to_end.rs index 70b057829c..919d7d13c7 100644 --- a/futures-util/src/io/read_to_end.rs +++ b/futures-util/src/io/read_to_end.rs @@ -1,4 +1,5 @@ use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncRead; use std::io; @@ -19,19 +20,20 @@ impl Unpin for ReadToEnd<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToEnd<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut Vec) -> Self { let start_len = buf.len(); - Self { - reader, - buf, - start_len, - } + Self { reader, buf, start_len } } } -struct Guard<'a> { buf: &'a mut Vec, len: usize } +struct Guard<'a> { + buf: &'a mut Vec, + len: usize, +} impl Drop for Guard<'_> { fn drop(&mut self) { - unsafe { self.buf.set_len(self.len); } + unsafe { + self.buf.set_len(self.len); + } } } @@ -51,7 +53,6 @@ pub(super) fn read_to_end_internal( start_len: usize, ) -> Poll> { let mut g = Guard { len: buf.len(), buf }; - let ret; loop { if g.len == g.buf.len() { unsafe { @@ -62,24 +63,24 @@ pub(super) fn read_to_end_internal( } } - match ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) { - Ok(0) => { - ret = Poll::Ready(Ok(g.len - start_len)); - break; - } - Ok(n) => g.len += n, - Err(e) => { - ret = Poll::Ready(Err(e)); - break; + let buf = &mut g.buf[g.len..]; + match ready!(rd.as_mut().poll_read(cx, buf)) { + Ok(0) => return Poll::Ready(Ok(g.len - start_len)), + Ok(n) => { + // We can't allow bogus values from read. If it is too large, the returned vec could have its length + // set past its capacity, or if it overflows the vec could be shortened which could create an invalid + // string if this is called via read_to_string. + assert!(n <= buf.len()); + g.len += n; } + Err(e) => return Poll::Ready(Err(e)), } } - - ret } impl Future for ReadToEnd<'_, A> - where A: AsyncRead + ?Sized + Unpin, +where + A: AsyncRead + ?Sized + Unpin, { type Output = io::Result; diff --git a/futures-util/src/io/read_to_string.rs b/futures-util/src/io/read_to_string.rs index 56c95ce18d..457af59e4f 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -1,5 +1,6 @@ use super::read_to_end::read_to_end_internal; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncRead; use std::pin::Pin; @@ -21,12 +22,7 @@ impl Unpin for ReadToString<'_, R> {} impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToString<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { let start_len = buf.len(); - Self { - reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - start_len, - } + Self { reader, bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len } } } @@ -38,12 +34,9 @@ fn read_to_string_internal( start_len: usize, ) -> Poll> { let ret = ready!(read_to_end_internal(reader, cx, bytes, start_len)); - if str::from_utf8(&bytes).is_err() { + if str::from_utf8(bytes).is_err() { Poll::Ready(ret.and_then(|_| { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - )) + Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")) })) } else { debug_assert!(buf.is_empty()); diff --git a/futures-util/src/io/read_until.rs b/futures-util/src/io/read_until.rs index 95c47e06b8..72b59eab13 100644 --- a/futures-util/src/io/read_until.rs +++ b/futures-util/src/io/read_until.rs @@ -1,4 +1,5 @@ use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncBufRead; use std::io; diff --git a/futures-util/src/io/repeat.rs b/futures-util/src/io/repeat.rs index 84abd7f769..2828bf0114 100644 --- a/futures-util/src/io/repeat.rs +++ b/futures-util/src/io/repeat.rs @@ -1,6 +1,5 @@ +use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; use futures_io::{AsyncRead, IoSliceMut}; use std::fmt; use std::io; @@ -58,12 +57,6 @@ impl AsyncRead for Repeat { } Poll::Ready(Ok(nwritten)) } - - #[cfg(feature = "read-initializer")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl fmt::Debug for Repeat { diff --git a/futures-util/src/io/split.rs b/futures-util/src/io/split.rs index d12df867fc..3f1b9af456 100644 --- a/futures-util/src/io/split.rs +++ b/futures-util/src/io/split.rs @@ -1,4 +1,6 @@ use crate::lock::BiLock; +use core::fmt; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::{AsyncRead, AsyncWrite, IoSlice, IoSliceMut}; use std::io; @@ -16,12 +18,9 @@ pub struct WriteHalf { handle: BiLock, } -fn lock_and_then( - lock: &BiLock, - cx: &mut Context<'_>, - f: F -) -> Poll> - where F: FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll> +fn lock_and_then(lock: &BiLock, cx: &mut Context<'_>, f: F) -> Poll> +where + F: FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll>, { let mut l = ready!(lock.poll_lock(cx)); f(l.as_pin_mut(), cx) @@ -32,30 +31,58 @@ pub(super) fn split(t: T) -> (ReadHalf, WriteHalf< (ReadHalf { handle: a }, WriteHalf { handle: b }) } +impl ReadHalf { + /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back + /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are + /// a matching pair originating from the same call to `AsyncReadExt::split`. + pub fn reunite(self, other: WriteHalf) -> Result> { + self.handle + .reunite(other.handle) + .map_err(|err| ReuniteError(ReadHalf { handle: err.0 }, WriteHalf { handle: err.1 })) + } +} + +impl WriteHalf { + /// Attempts to put the two "halves" of a split `AsyncRead + AsyncWrite` back + /// together. Succeeds only if the `ReadHalf` and `WriteHalf` are + /// a matching pair originating from the same call to `AsyncReadExt::split`. + pub fn reunite(self, other: ReadHalf) -> Result> { + other.reunite(self) + } +} + impl AsyncRead for ReadHalf { - fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_read(cx, buf)) } - fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) - -> Poll> - { + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_read_vectored(cx, bufs)) } } impl AsyncWrite for WriteHalf { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) - -> Poll> - { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_write(cx, buf)) } - fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) - -> Poll> - { + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { lock_and_then(&self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs)) } @@ -67,3 +94,22 @@ impl AsyncWrite for WriteHalf { lock_and_then(&self.handle, cx, |l, cx| l.poll_close(cx)) } } + +/// Error indicating a `ReadHalf` and `WriteHalf` were not two halves +/// of a `AsyncRead + AsyncWrite`, and thus could not be `reunite`d. +pub struct ReuniteError(pub ReadHalf, pub WriteHalf); + +impl fmt::Debug for ReuniteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ReuniteError").field(&"...").finish() + } +} + +impl fmt::Display for ReuniteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "tried to reunite a ReadHalf and WriteHalf that don't form a pair") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ReuniteError {} diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index b1f33fa468..2c494804d9 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -1,28 +1,24 @@ +use futures_core::ready; use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncRead, AsyncBufRead}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use std::{cmp, io}; +use futures_io::{AsyncBufRead, AsyncRead}; +use pin_project_lite::pin_project; use std::pin::Pin; +use std::{cmp, io}; -/// Reader for the [`take`](super::AsyncReadExt::take) method. -#[derive(Debug)] -#[must_use = "readers do nothing unless you `.await` or poll them"] -pub struct Take { - inner: R, - // Add '_' to avoid conflicts with `limit` method. - limit_: u64, +pin_project! { + /// Reader for the [`take`](super::AsyncReadExt::take) method. + #[derive(Debug)] + #[must_use = "readers do nothing unless you `.await` or poll them"] + pub struct Take { + #[pin] + inner: R, + limit: u64, + } } -impl Unpin for Take { } - impl Take { - unsafe_pinned!(inner: R); - unsafe_unpinned!(limit_: u64); - pub(super) fn new(inner: R, limit: u64) -> Self { - Self { inner, limit_: limit } + Self { inner, limit } } /// Returns the remaining number of bytes that can be @@ -49,7 +45,7 @@ impl Take { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` pub fn limit(&self) -> u64 { - self.limit_ + self.limit } /// Sets the number of bytes that can be read before this instance will @@ -79,132 +75,51 @@ impl Take { /// # Ok::<(), Box>(()) }).unwrap(); /// ``` pub fn set_limit(&mut self, limit: u64) { - self.limit_ = limit - } - - /// Gets a reference to the underlying reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_ref = take.get_ref(); - /// assert_eq!(cursor_ref.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_mut = take.get_mut(); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner + self.limit = limit } - /// Gets a pinned mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes the `Take`, returning the wrapped reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor = take.into_inner(); - /// assert_eq!(cursor.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); } impl AsyncRead for Take { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if self.limit_ == 0 { + let this = self.project(); + + if *this.limit == 0 { return Poll::Ready(Ok(0)); } - let max = std::cmp::min(buf.len() as u64, self.limit_) as usize; - let n = ready!(self.as_mut().inner().poll_read(cx, &mut buf[..max]))?; - *self.as_mut().limit_() -= n as u64; + let max = cmp::min(buf.len() as u64, *this.limit) as usize; + let n = ready!(this.inner.poll_read(cx, &mut buf[..max]))?; + *this.limit -= n as u64; Poll::Ready(Ok(n)) } - - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } } impl AsyncBufRead for Take { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, limit_ } = unsafe { self.get_unchecked_mut() }; - let inner = unsafe { Pin::new_unchecked(inner) }; + let this = self.project(); // Don't call into inner reader at all at EOF because it may still block - if *limit_ == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(&[])); } - let buf = ready!(inner.poll_fill_buf(cx)?); - let cap = cmp::min(buf.len() as u64, *limit_) as usize; + let buf = ready!(this.inner.poll_fill_buf(cx)?); + let cap = cmp::min(buf.len() as u64, *this.limit) as usize; Poll::Ready(Ok(&buf[..cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit_) as usize; - *self.as_mut().limit_() -= amt as u64; - self.inner().consume(amt); + let amt = cmp::min(amt as u64, *this.limit) as usize; + *this.limit -= amt as u64; + this.inner.consume(amt); } } diff --git a/futures-util/src/io/window.rs b/futures-util/src/io/window.rs index 3424197d75..77b7267c69 100644 --- a/futures-util/src/io/window.rs +++ b/futures-util/src/io/window.rs @@ -30,10 +30,7 @@ impl> Window { /// Further methods can be called on the returned `Window` to alter the /// window into the data provided. pub fn new(t: T) -> Self { - Self { - range: 0..t.as_ref().len(), - inner: t, - } + Self { range: 0..t.as_ref().len(), inner: t } } /// Gets a shared reference to the underlying buffer inside of this diff --git a/futures-util/src/io/write_all.rs b/futures-util/src/io/write_all.rs index 57f1400b0e..b134bf1b22 100644 --- a/futures-util/src/io/write_all.rs +++ b/futures-util/src/io/write_all.rs @@ -1,4 +1,5 @@ use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_io::AsyncWrite; use std::io; @@ -17,7 +18,7 @@ impl Unpin for WriteAll<'_, W> {} impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> { pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self { - WriteAll { writer, buf } + Self { writer, buf } } } @@ -33,7 +34,7 @@ impl Future for WriteAll<'_, W> { this.buf = rest; } if n == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } diff --git a/futures-util/src/io/write_all_vectored.rs b/futures-util/src/io/write_all_vectored.rs new file mode 100644 index 0000000000..a8fc4c641c --- /dev/null +++ b/futures-util/src/io/write_all_vectored.rs @@ -0,0 +1,193 @@ +use futures_core::future::Future; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use futures_io::AsyncWrite; +use futures_io::IoSlice; +use std::io; +use std::pin::Pin; + +/// Future for the +/// [`write_all_vectored`](super::AsyncWriteExt::write_all_vectored) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct WriteAllVectored<'a, W: ?Sized + Unpin> { + writer: &'a mut W, + bufs: &'a mut [IoSlice<'a>], +} + +impl Unpin for WriteAllVectored<'_, W> {} + +impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAllVectored<'a, W> { + pub(super) fn new(writer: &'a mut W, mut bufs: &'a mut [IoSlice<'a>]) -> Self { + IoSlice::advance_slices(&mut bufs, 0); + Self { writer, bufs } + } +} + +impl Future for WriteAllVectored<'_, W> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + while !this.bufs.is_empty() { + let n = ready!(Pin::new(&mut this.writer).poll_write_vectored(cx, this.bufs))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } else { + IoSlice::advance_slices(&mut this.bufs, n); + } + } + + Poll::Ready(Ok(())) + } +} + +#[cfg(test)] +mod tests { + use std::cmp::min; + use std::future::Future; + use std::io; + use std::pin::Pin; + use std::task::{Context, Poll}; + + use crate::io::{AsyncWrite, AsyncWriteExt, IoSlice}; + use crate::task::noop_waker; + + /// Create a new writer that reads from at most `n_bufs` and reads + /// `per_call` bytes (in total) per call to write. + fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { + TestWriter { n_bufs, per_call, written: Vec::new() } + } + + // TODO: maybe move this the future-test crate? + struct TestWriter { + n_bufs: usize, + per_call: usize, + written: Vec, + } + + impl AsyncWrite for TestWriter { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.poll_write_vectored(cx, &[IoSlice::new(buf)]) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + let mut left = self.per_call; + let mut written = 0; + for buf in bufs.iter().take(self.n_bufs) { + let n = min(left, buf.len()); + self.written.extend_from_slice(&buf[0..n]); + left -= n; + written += n; + } + Poll::Ready(Ok(written)) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + } + + // TODO: maybe move this the future-test crate? + macro_rules! assert_poll_ok { + ($e:expr, $expected:expr) => { + let expected = $expected; + match $e { + Poll::Ready(Ok(ok)) if ok == expected => {} + got => { + panic!("unexpected result, got: {:?}, wanted: Ready(Ok({:?}))", got, expected) + } + } + }; + } + + #[test] + fn test_writer_read_from_one_buf() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + let mut dst = test_writer(1, 2); + let mut dst = Pin::new(&mut dst); + + assert_poll_ok!(dst.as_mut().poll_write(&mut cx, &[]), 0); + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, &[]), 0); + + // Read at most 2 bytes. + assert_poll_ok!(dst.as_mut().poll_write(&mut cx, &[1, 1, 1]), 2); + let bufs = &[IoSlice::new(&[2, 2, 2])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 2); + + // Only read from first buf. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 1); + + assert_eq!(dst.written, &[1, 1, 2, 2, 3]); + } + + #[test] + fn test_writer_read_from_multiple_bufs() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + let mut dst = test_writer(3, 3); + let mut dst = Pin::new(&mut dst); + + // Read at most 3 bytes from two buffers. + let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 3); + + // Read at most 3 bytes from three buffers. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; + assert_poll_ok!(dst.as_mut().poll_write_vectored(&mut cx, bufs), 3); + + assert_eq!(dst.written, &[1, 2, 2, 3, 4, 5]); + } + + #[test] + fn test_write_all_vectored() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + #[rustfmt::skip] // Becomes unreadable otherwise. + let tests: Vec<(_, &'static [u8])> = vec![ + (vec![], &[]), + (vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]), + (vec![IoSlice::new(&[1])], &[1]), + (vec![IoSlice::new(&[1, 2])], &[1, 2]), + (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), + (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), + ]; + + for (mut input, wanted) in tests { + let mut dst = test_writer(2, 2); + { + let mut future = dst.write_all_vectored(&mut *input); + match Pin::new(&mut future).poll(&mut cx) { + Poll::Ready(Ok(())) => {} + other => panic!("unexpected result polling future: {:?}", other), + } + } + assert_eq!(&*dst.written, &*wanted); + } + } +} diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 21645b148d..9a10c93c9a 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -1,122 +1,337 @@ //! Combinators and utilities for working with `Future`s, `Stream`s, `Sink`s, //! and the `AsyncRead` and `AsyncWrite` traits. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] -#![cfg_attr(feature = "read-initializer", feature(read_initializer))] - +#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))] #![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] -#![warn(clippy::all)] - -// The solution for this lint is not available on 1.39 which is the current minimum supported version. -// Can be removed as of minimum supported 1.40 or if https://github.com/rust-lang/rust-clippy/issues/3941 -// get's implemented. -#![allow(clippy::mem_replace_with_default)] - -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] - -#![doc(html_root_url = "https://docs.rs/futures-util/0.3.0")] - -#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] -compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#![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")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); -#[cfg(all(feature = "read-initializer", not(feature = "unstable")))] -compile_error!("The `read-initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); - #[cfg(feature = "alloc")] extern crate alloc; -#[macro_use(ready)] -extern crate futures_core; - // Macro re-exports pub use futures_core::ready; pub use pin_utils::pin_mut; -// Not public API. #[cfg(feature = "async-await")] #[macro_use] -#[doc(hidden)] -pub mod async_await; +mod async_await; #[cfg(feature = "async-await")] #[doc(hidden)] pub use self::async_await::*; // Not public API. +#[cfg(feature = "async-await")] #[doc(hidden)] -pub use futures_core::core_reexport; +pub mod __private { + pub use crate::*; + pub use core::{ + option::Option::{self, None, Some}, + pin::Pin, + result::Result::{Err, Ok}, + }; -macro_rules! cfg_target_has_atomic { - ($($item:item)*) => {$( - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - $item - )*}; + pub mod async_await { + pub use crate::async_await::*; + } } #[cfg(feature = "sink")] macro_rules! delegate_sink { ($field:ident, $item:ty) => { fn poll_ready( - self: Pin<&mut Self>, - cx: &mut $crate::core_reexport::task::Context<'_>, - ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_ready(cx) + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_ready(cx) } - fn start_send( - self: Pin<&mut Self>, - item: $item, - ) -> Result<(), Self::Error> { - self.$field().start_send(item) + fn start_send(self: core::pin::Pin<&mut Self>, item: $item) -> Result<(), Self::Error> { + self.project().$field.start_send(item) } fn poll_flush( - self: Pin<&mut Self>, - cx: &mut $crate::core_reexport::task::Context<'_>, - ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_flush(cx) + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_flush(cx) } fn poll_close( - self: Pin<&mut Self>, - cx: &mut $crate::core_reexport::task::Context<'_>, - ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_close(cx) + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_close(cx) + } + }; +} + +macro_rules! delegate_future { + ($field:ident) => { + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + self.project().$field.poll(cx) + } + }; +} + +macro_rules! delegate_stream { + ($field:ident) => { + fn poll_next( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_next(cx) + } + fn size_hint(&self) -> (usize, Option) { + self.$field.size_hint() + } + }; +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_write { + ($field:ident) => { + fn poll_write( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + buf: &[u8], + ) -> core::task::Poll> { + self.project().$field.poll_write(cx, buf) + } + fn poll_write_vectored( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> core::task::Poll> { + self.project().$field.poll_write_vectored(cx, bufs) + } + fn poll_flush( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_flush(cx) + } + fn poll_close( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_close(cx) + } + }; +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_read { + ($field:ident) => { + fn poll_read( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + buf: &mut [u8], + ) -> core::task::Poll> { + self.project().$field.poll_read(cx, buf) + } + + fn poll_read_vectored( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + bufs: &mut [std::io::IoSliceMut<'_>], + ) -> core::task::Poll> { + self.project().$field.poll_read_vectored(cx, bufs) + } + }; +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_buf_read { + ($field:ident) => { + fn poll_fill_buf( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_fill_buf(cx) + } + + fn consume(self: core::pin::Pin<&mut Self>, amt: usize) { + self.project().$field.consume(amt) + } + }; +} + +macro_rules! delegate_access_inner { + ($field:ident, $inner:ty, ($($ind:tt)*)) => { + /// Acquires a reference to the underlying sink or stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &$inner { + (&self.$field) $($ind get_ref())* + } + + /// Acquires a mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut $inner { + (&mut self.$field) $($ind get_mut())* + } + + /// Acquires a pinned mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut $inner> { + self.project().$field $($ind get_pin_mut())* + } + + /// Consumes this combinator, returning the underlying sink or stream. + /// + /// Note that this may discard intermediate state of this combinator, so + /// care should be taken to avoid losing resources when this is called. + pub fn into_inner(self) -> $inner { + self.$field $($ind into_inner())* } } } +macro_rules! delegate_all { + (@trait Future $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::Future for $name<$($arg),*> where $t: futures_core::future::Future $(, $($bound)*)* { + type Output = <$t as futures_core::future::Future>::Output; + + delegate_future!(inner); + } + }; + (@trait FusedFuture $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::FusedFuture for $name<$($arg),*> where $t: futures_core::future::FusedFuture $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Stream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::Stream for $name<$($arg),*> where $t: futures_core::stream::Stream $(, $($bound)*)* { + type Item = <$t as futures_core::stream::Stream>::Item; + + delegate_stream!(inner); + } + }; + (@trait FusedStream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::FusedStream for $name<$($arg),*> where $t: futures_core::stream::FusedStream $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Sink $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + #[cfg(feature = "sink")] + impl<_Item, $($arg),*> futures_sink::Sink<_Item> for $name<$($arg),*> where $t: futures_sink::Sink<_Item> $(, $($bound)*)* { + type Error = <$t as futures_sink::Sink<_Item>>::Error; + + delegate_sink!(inner, _Item); + } + }; + (@trait Debug $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> core::fmt::Debug for $name<$($arg),*> where $t: core::fmt::Debug $(, $($bound)*)* { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.inner, f) + } + } + }; + (@trait AccessInner[$inner:ty, ($($ind:tt)*)] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + delegate_access_inner!(inner, $inner, ($($ind)*)); + } + }; + (@trait New[|$($param:ident: $paramt:ty),*| $cons:expr] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + pub(crate) fn new($($param: $paramt),*) -> Self { + Self { inner: $cons } + } + } + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($targs:tt)*])* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + pin_project_lite::pin_project! { + #[must_use = "futures/streams/sinks do nothing unless you `.await` or poll them"] + $(#[$attr])* + pub struct $name< $($arg),* > $(where $($bound)*)* { #[pin] inner: $t } + } + + impl<$($arg),*> $name< $($arg),* > $(where $($bound)*)* { + $($($item)*)* + } + + delegate_all!(@trait $ftrait $([$($targs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($ftargs:tt)*])* + $strait:ident $([$($stargs:tt)*])* $(+ $trait:ident $([$($targs:tt)*])*)* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + delegate_all!($(#[$attr])* $name<$($arg),*>($t) : $strait $([$($stargs)*])* $(+ $trait $([$($targs)*])*)* $({$($item)*})* $(where $($bound)*)*); + + delegate_all!(@trait $ftrait $([$($ftargs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; +} + pub mod future; -#[doc(hidden)] pub use crate::future::{FutureExt, TryFutureExt}; +#[doc(no_inline)] +pub use crate::future::{Future, FutureExt, TryFuture, TryFutureExt}; pub mod stream; -#[doc(hidden)] pub use crate::stream::{StreamExt, TryStreamExt}; +#[doc(no_inline)] +pub use crate::stream::{Stream, StreamExt, TryStream, TryStreamExt}; #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub mod sink; #[cfg(feature = "sink")] -#[doc(hidden)] pub use crate::sink::SinkExt; +#[doc(no_inline)] +pub use crate::sink::{Sink, SinkExt}; pub mod task; pub mod never; #[cfg(feature = "compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "compat")))] pub mod compat; #[cfg(feature = "io")] +#[cfg_attr(docsrs, doc(cfg(feature = "io")))] #[cfg(feature = "std")] pub mod io; #[cfg(feature = "io")] #[cfg(feature = "std")] -#[doc(hidden)] pub use crate::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt}; +#[doc(no_inline)] +pub use crate::io::{ + AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, + AsyncWriteExt, +}; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - pub mod lock; -} +#[cfg(feature = "alloc")] +pub mod lock; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod abortable; + +mod fns; +mod unfold_state; diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs index 374ea5032b..2f51ae7c98 100644 --- a/futures-util/src/lock/bilock.rs +++ b/futures-util/src/lock/bilock.rs @@ -1,17 +1,16 @@ //! Futures-powered synchronization primitives. -#[cfg(feature = "bilock")] -use futures_core::future::Future; -use futures_core::task::{Context, Poll, Waker}; +use alloc::boxed::Box; +use alloc::sync::Arc; use core::cell::UnsafeCell; -#[cfg(any(feature = "bilock", feature = "sink"))] use core::fmt; use core::ops::{Deref, DerefMut}; use core::pin::Pin; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering::SeqCst; -use alloc::boxed::Box; -use alloc::sync::Arc; +#[cfg(feature = "bilock")] +use futures_core::future::Future; +use futures_core::task::{Context, Poll, Waker}; /// A type of futures-powered synchronization primitive which is a mutex between /// two possible owners. @@ -35,6 +34,7 @@ use alloc::sync::Arc; /// This type is only available when the `bilock` feature of this /// library is activated. #[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub struct BiLock { arc: Arc>, } @@ -60,13 +60,10 @@ impl BiLock { /// will only be available through `Pin<&mut T>` (not `&mut T`) unless `T` is `Unpin`. /// Similarly, reuniting the lock and extracting the inner value is only /// possible when `T` is `Unpin`. - pub fn new(t: T) -> (BiLock, BiLock) { - let arc = Arc::new(Inner { - state: AtomicUsize::new(0), - value: Some(UnsafeCell::new(t)), - }); + pub fn new(t: T) -> (Self, Self) { + let arc = Arc::new(Inner { state: AtomicUsize::new(0), value: Some(UnsafeCell::new(t)) }); - (BiLock { arc: arc.clone() }, BiLock { arc }) + (Self { arc: arc.clone() }, Self { arc }) } /// Attempt to acquire this lock, returning `Pending` if it can't be @@ -88,6 +85,7 @@ impl BiLock { /// This function will panic if called outside the context of a future's /// task. pub fn poll_lock(&self, cx: &mut Context<'_>) -> Poll> { + let mut waker = None; loop { match self.arc.state.swap(1, SeqCst) { // Woohoo, we grabbed the lock! @@ -99,12 +97,14 @@ impl BiLock { // A task was previously blocked on this lock, likely our task, // so we need to update that task. n => unsafe { - drop(Box::from_raw(n as *mut Waker)); - } + let mut prev = Box::from_raw(n as *mut Waker); + *prev = cx.waker().clone(); + waker = Some(prev); + }, } // type ascription for safety's sake! - let me: Box = Box::new(cx.waker().clone()); + let me: Box = waker.take().unwrap_or_else(|| Box::new(cx.waker().clone())); let me = Box::into_raw(me) as usize; match self.arc.state.compare_exchange(1, me, SeqCst, SeqCst) { @@ -116,7 +116,7 @@ impl BiLock { // and before the compare_exchange. Deallocate what we just // allocated and go through the loop again. Err(0) => unsafe { - drop(Box::from_raw(me as *mut Waker)); + waker = Some(Box::from_raw(me as *mut Waker)); }, // The top of this loop set the previous state to 1, so if we @@ -140,16 +140,14 @@ impl BiLock { /// /// Note that the returned future will never resolve to an error. #[cfg(feature = "bilock")] + #[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub fn lock(&self) -> BiLockAcquire<'_, T> { - BiLockAcquire { - bilock: self, - } + BiLockAcquire { bilock: self } } /// Attempts to put the two "halves" of a `BiLock` back together and /// recover the original value. Succeeds only if the two `BiLock`s /// originated from the same call to `BiLock::new`. - #[cfg(any(feature = "bilock", feature = "sink"))] pub fn reunite(self, other: Self) -> Result> where T: Unpin, @@ -178,12 +176,11 @@ impl BiLock { // up as its now their turn. n => unsafe { Box::from_raw(n as *mut Waker).wake(); - } + }, } } } -#[cfg(any(feature = "bilock", feature = "sink"))] impl Inner { unsafe fn into_value(mut self) -> T { self.value.take().unwrap().into_inner() @@ -198,26 +195,21 @@ impl Drop for Inner { /// Error indicating two `BiLock`s were not two halves of a whole, and /// thus could not be `reunite`d. -#[cfg(any(feature = "bilock", feature = "sink"))] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub struct ReuniteError(pub BiLock, pub BiLock); -#[cfg(any(feature = "bilock", feature = "sink"))] impl fmt::Debug for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ReuniteError") - .field(&"...") - .finish() + f.debug_tuple("ReuniteError").field(&"...").finish() } } -#[cfg(any(feature = "bilock", feature = "sink"))] impl fmt::Display for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "tried to reunite two BiLocks that don't form a pair") } } -#[cfg(any(feature = "bilock", feature = "sink"))] #[cfg(feature = "std")] impl std::error::Error for ReuniteError {} @@ -227,6 +219,7 @@ impl std::error::Error for ReuniteError {} /// implementing `Deref` and `DerefMut` to `T`. When dropped, the lock will be /// unlocked. #[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] pub struct BiLockGuard<'a, T> { bilock: &'a BiLock, } @@ -262,6 +255,7 @@ impl Drop for BiLockGuard<'_, T> { /// Future returned by `BiLock::lock` which will resolve when the lock is /// acquired. #[cfg(feature = "bilock")] +#[cfg_attr(docsrs, doc(cfg(feature = "bilock")))] #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] pub struct BiLockAcquire<'a, T> { diff --git a/futures-util/src/lock/mod.rs b/futures-util/src/lock/mod.rs index 3db5e5b1f3..cf374c016f 100644 --- a/futures-util/src/lock/mod.rs +++ b/futures-util/src/lock/mod.rs @@ -3,16 +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(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, MutexLockFuture, MutexGuard}; +pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard, MutexLockFuture}; +#[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(feature = "bilock")] -pub use self::bilock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; +#[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 ccfbf731f8..85dcb1537b 100644 --- a/futures-util/src/lock/mutex.rs +++ b/futures-util/src/lock/mutex.rs @@ -1,14 +1,22 @@ use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll, Waker}; use slab::Slab; -use std::{fmt, mem}; use std::cell::UnsafeCell; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::pin::Pin; -use std::sync::Mutex as StdMutex; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Mutex as StdMutex; +use std::{fmt, mem}; /// A futures-aware mutex. +/// +/// # Fairness +/// +/// This mutex provides no fairness guarantees. Tasks may not acquire the mutex +/// in the order that they requested the lock, and it's possible for a single task +/// which repeatedly takes the lock to starve other tasks, which may be left waiting +/// indefinitely. pub struct Mutex { state: AtomicUsize, waiters: StdMutex>, @@ -32,8 +40,8 @@ impl From for Mutex { } impl Default for Mutex { - fn default() -> Mutex { - Mutex::new(Default::default()) + fn default() -> Self { + Self::new(Default::default()) } } @@ -45,27 +53,26 @@ enum Waiter { impl Waiter { fn register(&mut self, waker: &Waker) { match self { - Waiter::Waiting(w) if waker.will_wake(w) => {}, - _ => *self = Waiter::Waiting(waker.clone()), + Self::Waiting(w) if waker.will_wake(w) => {} + _ => *self = Self::Waiting(waker.clone()), } } fn wake(&mut self) { - match mem::replace(self, Waiter::Woken) { - Waiter::Waiting(waker) => waker.wake(), - Waiter::Woken => {}, + match mem::replace(self, Self::Woken) { + Self::Waiting(waker) => waker.wake(), + Self::Woken => {} } } } -#[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; impl Mutex { /// Creates a new futures-aware mutex. - pub fn new(t: T) -> Mutex { - Mutex { + pub fn new(t: T) -> Self { + Self { state: AtomicUsize::new(0), waiters: StdMutex::new(Slab::new()), value: UnsafeCell::new(t), @@ -105,10 +112,7 @@ impl Mutex { /// This method returns a future that will resolve once the lock has been /// successfully acquired. pub fn lock(&self) -> MutexLockFuture<'_, T> { - MutexLockFuture { - mutex: Some(self), - wait_key: WAIT_KEY_NONE, - } + MutexLockFuture { mutex: Some(self), wait_key: WAIT_KEY_NONE } } /// Returns a mutable reference to the underlying data. @@ -137,7 +141,7 @@ impl Mutex { if wait_key != WAIT_KEY_NONE { let mut waiters = self.waiters.lock().unwrap(); match waiters.remove(wait_key) { - Waiter::Waiting(_) => {}, + Waiter::Waiting(_) => {} Waiter::Woken => { // We were awoken, but then dropped before we could // wake up to acquire the lock. Wake up another @@ -183,13 +187,10 @@ impl fmt::Debug for MutexLockFuture<'_, T> { f.debug_struct("MutexLockFuture") .field("was_acquired", &self.mutex.is_none()) .field("mutex", &self.mutex) - .field("wait_key", &( - if self.wait_key == WAIT_KEY_NONE { - None - } else { - Some(self.wait_key) - } - )) + .field( + "wait_key", + &(if self.wait_key == WAIT_KEY_NONE { None } else { Some(self.wait_key) }), + ) .finish() } } @@ -281,16 +282,13 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { // Don't run the `drop` method for MutexGuard. The ownership of the underlying // locked state is being moved to the returned MappedMutexGuard. mem::forget(this); - MappedMutexGuard { mutex, value } + MappedMutexGuard { mutex, value, _marker: PhantomData } } } impl fmt::Debug for MutexGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MutexGuard") - .field("value", &&**self) - .field("mutex", &self.mutex) - .finish() + f.debug_struct("MutexGuard").field("value", &&**self).field("mutex", &self.mutex).finish() } } @@ -318,6 +316,7 @@ impl DerefMut for MutexGuard<'_, T> { pub struct MappedMutexGuard<'a, T: ?Sized, U: ?Sized> { mutex: &'a Mutex, value: *mut U, + _marker: PhantomData<&'a mut U>, } impl<'a, T: ?Sized, U: ?Sized> MappedMutexGuard<'a, T, U> { @@ -347,7 +346,7 @@ impl<'a, T: ?Sized, U: ?Sized> MappedMutexGuard<'a, T, U> { // Don't run the `drop` method for MappedMutexGuard. The ownership of the underlying // locked state is being moved to the returned MappedMutexGuard. mem::forget(this); - MappedMutexGuard { mutex, value } + MappedMutexGuard { mutex, value, _marker: PhantomData } } } @@ -394,8 +393,8 @@ unsafe impl Sync for MutexLockFuture<'_, T> {} // lock is essentially spinlock-equivalent (attempt to flip an atomic bool) unsafe impl Send for MutexGuard<'_, T> {} unsafe impl Sync for MutexGuard<'_, T> {} -unsafe impl Send for MappedMutexGuard<'_, T, U> {} -unsafe impl Sync for MappedMutexGuard<'_, T, U> {} +unsafe impl Send for MappedMutexGuard<'_, T, U> {} +unsafe impl Sync for MappedMutexGuard<'_, T, U> {} #[test] fn test_mutex_guard_debug_not_recurse() { diff --git a/futures-util/src/never.rs b/futures-util/src/never.rs index 767c5af568..e811f97df7 100644 --- a/futures-util/src/never.rs +++ b/futures-util/src/never.rs @@ -1,5 +1,6 @@ -//! Definition of the `Never` type, -//! a stand-in for the `!` type until it becomes stable. +//! This module contains the `Never` type. +//! +//! Values of this type can never be created and will never exist. /// A type with no possible values. /// diff --git a/futures-util/src/sink/buffer.rs b/futures-util/src/sink/buffer.rs index d2a3f9098b..4aa6c36033 100644 --- a/futures-util/src/sink/buffer.rs +++ b/futures-util/src/sink/buffer.rs @@ -1,68 +1,39 @@ -use futures_core::stream::{Stream, FusedStream}; +use alloc::collections::VecDeque; +use core::pin::Pin; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use core::pin::Pin; -use alloc::collections::VecDeque; - -/// Sink for the [`buffer`](super::SinkExt::buffer) method. -#[derive(Debug)] -#[must_use = "sinks do nothing unless polled"] -pub struct Buffer { - sink: Si, - buf: VecDeque, - - // Track capacity separately from the `VecDeque`, which may be rounded up - capacity: usize, +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`buffer`](super::SinkExt::buffer) method. + #[derive(Debug)] + #[must_use = "sinks do nothing unless polled"] + pub struct Buffer { + #[pin] + sink: Si, + buf: VecDeque, + + // Track capacity separately from the `VecDeque`, which may be rounded up + capacity: usize, + } } -impl Unpin for Buffer {} - impl, Item> Buffer { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(buf: VecDeque); - unsafe_unpinned!(capacity: usize); - pub(super) fn new(sink: Si, capacity: usize) -> Self { - Buffer { - sink, - buf: VecDeque::with_capacity(capacity), - capacity, - } - } - - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() + Self { sink, buf: VecDeque::with_capacity(capacity), capacity } } - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); - fn try_empty_buffer( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.as_mut().sink().poll_ready(cx))?; - while let Some(item) = self.as_mut().buf().pop_front() { - self.as_mut().sink().start_send(item)?; - if !self.buf.is_empty() { - ready!(self.as_mut().sink().poll_ready(cx))?; + fn try_empty_buffer(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + ready!(this.sink.as_mut().poll_ready(cx))?; + while let Some(item) = this.buf.pop_front() { + this.sink.as_mut().start_send(item)?; + if !this.buf.is_empty() { + ready!(this.sink.as_mut().poll_ready(cx))?; } } Poll::Ready(Ok(())) @@ -70,11 +41,14 @@ impl, Item> Buffer { } // Forwarding impl of Stream from the underlying sink -impl Stream for Buffer where S: Sink + Stream { +impl Stream for Buffer +where + S: Sink + Stream, +{ type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.sink().poll_next(cx) + self.project().sink.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -82,7 +56,10 @@ impl Stream for Buffer where S: Sink + Stream { } } -impl FusedStream for Buffer where S: Sink + FusedStream { +impl FusedStream for Buffer +where + S: Sink + FusedStream, +{ fn is_terminated(&self) -> bool { self.sink.is_terminated() } @@ -91,12 +68,9 @@ impl FusedStream for Buffer where S: Sink + FusedStream impl, Item> Sink for Buffer { type Error = Si::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.capacity == 0 { - return self.as_mut().sink().poll_ready(cx); + return self.project().sink.poll_ready(cx); } let _ = self.as_mut().try_empty_buffer(cx)?; @@ -108,35 +82,24 @@ impl, Item> Sink for Buffer { } } - fn start_send( - mut self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { if self.capacity == 0 { - self.as_mut().sink().start_send(item) + self.project().sink.start_send(item) } else { - self.as_mut().buf().push_back(item); + self.project().buf.push_back(item); Ok(()) } } - #[allow(clippy::debug_assert_with_mut_call)] - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_flush(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_flush(cx) } - #[allow(clippy::debug_assert_with_mut_call)] - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_close(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/sink/close.rs b/futures-util/src/sink/close.rs index 1514b4118c..43eea74b0f 100644 --- a/futures-util/src/sink/close.rs +++ b/futures-util/src/sink/close.rs @@ -16,23 +16,17 @@ impl Unpin for Close<'_, Si, Item> {} /// A future that completes when the sink has finished closing. /// -/// The sink itself is returned after closeing is complete. +/// The sink itself is returned after closing is complete. impl<'a, Si: Sink + Unpin + ?Sized, Item> Close<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si) -> Self { - Close { - sink, - _phantom: PhantomData, - } + Self { sink, _phantom: PhantomData } } } impl + Unpin + ?Sized, Item> Future for Close<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.sink).poll_close(cx) } } diff --git a/futures-util/src/sink/drain.rs b/futures-util/src/sink/drain.rs index 46de83b7c2..5295115b66 100644 --- a/futures-util/src/sink/drain.rs +++ b/futures-util/src/sink/drain.rs @@ -1,3 +1,4 @@ +use super::assert_sink; use crate::never::Never; use core::marker::PhantomData; use core::pin::Pin; @@ -26,7 +27,7 @@ pub struct Drain { /// # Ok::<(), futures::never::Never>(()) }).unwrap(); /// ``` pub fn drain() -> Drain { - Drain { marker: PhantomData } + assert_sink::(Drain { marker: PhantomData }) } impl Unpin for Drain {} @@ -34,31 +35,19 @@ impl Unpin for Drain {} impl Sink for Drain { type Error = Never; - fn poll_ready( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn start_send( - self: Pin<&mut Self>, - _item: T, - ) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, _item: T) -> Result<(), Self::Error> { Ok(()) } - fn poll_flush( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn poll_close( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } diff --git a/futures-util/src/sink/err_into.rs b/futures-util/src/sink/err_into.rs index af198feaa0..a64d1337ba 100644 --- a/futures-util/src/sink/err_into.rs +++ b/futures-util/src/sink/err_into.rs @@ -1,56 +1,34 @@ use crate::sink::{SinkExt, SinkMapErr}; -use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; -use futures_sink::{Sink}; -use pin_utils::unsafe_pinned; - -/// Sink for the [`sink_err_into`](super::SinkExt::sink_err_into) method. -#[derive(Debug)] -#[must_use = "sinks do nothing unless polled"] -pub struct SinkErrInto, Item, E> { - sink: SinkMapErr E>, +use futures_core::stream::{FusedStream, Stream}; +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`sink_err_into`](super::SinkExt::sink_err_into) method. + #[derive(Debug)] + #[must_use = "sinks do nothing unless polled"] + pub struct SinkErrInto, Item, E> { + #[pin] + sink: SinkMapErr E>, + } } impl SinkErrInto - where Si: Sink, - Si::Error: Into, +where + Si: Sink, + Si::Error: Into, { - unsafe_pinned!(sink: SinkMapErr E>); - pub(super) fn new(sink: Si) -> Self { - SinkErrInto { - sink: SinkExt::sink_map_err(sink, Into::into), - } - } - - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - self.sink.get_ref() - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - self.sink.get_mut() + Self { sink: SinkExt::sink_map_err(sink, Into::into) } } - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink.into_inner() - } + delegate_access_inner!(sink, Si, (.)); } impl Sink for SinkErrInto - where Si: Sink, - Si::Error: Into, +where + Si: Sink, + Si::Error: Into, { type Error = E; @@ -59,26 +37,19 @@ impl Sink for SinkErrInto // Forwarding impl of Stream from the underlying sink impl Stream for SinkErrInto - where S: Sink + Stream, - S::Error: Into +where + S: Sink + Stream, + S::Error: Into, { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkErrInto - where S: Sink + FusedStream, - S::Error: Into +where + S: Sink + FusedStream, + S::Error: Into, { fn is_terminated(&self) -> bool { self.sink.is_terminated() diff --git a/futures-util/src/sink/fanout.rs b/futures-util/src/sink/fanout.rs index 24e4de95eb..fe2038f27f 100644 --- a/futures-util/src/sink/fanout.rs +++ b/futures-util/src/sink/fanout.rs @@ -2,24 +2,25 @@ use core::fmt::{Debug, Formatter, Result as FmtResult}; use core::pin::Pin; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Sink that clones incoming items and forwards them to two sinks at the same time. -/// -/// Backpressure from any downstream sink propagates up, which means that this sink -/// can only process items as fast as its _slowest_ downstream sink. -#[must_use = "sinks do nothing unless polled"] -pub struct Fanout { - sink1: Si1, - sink2: Si2 +use pin_project_lite::pin_project; + +pin_project! { + /// Sink that clones incoming items and forwards them to two sinks at the same time. + /// + /// Backpressure from any downstream sink propagates up, which means that this sink + /// can only process items as fast as its _slowest_ downstream sink. + #[must_use = "sinks do nothing unless polled"] + pub struct Fanout { + #[pin] + sink1: Si1, + #[pin] + sink2: Si2 + } } impl Fanout { - unsafe_pinned!(sink1: Si1); - unsafe_pinned!(sink2: Si2); - - pub(super) fn new(sink1: Si1, sink2: Si2) -> Fanout { - Fanout { sink1, sink2 } + pub(super) fn new(sink1: Si1, sink2: Si2) -> Self { + Self { sink1, sink2 } } /// Get a shared reference to the inner sinks. @@ -34,10 +35,8 @@ impl Fanout { /// Get a pinned mutable reference to the inner sinks. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut Si1>, Pin<&mut Si2>) { - unsafe { - let Self { sink1, sink2 } = self.get_unchecked_mut(); - (Pin::new_unchecked(sink1), Pin::new_unchecked(sink2)) - } + let this = self.project(); + (this.sink1, this.sink2) } /// Consumes this combinator, returning the underlying sinks. @@ -51,56 +50,62 @@ impl Fanout { impl Debug for Fanout { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.debug_struct("Fanout") - .field("sink1", &self.sink1) - .field("sink2", &self.sink2) - .finish() + f.debug_struct("Fanout").field("sink1", &self.sink1).field("sink2", &self.sink2).finish() } } impl Sink for Fanout - where Si1: Sink, - Item: Clone, - Si2: Sink +where + Si1: Sink, + Item: Clone, + Si2: Sink, { type Error = Si1::Error; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_ready(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_ready(cx)?.is_ready(); + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + let sink1_ready = this.sink1.poll_ready(cx)?.is_ready(); + let sink2_ready = this.sink2.poll_ready(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } - fn start_send( - mut self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { - self.as_mut().sink1().start_send(item.clone())?; - self.as_mut().sink2().start_send(item)?; + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + let this = self.project(); + + this.sink1.start_send(item.clone())?; + this.sink2.start_send(item)?; Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_flush(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_flush(cx)?.is_ready(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + let sink1_ready = this.sink1.poll_flush(cx)?.is_ready(); + let sink2_ready = this.sink2.poll_flush(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_close(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_close(cx)?.is_ready(); + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + let sink1_ready = this.sink1.poll_close(cx)?.is_ready(); + let sink2_ready = this.sink2.poll_close(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; - if ready { Poll::Ready(Ok(())) } else { Poll::Pending } + if ready { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } } diff --git a/futures-util/src/sink/feed.rs b/futures-util/src/sink/feed.rs new file mode 100644 index 0000000000..6701f7a1b4 --- /dev/null +++ b/futures-util/src/sink/feed.rs @@ -0,0 +1,43 @@ +use core::pin::Pin; +use futures_core::future::Future; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use futures_sink::Sink; + +/// Future for the [`feed`](super::SinkExt::feed) method. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Feed<'a, Si: ?Sized, Item> { + sink: &'a mut Si, + item: Option, +} + +// Pinning is never projected to children +impl Unpin for Feed<'_, Si, Item> {} + +impl<'a, Si: Sink + Unpin + ?Sized, Item> Feed<'a, Si, Item> { + pub(super) fn new(sink: &'a mut Si, item: Item) -> Self { + Feed { sink, item: Some(item) } + } + + pub(super) fn sink_pin_mut(&mut self) -> Pin<&mut Si> { + Pin::new(self.sink) + } + + pub(super) fn is_item_pending(&self) -> bool { + self.item.is_some() + } +} + +impl + Unpin + ?Sized, Item> Future for Feed<'_, Si, Item> { + type Output = Result<(), Si::Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + let mut sink = Pin::new(&mut this.sink); + ready!(sink.as_mut().poll_ready(cx))?; + let item = this.item.take().expect("polled Feed after completion"); + sink.as_mut().start_send(item)?; + Poll::Ready(Ok(())) + } +} diff --git a/futures-util/src/sink/flush.rs b/futures-util/src/sink/flush.rs index 86fecc45d9..35a8372de7 100644 --- a/futures-util/src/sink/flush.rs +++ b/futures-util/src/sink/flush.rs @@ -23,20 +23,14 @@ impl Unpin for Flush<'_, Si, Item> {} /// all current requests are processed. impl<'a, Si: Sink + Unpin + ?Sized, Item> Flush<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si) -> Self { - Flush { - sink, - _phantom: PhantomData, - } + Self { sink, _phantom: PhantomData } } } impl + Unpin + ?Sized, Item> Future for Flush<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.sink).poll_flush(cx) } } diff --git a/futures-util/src/sink/map_err.rs b/futures-util/src/sink/map_err.rs index e999bea78f..9d2ab7b24b 100644 --- a/futures-util/src/sink/map_err.rs +++ b/futures-util/src/sink/map_err.rs @@ -1,87 +1,53 @@ use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use futures_sink::{Sink}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Sink for the [`sink_map_err`](super::SinkExt::sink_map_err) method. -#[derive(Debug, Clone)] -#[must_use = "sinks do nothing unless polled"] -pub struct SinkMapErr { - sink: Si, - f: Option, +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`sink_map_err`](super::SinkExt::sink_map_err) method. + #[derive(Debug, Clone)] + #[must_use = "sinks do nothing unless polled"] + pub struct SinkMapErr { + #[pin] + sink: Si, + f: Option, + } } -impl Unpin for SinkMapErr {} - impl SinkMapErr { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: Option); - - pub(super) fn new(sink: Si, f: F) -> SinkMapErr { - SinkMapErr { sink, f: Some(f) } - } - - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() + pub(super) fn new(sink: Si, f: F) -> Self { + Self { sink, f: Some(f) } } - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); fn take_f(self: Pin<&mut Self>) -> F { - self.f().take().expect("polled MapErr after completion") + self.project().f.take().expect("polled MapErr after completion") } } impl Sink for SinkMapErr - where Si: Sink, - F: FnOnce(Si::Error) -> E, +where + Si: Sink, + F: FnOnce(Si::Error) -> E, { type Error = E; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut().sink().poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().sink.poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) } - fn start_send( - mut self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { - self.as_mut().sink().start_send(item).map_err(|e| self.as_mut().take_f()(e)) + fn start_send(mut self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + self.as_mut().project().sink.start_send(item).map_err(|e| self.as_mut().take_f()(e)) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut().sink().poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().sink.poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut().sink().poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().project().sink.poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) } } @@ -89,16 +55,7 @@ impl Sink for SinkMapErr impl Stream for SinkMapErr { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkMapErr { diff --git a/futures-util/src/sink/mod.rs b/futures-util/src/sink/mod.rs index bc59ce5865..147e9adc93 100644 --- a/futures-util/src/sink/mod.rs +++ b/futures-util/src/sink/mod.rs @@ -1,14 +1,16 @@ -//! Sinks +//! Asynchronous sinks. //! -//! This module contains a number of functions for working with `Sink`s, -//! including the `SinkExt` trait which adds methods to `Sink` types. +//! This module contains: //! -//! This module is only available when the `sink` feature of this -//! library is activated, and it is activated by default. +//! - The [`Sink`] trait, which allows you to asynchronously write data. +//! - The [`SinkExt`] trait, which provides adapters for chaining and composing +//! sinks. +use crate::future::{assert_future, Either}; +use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{Stream, TryStream}; -use crate::future::Either; +use futures_core::task::{Context, Poll}; #[cfg(feature = "compat")] use crate::compat::CompatSink; @@ -24,6 +26,9 @@ pub use self::drain::{drain, Drain}; mod fanout; pub use self::fanout::Fanout; +mod feed; +pub use self::feed::Feed; + mod flush; pub use self::flush::Flush; @@ -39,6 +44,9 @@ pub use self::send::Send; mod send_all; pub use self::send_all::SendAll; +mod unfold; +pub use self::unfold::{unfold, Unfold}; + mod with; pub use self::with::With; @@ -67,12 +75,13 @@ pub trait SinkExt: Sink { /// Note that this function consumes the given sink, returning a wrapped /// version, much like `Iterator::map`. fn with(self, f: F) -> With - where F: FnMut(U) -> Fut, - Fut: Future>, - E: From, - Self: Sized + where + F: FnMut(U) -> Fut, + Fut: Future>, + E: From, + Self: Sized, { - With::new(self, f) + assert_sink::(With::new(self, f)) } /// Composes a function *in front of* the sink. @@ -108,11 +117,12 @@ pub trait SinkExt: Sink { /// # }); /// ``` fn with_flat_map(self, f: F) -> WithFlatMap - where F: FnMut(U) -> St, - St: Stream>, - Self: Sized + where + F: FnMut(U) -> St, + St: Stream>, + Self: Sized, { - WithFlatMap::new(self, f) + assert_sink::(WithFlatMap::new(self, f)) } /* @@ -131,23 +141,24 @@ pub trait SinkExt: Sink { /// Transforms the error returned by the sink. fn sink_map_err(self, f: F) -> SinkMapErr - where F: FnOnce(Self::Error) -> E, - Self: Sized, + where + F: FnOnce(Self::Error) -> E, + Self: Sized, { - SinkMapErr::new(self, f) + assert_sink::(SinkMapErr::new(self, f)) } /// Map this sink's error to a different error type using the `Into` trait. /// /// If wanting to map errors of a `Sink + Stream`, use `.sink_err_into().err_into()`. fn sink_err_into(self) -> err_into::SinkErrInto - where Self: Sized, - Self::Error: Into, + where + Self: Sized, + Self::Error: Into, { - SinkErrInto::new(self) + assert_sink::(SinkErrInto::new(self)) } - /// Adds a fixed-size buffer to the current sink. /// /// The resulting sink will buffer up to `capacity` items when the @@ -162,16 +173,18 @@ pub trait SinkExt: Sink { /// library is activated, and it is activated by default. #[cfg(feature = "alloc")] fn buffer(self, capacity: usize) -> Buffer - where Self: Sized, + where + Self: Sized, { - Buffer::new(self, capacity) + assert_sink::(Buffer::new(self, capacity)) } /// Close the sink. fn close(&mut self) -> Close<'_, Self, Item> - where Self: Unpin, + where + Self: Unpin, { - Close::new(self) + assert_future::, _>(Close::new(self)) } /// Fanout items to multiple sinks. @@ -179,11 +192,12 @@ pub trait SinkExt: Sink { /// This adapter clones each incoming item and forwards it to both this as well as /// the other sink at the same time. fn fanout(self, other: Si) -> Fanout - where Self: Sized, - Item: Clone, - Si: Sink + where + Self: Sized, + Item: Clone, + Si: Sink, { - Fanout::new(self, other) + assert_sink::(Fanout::new(self, other)) } /// Flush the sink, processing all pending items. @@ -191,21 +205,36 @@ pub trait SinkExt: Sink { /// This adapter is intended to be used when you want to stop sending to the sink /// until all current requests are processed. fn flush(&mut self) -> Flush<'_, Self, Item> - where Self: Unpin, + where + Self: Unpin, { - Flush::new(self) + assert_future::, _>(Flush::new(self)) } /// A future that completes after the given item has been fully processed /// into the sink, including flushing. /// /// Note that, **because of the flushing requirement, it is usually better - /// to batch together items to send via `send_all`, rather than flushing - /// between each item.** + /// to batch together items to send via `feed` or `send_all`, + /// rather than flushing between each item.** fn send(&mut self, item: Item) -> Send<'_, Self, Item> - where Self: Unpin, + where + Self: Unpin, { - Send::new(self, item) + assert_future::, _>(Send::new(self, item)) + } + + /// A future that completes after the given item has been received + /// by the sink. + /// + /// Unlike `send`, the returned future does not flush the sink. + /// It is the caller's responsibility to ensure all pending items + /// are processed, which can be done via `flush` or `close`. + fn feed(&mut self, item: Item) -> Feed<'_, Self, Item> + where + Self: Unpin, + { + assert_future::, _>(Feed::new(self, item)) } /// A future that completes after the given stream has been fully processed @@ -214,18 +243,20 @@ pub trait SinkExt: Sink { /// This future will drive the stream to keep producing items until it is /// exhausted, sending each item to the sink. It will complete once both the /// stream is exhausted, the sink has received all items, and the sink has - /// been flushed. Note that the sink is **not** closed. + /// been flushed. Note that the sink is **not** closed. If the stream produces + /// an error, that error will be returned by this future without flushing the sink. /// /// Doing `sink.send_all(stream)` is roughly equivalent to /// `stream.forward(sink)`. The returned future will exhaust all items from /// `stream` and send them to `self`. - fn send_all<'a, St>( - &'a mut self, - stream: &'a mut St - ) -> SendAll<'a, Self, St> - where St: TryStream + Stream + Unpin + ?Sized, - Self: Unpin, + fn send_all<'a, St>(&'a mut self, stream: &'a mut St) -> SendAll<'a, Self, St> + where + St: TryStream + Stream + Unpin + ?Sized, + // St: Stream> + Unpin + ?Sized, + Self: Unpin, { + // TODO: type mismatch resolving `::Item == std::result::Result>::Error>` + // assert_future::, _>(SendAll::new(self, stream)) SendAll::new(self, stream) } @@ -235,10 +266,11 @@ pub trait SinkExt: Sink { /// This can be used in combination with the `right_sink` method to write `if` /// statements that evaluate to different streams in different branches. fn left_sink(self) -> Either - where Si2: Sink, - Self: Sized + where + Si2: Sink, + Self: Sized, { - Either::Left(self) + assert_sink::(Either::Left(self)) } /// Wrap this stream in an `Either` stream, making it the right-hand variant @@ -247,18 +279,66 @@ pub trait SinkExt: Sink { /// This can be used in combination with the `left_sink` method to write `if` /// statements that evaluate to different streams in different branches. fn right_sink(self) -> Either - where Si1: Sink, - Self: Sized + where + Si1: Sink, + Self: Sized, { - Either::Right(self) + assert_sink::(Either::Right(self)) } /// Wraps a [`Sink`] into a sink compatible with libraries using /// futures 0.1 `Sink`. Requires the `compat` feature to be enabled. #[cfg(feature = "compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "compat")))] fn compat(self) -> CompatSink - where Self: Sized + Unpin, + where + Self: Sized + Unpin, { CompatSink::new(self) } + + /// A convenience method for calling [`Sink::poll_ready`] on [`Unpin`] + /// sink types. + fn poll_ready_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where + Self: Unpin, + { + Pin::new(self).poll_ready(cx) + } + + /// A convenience method for calling [`Sink::start_send`] on [`Unpin`] + /// sink types. + fn start_send_unpin(&mut self, item: Item) -> Result<(), Self::Error> + where + Self: Unpin, + { + Pin::new(self).start_send(item) + } + + /// A convenience method for calling [`Sink::poll_flush`] on [`Unpin`] + /// sink types. + fn poll_flush_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where + Self: Unpin, + { + Pin::new(self).poll_flush(cx) + } + + /// A convenience method for calling [`Sink::poll_close`] on [`Unpin`] + /// sink types. + fn poll_close_unpin(&mut self, cx: &mut Context<'_>) -> Poll> + where + Self: Unpin, + { + Pin::new(self).poll_close(cx) + } +} + +// Just a helper function to ensure the sinks we're returning all have the +// right implementations. +pub(crate) fn assert_sink(sink: S) -> S +where + S: Sink, +{ + sink } diff --git a/futures-util/src/sink/send.rs b/futures-util/src/sink/send.rs index dc7f0be254..6d21f33fe4 100644 --- a/futures-util/src/sink/send.rs +++ b/futures-util/src/sink/send.rs @@ -1,5 +1,7 @@ +use super::Feed; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::task::{Context, Poll}; use futures_sink::Sink; @@ -7,8 +9,7 @@ use futures_sink::Sink; #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Send<'a, Si: ?Sized, Item> { - sink: &'a mut Si, - item: Option, + feed: Feed<'a, Si, Item>, } // Pinning is never projected to children @@ -16,35 +17,24 @@ impl Unpin for Send<'_, Si, Item> {} impl<'a, Si: Sink + Unpin + ?Sized, Item> Send<'a, Si, Item> { pub(super) fn new(sink: &'a mut Si, item: Item) -> Self { - Send { - sink, - item: Some(item), - } + Self { feed: Feed::new(sink, item) } } } impl + Unpin + ?Sized, Item> Future for Send<'_, Si, Item> { type Output = Result<(), Si::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; - if let Some(item) = this.item.take() { - let mut sink = Pin::new(&mut this.sink); - match sink.as_mut().poll_ready(cx)? { - Poll::Ready(()) => sink.as_mut().start_send(item)?, - Poll::Pending => { - this.item = Some(item); - return Poll::Pending; - } - } + + if this.feed.is_item_pending() { + ready!(Pin::new(&mut this.feed).poll(cx))?; + debug_assert!(!this.feed.is_item_pending()); } // we're done sending the item, but want to block on flushing the // sink - ready!(Pin::new(&mut this.sink).poll_flush(cx))?; + ready!(this.feed.sink_pin_mut().poll_flush(cx))?; Poll::Ready(Ok(())) } diff --git a/futures-util/src/sink/send_all.rs b/futures-util/src/sink/send_all.rs index 255df4daea..1302dd2148 100644 --- a/futures-util/src/sink/send_all.rs +++ b/futures-util/src/sink/send_all.rs @@ -1,8 +1,9 @@ -use crate::stream::{StreamExt, TryStreamExt, Fuse}; +use crate::stream::{Fuse, StreamExt, TryStreamExt}; use core::fmt; use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{TryStream, Stream}; +use futures_core::ready; +use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; @@ -39,22 +40,16 @@ impl Unpin for SendAll<'_, Si, St> where Si: Unpin + ?Sized, St: TryStream + Unpin + ?Sized, -{} +{ +} impl<'a, Si, St, Ok, Error> SendAll<'a, Si, St> where Si: Sink + Unpin + ?Sized, St: TryStream + Stream + Unpin + ?Sized, { - pub(super) fn new( - sink: &'a mut Si, - stream: &'a mut St, - ) -> SendAll<'a, Si, St> { - SendAll { - sink, - stream: stream.fuse(), - buffered: None, - } + pub(super) fn new(sink: &'a mut Si, stream: &'a mut St) -> Self { + Self { sink, stream: stream.fuse(), buffered: None } } fn try_start_send( @@ -64,9 +59,7 @@ where ) -> Poll> { debug_assert!(self.buffered.is_none()); match Pin::new(&mut self.sink).poll_ready(cx)? { - Poll::Ready(()) => { - Poll::Ready(Pin::new(&mut self.sink).start_send(item)) - } + Poll::Ready(()) => Poll::Ready(Pin::new(&mut self.sink).start_send(item)), Poll::Pending => { self.buffered = Some(item); Poll::Pending @@ -82,10 +75,7 @@ where { type Output = Result<(), Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = &mut *self; // If we've got an item buffered already, we need to write it to the // sink before we can do anything else @@ -95,16 +85,14 @@ where loop { match this.stream.try_poll_next_unpin(cx)? { - Poll::Ready(Some(item)) => { - ready!(this.try_start_send(cx, item))? - } + Poll::Ready(Some(item)) => ready!(this.try_start_send(cx, item))?, Poll::Ready(None) => { ready!(Pin::new(&mut this.sink).poll_flush(cx))?; - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); } Poll::Pending => { ready!(Pin::new(&mut this.sink).poll_flush(cx))?; - return Poll::Pending + return Poll::Pending; } } } diff --git a/futures-util/src/sink/unfold.rs b/futures-util/src/sink/unfold.rs new file mode 100644 index 0000000000..330a068c31 --- /dev/null +++ b/futures-util/src/sink/unfold.rs @@ -0,0 +1,86 @@ +use super::assert_sink; +use crate::unfold_state::UnfoldState; +use core::{future::Future, pin::Pin}; +use futures_core::ready; +use futures_core::task::{Context, Poll}; +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`unfold`] function. + #[derive(Debug)] + #[must_use = "sinks do nothing unless polled"] + pub struct Unfold { + function: F, + #[pin] + state: UnfoldState, + } +} + +/// Create a sink from a function which processes one item at a time. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::sink::{self, SinkExt}; +/// +/// let unfold = sink::unfold(0, |mut sum, i: i32| { +/// async move { +/// sum += i; +/// eprintln!("{}", i); +/// Ok::<_, futures::never::Never>(sum) +/// } +/// }); +/// futures::pin_mut!(unfold); +/// unfold.send(5).await?; +/// # Ok::<(), futures::never::Never>(()) }).unwrap(); +/// ``` +pub fn unfold(init: T, function: F) -> Unfold +where + F: FnMut(T, Item) -> R, + R: Future>, +{ + assert_sink::(Unfold { function, state: UnfoldState::Value { value: init } }) +} + +impl Sink for Unfold +where + F: FnMut(T, Item) -> R, + R: Future>, +{ + type Error = E; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + let mut this = self.project(); + let future = match this.state.as_mut().take_value() { + Some(value) => (this.function)(value, item), + None => panic!("start_send called without poll_ready being called first"), + }; + this.state.set(UnfoldState::Future { future }); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + Poll::Ready(if let Some(future) = this.state.as_mut().project_future() { + match ready!(future.poll(cx)) { + Ok(state) => { + this.state.set(UnfoldState::Value { value: state }); + Ok(()) + } + Err(err) => Err(err), + } + } else { + Ok(()) + }) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} diff --git a/futures-util/src/sink/with.rs b/futures-util/src/sink/with.rs index 5acac0176e..86d3dcc7b8 100644 --- a/futures-util/src/sink/with.rs +++ b/futures-util/src/sink/with.rs @@ -2,57 +2,61 @@ use core::fmt; use core::marker::PhantomData; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Sink for the [`with`](super::SinkExt::with) method. -#[must_use = "sinks do nothing unless polled"] -pub struct With { - sink: Si, - f: F, - state: Option, - _phantom: PhantomData Item>, +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`with`](super::SinkExt::with) method. + #[must_use = "sinks do nothing unless polled"] + pub struct With { + #[pin] + sink: Si, + f: F, + #[pin] + state: Option, + _phantom: PhantomData Item>, + } } -impl Unpin for With -where - Si: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for With where Si: fmt::Debug, Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("With") - .field("sink", &self.sink) - .field("state", &self.state) - .finish() + f.debug_struct("With").field("sink", &self.sink).field("state", &self.state).finish() } } impl With -where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(state: Option); - pub(super) fn new(sink: Si, f: F) -> Self - where - Fut: Future>, - E: From, + where + Fut: Future>, + E: From, { - With { - state: None, - sink, - f, + Self { state: None, sink, f, _phantom: PhantomData } + } +} + +impl Clone for With +where + Si: Clone, + F: Clone, + Fut: Clone, +{ + fn clone(&self) -> Self { + Self { + state: self.state.clone(), + sink: self.sink.clone(), + f: self.f.clone(), _phantom: PhantomData, } } @@ -60,109 +64,71 @@ where Si: Sink, // Forwarding impl of Stream from the underlying sink impl Stream for With - where S: Stream + Sink, - F: FnMut(U) -> Fut, - Fut: Future +where + S: Stream + Sink, + F: FnMut(U) -> Fut, + Fut: Future, { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl With - where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future>, - E: From, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future>, + E: From, { - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); /// Completes the processing of previous item if any. - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let item = match self.as_mut().state().as_pin_mut() { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + let item = match this.state.as_mut().as_pin_mut() { None => return Poll::Ready(Ok(())), Some(fut) => ready!(fut.poll(cx))?, }; - self.as_mut().state().set(None); - self.as_mut().sink().start_send(item)?; + this.state.set(None); + this.sink.start_send(item)?; Poll::Ready(Ok(())) } } impl Sink for With - where Si: Sink, - F: FnMut(U) -> Fut, - Fut: Future>, - E: From, +where + Si: Sink, + F: FnMut(U) -> Fut, + Fut: Future>, + E: From, { type Error = E; - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_ready(cx)?); + ready!(self.project().sink.poll_ready(cx)?); Poll::Ready(Ok(())) } - fn start_send( - mut self: Pin<&mut Self>, - item: U, - ) -> Result<(), Self::Error> { - let future = (self.as_mut().f())(item); - self.as_mut().state().set(Some(future)); + fn start_send(self: Pin<&mut Self>, item: U) -> Result<(), Self::Error> { + let mut this = self.project(); + + assert!(this.state.is_none()); + this.state.set(Some((this.f)(item))); Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_flush(cx))?; + ready!(self.project().sink.poll_flush(cx)?); Poll::Ready(Ok(())) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_close(cx))?; + ready!(self.project().sink.poll_close(cx)?); Poll::Ready(Ok(())) } } diff --git a/futures-util/src/sink/with_flat_map.rs b/futures-util/src/sink/with_flat_map.rs index 05ab77e7b1..2ae877a24b 100644 --- a/futures-util/src/sink/with_flat_map.rs +++ b/futures-util/src/sink/with_flat_map.rs @@ -1,27 +1,26 @@ use core::fmt; use core::marker::PhantomData; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Sink for the [`with_flat_map`](super::SinkExt::with_flat_map) method. -#[must_use = "sinks do nothing unless polled"] -pub struct WithFlatMap { - sink: Si, - f: F, - stream: Option, - buffer: Option, - _marker: PhantomData, +use pin_project_lite::pin_project; + +pin_project! { + /// Sink for the [`with_flat_map`](super::SinkExt::with_flat_map) method. + #[must_use = "sinks do nothing unless polled"] + pub struct WithFlatMap { + #[pin] + sink: Si, + f: F, + #[pin] + stream: Option, + buffer: Option, + _marker: PhantomData, + } } -impl Unpin for WithFlatMap -where - Si: Unpin, - St: Unpin, -{} - impl fmt::Debug for WithFlatMap where Si: fmt::Debug, @@ -43,69 +42,32 @@ where F: FnMut(U) -> St, St: Stream>, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(stream: Option); - pub(super) fn new(sink: Si, f: F) -> Self { - WithFlatMap { - sink, - f, - stream: None, - buffer: None, - _marker: PhantomData, - } + Self { sink, f, stream: None, buffer: None, _marker: PhantomData } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } + delegate_access_inner!(sink, Si, ()); - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } + fn try_empty_stream(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } - - fn try_empty_stream( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let WithFlatMap { sink, stream, buffer, .. } = - unsafe { self.get_unchecked_mut() }; - let mut sink = unsafe { Pin::new_unchecked(sink) }; - let mut stream = unsafe { Pin::new_unchecked(stream) }; - - if buffer.is_some() { - ready!(sink.as_mut().poll_ready(cx))?; - let item = buffer.take().unwrap(); - sink.as_mut().start_send(item)?; + if this.buffer.is_some() { + ready!(this.sink.as_mut().poll_ready(cx))?; + let item = this.buffer.take().unwrap(); + this.sink.as_mut().start_send(item)?; } - if let Some(mut some_stream) = stream.as_mut().as_pin_mut() { + if let Some(mut some_stream) = this.stream.as_mut().as_pin_mut() { while let Some(item) = ready!(some_stream.as_mut().poll_next(cx)?) { - match sink.as_mut().poll_ready(cx)? { - Poll::Ready(()) => sink.as_mut().start_send(item)?, + match this.sink.as_mut().poll_ready(cx)? { + Poll::Ready(()) => this.sink.as_mut().start_send(item)?, Poll::Pending => { - *buffer = Some(item); + *this.buffer = Some(item); return Poll::Pending; } }; } } - stream.set(None); + this.stream.set(None); Poll::Ready(Ok(())) } } @@ -119,16 +81,7 @@ where { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for WithFlatMap @@ -150,36 +103,25 @@ where { type Error = Si::Error; - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.try_empty_stream(cx) } - fn start_send( - mut self: Pin<&mut Self>, - item: U, - ) -> Result<(), Self::Error> { - assert!(self.stream.is_none()); - let stream = (self.as_mut().f())(item); - self.stream().set(Some(stream)); + fn start_send(self: Pin<&mut Self>, item: U) -> Result<(), Self::Error> { + let mut this = self.project(); + + assert!(this.stream.is_none()); + this.stream.set(Some((this.f)(item))); Ok(()) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_flush(cx) + self.project().sink.poll_flush(cx) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_close(cx) + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/stream/abortable.rs b/futures-util/src/stream/abortable.rs new file mode 100644 index 0000000000..1fea895822 --- /dev/null +++ b/futures-util/src/stream/abortable.rs @@ -0,0 +1,19 @@ +use super::assert_stream; +use crate::stream::{AbortHandle, Abortable}; +use crate::Stream; + +/// Creates a new `Abortable` stream and an `AbortHandle` which can be used to stop it. +/// +/// This function is a convenient (but less flexible) alternative to calling +/// `AbortHandle::new` and `Abortable::new` manually. +/// +/// This function is only available when the `std` or `alloc` feature of this +/// library is activated, and it is activated by default. +pub fn abortable(stream: St) -> (Abortable, AbortHandle) +where + St: Stream, +{ + let (handle, reg) = AbortHandle::new_pair(); + let abortable = assert_stream::(Abortable::new(stream, reg)); + (abortable, handle) +} diff --git a/futures-util/src/stream/empty.rs b/futures-util/src/stream/empty.rs index 903af6851d..e4fd87326b 100644 --- a/futures-util/src/stream/empty.rs +++ b/futures-util/src/stream/empty.rs @@ -1,3 +1,4 @@ +use super::assert_stream; use core::marker::PhantomData; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; @@ -7,16 +8,14 @@ use futures_core::task::{Context, Poll}; #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Empty { - _phantom: PhantomData + _phantom: PhantomData, } /// Creates a stream which contains no elements. /// /// The returned stream will always return `Ready(None)` when polled. pub fn empty() -> Empty { - Empty { - _phantom: PhantomData - } + assert_stream::(Empty { _phantom: PhantomData }) } impl Unpin for Empty {} @@ -38,3 +37,9 @@ impl Stream for Empty { (0, Some(0)) } } + +impl Clone for Empty { + fn clone(&self) -> Self { + empty() + } +} diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index a30cbaa40f..f596b3b0e3 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -1,19 +1,26 @@ use crate::stream::{FuturesUnordered, StreamExt}; -use futures_core::future::Future; -use futures_core::stream::Stream; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use alloc::collections::binary_heap::{BinaryHeap, PeekMut}; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::pin::Pin; -use alloc::collections::binary_heap::{BinaryHeap, PeekMut}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -struct OrderWrapper { - data: T, // A future or a future's output - index: usize, +use futures_core::future::Future; +use futures_core::ready; +use futures_core::stream::Stream; +use futures_core::{ + task::{Context, Poll}, + FusedStream, +}; +use pin_project_lite::pin_project; + +pin_project! { + #[must_use = "futures do nothing unless you `.await` or poll them"] + #[derive(Debug)] + struct OrderWrapper { + #[pin] + data: T, // A future or a future's output + index: usize, + } } impl PartialEq for OrderWrapper { @@ -37,21 +44,15 @@ impl Ord for OrderWrapper { } } -impl OrderWrapper { - unsafe_pinned!(data: T); -} - impl Future for OrderWrapper - where T: Future +where + T: Future, { type Output = OrderWrapper; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut().data().as_mut().poll(cx) - .map(|output| OrderWrapper { data: output, index: self.index }) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let index = self.index; + self.project().data.poll(cx).map(|output| OrderWrapper { data: output, index }) } } @@ -105,8 +106,8 @@ impl FuturesOrdered { /// /// The returned `FuturesOrdered` does not contain any futures and, in this /// state, `FuturesOrdered::poll_next` will return `Poll::Ready(None)`. - pub fn new() -> FuturesOrdered { - FuturesOrdered { + pub fn new() -> Self { + Self { in_progress_queue: FuturesUnordered::new(), queued_outputs: BinaryHeap::new(), next_incoming_index: 0, @@ -135,28 +136,22 @@ impl FuturesOrdered { /// must ensure that `FuturesOrdered::poll` is called in order to receive /// task notifications. pub fn push(&mut self, future: Fut) { - let wrapped = OrderWrapper { - data: future, - index: self.next_incoming_index, - }; + let wrapped = OrderWrapper { data: future, index: self.next_incoming_index }; self.next_incoming_index += 1; self.in_progress_queue.push(wrapped); } } impl Default for FuturesOrdered { - fn default() -> FuturesOrdered { - FuturesOrdered::new() + fn default() -> Self { + Self::new() } } impl Stream for FuturesOrdered { type Item = Fut::Output; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_> - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = &mut *self; // Check to see if we've already received the next value @@ -199,7 +194,27 @@ impl FromIterator for FuturesOrdered { where T: IntoIterator, { - let acc = FuturesOrdered::new(); - iter.into_iter().fold(acc, |mut acc, item| { acc.push(item); acc }) + let acc = Self::new(); + iter.into_iter().fold(acc, |mut acc, item| { + acc.push(item); + acc + }) + } +} + +impl FusedStream for FuturesOrdered { + fn is_terminated(&self) -> bool { + self.in_progress_queue.is_terminated() && self.queued_outputs.is_empty() + } +} + +impl Extend for FuturesOrdered { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.push(item); + } } } diff --git a/futures-util/src/stream/futures_unordered/iter.rs b/futures-util/src/stream/futures_unordered/iter.rs index ef7b15aed8..04db5ee753 100644 --- a/futures-util/src/stream/futures_unordered/iter.rs +++ b/futures-util/src/stream/futures_unordered/iter.rs @@ -1,41 +1,83 @@ -use super::FuturesUnordered; use super::task::Task; +use super::FuturesUnordered; use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::Ordering::Relaxed; -#[derive(Debug)] /// Mutable iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IterPinMut<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, - pub(super) _marker: PhantomData<&'a mut FuturesUnordered> + pub(super) _marker: PhantomData<&'a mut FuturesUnordered>, } -#[derive(Debug)] /// Mutable iterator over all futures in the unordered set. -pub struct IterMut<'a, Fut: Unpin> (pub(super) IterPinMut<'a, Fut>); - #[derive(Debug)] +pub struct IterMut<'a, Fut: Unpin>(pub(super) IterPinMut<'a, Fut>); + /// Immutable iterator over all futures in the unordered set. +#[derive(Debug)] pub struct IterPinRef<'a, Fut> { pub(super) task: *const Task, pub(super) len: usize, pub(super) pending_next_all: *mut Task, - pub(super) _marker: PhantomData<&'a FuturesUnordered> + pub(super) _marker: PhantomData<&'a FuturesUnordered>, } -#[derive(Debug)] /// Immutable iterator over all the futures in the unordered set. -pub struct Iter<'a, Fut: Unpin> (pub(super) IterPinRef<'a, Fut>); +#[derive(Debug)] +pub struct Iter<'a, Fut: Unpin>(pub(super) IterPinRef<'a, Fut>); + +/// Owned iterator over all futures in the unordered set. +#[derive(Debug)] +pub struct IntoIter { + pub(super) len: usize, + pub(super) inner: FuturesUnordered, +} + +impl Iterator for IntoIter { + type Item = Fut; + + fn next(&mut self) -> Option { + // `head_all` can be accessed directly and we don't need to spin on + // `Task::next_all` since we have exclusive access to the set. + let task = self.inner.head_all.get_mut(); + + if (*task).is_null() { + return None; + } + + unsafe { + // Moving out of the future is safe because it is `Unpin` + let future = (*(**task).future.get()).take().unwrap(); + + // Mutable access to a previously shared `FuturesUnordered` implies + // that the other threads already released the object before the + // current thread acquired it, so relaxed ordering can be used and + // valid `next_all` checks can be skipped. + let next = (**task).next_all.load(Relaxed); + *task = next; + self.len -= 1; + Some(future) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl ExactSizeIterator for IntoIter {} impl<'a, Fut> Iterator for IterPinMut<'a, Fut> { type Item = Pin<&'a mut Fut>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.task.is_null() { return None; } + unsafe { let future = (*(*self.task).future.get()).as_mut().unwrap(); @@ -60,7 +102,7 @@ impl ExactSizeIterator for IterPinMut<'_, Fut> {} impl<'a, Fut: Unpin> Iterator for IterMut<'a, Fut> { type Item = &'a mut Fut; - fn next(&mut self) -> Option<&'a mut Fut> { + fn next(&mut self) -> Option { self.0.next().map(Pin::get_mut) } @@ -74,10 +116,11 @@ impl ExactSizeIterator for IterMut<'_, Fut> {} impl<'a, Fut> Iterator for IterPinRef<'a, Fut> { type Item = Pin<&'a Fut>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.task.is_null() { return None; } + unsafe { let future = (*(*self.task).future.get()).as_ref().unwrap(); @@ -85,10 +128,7 @@ impl<'a, Fut> Iterator for IterPinRef<'a, Fut> { // `head_all` was initially read for this iterator implies acquire // ordering for all previously inserted nodes (and we don't need to // read `len_all` again for any other nodes). - let next = (*self.task).spin_next_all( - self.pending_next_all, - Relaxed, - ); + let next = (*self.task).spin_next_all(self.pending_next_all, Relaxed); self.task = next; self.len -= 1; Some(Pin::new_unchecked(future)) @@ -105,7 +145,7 @@ impl ExactSizeIterator for IterPinRef<'_, Fut> {} impl<'a, Fut: Unpin> Iterator for Iter<'a, Fut> { type Item = &'a Fut; - fn next(&mut self) -> Option<&'a Fut> { + fn next(&mut self) -> Option { self.0.next().map(Pin::get_ref) } @@ -115,3 +155,14 @@ impl<'a, Fut: Unpin> Iterator for Iter<'a, Fut> { } impl ExactSizeIterator for Iter<'_, Fut> {} + +// SAFETY: we do nothing thread-local and there is no interior mutability, +// so the usual structural `Send`/`Sync` apply. +unsafe impl Send for IterPinRef<'_, Fut> {} +unsafe impl Sync for IterPinRef<'_, Fut> {} + +unsafe impl Send for IterPinMut<'_, Fut> {} +unsafe impl Sync for IterPinMut<'_, Fut> {} + +unsafe impl Send for IntoIter {} +unsafe impl Sync for IntoIter {} diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs index 5d93a646a0..fdbd53de8e 100644 --- a/futures-util/src/stream/futures_unordered/mod.rs +++ b/futures-util/src/stream/futures_unordered/mod.rs @@ -3,11 +3,8 @@ //! This module is only available when the `std` or `alloc` feature of this //! library is activated, and it is activated by default. -use futures_core::future::Future; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -use futures_task::{FutureObj, LocalFutureObj, Spawn, LocalSpawn, SpawnError}; use crate::task::AtomicWaker; +use alloc::sync::{Arc, Weak}; use core::cell::UnsafeCell; use core::fmt::{self, Debug}; use core::iter::FromIterator; @@ -16,36 +13,22 @@ use core::mem; use core::pin::Pin; use core::ptr; use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst}; -use core::sync::atomic::{AtomicPtr, AtomicBool}; -use alloc::sync::{Arc, Weak}; +use core::sync::atomic::{AtomicBool, AtomicPtr}; +use futures_core::future::Future; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError}; mod abort; mod iter; -pub use self::iter::{Iter, IterMut, IterPinMut, IterPinRef}; +pub use self::iter::{IntoIter, Iter, IterMut, IterPinMut, IterPinRef}; mod task; use self::task::Task; mod ready_to_run_queue; -use self::ready_to_run_queue::{ReadyToRunQueue, Dequeue}; - -/// 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. -const YIELD_EVERY: usize = 32; +use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue}; /// A set of futures which may complete in any order. /// @@ -79,18 +62,14 @@ unsafe impl Sync for FuturesUnordered {} impl Unpin for FuturesUnordered {} impl Spawn for FuturesUnordered> { - fn spawn_obj(&self, future_obj: FutureObj<'static, ()>) - -> Result<(), SpawnError> - { + fn spawn_obj(&self, future_obj: FutureObj<'static, ()>) -> Result<(), SpawnError> { self.push(future_obj); Ok(()) } } impl LocalSpawn for FuturesUnordered> { - fn spawn_local_obj(&self, future_obj: LocalFutureObj<'static, ()>) - -> Result<(), SpawnError> - { + fn spawn_local_obj(&self, future_obj: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { self.push(future_obj); Ok(()) } @@ -118,16 +97,22 @@ 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 FuturesUnordered { +impl Default for FuturesUnordered { + fn default() -> Self { + Self::new() + } +} + +impl FuturesUnordered { /// Constructs a new, empty [`FuturesUnordered`]. /// /// The returned [`FuturesUnordered`] does not contain any futures. /// In this state, [`FuturesUnordered::poll_next`](Stream::poll_next) will /// return [`Poll::Ready(None)`](Poll::Ready). - pub fn new() -> FuturesUnordered { + pub fn new() -> Self { let stub = Arc::new(Task { future: UnsafeCell::new(None), next_all: AtomicPtr::new(ptr::null_mut()), @@ -136,8 +121,9 @@ 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 = &*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 _), @@ -145,21 +131,13 @@ impl FuturesUnordered { stub, }); - FuturesUnordered { + Self { head_all: AtomicPtr::new(ptr::null_mut()), ready_to_run_queue, is_terminated: AtomicBool::new(false), } } -} -impl Default for FuturesUnordered { - fn default() -> FuturesUnordered { - FuturesUnordered::new() - } -} - -impl FuturesUnordered { /// Returns the number of futures contained in the set. /// /// This represents the total number of in-flight futures. @@ -190,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 @@ -209,24 +188,26 @@ impl FuturesUnordered { } /// Returns an iterator that allows inspecting each future in the set. - pub fn iter(&self) -> Iter<'_, Fut> where Fut: Unpin { + pub fn iter(&self) -> Iter<'_, Fut> + where + Fut: Unpin, + { Iter(Pin::new(self).iter_pin_ref()) } /// Returns an iterator that allows inspecting each future in the set. - fn iter_pin_ref(self: Pin<&Self>) -> IterPinRef<'_, Fut> { + pub fn iter_pin_ref(self: Pin<&Self>) -> IterPinRef<'_, Fut> { let (task, len) = self.atomic_load_head_and_len_all(); + let pending_next_all = self.pending_next_all(); - IterPinRef { - task, - len, - pending_next_all: self.pending_next_all(), - _marker: PhantomData, - } + IterPinRef { task, len, pending_next_all, _marker: PhantomData } } /// Returns an iterator that allows modifying each future in the set. - pub fn iter_mut(&mut self) -> IterMut<'_, Fut> where Fut: Unpin { + pub fn iter_mut(&mut self) -> IterMut<'_, Fut> + where + Fut: Unpin, + { IterMut(Pin::new(self).iter_pin_mut()) } @@ -235,19 +216,9 @@ impl FuturesUnordered { // `head_all` can be accessed directly and we don't need to spin on // `Task::next_all` since we have exclusive access to the set. let task = *self.head_all.get_mut(); - let len = if task.is_null() { - 0 - } else { - unsafe { - *(*task).len_all.get() - } - }; + let len = if task.is_null() { 0 } else { unsafe { *(*task).len_all.get() } }; - IterPinMut { - task, - len, - _marker: PhantomData - } + IterPinMut { task, len, _marker: PhantomData } } /// Returns the current head node and number of futures in the list of all @@ -267,7 +238,7 @@ impl FuturesUnordered { (task, len) } - /// Releases the task. It destorys the future inside and either drops + /// Releases the task. It destroys the future inside and either drops /// the `Arc` or transfers ownership to the ready to run queue. /// The task this method is called on must have been unlinked before. fn release_task(&mut self, task: Arc>) { @@ -406,19 +377,20 @@ 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 _ } } impl Stream for FuturesUnordered { type Item = Fut::Output; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + 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()); @@ -471,14 +443,11 @@ impl Stream for FuturesUnordered { // Double check that the call to `release_task` really // happened. Calling it required the task to be unlinked. - debug_assert_eq!( - task.next_all.load(Relaxed), - self.pending_next_all() - ); + debug_assert_eq!(task.next_all.load(Relaxed), self.pending_next_all()); unsafe { debug_assert!((*task.prev_all.get()).is_null()); } - continue + continue; } }; @@ -518,10 +487,7 @@ impl Stream for FuturesUnordered { } } - let mut bomb = Bomb { - task: Some(task), - queue: &mut *self, - }; + let mut bomb = Bomb { task: Some(task), queue: &mut *self }; // Poll the underlying future with the appropriate waker // implementation. This is where a large bit of the unsafety @@ -535,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 @@ -548,20 +518,23 @@ 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; } - continue - } - Poll::Ready(output) => { - return Poll::Ready(Some(output)) + continue; } + Poll::Ready(output) => return Poll::Ready(Some(output)), } } } @@ -578,19 +551,33 @@ impl Debug for FuturesUnordered { } } +impl FuturesUnordered { + /// Clears the set, removing all futures. + pub fn clear(&mut self) { + self.clear_head_all(); + + // we just cleared all the tasks, and we have &mut self, so this is safe. + unsafe { self.ready_to_run_queue.clear() }; + + self.is_terminated.store(false, Relaxed); + } + + fn clear_head_all(&mut self) { + while !self.head_all.get_mut().is_null() { + let head = *self.head_all.get_mut(); + let task = unsafe { self.unlink(head) }; + self.release_task(task); + } + } +} + impl Drop for FuturesUnordered { fn drop(&mut self) { // When a `FuturesUnordered` is dropped we want to drop all futures // associated with it. At the same time though there may be tons of // wakers flying around which contain `Task` references // inside them. We'll let those naturally get deallocated. - unsafe { - while !self.head_all.get_mut().is_null() { - let head = *self.head_all.get_mut(); - let task = self.unlink(head); - self.release_task(task); - } - } + self.clear_head_all(); // Note that at this point we could still have a bunch of tasks in the // ready to run queue. None of those tasks, however, have futures @@ -607,13 +594,48 @@ impl Drop for FuturesUnordered { } } -impl FromIterator for FuturesUnordered { +impl<'a, Fut: Unpin> IntoIterator for &'a FuturesUnordered { + type Item = &'a Fut; + type IntoIter = Iter<'a, Fut>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, Fut: Unpin> IntoIterator for &'a mut FuturesUnordered { + type Item = &'a mut Fut; + type IntoIter = IterMut<'a, Fut>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl IntoIterator for FuturesUnordered { + type Item = Fut; + type IntoIter = IntoIter; + + fn into_iter(mut self) -> Self::IntoIter { + // `head_all` can be accessed directly and we don't need to spin on + // `Task::next_all` since we have exclusive access to the set. + let task = *self.head_all.get_mut(); + let len = if task.is_null() { 0 } else { unsafe { *(*task).len_all.get() } }; + + IntoIter { len, inner: self } + } +} + +impl FromIterator for FuturesUnordered { fn from_iter(iter: I) -> Self where I: IntoIterator, { - let acc = FuturesUnordered::new(); - iter.into_iter().fold(acc, |acc, item| { acc.push(item); acc }) + let acc = Self::new(); + iter.into_iter().fold(acc, |acc, item| { + acc.push(item); + acc + }) } } @@ -622,3 +644,14 @@ impl FusedStream for FuturesUnordered { self.is_terminated.load(Relaxed) } } + +impl Extend for FuturesUnordered { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.push(item); + } + } +} diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs index 210519585e..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 @@ -1,9 +1,9 @@ use crate::task::AtomicWaker; +use alloc::sync::Arc; use core::cell::UnsafeCell; use core::ptr; use core::sync::atomic::AtomicPtr; -use core::sync::atomic::Ordering::{Relaxed, Acquire, Release, AcqRel}; -use alloc::sync::Arc; +use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use super::abort::abort; use super::task::Task; @@ -83,7 +83,28 @@ impl ReadyToRunQueue { } pub(super) fn stub(&self) -> *const Task { - &*self.stub + Arc::as_ptr(&self.stub) + } + + // Clear the queue of tasks. + // + // Note that each task has a strong reference count associated with it + // which is owned by the ready to run queue. This method just pulls out + // tasks and drops their refcounts. + // + // # Safety + // + // - All tasks **must** have had their futures dropped already (by FuturesUnordered::clear) + // - The caller **must** guarantee unique access to `self` + pub(crate) unsafe fn clear(&self) { + loop { + // SAFETY: We have the guarantee of mutual exclusion required by `dequeue`. + match self.dequeue() { + Dequeue::Empty => break, + Dequeue::Inconsistent => abort("inconsistent in drop"), + Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), + } + } } } @@ -91,19 +112,11 @@ impl Drop for ReadyToRunQueue { fn drop(&mut self) { // Once we're in the destructor for `Inner` we need to clear out // the ready to run queue of tasks if there's anything left in there. - // - // Note that each task has a strong reference count associated with it - // which is owned by the ready to run queue. All tasks should have had - // their futures dropped already by the `FuturesUnordered` destructor - // above, so we're just pulling out tasks and dropping their refcounts. + + // All tasks have had their futures dropped already by the `FuturesUnordered` + // destructor above, and we have &mut self, so this is safe. unsafe { - loop { - match self.dequeue() { - Dequeue::Empty => break, - Dequeue::Inconsistent => abort("inconsistent in drop"), - Dequeue::Data(ptr) => drop(Arc::from_raw(ptr)), - } - } + self.clear(); } } } diff --git a/futures-util/src/stream/futures_unordered/task.rs b/futures-util/src/stream/futures_unordered/task.rs index 9277229223..ec2114effa 100644 --- a/futures-util/src/stream/futures_unordered/task.rs +++ b/futures-util/src/stream/futures_unordered/task.rs @@ -1,11 +1,11 @@ -use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicPtr, AtomicBool}; -use core::sync::atomic::Ordering::{self, SeqCst}; use alloc::sync::{Arc, Weak}; +use core::cell::UnsafeCell; +use core::sync::atomic::Ordering::{self, Relaxed, SeqCst}; +use core::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::task::{ArcWake, WakerRef, waker_ref}; -use super::ReadyToRunQueue; use super::abort::abort; +use super::ReadyToRunQueue; +use crate::task::{waker_ref, ArcWake, WakerRef}; pub(super) struct Task { // The future @@ -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. @@ -62,7 +69,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(); } } @@ -70,7 +77,7 @@ impl ArcWake for Task { impl Task { /// Returns a waker reference for this task without cloning the Arc. - pub(super) fn waker_ref<'a>(this: &'a Arc>) -> WakerRef<'a> { + pub(super) fn waker_ref(this: &Arc) -> WakerRef<'_> { waker_ref(this) } diff --git a/futures-util/src/stream/iter.rs b/futures-util/src/stream/iter.rs index e9df81cef0..20471c2ed0 100644 --- a/futures-util/src/stream/iter.rs +++ b/futures-util/src/stream/iter.rs @@ -1,9 +1,10 @@ +use super::assert_stream; use core::pin::Pin; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; /// Stream for the [`iter`] function. -#[derive(Debug)] +#[derive(Debug, Clone)] #[must_use = "streams do nothing unless polled"] pub struct Iter { iter: I, @@ -26,15 +27,15 @@ impl Unpin for Iter {} /// # }); /// ``` pub fn iter(i: I) -> Iter - where I: IntoIterator, +where + I: IntoIterator, { - Iter { - iter: i.into_iter(), - } + assert_stream::(Iter { iter: i.into_iter() }) } impl Stream for Iter - where I: Iterator, +where + I: Iterator, { type Item = I::Item; diff --git a/futures-util/src/stream/mod.rs b/futures-util/src/stream/mod.rs index ba3575bf02..ec685b9848 100644 --- a/futures-util/src/stream/mod.rs +++ b/futures-util/src/stream/mod.rs @@ -1,8 +1,13 @@ -//! Streams +//! Asynchronous streams. //! -//! This module contains a number of functions for working with `Stream`s, -//! including the [`StreamExt`] trait and the [`TryStreamExt`] trait which add -//! methods to `Stream` types +//! This module contains: +//! +//! - The [`Stream`] trait, for objects that can asynchronously produce a +//! sequence of values. +//! - The [`StreamExt`] and [`TryStreamExt`] trait, which provides adapters for +//! chaining and composing streams. +//! - Top-level stream constructors like [`iter`](iter()) which creates a +//! stream from an iterator. #[cfg(feature = "alloc")] pub use futures_core::stream::{BoxStream, LocalBoxStream}; @@ -13,9 +18,9 @@ pub use futures_core::stream::{FusedStream, Stream, TryStream}; #[allow(clippy::module_inception)] mod stream; pub use self::stream::{ - Chain, Collect, Concat, Enumerate, Filter, FilterMap, Flatten, Fold, ForEach, Fuse, Inspect, - Map, Next, Peek, Peekable, Scan, SelectNextSome, Skip, SkipWhile, StreamExt, StreamFuture, Take, - TakeWhile, Then, Zip, + Chain, Collect, Concat, Cycle, Enumerate, Filter, FilterMap, FlatMap, Flatten, Fold, ForEach, + Fuse, Inspect, Map, Next, NextIf, NextIfEq, Peek, PeekMut, Peekable, Scan, SelectNextSome, + Skip, SkipWhile, StreamExt, StreamFuture, Take, TakeUntil, TakeWhile, Then, Unzip, Zip, }; #[cfg(feature = "std")] @@ -24,15 +29,20 @@ pub use self::stream::CatchUnwind; #[cfg(feature = "alloc")] pub use self::stream::Chunks; +#[cfg(feature = "alloc")] +pub use self::stream::ReadyChunks; + #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub use self::stream::Forward; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] pub use self::stream::{BufferUnordered, Buffered, ForEachConcurrent}; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "sink")] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] #[cfg(feature = "alloc")] pub use self::stream::{ReuniteError, SplitSink, SplitStream}; @@ -40,16 +50,20 @@ mod try_stream; pub use self::try_stream::{ try_unfold, AndThen, ErrInto, InspectErr, InspectOk, IntoStream, MapErr, MapOk, OrElse, TryCollect, TryConcat, TryFilter, TryFilterMap, TryFlatten, TryFold, TryForEach, TryNext, - TrySkipWhile, TryStreamExt, TryUnfold, + TrySkipWhile, TryStreamExt, TryTakeWhile, TryUnfold, }; #[cfg(feature = "io")] +#[cfg_attr(docsrs, doc(cfg(feature = "io")))] #[cfg(feature = "std")] pub use self::try_stream::IntoAsyncRead; -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use self::try_stream::{TryBufferUnordered, TryBuffered, TryForEachConcurrent}; + #[cfg(feature = "alloc")] -pub use self::try_stream::{TryBufferUnordered, TryForEachConcurrent}; +pub use self::try_stream::{TryChunks, TryChunksError}; // Primitive streams @@ -59,6 +73,9 @@ pub use self::iter::{iter, Iter}; mod repeat; pub use self::repeat::{repeat, Repeat}; +mod repeat_with; +pub use self::repeat_with::{repeat_with, RepeatWith}; + mod empty; pub use self::empty::{empty, Empty}; @@ -71,26 +88,56 @@ pub use self::pending::{pending, Pending}; mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; +mod poll_immediate; +pub use self::poll_immediate::{poll_immediate, PollImmediate}; + mod select; pub use self::select::{select, Select}; +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")] - mod select_all; - #[cfg(feature = "alloc")] - pub use self::select_all::{select_all, SelectAll}; +#[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. +pub(crate) fn assert_stream(stream: S) -> S +where + S: Stream, +{ + stream } diff --git a/futures-util/src/stream/once.rs b/futures-util/src/stream/once.rs index 4f68b0cedd..ee21c8b594 100644 --- a/futures-util/src/stream/once.rs +++ b/futures-util/src/stream/once.rs @@ -1,8 +1,10 @@ +use super::assert_stream; use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; /// Creates a stream of a single element. /// @@ -16,34 +18,36 @@ use pin_utils::unsafe_pinned; /// # }); /// ``` pub fn once(future: Fut) -> Once { - Once { future: Some(future) } + assert_stream::(Once::new(future)) } -/// A stream which emits single element and then EOF. -/// -/// This stream will never block and is always ready. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Once { - future: Option +pin_project! { + /// A stream which emits single element and then EOF. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Once { + #[pin] + future: Option + } } -impl Unpin for Once {} - impl Once { - unsafe_pinned!(future: Option); + pub(crate) fn new(future: Fut) -> Self { + Self { future: Some(future) } + } } impl Stream for Once { type Item = Fut::Output; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let v = match self.as_mut().future().as_pin_mut() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let v = match this.future.as_mut().as_pin_mut() { Some(fut) => ready!(fut.poll(cx)), None => return Poll::Ready(None), }; - self.as_mut().future().set(None); + this.future.set(None); Poll::Ready(Some(v)) } diff --git a/futures-util/src/stream/pending.rs b/futures-util/src/stream/pending.rs index bbe07504b7..d7030ff3cc 100644 --- a/futures-util/src/stream/pending.rs +++ b/futures-util/src/stream/pending.rs @@ -1,3 +1,4 @@ +use super::assert_stream; use core::marker; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; @@ -14,7 +15,7 @@ pub struct Pending { /// /// The returned stream will always return `Pending` when polled. pub fn pending() -> Pending { - Pending { _data: marker::PhantomData } + assert_stream::(Pending { _data: marker::PhantomData }) } impl Unpin for Pending {} @@ -36,3 +37,9 @@ impl Stream for Pending { (0, Some(0)) } } + +impl Clone for Pending { + fn clone(&self) -> Self { + pending() + } +} diff --git a/futures-util/src/stream/poll_fn.rs b/futures-util/src/stream/poll_fn.rs index e33ca577ae..b9bd7d1664 100644 --- a/futures-util/src/stream/poll_fn.rs +++ b/futures-util/src/stream/poll_fn.rs @@ -1,5 +1,6 @@ //! Definition of the `PollFn` combinator +use super::assert_stream; use core::fmt; use core::pin::Pin; use futures_core::stream::Stream; @@ -41,7 +42,7 @@ pub fn poll_fn(f: F) -> PollFn where F: FnMut(&mut Context<'_>) -> Poll>, { - PollFn { f } + assert_stream::(PollFn { f }) } impl Stream for PollFn diff --git a/futures-util/src/stream/poll_immediate.rs b/futures-util/src/stream/poll_immediate.rs new file mode 100644 index 0000000000..c7e8a5b3c6 --- /dev/null +++ b/futures-util/src/stream/poll_immediate.rs @@ -0,0 +1,80 @@ +use core::pin::Pin; +use futures_core::task::{Context, Poll}; +use futures_core::Stream; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [poll_immediate](poll_immediate()) function. + /// + /// It will never return [Poll::Pending](core::task::Poll::Pending) + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct PollImmediate { + #[pin] + stream: Option + } +} + +impl Stream for PollImmediate +where + S: Stream, +{ + type Item = Poll; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let stream = match this.stream.as_mut().as_pin_mut() { + // inner is gone, so we can continue to signal that the stream is closed. + None => return Poll::Ready(None), + Some(inner) => inner, + }; + + match stream.poll_next(cx) { + Poll::Ready(Some(t)) => Poll::Ready(Some(Poll::Ready(t))), + Poll::Ready(None) => { + this.stream.set(None); + Poll::Ready(None) + } + Poll::Pending => Poll::Ready(Some(Poll::Pending)), + } + } + + fn size_hint(&self) -> (usize, Option) { + self.stream.as_ref().map_or((0, Some(0)), Stream::size_hint) + } +} + +impl super::FusedStream for PollImmediate { + fn is_terminated(&self) -> bool { + self.stream.is_none() + } +} + +/// Creates a new stream that always immediately returns [Poll::Ready](core::task::Poll::Ready) when awaiting it. +/// +/// This is useful when immediacy is more important than waiting for the next item to be ready. +/// +/// # Examples +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::stream::{self, StreamExt}; +/// use futures::task::Poll; +/// +/// let mut r = stream::poll_immediate(Box::pin(stream::iter(1_u32..3))); +/// assert_eq!(r.next().await, Some(Poll::Ready(1))); +/// assert_eq!(r.next().await, Some(Poll::Ready(2))); +/// assert_eq!(r.next().await, None); +/// +/// let mut p = stream::poll_immediate(Box::pin(stream::once(async { +/// futures::pending!(); +/// 42_u8 +/// }))); +/// assert_eq!(p.next().await, Some(Poll::Pending)); +/// assert_eq!(p.next().await, Some(Poll::Ready(42))); +/// assert_eq!(p.next().await, None); +/// # }); +/// ``` +pub fn poll_immediate(s: S) -> PollImmediate { + super::assert_stream::, PollImmediate>(PollImmediate { stream: Some(s) }) +} diff --git a/futures-util/src/stream/repeat.rs b/futures-util/src/stream/repeat.rs index 21749eb71d..3f9aa87d5c 100644 --- a/futures-util/src/stream/repeat.rs +++ b/futures-util/src/stream/repeat.rs @@ -1,9 +1,10 @@ +use super::assert_stream; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; /// Stream for the [`repeat`] function. -#[derive(Debug)] +#[derive(Debug, Clone)] #[must_use = "streams do nothing unless polled"] pub struct Repeat { item: T, @@ -24,15 +25,17 @@ pub struct Repeat { /// # }); /// ``` pub fn repeat(item: T) -> Repeat - where T: Clone +where + T: Clone, { - Repeat { item } + assert_stream::(Repeat { item }) } impl Unpin for Repeat {} impl Stream for Repeat - where T: Clone +where + T: Clone, { type Item = T; @@ -46,7 +49,8 @@ impl Stream for Repeat } impl FusedStream for Repeat - where T: Clone, +where + T: Clone, { fn is_terminated(&self) -> bool { false diff --git a/futures-util/src/stream/repeat_with.rs b/futures-util/src/stream/repeat_with.rs new file mode 100644 index 0000000000..f5a81b4ed4 --- /dev/null +++ b/futures-util/src/stream/repeat_with.rs @@ -0,0 +1,93 @@ +use super::assert_stream; +use core::pin::Pin; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; + +/// An stream that repeats elements of type `A` endlessly by +/// applying the provided closure `F: FnMut() -> A`. +/// +/// This `struct` is created by the [`repeat_with()`] function. +/// See its documentation for more. +#[derive(Debug, Clone)] +#[must_use = "streams do nothing unless polled"] +pub struct RepeatWith { + repeater: F, +} + +impl A> Unpin for RepeatWith {} + +impl A> Stream for RepeatWith { + type Item = A; + + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Some((&mut self.repeater)())) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::max_value(), None) + } +} + +impl A> FusedStream for RepeatWith { + fn is_terminated(&self) -> bool { + false + } +} + +/// Creates a new stream that repeats elements of type `A` endlessly by +/// applying the provided closure, the repeater, `F: FnMut() -> A`. +/// +/// The `repeat_with()` function calls the repeater over and over again. +/// +/// Infinite stream like `repeat_with()` are often used with adapters like +/// [`stream.take()`], in order to make them finite. +/// +/// If the element type of the stream you need implements [`Clone`], and +/// it is OK to keep the source element in memory, you should instead use +/// the [`stream.repeat()`] function. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # futures::executor::block_on(async { +/// use futures::stream::{self, StreamExt}; +/// +/// // let's assume we have some value of a type that is not `Clone` +/// // or which don't want to have in memory just yet because it is expensive: +/// #[derive(PartialEq, Debug)] +/// struct Expensive; +/// +/// // a particular value forever: +/// let mut things = stream::repeat_with(|| Expensive); +/// +/// assert_eq!(Some(Expensive), things.next().await); +/// assert_eq!(Some(Expensive), things.next().await); +/// assert_eq!(Some(Expensive), things.next().await); +/// # }); +/// ``` +/// +/// Using mutation and going finite: +/// +/// ```rust +/// # futures::executor::block_on(async { +/// use futures::stream::{self, StreamExt}; +/// +/// // From the zeroth to the third power of two: +/// let mut curr = 1; +/// let mut pow2 = stream::repeat_with(|| { let tmp = curr; curr *= 2; tmp }) +/// .take(4); +/// +/// assert_eq!(Some(1), pow2.next().await); +/// assert_eq!(Some(2), pow2.next().await); +/// assert_eq!(Some(4), pow2.next().await); +/// assert_eq!(Some(8), pow2.next().await); +/// +/// // ... and now we're done +/// assert_eq!(None, pow2.next().await); +/// # }); +/// ``` +pub fn repeat_with A>(repeater: F) -> RepeatWith { + assert_stream::(RepeatWith { repeater }) +} diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index b5fb8133b2..0c1e3af782 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -1,45 +1,68 @@ -use crate::stream::{StreamExt, Fuse}; +use super::assert_stream; +use crate::stream::{select_with_strategy, PollNext, SelectWithStrategy}; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; -/// Stream for the [`select()`] function. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Select { - stream1: Fuse, - stream2: Fuse, - flag: bool, +pin_project! { + /// Stream for the [`select()`] function. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Select { + #[pin] + inner: SelectWithStrategy PollNext, PollNext>, + } } -impl Unpin for Select {} - /// This function will attempt to pull items from both streams. Each /// stream will be polled in a round-robin fashion, and whenever a stream is /// ready to yield an item that item is yielded. /// -/// 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 +where + St1: Stream, + St2: Stream, { - Select { - stream1: stream1.fuse(), - stream2: stream2.fuse(), - flag: false, + fn round_robin(last: &mut PollNext) -> PollNext { + last.toggle() } + + assert_stream::(Select { + inner: select_with_strategy(stream1, stream2, round_robin), + }) } 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 @@ -48,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 @@ -57,10 +80,8 @@ impl Select { /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - unsafe { - let Self { stream1, stream2, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(stream1).get_pin_mut(), Pin::new_unchecked(stream2).get_pin_mut()) - } + let this = self.project(); + this.inner.get_pin_mut() } /// Consumes this combinator, returning the underlying streams. @@ -68,65 +89,29 @@ 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() } } impl FusedStream for Select - where St1: Stream, - St2: Stream +where + St1: Stream, + St2: Stream, { fn is_terminated(&self) -> bool { - self.stream1.is_terminated() && self.stream2.is_terminated() + self.inner.is_terminated() } } impl Stream for Select - where St1: Stream, - St2: Stream +where + St1: Stream, + St2: Stream, { type Item = St1::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let Select { flag, stream1, stream2 } = - unsafe { self.get_unchecked_mut() }; - let stream1 = unsafe { Pin::new_unchecked(stream1) }; - let stream2 = unsafe { Pin::new_unchecked(stream2) }; - - if !*flag { - poll_inner(flag, stream1, stream2, cx) - } else { - poll_inner(flag, stream2, 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, + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.inner.poll_next(cx) } } diff --git a/futures-util/src/stream/select_all.rs b/futures-util/src/stream/select_all.rs index d25768904c..3474331adc 100644 --- a/futures-util/src/stream/select_all.rs +++ b/futures-util/src/stream/select_all.rs @@ -4,26 +4,33 @@ use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use crate::stream::{StreamExt, StreamFuture, FuturesUnordered}; +use pin_project_lite::pin_project; -/// An unbounded set of streams -/// -/// This "combinator" provides the ability to maintain a set of streams -/// and drive them all to completion. -/// -/// Streams are pushed into this set and their realized values are -/// yielded as they become ready. Streams will only be polled when they -/// generate notifications. This allows to coordinate a large number of streams. -/// -/// Note that you can create a ready-made `SelectAll` via the -/// `select_all` function in the `stream` module, or you can start with an -/// empty set with the `SelectAll::new` constructor. -#[must_use = "streams do nothing unless polled"] -pub struct SelectAll { - inner: FuturesUnordered>, +use super::assert_stream; +use crate::stream::{futures_unordered, FuturesUnordered, StreamExt, StreamFuture}; + +pin_project! { + /// An unbounded set of streams + /// + /// This "combinator" provides the ability to maintain a set of streams + /// and drive them all to completion. + /// + /// Streams are pushed into this set and their realized values are + /// yielded as they become ready. Streams will only be polled when they + /// generate notifications. This allows to coordinate a large number of streams. + /// + /// Note that you can create a ready-made `SelectAll` via the + /// `select_all` function in the `stream` module, or you can start with an + /// empty set with the `SelectAll::new` constructor. + #[must_use = "streams do nothing unless polled"] + pub struct SelectAll { + #[pin] + inner: FuturesUnordered>, + } } impl Debug for SelectAll { @@ -37,8 +44,8 @@ impl SelectAll { /// /// The returned `SelectAll` does not contain any streams and, in this /// state, `SelectAll::poll` will return `Poll::Ready(None)`. - pub fn new() -> SelectAll { - SelectAll { inner: FuturesUnordered::new() } + pub fn new() -> Self { + Self { inner: FuturesUnordered::new() } } /// Returns the number of streams contained in the set. @@ -62,21 +69,33 @@ impl SelectAll { pub fn push(&mut self, stream: St) { self.inner.push(stream.into_future()); } + + /// Returns an iterator that allows inspecting each stream in the set. + pub fn iter(&self) -> Iter<'_, St> { + Iter(self.inner.iter()) + } + + /// Returns an iterator that allows modifying each stream in the set. + pub fn iter_mut(&mut self) -> IterMut<'_, St> { + IterMut(self.inner.iter_mut()) + } + + /// Clears the set, removing all streams. + pub fn clear(&mut self) { + self.inner.clear() + } } impl Default for SelectAll { - fn default() -> SelectAll { - SelectAll::new() + fn default() -> Self { + Self::new() } } impl Stream for SelectAll { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match ready!(self.inner.poll_next_unpin(cx)) { Some((Some(item), remaining)) => { @@ -109,13 +128,14 @@ impl FusedStream for SelectAll { /// streams internally, in the order they become available. /// /// Note that the returned set can also be used to dynamically push more -/// futures into the set as they become available. +/// streams into the set as they become available. /// /// This function is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. pub fn select_all(streams: I) -> SelectAll - where I: IntoIterator, - I::Item: Stream + Unpin +where + I: IntoIterator, + I::Item: Stream + Unpin, { let mut set = SelectAll::new(); @@ -123,7 +143,7 @@ pub fn select_all(streams: I) -> SelectAll set.push(stream); } - set + assert_stream::<::Item, _>(set) } impl FromIterator for SelectAll { @@ -131,3 +151,104 @@ impl FromIterator for SelectAll { select_all(iter) } } + +impl Extend for SelectAll { + fn extend>(&mut self, iter: T) { + for st in iter { + self.push(st) + } + } +} + +impl IntoIterator for SelectAll { + type Item = St; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter(self.inner.into_iter()) + } +} + +impl<'a, St: Stream + Unpin> IntoIterator for &'a SelectAll { + type Item = &'a St; + type IntoIter = Iter<'a, St>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, St: Stream + Unpin> IntoIterator for &'a mut SelectAll { + type Item = &'a mut St; + type IntoIter = IterMut<'a, St>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +/// Immutable iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct Iter<'a, St: Unpin>(futures_unordered::Iter<'a, StreamFuture>); + +/// Mutable iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct IterMut<'a, St: Unpin>(futures_unordered::IterMut<'a, StreamFuture>); + +/// Owned iterator over all streams in the unordered set. +#[derive(Debug)] +pub struct IntoIter(futures_unordered::IntoIter>); + +impl<'a, St: Stream + Unpin> Iterator for Iter<'a, St> { + type Item = &'a St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.get_ref(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for Iter<'_, St> {} + +impl<'a, St: Stream + Unpin> Iterator for IterMut<'a, St> { + type Item = &'a mut St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.get_mut(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for IterMut<'_, St> {} + +impl Iterator for IntoIter { + type Item = St; + + fn next(&mut self) -> Option { + let st = self.0.next()?; + let next = st.into_inner(); + // This should always be true because FuturesUnordered removes completed futures. + debug_assert!(next.is_some()); + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for IntoIter {} diff --git a/futures-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/buffer_unordered.rs b/futures-util/src/stream/stream/buffer_unordered.rs index bea6e5b4dd..d64c142b41 100644 --- a/futures-util/src/stream/stream/buffer_unordered.rs +++ b/futures-util/src/stream/stream/buffer_unordered.rs @@ -1,30 +1,28 @@ use crate::stream::{Fuse, FuturesUnordered, StreamExt}; +use core::fmt; +use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use core::fmt; -use core::pin::Pin; +use pin_project_lite::pin_project; -/// Stream for the [`buffer_unordered`](super::StreamExt::buffer_unordered) -/// method. -#[must_use = "streams do nothing unless polled"] -pub struct BufferUnordered -where - St: Stream, -{ - stream: Fuse, - in_progress_queue: FuturesUnordered, - max: usize, +pin_project! { + /// Stream for the [`buffer_unordered`](super::StreamExt::buffer_unordered) + /// method. + #[must_use = "streams do nothing unless polled"] + pub struct BufferUnordered + where + St: Stream, + { + #[pin] + stream: Fuse, + in_progress_queue: FuturesUnordered, + max: usize, + } } -impl Unpin for BufferUnordered -where - St: Stream + Unpin, -{} - impl fmt::Debug for BufferUnordered where St: Stream + fmt::Debug, @@ -43,52 +41,19 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesUnordered); - - pub(super) fn new(stream: St, n: usize) -> BufferUnordered + pub(super) fn new(stream: St, n: usize) -> Self where St: Stream, St::Item: Future, { - BufferUnordered { + Self { stream: super::Fuse::new(stream), in_progress_queue: FuturesUnordered::new(), max: n, } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for BufferUnordered @@ -98,27 +63,26 @@ where { type Item = ::Output; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + while this.in_progress_queue.len() < *this.max { + match this.stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match this.in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if this.stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 2445a85c52..6052a737ba 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -1,31 +1,29 @@ use crate::stream::{Fuse, FuturesOrdered, StreamExt}; +use core::fmt; +use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use core::fmt; -use core::pin::Pin; - -/// Stream for the [`buffered`](super::StreamExt::buffered) method. -#[must_use = "streams do nothing unless polled"] -pub struct Buffered -where - St: Stream, - St::Item: Future, -{ - stream: Fuse, - in_progress_queue: FuturesOrdered, - max: usize, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`buffered`](super::StreamExt::buffered) method. + #[must_use = "streams do nothing unless polled"] + pub struct Buffered + where + St: Stream, + St::Item: Future, + { + #[pin] + stream: Fuse, + in_progress_queue: FuturesOrdered, + max: usize, + } } -impl Unpin for Buffered -where - St: Stream + Unpin, - St::Item: Future, -{} - impl fmt::Debug for Buffered where St: Stream + fmt::Debug, @@ -45,48 +43,11 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesOrdered); - - pub(super) fn new(stream: St, n: usize) -> Buffered { - Buffered { - stream: super::Fuse::new(stream), - in_progress_queue: FuturesOrdered::new(), - max: n, - } + pub(super) fn new(stream: St, n: usize) -> Self { + Self { stream: super::Fuse::new(stream), in_progress_queue: FuturesOrdered::new(), max: n } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Buffered @@ -96,27 +57,26 @@ where { type Item = ::Output; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - // Try to spawn off as many futures as possible by filling up - // our in_progress_queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + // First up, try to spawn off as many futures as possible by filling up + // our queue of futures. + while this.in_progress_queue.len() < *this.max { + match this.stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - let res = self.as_mut().in_progress_queue().poll_next_unpin(cx); + let res = this.in_progress_queue.poll_next_unpin(cx); if let Some(val) = ready!(res) { - return Poll::Ready(Some(val)) + return Poll::Ready(Some(val)); } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if this.stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/catch_unwind.rs b/futures-util/src/stream/stream/catch_unwind.rs index 8d2dcf7663..09a6dc1b76 100644 --- a/futures-util/src/stream/stream/catch_unwind.rs +++ b/futures-util/src/stream/stream/catch_unwind.rs @@ -1,47 +1,46 @@ -use futures_core::stream::{Stream, FusedStream}; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; use std::any::Any; +use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe}; use std::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; - -/// Stream for the [`catch_unwind`](super::StreamExt::catch_unwind) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct CatchUnwind { - stream: St, - caught_unwind: bool, + +pin_project! { + /// Stream for the [`catch_unwind`](super::StreamExt::catch_unwind) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct CatchUnwind { + #[pin] + stream: St, + caught_unwind: bool, + } } impl CatchUnwind { - unsafe_pinned!(stream: St); - unsafe_unpinned!(caught_unwind: bool); - - pub(super) fn new(stream: St) -> CatchUnwind { - CatchUnwind { stream, caught_unwind: false } + pub(super) fn new(stream: St) -> Self { + Self { stream, caught_unwind: false } } + + delegate_access_inner!(stream, St, ()); } impl Stream for CatchUnwind { type Item = Result>; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.caught_unwind { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if *this.caught_unwind { Poll::Ready(None) } else { - let res = catch_unwind(AssertUnwindSafe(|| { - self.as_mut().stream().poll_next(cx) - })); + let res = catch_unwind(AssertUnwindSafe(|| this.stream.as_mut().poll_next(cx))); match res { Ok(poll) => poll.map(|opt| opt.map(Ok)), Err(e) => { - *self.as_mut().caught_unwind() = true; + *this.caught_unwind = true; Poll::Ready(Some(Err(e))) - }, + } } } } diff --git a/futures-util/src/stream/stream/chain.rs b/futures-util/src/stream/stream/chain.rs index b2ada69c11..c5da35e25e 100644 --- a/futures-util/src/stream/stream/chain.rs +++ b/futures-util/src/stream/stream/chain.rs @@ -1,35 +1,36 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Stream for the [`chain`](super::StreamExt::chain) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Chain { - first: Option, - second: St2, +pin_project! { + /// Stream for the [`chain`](super::StreamExt::chain) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Chain { + #[pin] + first: Option, + #[pin] + second: St2, + } } // All interactions with `Pin<&mut Chain<..>>` happen through these methods impl Chain -where St1: Stream, - St2: Stream, +where + St1: Stream, + St2: Stream, { - unsafe_pinned!(first: Option); - unsafe_pinned!(second: St2); - - pub(super) fn new(stream1: St1, stream2: St2) -> Chain { - Chain { - first: Some(stream1), - second: stream2, - } + pub(super) fn new(stream1: St1, stream2: St2) -> Self { + Self { first: Some(stream1), second: stream2 } } } impl FusedStream for Chain -where St1: Stream, - St2: FusedStream, +where + St1: Stream, + St2: FusedStream, { fn is_terminated(&self) -> bool { self.first.is_none() && self.second.is_terminated() @@ -37,22 +38,21 @@ where St1: Stream, } impl Stream for Chain -where St1: Stream, - St2: Stream, +where + St1: Stream, + St2: Stream, { type Item = St1::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if let Some(first) = self.as_mut().first().as_pin_mut() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if let Some(first) = this.first.as_mut().as_pin_mut() { if let Some(item) = ready!(first.poll_next(cx)) { - return Poll::Ready(Some(item)) + return Poll::Ready(Some(item)); } } - self.as_mut().first().set(None); - self.as_mut().second().poll_next(cx) + this.first.set(None); + this.second.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -64,7 +64,7 @@ where St1: Stream, let upper = match (first_upper, second_upper) { (Some(x), Some(y)) => x.checked_add(y), - _ => None + _ => None, }; (lower, upper) diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index b42d1d178d..8457869999 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -1,102 +1,72 @@ use crate::stream::Fuse; -use futures_core::stream::{Stream, FusedStream}; +use alloc::vec::Vec; +use core::mem; +use core::pin::Pin; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use core::mem; -use core::pin::Pin; -use alloc::vec::Vec; - -/// Stream for the [`chunks`](super::StreamExt::chunks) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Chunks { - stream: Fuse, - items: Vec, - cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`chunks`](super::StreamExt::chunks) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Chunks { + #[pin] + stream: Fuse, + items: Vec, + cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 + } } -impl Unpin for Chunks {} - -impl Chunks where St: Stream { - unsafe_unpinned!(items: Vec); - unsafe_pinned!(stream: Fuse); - - pub(super) fn new(stream: St, capacity: usize) -> Chunks { +impl Chunks +where + St: Stream, +{ + pub(super) fn new(stream: St, capacity: usize) -> Self { assert!(capacity > 0); - Chunks { + Self { stream: super::Fuse::new(stream), items: Vec::with_capacity(capacity), cap: capacity, } } - fn take(mut self: Pin<&mut Self>) -> Vec { + fn take(self: Pin<&mut Self>) -> Vec { let cap = self.cap; - mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) + mem::replace(self.project().items, Vec::with_capacity(cap)) } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Chunks { type Item = Vec; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { + match ready!(this.stream.as_mut().poll_next(cx)) { // Push the item into the buffer and check whether it is full. // If so, replace our buffer with a new and empty one and return // the full one. Some(item) => { - self.as_mut().items().push(item); - if self.items.len() >= self.cap { - return Poll::Ready(Some(self.as_mut().take())) + this.items.push(item); + if this.items.len() >= *this.cap { + return Poll::Ready(Some(self.take())); } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. None => { - let last = if self.items.is_empty() { + let last = if this.items.is_empty() { None } else { - let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + let full_buf = mem::replace(this.items, Vec::new()); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/collect.rs b/futures-util/src/stream/stream/collect.rs index 127a3f7d25..b0e81b9ce0 100644 --- a/futures-util/src/stream/stream/collect.rs +++ b/futures-util/src/stream/stream/collect.rs @@ -1,39 +1,36 @@ use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`collect`](super::StreamExt::collect) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Collect { - stream: St, - collection: C, +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`collect`](super::StreamExt::collect) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Collect { + #[pin] + stream: St, + collection: C, + } } -impl Unpin for Collect {} - impl Collect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(collection: C); - - fn finish(mut self: Pin<&mut Self>) -> C { - mem::replace(self.as_mut().collection(), Default::default()) + fn finish(self: Pin<&mut Self>) -> C { + mem::replace(self.project().collection, Default::default()) } - pub(super) fn new(stream: St) -> Collect { - Collect { - stream, - collection: Default::default(), - } + pub(super) fn new(stream: St) -> Self { + Self { stream, collection: Default::default() } } } impl FusedFuture for Collect -where St: FusedStream, - C: Default + Extend +where + St: FusedStream, + C: Default + Extend, { fn is_terminated(&self) -> bool { self.stream.is_terminated() @@ -41,16 +38,18 @@ where St: FusedStream, } impl Future for Collect -where St: Stream, - C: Default + Extend +where + St: Stream, + C: Default + Extend, { type Output = C; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().collection().extend(Some(e)), - None => return Poll::Ready(self.as_mut().finish()), + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(e) => this.collection.extend(Some(e)), + None => return Poll::Ready(self.finish()), } } } diff --git a/futures-util/src/stream/stream/concat.rs b/futures-util/src/stream/stream/concat.rs index 704efc79fd..7e058b2315 100644 --- a/futures-util/src/stream/stream/concat.rs +++ b/futures-util/src/stream/stream/concat.rs @@ -1,56 +1,49 @@ use core::pin::Pin; -use futures_core::future::{Future, FusedFuture}; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`concat`](super::StreamExt::concat) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Concat { - stream: St, - accum: Option, +pin_project! { + /// Future for the [`concat`](super::StreamExt::concat) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Concat { + #[pin] + stream: St, + accum: Option, + } } -impl Unpin for Concat {} - impl Concat -where St: Stream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: Stream, + St::Item: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - - pub(super) fn new(stream: St) -> Concat { - Concat { - stream, - accum: None, - } + pub(super) fn new(stream: St) -> Self { + Self { stream, accum: None } } } impl Future for Concat -where St: Stream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: Stream, + St::Item: Extend<::Item> + IntoIterator + Default, { type Output = St::Item; - fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> - ) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { - match ready!(self.as_mut().stream().poll_next(cx)) { - None => { - return Poll::Ready(self.as_mut().accum().take().unwrap_or_default()) - } + match ready!(this.stream.as_mut().poll_next(cx)) { + None => return Poll::Ready(this.accum.take().unwrap_or_default()), Some(e) => { - let accum = self.as_mut().accum(); - if let Some(a) = accum { + if let Some(a) = this.accum { a.extend(e) } else { - *accum = Some(e) + *this.accum = Some(e) } } } @@ -59,9 +52,9 @@ where St: Stream, } impl FusedFuture for Concat -where St: FusedStream, - St::Item: Extend<::Item> + - IntoIterator + Default, +where + St: FusedStream, + St::Item: Extend<::Item> + IntoIterator + Default, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.stream.is_terminated() diff --git a/futures-util/src/stream/stream/count.rs b/futures-util/src/stream/stream/count.rs new file mode 100644 index 0000000000..513cab7b6a --- /dev/null +++ b/futures-util/src/stream/stream/count.rs @@ -0,0 +1,53 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`count`](super::StreamExt::count) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Count { + #[pin] + stream: St, + count: usize + } +} + +impl fmt::Debug for Count +where + St: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Count").field("stream", &self.stream).field("count", &self.count).finish() + } +} + +impl Count { + pub(super) fn new(stream: St) -> Self { + Self { stream, count: 0 } + } +} + +impl FusedFuture for Count { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + } +} + +impl Future for Count { + type Output = usize; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + Poll::Ready(loop { + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(_) => *this.count += 1, + None => break *this.count, + } + }) + } +} diff --git a/futures-util/src/stream/stream/cycle.rs b/futures-util/src/stream/stream/cycle.rs new file mode 100644 index 0000000000..507431d24f --- /dev/null +++ b/futures-util/src/stream/stream/cycle.rs @@ -0,0 +1,68 @@ +use core::pin::Pin; +use core::usize; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`cycle`](super::StreamExt::cycle) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Cycle { + orig: St, + #[pin] + stream: St, + } +} + +impl Cycle +where + St: Clone + Stream, +{ + pub(super) fn new(stream: St) -> Self { + Self { orig: stream.clone(), stream } + } +} + +impl Stream for Cycle +where + St: Clone + Stream, +{ + type Item = St::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + match ready!(this.stream.as_mut().poll_next(cx)) { + None => { + this.stream.set(this.orig.clone()); + this.stream.poll_next(cx) + } + item => Poll::Ready(item), + } + } + + fn size_hint(&self) -> (usize, Option) { + // the cycle stream is either empty or infinite + match self.orig.size_hint() { + size @ (0, Some(0)) => size, + (0, _) => (0, None), + _ => (usize::max_value(), None), + } + } +} + +impl FusedStream for Cycle +where + St: Clone + Stream, +{ + fn is_terminated(&self) -> bool { + // the cycle stream is either empty or infinite + if let (0, Some(0)) = self.size_hint() { + true + } else { + false + } + } +} diff --git a/futures-util/src/stream/stream/enumerate.rs b/futures-util/src/stream/stream/enumerate.rs index 6366c8b7f3..1cf9d49aaa 100644 --- a/futures-util/src/stream/stream/enumerate.rs +++ b/futures-util/src/stream/stream/enumerate.rs @@ -1,62 +1,28 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Stream for the [`enumerate`](super::StreamExt::enumerate) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Enumerate { - stream: St, - count: usize, +pin_project! { + /// Stream for the [`enumerate`](super::StreamExt::enumerate) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Enumerate { + #[pin] + stream: St, + count: usize, + } } -impl Unpin for Enumerate {} - impl Enumerate { - unsafe_pinned!(stream: St); - unsafe_unpinned!(count: usize); - - pub(super) fn new(stream: St) -> Enumerate { - Enumerate { - stream, - count: 0, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream + pub(super) fn new(stream: St) -> Self { + Self { stream, count: 0 } } - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Enumerate { @@ -68,15 +34,14 @@ impl FusedStream for Enumerate { impl Stream for Enumerate { type Item = (usize, St::Item); - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - match ready!(self.as_mut().stream().poll_next(cx)) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + match ready!(this.stream.poll_next(cx)) { Some(item) => { - let count = self.count; - *self.as_mut().count() += 1; - Poll::Ready(Some((count, item))) + let prev_count = *this.count; + *this.count += 1; + Poll::Ready(Some((prev_count, item))) } None => Poll::Ready(None), } diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index 06335f1ee0..ccf1a5122f 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -1,29 +1,29 @@ +use crate::fns::FnMut1; use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`filter`](super::StreamExt::filter) method. -#[must_use = "streams do nothing unless polled"] -pub struct Filter - where St: Stream, -{ - stream: St, - f: F, - pending_fut: Option, - pending_item: Option, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`filter`](super::StreamExt::filter) method. + #[must_use = "streams do nothing unless polled"] + pub struct Filter + where St: Stream, + { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + } } -impl Unpin for Filter -where - St: Stream + Unpin, - Fut: Unpin, -{} - impl fmt::Debug for Filter where St: Stream + fmt::Debug, @@ -39,98 +39,57 @@ where } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Filter -where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: for<'a> FnMut1<&'a St::Item, Output = Fut>, + Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - - pub(super) fn new(stream: St, f: F) -> Filter { - Filter { - stream, - f, - pending_fut: None, - pending_item: None, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending_fut: None, pending_item: None } } - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Filter - where St: Stream + FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream + FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_fut.is_none() && self.stream.is_terminated() } } +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 impl Stream for Filter - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: for<'a> FnMut1<&'a St::Item, Output = Fut>, + Fut: Future, { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let res = ready!(fut.poll(cx)); + this.pending_fut.set(None); + if res { + break this.pending_item.take(); + } + *this.pending_item = None; + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + this.pending_fut.set(Some(this.f.call_mut(&item))); + *this.pending_item = Some(item); + } else { + break None; } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(item)); - } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -147,9 +106,10 @@ impl Stream for Filter // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Filter - where S: Stream + Sink, - F: FnMut(&S::Item) -> Fut, - Fut: Future, +where + S: Stream + Sink, + F: FnMut(&S::Item) -> Fut, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/filter_map.rs b/futures-util/src/stream/stream/filter_map.rs index 532e6cad96..02a0a4386e 100644 --- a/futures-util/src/stream/stream/filter_map.rs +++ b/futures-util/src/stream/stream/filter_map.rs @@ -1,26 +1,26 @@ +use crate::fns::FnMut1; use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`filter_map`](super::StreamExt::filter_map) method. -#[must_use = "streams do nothing unless polled"] -pub struct FilterMap { - stream: St, - f: F, - pending: Option, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`filter_map`](super::StreamExt::filter_map) method. + #[must_use = "streams do nothing unless polled"] + pub struct FilterMap { + #[pin] + stream: St, + f: F, + #[pin] + pending: Option, + } } -impl Unpin for FilterMap -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for FilterMap where St: fmt::Debug, @@ -35,55 +35,23 @@ where } impl FilterMap - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - - pub(super) fn new(stream: St, f: F) -> FilterMap { - FilterMap { stream, f, pending: None } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending: None } } - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for FilterMap - where St: Stream + FusedStream, - F: FnMut(St::Item) -> Fut, - Fut: Future>, +where + St: Stream + FusedStream, + F: FnMut1, + Fut: Future>, { fn is_terminated(&self) -> bool { self.pending.is_none() && self.stream.is_terminated() @@ -91,32 +59,31 @@ impl FusedStream for FilterMap } impl Stream for FilterMap - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future>, +where + St: Stream, + F: FnMut1, + Fut: Future>, { type Item = T; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let item = ready!(self.as_mut().pending().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending().set(None); - if item.is_some() { - return Poll::Ready(item); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + Poll::Ready(loop { + if let Some(p) = this.pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let item = ready!(p.poll(cx)); + this.pending.set(None); + if item.is_some() { + break item; + } + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + // No item in progress, but the stream is still going + this.pending.set(Some(this.f.call_mut(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -133,9 +100,10 @@ impl Stream for FilterMap // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for FilterMap - where S: Stream + Sink, - F: FnMut(S::Item) -> Fut, - Fut: Future, +where + S: Stream + Sink, + F: FnMut1, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/flatten.rs b/futures-util/src/stream/stream/flatten.rs index b19ffc036e..9f6b7a472d 100644 --- a/futures-util/src/stream/stream/flatten.rs +++ b/futures-util/src/stream/stream/flatten.rs @@ -1,79 +1,32 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Stream for the [`flatten`](super::StreamExt::flatten) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Flatten -where - St: Stream, -{ - stream: St, - next: Option, -} - -impl Unpin for Flatten -where - St: Stream + Unpin, - St::Item: Unpin, -{ -} - -impl Flatten -where - St: Stream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); +pin_project! { + /// Stream for the [`flatten`](super::StreamExt::flatten) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Flatten { + #[pin] + stream: St, + #[pin] + next: Option, + } } -impl Flatten -where - St: Stream, - St::Item: Stream, -{ +impl Flatten { pub(super) fn new(stream: St) -> Self { Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Flatten +impl FusedStream for Flatten where St: FusedStream, St::Item: Stream, @@ -83,34 +36,34 @@ where } } -impl Stream for Flatten +impl Stream for Flatten where St: Stream, St::Item: Stream, { type Item = ::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + Poll::Ready(loop { + if let Some(s) = this.next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.poll_next(cx)) { + break Some(item); + } else { + this.next.set(None); } - } - - if let Some(item) = ready!(self.as_mut().next().as_pin_mut().unwrap().poll_next(cx)) { - return Poll::Ready(Some(item)); + } else if let Some(s) = ready!(this.stream.as_mut().poll_next(cx)) { + this.next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Flatten +impl Sink for Flatten where S: Stream + Sink, { diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs new file mode 100644 index 0000000000..07f971c55a --- /dev/null +++ b/futures-util/src/stream/stream/flatten_unordered.rs @@ -0,0 +1,509 @@ +use alloc::sync::Arc; +use core::{ + cell::UnsafeCell, + convert::identity, + fmt, + num::NonZeroUsize, + pin::Pin, + sync::atomic::{AtomicU8, Ordering}, +}; + +use pin_project_lite::pin_project; + +use futures_core::{ + future::Future, + ready, + stream::{FusedStream, Stream}, + task::{Context, Poll, Waker}, +}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use futures_task::{waker, ArcWake}; + +use crate::stream::FuturesUnordered; + +/// There is nothing to poll and stream isn't being +/// polled or waking at the moment. +const NONE: u8 = 0; + +/// Inner streams need to be polled. +const NEED_TO_POLL_INNER_STREAMS: u8 = 1; + +/// The base stream needs to be polled. +const NEED_TO_POLL_STREAM: u8 = 0b10; + +/// It needs to poll base stream and inner streams. +const NEED_TO_POLL_ALL: u8 = NEED_TO_POLL_INNER_STREAMS | NEED_TO_POLL_STREAM; + +/// The current stream is being polled at the moment. +const POLLING: u8 = 0b100; + +/// Inner streams are being woken at the moment. +const WAKING_INNER_STREAMS: u8 = 0b1000; + +/// The base stream is being woken at the moment. +const WAKING_STREAM: u8 = 0b10000; + +/// The base stream and inner streams are being woken at the moment. +const WAKING_ALL: u8 = WAKING_STREAM | WAKING_INNER_STREAMS; + +/// The stream was waked and will be polled. +const WOKEN: u8 = 0b100000; + +/// Determines what needs to be polled, and is stream being polled at the +/// moment or not. +#[derive(Clone, Debug)] +struct SharedPollState { + state: Arc, +} + +impl SharedPollState { + /// Constructs new `SharedPollState` with the given state. + fn new(value: u8) -> SharedPollState { + SharedPollState { state: Arc::new(AtomicU8::new(value)) } + } + + /// Attempts to start polling, returning stored state in case of success. + /// Returns `None` if some waker is waking at the moment. + fn start_polling( + &self, + ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + let value = self + .state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + if value & WAKING_ALL == NONE { + Some(POLLING) + } else { + None + } + }) + .ok()?; + let bomb = PollStateBomb::new(self, SharedPollState::reset); + + Some((value, bomb)) + } + + /// Starts the waking process and performs bitwise or with the given value. + fn start_waking( + &self, + to_poll: u8, + waking: u8, + ) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + let value = self + .state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + // Waking process for this waker already started + if value & waking != NONE { + return None; + } + let mut next_value = value | to_poll; + // Only start the waking process if we're not in the polling phase and the stream isn't woken already + if value & (WOKEN | POLLING) == NONE { + next_value |= waking; + } + + if next_value != value { + Some(next_value) + } else { + None + } + }) + .ok()?; + + if value & (WOKEN | POLLING) == NONE { + let bomb = PollStateBomb::new(self, move |state| state.stop_waking(waking)); + + Some((value, bomb)) + } else { + None + } + } + + /// Sets current state to + /// - `!POLLING` allowing to use wakers + /// - `WOKEN` if the state was changed during `POLLING` phase as waker will be called, + /// or `will_be_woken` flag supplied + /// - `!WAKING_ALL` as + /// * Wakers called during the `POLLING` phase won't propagate their calls + /// * `POLLING` phase can't start if some of the wakers are active + /// So no wrapped waker can touch the inner waker's cell, it's safe to poll again. + fn stop_polling(&self, to_poll: u8, will_be_woken: bool) -> u8 { + self.state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut value| { + let mut next_value = to_poll; + + value &= NEED_TO_POLL_ALL; + if value != NONE || will_be_woken { + next_value |= WOKEN; + } + next_value |= value; + + Some(next_value & !POLLING & !WAKING_ALL) + }) + .unwrap() + } + + /// Toggles state to non-waking, allowing to start polling. + fn stop_waking(&self, waking: u8) -> u8 { + self.state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |value| { + let mut next_value = value & !waking; + // Waker will be called only if the current waking state is the same as the specified waker state + if value & WAKING_ALL == waking { + next_value |= WOKEN; + } + + if next_value != value { + Some(next_value) + } else { + None + } + }) + .unwrap_or_else(identity) + } + + /// Resets current state allowing to poll the stream and wake up wakers. + fn reset(&self) -> u8 { + self.state.swap(NEED_TO_POLL_ALL, Ordering::AcqRel) + } +} + +/// Used to execute some function on the given state when dropped. +struct PollStateBomb<'a, F: FnOnce(&SharedPollState) -> u8> { + state: &'a SharedPollState, + drop: Option, +} + +impl<'a, F: FnOnce(&SharedPollState) -> u8> PollStateBomb<'a, F> { + /// Constructs new bomb with the given state. + fn new(state: &'a SharedPollState, drop: F) -> Self { + Self { state, drop: Some(drop) } + } + + /// Deactivates bomb, forces it to not call provided function when dropped. + fn deactivate(mut self) { + self.drop.take(); + } + + /// Manually fires the bomb, returning supplied state. + fn fire(mut self) -> Option { + self.drop.take().map(|drop| (drop)(self.state)) + } +} + +impl u8> Drop for PollStateBomb<'_, F> { + fn drop(&mut self) { + if let Some(drop) = self.drop.take() { + (drop)(self.state); + } + } +} + +/// Will update state with the provided value on `wake_by_ref` call +/// and then, if there is a need, call `inner_waker`. +struct InnerWaker { + inner_waker: UnsafeCell>, + poll_state: SharedPollState, + need_to_poll: u8, +} + +unsafe impl Send for InnerWaker {} +unsafe impl Sync for InnerWaker {} + +impl InnerWaker { + /// Replaces given waker's inner_waker for polling stream/futures which will + /// update poll state on `wake_by_ref` call. Use only if you need several + /// contexts. + /// + /// ## Safety + /// + /// This function will modify waker's `inner_waker` via `UnsafeCell`, so + /// it should be used only during `POLLING` phase. + unsafe fn replace_waker(self_arc: &mut Arc, cx: &Context<'_>) -> Waker { + *self_arc.inner_waker.get() = cx.waker().clone().into(); + waker(self_arc.clone()) + } + + /// Attempts to start the waking process for the waker with the given value. + /// If succeeded, then the stream isn't yet woken and not being polled at the moment. + fn start_waking(&self) -> Option<(u8, PollStateBomb<'_, impl FnOnce(&SharedPollState) -> u8>)> { + self.poll_state.start_waking(self.need_to_poll, self.waking_state()) + } + + /// Returns the corresponding waking state toggled by this waker. + fn waking_state(&self) -> u8 { + self.need_to_poll << 3 + } +} + +impl ArcWake for InnerWaker { + fn wake_by_ref(self_arc: &Arc) { + if let Some((_, state_bomb)) = self_arc.start_waking() { + // Safety: now state is not `POLLING` + let waker_opt = unsafe { self_arc.inner_waker.get().as_ref().unwrap() }; + + if let Some(inner_waker) = waker_opt.clone() { + // Stop waking to allow polling stream + let poll_state_value = state_bomb.fire().unwrap(); + + // Here we want to call waker only if stream isn't woken yet and + // also to optimize the case when two wakers are called at the same time. + // + // In this case the best strategy will be to propagate only the latest waker's awake, + // and then poll both entities in a single `poll_next` call + if poll_state_value & (WOKEN | WAKING_ALL) == self_arc.waking_state() { + // Wake up inner waker + inner_waker.wake(); + } + } + } + } +} + +pin_project! { + /// Future which contains optional stream. + /// + /// If it's `Some`, it will attempt to call `poll_next` on it, + /// returning `Some((item, next_item_fut))` in case of `Poll::Ready(Some(...))` + /// or `None` in case of `Poll::Ready(None)`. + /// + /// If `poll_next` will return `Poll::Pending`, it will be forwarded to + /// the future and current task will be notified by waker. + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct PollStreamFut { + #[pin] + stream: Option, + } +} + +impl PollStreamFut { + /// Constructs new `PollStreamFut` using given `stream`. + fn new(stream: impl Into>) -> Self { + Self { stream: stream.into() } + } +} + +impl Future for PollStreamFut { + type Output = Option<(St::Item, PollStreamFut)>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut stream = self.project().stream; + + let item = if let Some(stream) = stream.as_mut().as_pin_mut() { + ready!(stream.poll_next(cx)) + } else { + None + }; + let next_item_fut = PollStreamFut::new(stream.get_mut().take()); + let out = item.map(|item| (item, next_item_fut)); + + Poll::Ready(out) + } +} + +pin_project! { + /// Stream for the [`flatten_unordered`](super::StreamExt::flatten_unordered) + /// method. + #[project = FlattenUnorderedProj] + #[must_use = "streams do nothing unless polled"] + pub struct FlattenUnordered where St: Stream { + #[pin] + inner_streams: FuturesUnordered>, + #[pin] + stream: St, + poll_state: SharedPollState, + limit: Option, + is_stream_done: bool, + inner_streams_waker: Arc, + stream_waker: Arc, + } +} + +impl fmt::Debug for FlattenUnordered +where + St: Stream + fmt::Debug, + St::Item: Stream + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlattenUnordered") + .field("poll_state", &self.poll_state) + .field("inner_streams", &self.inner_streams) + .field("limit", &self.limit) + .field("stream", &self.stream) + .field("is_stream_done", &self.is_stream_done) + .finish() + } +} + +impl FlattenUnordered +where + St: Stream, + St::Item: Stream + Unpin, +{ + pub(super) fn new(stream: St, limit: Option) -> FlattenUnordered { + let poll_state = SharedPollState::new(NEED_TO_POLL_STREAM); + + FlattenUnordered { + inner_streams: FuturesUnordered::new(), + stream, + is_stream_done: false, + limit: limit.and_then(NonZeroUsize::new), + inner_streams_waker: Arc::new(InnerWaker { + inner_waker: UnsafeCell::new(None), + poll_state: poll_state.clone(), + need_to_poll: NEED_TO_POLL_INNER_STREAMS, + }), + stream_waker: Arc::new(InnerWaker { + inner_waker: UnsafeCell::new(None), + poll_state: poll_state.clone(), + need_to_poll: NEED_TO_POLL_STREAM, + }), + poll_state, + } + } + + delegate_access_inner!(stream, St, ()); +} + +impl FlattenUnorderedProj<'_, St> +where + St: Stream, +{ + /// Checks if current `inner_streams` size is less than optional limit. + fn is_exceeded_limit(&self) -> bool { + self.limit.map_or(false, |limit| self.inner_streams.len() >= limit.get()) + } +} + +impl FusedStream for FlattenUnordered +where + St: FusedStream, + St::Item: FusedStream + Unpin, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.inner_streams.is_empty() + } +} + +impl Stream for FlattenUnordered +where + St: Stream, + St::Item: Stream + Unpin, +{ + type Item = ::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut next_item = None; + let mut need_to_poll_next = NONE; + + let mut this = self.as_mut().project(); + + let (mut poll_state_value, state_bomb) = match this.poll_state.start_polling() { + Some(value) => value, + _ => { + // Waker was called, just wait for the next poll + return Poll::Pending; + } + }; + + if poll_state_value & NEED_TO_POLL_STREAM != NONE { + // Safety: now state is `POLLING`. + let stream_waker = unsafe { InnerWaker::replace_waker(this.stream_waker, cx) }; + + // Here we need to poll the base stream. + // + // To improve performance, we will attempt to place as many items as we can + // to the `FuturesUnordered` bucket before polling inner streams + loop { + if this.is_exceeded_limit() || *this.is_stream_done { + // We either exceeded the limit or the stream is exhausted + if !*this.is_stream_done { + // The stream needs to be polled in the next iteration + need_to_poll_next |= NEED_TO_POLL_STREAM; + } + + break; + } else { + match this.stream.as_mut().poll_next(&mut Context::from_waker(&stream_waker)) { + Poll::Ready(Some(inner_stream)) => { + // Add new stream to the inner streams bucket + this.inner_streams.as_mut().push(PollStreamFut::new(inner_stream)); + // Inner streams must be polled afterward + poll_state_value |= NEED_TO_POLL_INNER_STREAMS; + } + Poll::Ready(None) => { + // Mark the stream as done + *this.is_stream_done = true; + } + Poll::Pending => { + break; + } + } + } + } + } + + if poll_state_value & NEED_TO_POLL_INNER_STREAMS != NONE { + // Safety: now state is `POLLING`. + let inner_streams_waker = + unsafe { InnerWaker::replace_waker(this.inner_streams_waker, cx) }; + + match this + .inner_streams + .as_mut() + .poll_next(&mut Context::from_waker(&inner_streams_waker)) + { + Poll::Ready(Some(Some((item, next_item_fut)))) => { + // Push next inner stream item future to the list of inner streams futures + this.inner_streams.as_mut().push(next_item_fut); + // Take the received item + next_item = Some(item); + // On the next iteration, inner streams must be polled again + need_to_poll_next |= NEED_TO_POLL_INNER_STREAMS; + } + Poll::Ready(Some(None)) => { + // On the next iteration, inner streams must be polled again + need_to_poll_next |= NEED_TO_POLL_INNER_STREAMS; + } + _ => {} + } + } + + // We didn't have any `poll_next` panic, so it's time to deactivate the bomb + state_bomb.deactivate(); + + let mut force_wake = + // we need to poll the stream and didn't reach the limit yet + need_to_poll_next & NEED_TO_POLL_STREAM != NONE && !this.is_exceeded_limit() + // or we need to poll inner streams again + || need_to_poll_next & NEED_TO_POLL_INNER_STREAMS != NONE; + + // Stop polling and swap the latest state + poll_state_value = this.poll_state.stop_polling(need_to_poll_next, force_wake); + // If state was changed during `POLLING` phase, need to manually call a waker + force_wake |= poll_state_value & NEED_TO_POLL_ALL != NONE; + + let is_done = *this.is_stream_done && this.inner_streams.is_empty(); + + if next_item.is_some() || is_done { + Poll::Ready(next_item) + } else { + if force_wake { + cx.waker().wake_by_ref(); + } + + Poll::Pending + } + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for FlattenUnordered +where + St: Stream + Sink, +{ + type Error = St::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures-util/src/stream/stream/fold.rs b/futures-util/src/stream/stream/fold.rs index e92a72ed90..b8b55ecb67 100644 --- a/futures-util/src/stream/stream/fold.rs +++ b/futures-util/src/stream/stream/fold.rs @@ -1,21 +1,24 @@ 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_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`fold`](super::StreamExt::fold) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Fold { - stream: St, - f: F, - accum: Option, - future: Option, +pin_project! { + /// Future for the [`fold`](super::StreamExt::fold) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Fold { + #[pin] + stream: St, + f: F, + accum: Option, + #[pin] + future: Option, + } } -impl Unpin for Fold {} - impl fmt::Debug for Fold where St: fmt::Debug, @@ -32,29 +35,21 @@ where } impl Fold -where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - - pub(super) fn new(stream: St, f: F, t: T) -> Fold { - Fold { - stream, - f, - accum: Some(t), - future: None, - } + pub(super) fn new(stream: St, f: F, t: T) -> Self { + Self { stream, f, accum: Some(t), future: None } } } impl FusedFuture for Fold - where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.future.is_none() @@ -62,31 +57,32 @@ impl FusedFuture for Fold } impl Future for Fold - where St: Stream, - F: FnMut(T, St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(T, St::Item) -> Fut, + Fut: Future, { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } - - let item = ready!(self.as_mut().stream().poll_next(cx)); - let accum = self.as_mut().accum().take() - .expect("Fold polled after completion"); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); + 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 + *this.accum = Some(ready!(fut.poll(cx))); + this.future.set(None); + } else if this.accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(this.stream.as_mut().poll_next(cx)); + let a = this.accum.take().unwrap(); + if let Some(item) = res { + this.future.set(Some((this.f)(a, item))); + } else { + break a; + } } else { - return Poll::Ready(accum) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/stream/for_each.rs b/futures-util/src/stream/stream/for_each.rs index f8adcb2927..5302b0e034 100644 --- a/futures-util/src/stream/stream/for_each.rs +++ b/futures-util/src/stream/stream/for_each.rs @@ -1,24 +1,23 @@ use core::fmt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`for_each`](super::StreamExt::for_each) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ForEach { - stream: St, - f: F, - future: Option, +pin_project! { + /// Future for the [`for_each`](super::StreamExt::for_each) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct ForEach { + #[pin] + stream: St, + f: F, + #[pin] + future: Option, + } } -impl Unpin for ForEach -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEach where St: fmt::Debug, @@ -33,27 +32,21 @@ where } impl ForEach -where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - - pub(super) fn new(stream: St, f: F) -> ForEach { - ForEach { - stream, - f, - future: None, - } + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, future: None } } } impl FusedFuture for ForEach - where St: FusedStream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -61,28 +54,25 @@ impl FusedFuture for ForEach } impl Future for ForEach - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + let mut this = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.poll(cx)); - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); - } - None => { - return Poll::Ready(()); - } + if let Some(fut) = this.future.as_mut().as_pin_mut() { + ready!(fut.poll(cx)); + this.future.set(None); + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + this.future.set(Some((this.f)(item))); + } else { + break; } } + Poll::Ready(()) } } diff --git a/futures-util/src/stream/stream/for_each_concurrent.rs b/futures-util/src/stream/stream/for_each_concurrent.rs index 18ca4bd34e..6c18753eb9 100644 --- a/futures-util/src/stream/stream/for_each_concurrent.rs +++ b/futures-util/src/stream/stream/for_each_concurrent.rs @@ -1,27 +1,25 @@ use crate::stream::{FuturesUnordered, StreamExt}; use core::fmt; -use core::pin::Pin; use core::num::NonZeroUsize; +use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`for_each_concurrent`](super::StreamExt::for_each_concurrent) -/// method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ForEachConcurrent { - stream: Option, - f: F, - futures: FuturesUnordered, - limit: Option, +pin_project! { + /// Future for the [`for_each_concurrent`](super::StreamExt::for_each_concurrent) + /// method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct ForEachConcurrent { + #[pin] + stream: Option, + f: F, + futures: FuturesUnordered, + limit: Option, + } } -impl Unpin for ForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEachConcurrent where St: fmt::Debug, @@ -37,17 +35,13 @@ where } impl ForEachConcurrent -where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - - pub(super) fn new(stream: St, limit: Option, f: F) -> ForEachConcurrent { - ForEachConcurrent { + pub(super) fn new(stream: St, limit: Option, f: F) -> Self { + Self { stream: Some(stream), // Note: `limit` = 0 gets ignored. limit: limit.and_then(NonZeroUsize::new), @@ -58,9 +52,10 @@ where St: Stream, } impl FusedFuture for ForEachConcurrent - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.stream.is_none() && self.futures.is_empty() @@ -68,27 +63,27 @@ impl FusedFuture for ForEachConcurrent } impl Future for ForEachConcurrent - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + let mut this = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { + if this.limit.map(|limit| limit.get() > this.futures.len()).unwrap_or(true) { let mut stream_completed = false; - let elem = if let Some(stream) = self.as_mut().stream().as_pin_mut() { + let elem = if let Some(stream) = this.stream.as_mut().as_pin_mut() { match stream.poll_next(cx) { Poll::Ready(Some(elem)) => { made_progress_this_iter = true; Some(elem) - }, + } Poll::Ready(None) => { stream_completed = true; None @@ -99,21 +94,20 @@ impl Future for ForEachConcurrent None }; if stream_completed { - self.as_mut().stream().set(None); + this.stream.set(None); } if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + this.futures.push((this.f)(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match this.futures.poll_next_unpin(cx) { Poll::Ready(Some(())) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { - return Poll::Ready(()) + if this.stream.is_none() { + return Poll::Ready(()); } - }, + } Poll::Pending => {} } diff --git a/futures-util/src/stream/stream/forward.rs b/futures-util/src/stream/stream/forward.rs index fd89625093..1fe24273aa 100644 --- a/futures-util/src/stream/stream/forward.rs +++ b/futures-util/src/stream/stream/forward.rs @@ -1,59 +1,33 @@ -use crate::stream::{StreamExt, Fuse}; +use crate::stream::Fuse; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; -use futures_core::stream::{Stream, TryStream}; +use futures_core::ready; +use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -const INVALID_POLL: &str = "polled `Forward` after completion"; - -/// Future for the [`forward`](super::StreamExt::forward) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Forward { - sink: Option, - stream: Fuse, - buffered_item: Option, -} - -impl Unpin for Forward {} - -impl Forward -where - Si: Sink, - St: TryStream + Stream, -{ - unsafe_pinned!(sink: Option); - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(buffered_item: Option); - - pub(super) fn new(stream: St, sink: Si) -> Self { - Forward { - sink: Some(sink), - stream: stream.fuse(), - buffered_item: None, - } +pin_project! { + /// Future for the [`forward`](super::StreamExt::forward) method. + #[project = ForwardProj] + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Forward { + #[pin] + sink: Option, + #[pin] + stream: Fuse, + buffered_item: Option, } +} - fn try_start_send( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - item: St::Ok, - ) -> Poll> { - debug_assert!(self.buffered_item.is_none()); - { - let mut sink = self.as_mut().sink().as_pin_mut().unwrap(); - if sink.as_mut().poll_ready(cx)?.is_ready() { - return Poll::Ready(sink.start_send(item)); - } - } - *self.as_mut().buffered_item() = Some(item); - Poll::Pending +impl Forward { + pub(crate) fn new(stream: St, sink: Si) -> Self { + Self { sink: Some(sink), stream: Fuse::new(stream), buffered_item: None } } } -impl FusedFuture for Forward +impl FusedFuture for Forward where Si: Sink, St: Stream>, @@ -63,35 +37,37 @@ where } } -impl Future for Forward +impl Future for Forward where Si: Sink, St: Stream>, { type Output = Result<(), E>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - // If we've got an item buffered already, we need to write it to the - // sink before we can do anything else - if let Some(item) = self.as_mut().buffered_item().take() { - ready!(self.as_mut().try_start_send(cx, item))?; - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let ForwardProj { mut sink, mut stream, buffered_item } = self.project(); + let mut si = sink.as_mut().as_pin_mut().expect("polled `Forward` after completion"); loop { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(item)) => - ready!(self.as_mut().try_start_send(cx, item))?, + // If we've got an item buffered already, we need to write it to the + // sink before we can do anything else + if buffered_item.is_some() { + ready!(si.as_mut().poll_ready(cx))?; + si.as_mut().start_send(buffered_item.take().unwrap())?; + } + + match stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(item)) => { + *buffered_item = Some(item); + } Poll::Ready(None) => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_close(cx))?; - self.as_mut().sink().set(None); - return Poll::Ready(Ok(())) + ready!(si.poll_close(cx))?; + sink.set(None); + return Poll::Ready(Ok(())); } Poll::Pending => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_flush(cx))?; - return Poll::Pending + ready!(si.poll_flush(cx))?; + return Poll::Pending; } } } diff --git a/futures-util/src/stream/stream/fuse.rs b/futures-util/src/stream/stream/fuse.rs index 9085dc553c..fe67813e81 100644 --- a/futures-util/src/stream/stream/fuse.rs +++ b/futures-util/src/stream/stream/fuse.rs @@ -1,26 +1,25 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`fuse`](super::StreamExt::fuse) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Fuse { - stream: St, - done: bool, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`fuse`](super::StreamExt::fuse) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Fuse { + #[pin] + stream: St, + done: bool, + } } -impl Unpin for Fuse {} - impl Fuse { - unsafe_pinned!(stream: St); - unsafe_unpinned!(done: bool); - - pub(super) fn new(stream: St) -> Fuse { - Fuse { stream, done: false } + pub(super) fn new(stream: St) -> Self { + Self { stream, done: false } } /// Returns whether the underlying stream has finished or not. @@ -32,37 +31,7 @@ impl Fuse { self.done } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Fuse { @@ -74,17 +43,16 @@ impl FusedStream for Fuse { impl Stream for Fuse { type Item = S::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.done { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + if *this.done { return Poll::Ready(None); } - let item = ready!(self.as_mut().stream().poll_next(cx)); + let item = ready!(this.stream.poll_next(cx)); if item.is_none() { - *self.as_mut().done() = true; + *this.done = true; } Poll::Ready(item) } diff --git a/futures-util/src/stream/stream/inspect.rs b/futures-util/src/stream/stream/inspect.rs deleted file mode 100644 index e34970ae65..0000000000 --- a/futures-util/src/stream/stream/inspect.rs +++ /dev/null @@ -1,119 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect`](super::StreamExt::inspect) method. -#[must_use = "streams do nothing unless polled"] -pub struct Inspect { - stream: St, - f: F, -} - -impl Unpin for Inspect {} - -impl fmt::Debug for Inspect -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Inspect") - .field("stream", &self.stream) - .finish() - } -} - -impl Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Inspect { - Inspect { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for Inspect - where St: FusedStream, - F: FnMut(&St::Item), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -// used by `TryStreamExt::{inspect_ok, inspect_err}` -#[inline] -pub(crate) fn inspect(x: T, mut f: F) -> T { - f(&x); - x -} - -impl Stream for Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - type Item = St::Item; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|e| inspect(e, self.as_mut().f()))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for Inspect - where S: Stream + Sink, - F: FnMut(&S::Item), -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/stream/into_future.rs b/futures-util/src/stream/stream/into_future.rs index abae98c0c9..8abfddcccd 100644 --- a/futures-util/src/stream/stream/into_future.rs +++ b/futures-util/src/stream/stream/into_future.rs @@ -1,9 +1,9 @@ use crate::stream::StreamExt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; /// Future for the [`into_future`](super::StreamExt::into_future) method. #[derive(Debug)] @@ -12,13 +12,9 @@ pub struct StreamFuture { stream: Option, } -impl Unpin for StreamFuture {} - impl StreamFuture { - unsafe_pinned!(stream: Option); - - pub(super) fn new(stream: St) -> StreamFuture { - StreamFuture { stream: Some(stream) } + pub(super) fn new(stream: St) -> Self { + Self { stream: Some(stream) } } /// Acquires a reference to the underlying stream that this combinator is @@ -57,7 +53,7 @@ impl StreamFuture { /// in order to return it to the caller of `Future::poll` if the stream yielded /// an element. pub fn get_pin_mut(self: Pin<&mut Self>) -> Option> { - self.stream().as_pin_mut() + self.get_mut().stream.as_mut().map(Pin::new) } /// Consumes this combinator, returning the underlying stream. @@ -83,10 +79,7 @@ impl FusedFuture for StreamFuture { impl Future for StreamFuture { type Output = (Option, St); - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let item = { let s = self.stream.as_mut().expect("polling StreamFuture twice"); ready!(s.poll_next_unpin(cx)) diff --git a/futures-util/src/stream/stream/map.rs b/futures-util/src/stream/stream/map.rs index 81194342c4..88bb6129d4 100644 --- a/futures-util/src/stream/stream/map.rs +++ b/futures-util/src/stream/stream/map.rs @@ -1,98 +1,62 @@ use core::fmt; use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Stream for the [`map`](super::StreamExt::map) method. -#[must_use = "streams do nothing unless polled"] -pub struct Map { - stream: St, - f: F, -} +use crate::fns::FnMut1; -impl Unpin for Map {} +pin_project! { + /// Stream for the [`map`](super::StreamExt::map) method. + #[must_use = "streams do nothing unless polled"] + pub struct Map { + #[pin] + stream: St, + f: F, + } +} impl fmt::Debug for Map where St: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Map") - .field("stream", &self.stream) - .finish() + f.debug_struct("Map").field("stream", &self.stream).finish() } } -impl Map - where St: Stream, - F: FnMut(St::Item) -> T, -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Map { - Map { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream +impl Map { + pub(crate) fn new(stream: St, f: F) -> Self { + Self { stream, f } } - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Map - where St: FusedStream, - F: FnMut(St::Item) -> T, +impl FusedStream for Map +where + St: FusedStream, + F: FnMut1, { fn is_terminated(&self) -> bool { self.stream.is_terminated() } } -impl Stream for Map - where St: Stream, - F: FnMut(St::Item) -> T, +impl Stream for Map +where + St: Stream, + F: FnMut1, { - type Item = T; + type Item = F::Output; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|x| self.as_mut().f()(x))) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let res = ready!(this.stream.as_mut().poll_next(cx)); + Poll::Ready(res.map(|x| this.f.call_mut(x))) } fn size_hint(&self) -> (usize, Option) { @@ -102,11 +66,12 @@ impl Stream for Map // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Map - where S: Stream + Sink, - F: FnMut(S::Item) -> T, +impl Sink for Map +where + St: Stream + Sink, + F: FnMut1, { - type Error = S::Error; + type Error = St::Error; delegate_sink!(stream, Item); } diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index da5ade85bb..642b91e823 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -3,9 +3,12 @@ //! This module contains a number of functions for working with `Stream`s, //! including the `StreamExt` trait which adds methods to `Stream` types. -use crate::future::Either; +use crate::future::{assert_future, Either}; +use crate::stream::assert_stream; #[cfg(feature = "alloc")] use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::pin::Pin; #[cfg(feature = "sink")] use futures_core::stream::TryStream; @@ -19,6 +22,8 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; +use crate::fns::{inspect_fn, InspectFn}; + mod chain; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chain::Chain; @@ -27,10 +32,22 @@ mod collect; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::collect::Collect; +mod unzip; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::unzip::Unzip; + mod concat; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::concat::Concat; +mod count; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::count::Count; + +mod cycle; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::cycle::Cycle; + mod enumerate; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::enumerate::Enumerate; @@ -44,18 +61,39 @@ mod filter_map; pub use self::filter_map::FilterMap; mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; + +delegate_all!( + /// Stream for the [`flatten`](StreamExt::flatten) method. + Flatten( + flatten::Flatten + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| flatten::Flatten::new(x)] + where St: Stream +); mod fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 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; + #[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::forward::Forward; +delegate_all!( + /// Future for the [`forward`](super::StreamExt::forward) method. + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] + Forward( + forward::Forward + ): Debug + Future + FusedFuture + New[|x: St, y: Si| forward::Forward::new(x, y)] + where St: TryStream +); mod for_each; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -69,15 +107,24 @@ mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::StreamFuture; -mod inspect; -pub(crate) use self::inspect::inspect; // used by `TryStreamExt::{inspect_ok, inspect_err}` -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; +delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + Inspect( + map::Map> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] +); mod map; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::map::Map; +delegate_all!( + /// Stream for the [`flat_map`](StreamExt::flat_map) method. + FlatMap( + flatten::Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| flatten::Flatten::new(Map::new(x, f))] +); + mod next; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::next::Next; @@ -88,7 +135,7 @@ pub use self::select_next_some::SelectNextSome; mod peek; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::peek::{Peek, Peekable}; +pub use self::peek::{NextIf, NextIfEq, Peek, PeekMut, Peekable}; mod skip; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -106,6 +153,10 @@ mod take_while; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::take_while::TakeWhile; +mod take_until; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::take_until::TakeUntil; + mod then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::then::Then; @@ -120,37 +171,70 @@ mod chunks; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chunks::Chunks; +#[cfg(feature = "alloc")] +mod ready_chunks; +#[cfg(feature = "alloc")] +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::ready_chunks::ReadyChunks; + mod scan; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::scan::Scan; -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 flatten_unordered; - #[cfg(feature = "sink")] - #[cfg(feature = "alloc")] - mod split; - #[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 = "alloc")] +#[allow(unreachable_pub)] +pub use self::flatten_unordered::FlattenUnordered; + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +delegate_all!( + /// Stream for the [`flat_map_unordered`](StreamExt::flat_map_unordered) method. + FlatMapUnordered( + FlattenUnordered> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, limit: Option, f: F| FlattenUnordered::new(Map::new(x, f), limit)] + where St: Stream, U: Stream, U: Unpin, F: FnMut(St::Item) -> U +); + +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +mod for_each_concurrent; +#[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(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; @@ -190,7 +274,7 @@ pub trait StreamExt: Stream { where Self: Unpin, { - Next::new(self) + assert_future::, _>(Next::new(self)) } /// Converts this stream into a future of `(next_item, tail_of_stream)`. @@ -225,7 +309,7 @@ pub trait StreamExt: Stream { where Self: Sized + Unpin, { - StreamFuture::new(self) + assert_future::<(Option, Self), _>(StreamFuture::new(self)) } /// Maps this stream's items to a different type, returning a new stream of @@ -256,7 +340,7 @@ pub trait StreamExt: Stream { F: FnMut(Self::Item) -> T, Self: Sized, { - Map::new(self, f) + assert_stream::(Map::new(self, f)) } /// Creates a stream which gives the current iteration count as well as @@ -273,7 +357,7 @@ pub trait StreamExt: Stream { /// # Overflow Behavior /// /// The method does no guarding against overflows, so enumerating more than - /// [`usize::max_value()`] elements either produces the wrong result or panics. If + /// [`prim@usize::max_value()`] elements either produces the wrong result or panics. If /// debug assertions are enabled, a panic is guaranteed. /// /// # Panics @@ -301,7 +385,7 @@ pub trait StreamExt: Stream { where Self: Sized, { - Enumerate::new(self) + assert_stream::<(usize, Self::Item), _>(Enumerate::new(self)) } /// Filters the values produced by this stream according to the provided @@ -325,9 +409,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 @@ -336,7 +420,7 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - Filter::new(self, f) + assert_stream::(Filter::new(self, f)) } /// Filters the values produced by this stream while simultaneously mapping @@ -357,11 +441,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 @@ -370,7 +454,7 @@ pub trait StreamExt: Stream { Fut: Future>, Self: Sized, { - FilterMap::new(self, f) + assert_stream::(FilterMap::new(self, f)) } /// Computes from this stream's items new items of a different type using @@ -401,7 +485,7 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - Then::new(self, f) + assert_stream::(Then::new(self, f)) } /// Transforms a stream into a collection, returning a @@ -433,7 +517,46 @@ pub trait StreamExt: Stream { where Self: Sized, { - Collect::new(self) + assert_future::(Collect::new(self)) + } + + /// Converts a stream of pairs into a future, which + /// resolves to pair of containers. + /// + /// `unzip()` produces a future, which resolves to two + /// collections: one from the left elements of the pairs, + /// and one from the right elements. + /// + /// The returned future will be resolved when the stream terminates. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::mpsc; + /// use futures::stream::StreamExt; + /// use std::thread; + /// + /// let (tx, rx) = mpsc::unbounded(); + /// + /// thread::spawn(move || { + /// tx.unbounded_send((1, 2)).unwrap(); + /// tx.unbounded_send((3, 4)).unwrap(); + /// tx.unbounded_send((5, 6)).unwrap(); + /// }); + /// + /// let (o1, o2): (Vec<_>, Vec<_>) = rx.unzip().await; + /// assert_eq!(o1, vec![1, 3, 5]); + /// assert_eq!(o2, vec![2, 4, 6]); + /// # }); + /// ``` + fn unzip(self) -> Unzip + where + FromA: Default + Extend, + FromB: Default + Extend, + Self: Sized + Stream, + { + assert_future::<(FromA, FromB), _>(Unzip::new(self)) } /// Concatenate all items of a stream into a single extendable @@ -473,7 +596,69 @@ pub trait StreamExt: Stream { Self: Sized, Self::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default, { - Concat::new(self) + assert_future::(Concat::new(self)) + } + + /// Drives the stream to completion, counting the number of items. + /// + /// # Overflow Behavior + /// + /// The method does no guarding against overflows, so counting elements of a + /// stream with more than [`usize::MAX`] elements either produces the wrong + /// result or panics. If debug assertions are enabled, a panic is guaranteed. + /// + /// # Panics + /// + /// This function might panic if the iterator has more than [`usize::MAX`] + /// elements. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..=10); + /// let count = stream.count().await; + /// + /// assert_eq!(count, 10); + /// # }); + /// ``` + fn count(self) -> Count + where + Self: Sized, + { + assert_future::(Count::new(self)) + } + + /// Repeats a stream endlessly. + /// + /// The stream never terminates. Note that you likely want to avoid + /// usage of `collect` or such on the returned stream as it will exhaust + /// available memory as it tries to just fill up all RAM. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// let a = [1, 2, 3]; + /// let mut s = stream::iter(a.iter()).cycle(); + /// + /// assert_eq!(s.next().await, Some(&1)); + /// assert_eq!(s.next().await, Some(&2)); + /// assert_eq!(s.next().await, Some(&3)); + /// assert_eq!(s.next().await, Some(&1)); + /// assert_eq!(s.next().await, Some(&2)); + /// assert_eq!(s.next().await, Some(&3)); + /// assert_eq!(s.next().await, Some(&1)); + /// # }); + /// ``` + fn cycle(self) -> Cycle + where + Self: Sized + Clone, + { + assert_stream::(Cycle::new(self)) } /// Execute an accumulating asynchronous computation over a stream, @@ -502,7 +687,51 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - Fold::new(self, f, init) + 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. @@ -541,13 +770,146 @@ pub trait StreamExt: Stream { Self::Item: Stream, Self: Sized, { - Flatten::new(self) + assert_stream::<::Item, _>(Flatten::new(self)) + } + + /// Flattens a stream of streams into just one continuous stream. Polls + /// inner streams concurrently. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::mpsc; + /// use futures::stream::StreamExt; + /// use std::thread; + /// + /// let (tx1, rx1) = mpsc::unbounded(); + /// let (tx2, rx2) = mpsc::unbounded(); + /// let (tx3, rx3) = mpsc::unbounded(); + /// + /// thread::spawn(move || { + /// tx1.unbounded_send(1).unwrap(); + /// tx1.unbounded_send(2).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx2.unbounded_send(3).unwrap(); + /// tx2.unbounded_send(4).unwrap(); + /// }); + /// thread::spawn(move || { + /// tx3.unbounded_send(rx1).unwrap(); + /// tx3.unbounded_send(rx2).unwrap(); + /// }); + /// + /// let mut output = rx3.flatten_unordered(None).collect::>().await; + /// output.sort(); + /// + /// assert_eq!(output, vec![1, 2, 3, 4]); + /// # }); + /// ``` + #[cfg(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn flatten_unordered(self, limit: impl Into>) -> FlattenUnordered + where + Self::Item: Stream + Unpin, + Self: Sized, + { + FlattenUnordered::new(self, limit.into()) + } + + /// Maps a stream like [`StreamExt::map`] but flattens nested `Stream`s. + /// + /// [`StreamExt::map`] is very useful, but if it produces a `Stream` instead, + /// you would have to chain combinators like `.map(f).flatten()` while this + /// combinator provides ability to write `.flat_map(f)` instead of chaining. + /// + /// The provided closure which produces inner streams is executed over all elements + /// of stream as last inner stream is terminated and next stream item is available. + /// + /// Note that this function consumes the stream passed into it and returns a + /// wrapped version of it, similar to the existing `flat_map` methods in the + /// standard library. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..=3); + /// let stream = stream.flat_map(|x| stream::iter(vec![x + 3; x])); + /// + /// assert_eq!(vec![4, 5, 5, 6, 6, 6], stream.collect::>().await); + /// # }); + /// ``` + fn flat_map(self, f: F) -> FlatMap + where + F: FnMut(Self::Item) -> U, + U: Stream, + Self: Sized, + { + assert_stream::(FlatMap::new(self, f)) + } + + /// Maps a stream like [`StreamExt::map`] but flattens nested `Stream`s + /// and polls them concurrently, yielding items in any order, as they made + /// available. + /// + /// [`StreamExt::map`] is very useful, but if it produces `Stream`s + /// instead, and you need to poll all of them concurrently, you would + /// have to use something like `for_each_concurrent` and merge values + /// by hand. This combinator provides ability to collect all values + /// from concurrently polled streams into one stream. + /// + /// The first argument is an optional limit on the number of concurrently + /// polled streams. If this limit is not `None`, no more than `limit` streams + /// will be polled concurrently. The `limit` argument is of type + /// `Into>`, and so can be provided as either `None`, + /// `Some(10)`, or just `10`. Note: a limit of zero is interpreted as + /// no limit at all, and will have the same result as passing in `None`. + /// + /// The provided closure which produces inner streams is executed over + /// all elements of stream as next stream item is available and limit + /// of concurrently processed streams isn't exceeded. + /// + /// Note that this function consumes the stream passed into it and + /// returns a wrapped version of it. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// + /// let stream = stream::iter(1..5); + /// let stream = stream.flat_map_unordered(1, |x| stream::iter(vec![x; x])); + /// let mut values = stream.collect::>().await; + /// values.sort(); + /// + /// assert_eq!(vec![1usize, 2, 2, 3, 3, 3, 4, 4, 4, 4], values); + /// # }); + /// ``` + #[cfg(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn flat_map_unordered( + self, + limit: impl Into>, + f: F, + ) -> FlatMapUnordered + where + U: Stream + Unpin, + F: FnMut(Self::Item) -> U, + Self: Sized, + { + FlatMapUnordered::new(self, limit.into(), f) } - /// Combinator similar to [`StreamExt::fold`] that holds internal state and produces a new stream. + /// Combinator similar to [`StreamExt::fold`] that holds internal state + /// and produces a new stream. /// - /// Accepts initial state and closure which will be applied to each element of the stream until provided closure - /// returns `None`. Once `None` is returned, stream will be terminated. + /// Accepts initial state and closure which will be applied to each element + /// of the stream until provided closure returns `None`. Once `None` is + /// returned, stream will be terminated. /// /// # Examples /// @@ -572,7 +934,7 @@ pub trait StreamExt: Stream { Fut: Future>, Self: Sized, { - Scan::new(self, initial_state, f) + assert_stream::(Scan::new(self, initial_state, f)) } /// Skip elements on this stream while the provided asynchronous predicate @@ -580,7 +942,7 @@ pub trait StreamExt: Stream { /// /// This function, like `Iterator::skip_while`, will skip elements on the /// stream until the predicate `f` resolves to `false`. Once one element - /// returns false all future elements will be returned from the underlying + /// returns `false`, all future elements will be returned from the underlying /// stream. /// /// # Examples @@ -603,7 +965,7 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - SkipWhile::new(self, f) + assert_stream::(SkipWhile::new(self, f)) } /// Take elements from this stream while the provided asynchronous predicate @@ -611,7 +973,7 @@ pub trait StreamExt: Stream { /// /// This function, like `Iterator::take_while`, will take elements from the /// stream until the predicate `f` resolves to `false`. Once one element - /// returns false it will always return that the stream is done. + /// returns `false`, it will always return that the stream is done. /// /// # Examples /// @@ -633,7 +995,51 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - TakeWhile::new(self, f) + assert_stream::(TakeWhile::new(self, f)) + } + + /// Take elements from this stream until the provided future resolves. + /// + /// This function will take elements from the stream until the provided + /// stopping future `fut` resolves. Once the `fut` future becomes ready, + /// this stream combinator will always return that the stream is done. + /// + /// The stopping future may return any type. Once the stream is stopped + /// the result of the stopping future may be accessed with `TakeUntil::take_result()`. + /// The stream may also be resumed with `TakeUntil::take_future()`. + /// See the documentation of [`TakeUntil`] for more information. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future; + /// use futures::stream::{self, StreamExt}; + /// use futures::task::Poll; + /// + /// let stream = stream::iter(1..=10); + /// + /// let mut i = 0; + /// let stop_fut = future::poll_fn(|_cx| { + /// i += 1; + /// if i <= 5 { + /// Poll::Pending + /// } else { + /// Poll::Ready(()) + /// } + /// }); + /// + /// let stream = stream.take_until(stop_fut); + /// + /// assert_eq!(vec![1, 2, 3, 4, 5], stream.collect::>().await); + /// # }); + /// ``` + fn take_until(self, fut: Fut) -> TakeUntil + where + Fut: Future, + Self: Sized, + { + assert_stream::(TakeUntil::new(self, fut)) } /// Runs this stream to completion, executing the provided asynchronous @@ -675,7 +1081,7 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - ForEach::new(self, f) + assert_future::<(), _>(ForEach::new(self, f)) } /// Runs this stream to completion, executing the provided asynchronous @@ -723,7 +1129,7 @@ pub trait StreamExt: Stream { /// fut.await; /// # }) /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn for_each_concurrent( self, @@ -735,7 +1141,7 @@ pub trait StreamExt: Stream { Fut: Future, Self: Sized, { - ForEachConcurrent::new(self, limit.into(), f) + assert_future::<(), _>(ForEachConcurrent::new(self, limit.into(), f)) } /// Creates a new stream of at most `n` items of the underlying stream. @@ -758,7 +1164,7 @@ pub trait StreamExt: Stream { where Self: Sized, { - Take::new(self, n) + assert_stream::(Take::new(self, n)) } /// Creates a new stream which skips `n` items of the underlying stream. @@ -781,7 +1187,7 @@ pub trait StreamExt: Stream { where Self: Sized, { - Skip::new(self, n) + assert_stream::(Skip::new(self, n)) } /// Fuse a stream such that [`poll_next`](Stream::poll_next) will never @@ -827,7 +1233,7 @@ pub trait StreamExt: Stream { where Self: Sized, { - Fuse::new(self) + assert_stream::(Fuse::new(self)) } /// Borrows a stream, rather than consuming it. @@ -905,7 +1311,7 @@ pub trait StreamExt: Stream { where Self: Sized + std::panic::UnwindSafe, { - CatchUnwind::new(self) + assert_stream(CatchUnwind::new(self)) } /// Wrap the stream in a Box, pinning it. @@ -917,7 +1323,7 @@ pub trait StreamExt: Stream { where Self: Sized + Send + 'a, { - Box::pin(self) + assert_stream::(Box::pin(self)) } /// Wrap the stream in a Box, pinning it. @@ -931,7 +1337,7 @@ pub trait StreamExt: Stream { where Self: Sized + 'a, { - Box::pin(self) + assert_stream::(Box::pin(self)) } /// An adaptor for creating a buffered list of pending futures. @@ -946,14 +1352,14 @@ pub trait StreamExt: Stream { /// /// This method is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn buffered(self, n: usize) -> Buffered where Self::Item: Future, Self: Sized, { - Buffered::new(self, n) + assert_stream::<::Output, _>(Buffered::new(self, n)) } /// An adaptor for creating a buffered list of pending futures (unordered). @@ -991,14 +1397,14 @@ pub trait StreamExt: Stream { /// assert_eq!(buffered.next().await, None); /// # Ok::<(), i32>(()) }).unwrap(); /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn buffer_unordered(self, n: usize) -> BufferUnordered where Self::Item: Future, Self: Sized, { - BufferUnordered::new(self, n) + assert_stream::<::Output, _>(BufferUnordered::new(self, n)) } /// An adapter for zipping two streams together. @@ -1028,7 +1434,7 @@ pub trait StreamExt: Stream { St: Stream, Self: Sized, { - Zip::new(self, other) + assert_stream::<(Self::Item, St::Item), _>(Zip::new(self, other)) } /// Adapter for chaining two streams. @@ -1059,7 +1465,7 @@ pub trait StreamExt: Stream { St: Stream, Self: Sized, { - Chain::new(self, other) + assert_stream::(Chain::new(self, other)) } /// Creates a new stream which exposes a `peek` method. @@ -1069,7 +1475,7 @@ pub trait StreamExt: Stream { where Self: Sized, { - Peekable::new(self) + assert_stream::(Peekable::new(self)) } /// An adaptor for chunking up items of the stream inside a vector. @@ -1095,7 +1501,33 @@ pub trait StreamExt: Stream { where Self: Sized, { - Chunks::new(self, capacity) + assert_stream::, _>(Chunks::new(self, capacity)) + } + + /// An adaptor for chunking up ready items of the stream inside a vector. + /// + /// This combinator will attempt to pull ready items from this stream and + /// buffer them into a local vector. At most `capacity` items will get + /// buffered before they're yielded from the returned stream. If underlying + /// stream returns `Poll::Pending`, and collected chunk is not empty, it will + /// be immediately returned. + /// + /// If the underlying stream ended and only a partial vector was created, + /// it'll be returned. Additionally if an error happens from the underlying + /// stream then the currently buffered items will be yielded. + /// + /// This method is only available when the `std` or `alloc` feature of this + /// library is activated, and it is activated by default. + /// + /// # Panics + /// + /// This method will panic if `capacity` is zero. + #[cfg(feature = "alloc")] + fn ready_chunks(self, capacity: usize) -> ReadyChunks + where + Self: Sized, + { + assert_stream::, _>(ReadyChunks::new(self, capacity)) } /// A future that completes after the given stream has been fully processed @@ -1107,17 +1539,22 @@ pub trait StreamExt: Stream { /// the sink is closed. Note that neither the original stream nor provided /// sink will be output by this future. Pass the sink by `Pin<&mut S>` /// (for example, via `forward(&mut sink)` inside an `async` fn/block) in - /// order to preserve access to the `Sink`. + /// order to preserve access to the `Sink`. If the stream produces an error, + /// that error will be returned by this future without flushing/closing the sink. #[cfg(feature = "sink")] + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] fn forward(self, sink: S) -> Forward where - S: Sink<::Ok>, - Self: TryStream + Sized, + S: Sink, + Self: TryStream + Sized, + // Self: TryStream + Sized + Stream::Ok, ::Error>>, { + // TODO: type mismatch resolving `::Item == std::result::Result<::Ok, ::Error>` + // assert_future::, _>(Forward::new(self, sink)) Forward::new(self, sink) } - /// Splits this `Stream + Sink` object into separate `Stream` and `Sink` + /// Splits this `Stream + Sink` object into separate `Sink` and `Stream` /// objects. /// /// This can be useful when you want to split ownership between tasks, or @@ -1127,13 +1564,18 @@ pub trait StreamExt: Stream { /// This method is only available when the `std` or `alloc` feature of this /// library is activated, and it is activated by default. #[cfg(feature = "sink")] - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg_attr(docsrs, doc(cfg(feature = "sink")))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn split(self) -> (SplitSink, SplitStream) where Self: Sink + Sized, { - split::split(self) + let (sink, stream) = split::split(self); + ( + crate::sink::assert_sink::(sink), + assert_stream::(stream), + ) } /// Do something with each item of this stream, afterwards passing it on. @@ -1146,7 +1588,7 @@ pub trait StreamExt: Stream { F: FnMut(&Self::Item), Self: Sized, { - Inspect::new(self, f) + assert_stream::(Inspect::new(self, f)) } /// Wrap this stream in an `Either` stream, making it the left-hand variant @@ -1159,7 +1601,7 @@ pub trait StreamExt: Stream { B: Stream, Self: Sized, { - Either::Left(self) + assert_stream::(Either::Left(self)) } /// Wrap this stream in an `Either` stream, making it the right-hand variant @@ -1172,7 +1614,7 @@ pub trait StreamExt: Stream { B: Stream, Self: Sized, { - Either::Right(self) + assert_stream::(Either::Right(self)) } /// A convenience method for calling [`Stream::poll_next`] on [`Unpin`] @@ -1236,6 +1678,6 @@ pub trait StreamExt: Stream { where Self: Unpin + FusedStream, { - SelectNextSome::new(self) + assert_future::(SelectNextSome::new(self)) } } diff --git a/futures-util/src/stream/stream/next.rs b/futures-util/src/stream/stream/next.rs index 2d7463213b..8d8347aa03 100644 --- a/futures-util/src/stream/stream/next.rs +++ b/futures-util/src/stream/stream/next.rs @@ -15,7 +15,7 @@ impl Unpin for Next<'_, St> {} impl<'a, St: ?Sized + Stream + Unpin> Next<'a, St> { pub(super) fn new(stream: &'a mut St) -> Self { - Next { stream } + Self { stream } } } @@ -28,10 +28,7 @@ impl FusedFuture for Next<'_, St> { impl Future for Next<'_, St> { type Output = Option; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.stream.poll_next_unpin(cx) } } diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index 9272bafe90..c72dfc3666 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -1,112 +1,187 @@ -use crate::future::Either; +use crate::fns::FnOnce1; use crate::stream::{Fuse, StreamExt}; use core::fmt; +use core::marker::PhantomData; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// A `Stream` that implements a `peek` method. -/// -/// The `peek` method can be used to retrieve a reference -/// to the next `Stream::Item` if available. A subsequent -/// call to `poll` will return the owned item. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Peekable { - stream: Fuse, - peeked: Option, +pin_project! { + /// A `Stream` that implements a `peek` method. + /// + /// The `peek` method can be used to retrieve a reference + /// to the next `Stream::Item` if available. A subsequent + /// call to `poll` will return the owned item. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Peekable { + #[pin] + stream: Fuse, + peeked: Option, + } } -impl Unpin for Peekable {} - impl Peekable { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(peeked: Option); - - pub(super) fn new(stream: St) -> Peekable { - Peekable { - stream: stream.fuse(), - peeked: None, - } + pub(super) fn new(stream: St) -> Self { + Self { stream: stream.fuse(), peeked: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } + delegate_access_inner!(stream, St, (.)); - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() + /// Produces a future which retrieves a reference to the next item + /// in the stream, or `None` if the underlying stream terminates. + pub fn peek(self: Pin<&mut Self>) -> Peek<'_, St> { + Peek { inner: Some(self) } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. + /// Peek retrieves a reference to the next item in the stream. /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } + /// This method polls the underlying stream and return either a reference + /// to the next item if the stream is ready or passes through any errors. + pub fn poll_peek(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() + Poll::Ready(loop { + if this.peeked.is_some() { + break this.peeked.as_ref(); + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + *this.peeked = Some(item); + } else { + break None; + } + }) } - /// Produces a `Peek` future which retrieves a reference to the next item + /// Produces a future which retrieves a mutable reference to the next item /// in the stream, or `None` if the underlying stream terminates. - pub fn peek(self: Pin<&mut Self>) -> Peek<'_, St> { - Peek { inner: Some(self) } + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(vec![1, 2, 3]).peekable(); + /// pin_mut!(stream); + /// + /// assert_eq!(stream.as_mut().peek_mut().await, Some(&mut 1)); + /// assert_eq!(stream.as_mut().next().await, Some(1)); + /// + /// // Peek into the stream and modify the value which will be returned next + /// if let Some(p) = stream.as_mut().peek_mut().await { + /// if *p == 2 { + /// *p = 5; + /// } + /// } + /// + /// assert_eq!(stream.collect::>().await, vec![5, 3]); + /// # }); + /// ``` + pub fn peek_mut(self: Pin<&mut Self>) -> PeekMut<'_, St> { + PeekMut { inner: Some(self) } } - /// Attempt to poll the underlying stream, and return the mutable borrow - /// in case that is desirable to try for another time. - /// In case a peeking poll is successful, the reference to the next item - /// will be in the `Either::Right` variant; otherwise, the mutable borrow - /// will be in the `Either::Left` variant. - fn do_poll_peek( - mut self: Pin<&mut Self>, + /// Peek retrieves a mutable reference to the next item in the stream. + pub fn poll_peek_mut( + self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Either, Option<&St::Item>> { - if self.peeked.is_some() { - let this: &Self = self.into_ref().get_ref(); - return Either::Right(this.peeked.as_ref()); - } - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(None) => Either::Right(None), - Poll::Ready(Some(item)) => { - *self.as_mut().peeked() = Some(item); - let this: &Self = self.into_ref().get_ref(); - Either::Right(this.peeked.as_ref()) + ) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if this.peeked.is_some() { + break this.peeked.as_mut(); + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + *this.peeked = Some(item); + } else { + break None; } - _ => Either::Left(self), - } + }) } - /// Peek retrieves a reference to the next item in the stream. + /// Creates a future which will consume and return the next value of this + /// stream if a condition is true. /// - /// This method polls the underlying stream and return either a reference - /// to the next item if the stream is ready or passes through any errors. - pub fn poll_peek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - match self.do_poll_peek(cx) { - Either::Left(_) => Poll::Pending, - Either::Right(poll) => Poll::Ready(poll), + /// If `func` returns `true` for the next value of this stream, consume and + /// return it. Otherwise, return `None`. + /// + /// # Examples + /// + /// Consume a number if it's equal to 0. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(0..5).peekable(); + /// pin_mut!(stream); + /// // The first item of the stream is 0; consume it. + /// assert_eq!(stream.as_mut().next_if(|&x| x == 0).await, Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(stream.as_mut().next_if(|&x| x == 0).await, None); + /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(stream.next().await, Some(1)); + /// # }); + /// ``` + /// + /// Consume any number less than 10. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(1..20).peekable(); + /// pin_mut!(stream); + /// // Consume all numbers less than 10 + /// while stream.as_mut().next_if(|&x| x < 10).await.is_some() {} + /// // The next value returned will be 10 + /// assert_eq!(stream.next().await, Some(10)); + /// # }); + /// ``` + pub fn next_if(self: Pin<&mut Self>, func: F) -> NextIf<'_, St, F> + where + F: FnOnce(&St::Item) -> bool, + { + NextIf { inner: Some((self, func)) } + } + + /// Creates a future which will consume and return the next item if it is + /// equal to `expected`. + /// + /// # Example + /// + /// Consume a number if it's equal to 0. + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// use futures::pin_mut; + /// + /// let stream = stream::iter(0..5).peekable(); + /// pin_mut!(stream); + /// // The first item of the stream is 0; consume it. + /// assert_eq!(stream.as_mut().next_if_eq(&0).await, Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(stream.as_mut().next_if_eq(&0).await, None); + /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(stream.next().await, Some(1)); + /// # }); + /// ``` + pub fn next_if_eq<'a, T>(self: Pin<&'a mut Self>, expected: &'a T) -> NextIfEq<'a, St, T> + where + T: ?Sized, + St::Item: PartialEq, + { + NextIfEq { + inner: NextIf { inner: Some((self, NextIfEqFn { expected, _next: PhantomData })) }, } } } @@ -120,11 +195,12 @@ impl FusedStream for Peekable { impl Stream for Peekable { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(item) = self.as_mut().peeked().take() { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if let Some(item) = this.peeked.take() { return Poll::Ready(Some(item)); } - self.as_mut().stream().poll_next(cx) + this.stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -150,23 +226,21 @@ where delegate_sink!(stream, Item); } -/// Future for the [`Peekable::peek()`](self::Peekable::peek) function from [`Peekable`] -#[must_use = "futures do nothing unless polled"] -pub struct Peek<'a, St: Stream> { - inner: Option>>, +pin_project! { + /// Future for the [`Peekable::peek`](self::Peekable::peek) method. + #[must_use = "futures do nothing unless polled"] + pub struct Peek<'a, St: Stream> { + inner: Option>>, + } } -impl Unpin for Peek<'_, St> {} - impl fmt::Debug for Peek<'_, St> where St: Stream + fmt::Debug, St::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Peek") - .field("inner", &self.inner) - .finish() + f.debug_struct("Peek").field("inner", &self.inner).finish() } } @@ -181,17 +255,179 @@ where St: Stream, { type Output = Option<&'a St::Item>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(peekable) = self.inner.take() { - match peekable.do_poll_peek(cx) { - Either::Left(peekable) => { - self.inner = Some(peekable); - Poll::Pending + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some(peekable) = inner { + ready!(peekable.as_mut().poll_peek(cx)); + + inner.take().unwrap().poll_peek(cx) + } else { + panic!("Peek polled after completion") + } + } +} + +pin_project! { + /// Future for the [`Peekable::peek_mut`](self::Peekable::peek_mut) method. + #[must_use = "futures do nothing unless polled"] + pub struct PeekMut<'a, St: Stream> { + inner: Option>>, + } +} + +impl fmt::Debug for PeekMut<'_, St> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PeekMut").field("inner", &self.inner).finish() + } +} + +impl FusedFuture for PeekMut<'_, St> { + fn is_terminated(&self) -> bool { + self.inner.is_none() + } +} + +impl<'a, St> Future for PeekMut<'a, St> +where + St: Stream, +{ + type Output = Option<&'a mut St::Item>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some(peekable) = inner { + ready!(peekable.as_mut().poll_peek_mut(cx)); + + inner.take().unwrap().poll_peek_mut(cx) + } else { + panic!("PeekMut polled after completion") + } + } +} + +pin_project! { + /// Future for the [`Peekable::next_if`](self::Peekable::next_if) method. + #[must_use = "futures do nothing unless polled"] + pub struct NextIf<'a, St: Stream, F> { + inner: Option<(Pin<&'a mut Peekable>, F)>, + } +} + +impl fmt::Debug for NextIf<'_, St, F> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NextIf").field("inner", &self.inner.as_ref().map(|(s, _f)| s)).finish() + } +} + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl FusedFuture for NextIf<'_, St, F> +where + St: Stream, + F: for<'a> FnOnce1<&'a St::Item, Output = bool>, +{ + fn is_terminated(&self) -> bool { + self.inner.is_none() + } +} + +#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 +impl Future for NextIf<'_, St, F> +where + St: Stream, + F: for<'a> FnOnce1<&'a St::Item, Output = bool>, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some((peekable, _)) = inner { + let res = ready!(peekable.as_mut().poll_next(cx)); + + let (peekable, func) = inner.take().unwrap(); + match res { + Some(ref matched) if func.call_once(matched) => Poll::Ready(res), + other => { + let peekable = peekable.project(); + // Since we called `self.next()`, we consumed `self.peeked`. + assert!(peekable.peeked.is_none()); + *peekable.peeked = other; + Poll::Ready(None) } - Either::Right(peek) => Poll::Ready(peek), } } else { - panic!("Peek polled after completion") + panic!("NextIf polled after completion") } } } + +pin_project! { + /// Future for the [`Peekable::next_if_eq`](self::Peekable::next_if_eq) method. + #[must_use = "futures do nothing unless polled"] + pub struct NextIfEq<'a, St: Stream, T: ?Sized> { + #[pin] + inner: NextIf<'a, St, NextIfEqFn<'a, T, St::Item>>, + } +} + +impl fmt::Debug for NextIfEq<'_, St, T> +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, + T: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NextIfEq") + .field("inner", &self.inner.inner.as_ref().map(|(s, _f)| s)) + .finish() + } +} + +impl FusedFuture for NextIfEq<'_, St, T> +where + St: Stream, + T: ?Sized, + St::Item: PartialEq, +{ + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } +} + +impl Future for NextIfEq<'_, St, T> +where + St: Stream, + T: ?Sized, + St::Item: PartialEq, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().inner.poll(cx) + } +} + +struct NextIfEqFn<'a, T: ?Sized, Item> { + expected: &'a T, + _next: PhantomData, +} + +impl FnOnce1<&Item> for NextIfEqFn<'_, T, Item> +where + T: ?Sized, + Item: PartialEq, +{ + type Output = bool; + + fn call_once(self, next: &Item) -> Self::Output { + next == self.expected + } +} diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs new file mode 100644 index 0000000000..5ebc9582db --- /dev/null +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -0,0 +1,114 @@ +use crate::stream::Fuse; +use alloc::vec::Vec; +use core::mem; +use core::pin::Pin; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`ready_chunks`](super::StreamExt::ready_chunks) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct ReadyChunks { + #[pin] + stream: Fuse, + items: Vec, + cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 + } +} + +impl ReadyChunks +where + St: Stream, +{ + pub(super) fn new(stream: St, capacity: usize) -> Self { + assert!(capacity > 0); + + Self { + stream: super::Fuse::new(stream), + items: Vec::with_capacity(capacity), + cap: capacity, + } + } + + delegate_access_inner!(stream, St, (.)); +} + +impl Stream for ReadyChunks { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + loop { + match this.stream.as_mut().poll_next(cx) { + // Flush all collected data if underlying stream doesn't contain + // more ready values + Poll::Pending => { + return if this.items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(mem::replace(this.items, Vec::with_capacity(*this.cap)))) + } + } + + // Push the ready item into the buffer and check whether it is full. + // If so, replace our buffer with a new and empty one and return + // the full one. + Poll::Ready(Some(item)) => { + this.items.push(item); + if this.items.len() >= *this.cap { + return Poll::Ready(Some(mem::replace( + this.items, + Vec::with_capacity(*this.cap), + ))); + } + } + + // Since the underlying stream ran out of values, return what we + // have buffered, if we have anything. + Poll::Ready(None) => { + let last = if this.items.is_empty() { + None + } else { + let full_buf = mem::replace(this.items, Vec::new()); + Some(full_buf) + }; + + return Poll::Ready(last); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let (lower, upper) = self.stream.size_hint(); + let lower = lower.saturating_add(chunk_len); + let upper = match upper { + Some(x) => x.checked_add(chunk_len), + None => None, + }; + (lower, upper) + } +} + +impl FusedStream for ReadyChunks { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.items.is_empty() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for ReadyChunks +where + S: Stream + Sink, +{ + type Error = S::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures-util/src/stream/stream/scan.rs b/futures-util/src/stream/stream/scan.rs index 4f937f4fd9..f5cfde9c36 100644 --- a/futures-util/src/stream/stream/scan.rs +++ b/futures-util/src/stream/stream/scan.rs @@ -1,27 +1,30 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; struct StateFn { state: S, f: F, } -/// Stream for the [`scan`](super::StreamExt::scan) method. -#[must_use = "streams do nothing unless polled"] -pub struct Scan { - stream: St, - state_f: Option>, - future: Option, +pin_project! { + /// Stream for the [`scan`](super::StreamExt::scan) method. + #[must_use = "streams do nothing unless polled"] + pub struct Scan { + #[pin] + stream: St, + state_f: Option>, + #[pin] + future: Option, + } } -impl Unpin for Scan {} - impl fmt::Debug for Scan where St: Stream + fmt::Debug, @@ -40,10 +43,6 @@ where } impl Scan { - unsafe_pinned!(stream: St); - unsafe_unpinned!(state_f: Option>); - unsafe_pinned!(future: Option); - /// Checks if internal state is `None`. fn is_done_taking(&self) -> bool { self.state_f.is_none() @@ -56,48 +55,11 @@ where F: FnMut(&mut S, St::Item) -> Fut, Fut: Future>, { - pub(super) fn new(stream: St, initial_state: S, f: F) -> Scan { - Scan { - stream, - state_f: Some(StateFn { - state: initial_state, - f, - }), - future: None, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream + pub(super) fn new(stream: St, initial_state: S, f: F) -> Self { + Self { stream, state_f: Some(StateFn { state: initial_state, f }), future: None } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Scan @@ -108,29 +70,30 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.is_done_taking() { return Poll::Ready(None); } - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let state_f = self.as_mut().state_f().as_mut().unwrap(); - let fut = (state_f.f)(&mut state_f.state, item); - self.as_mut().future().set(Some(fut)); - } - - let item = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - if item.is_none() { - self.as_mut().state_f().take(); - } - - Poll::Ready(item) + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(fut) = this.future.as_mut().as_pin_mut() { + let item = ready!(fut.poll(cx)); + this.future.set(None); + + if item.is_none() { + *this.state_f = None; + } + + break item; + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + let state_f = this.state_f.as_mut().unwrap(); + this.future.set(Some((state_f.f)(&mut state_f.state, item))) + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { @@ -155,11 +118,11 @@ where // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Scan +impl Sink for Scan where - S: Stream + Sink, + St: Stream + Sink, { - type Error = S::Error; + type Error = St::Error; delegate_sink!(stream, Item); } diff --git a/futures-util/src/stream/stream/select_next_some.rs b/futures-util/src/stream/stream/select_next_some.rs index 884f252769..3115e14d9a 100644 --- a/futures-util/src/stream/stream/select_next_some.rs +++ b/futures-util/src/stream/stream/select_next_some.rs @@ -1,8 +1,9 @@ +use crate::stream::StreamExt; use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::FusedStream; -use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use crate::stream::StreamExt; /// Future for the [`select_next_some`](super::StreamExt::select_next_some) /// method. @@ -14,7 +15,7 @@ pub struct SelectNextSome<'a, St: ?Sized> { impl<'a, St: ?Sized> SelectNextSome<'a, St> { pub(super) fn new(stream: &'a mut St) -> Self { - SelectNextSome { stream } + Self { stream } } } diff --git a/futures-util/src/stream/stream/skip.rs b/futures-util/src/stream/stream/skip.rs index 0b7c632daf..f495779521 100644 --- a/futures-util/src/stream/stream/skip.rs +++ b/futures-util/src/stream/stream/skip.rs @@ -1,62 +1,28 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`skip`](super::StreamExt::skip) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Skip { - stream: St, - remaining: usize, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`skip`](super::StreamExt::skip) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Skip { + #[pin] + stream: St, + remaining: usize, + } } -impl Unpin for Skip {} - impl Skip { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - - pub(super) fn new(stream: St, n: usize) -> Skip { - Skip { - stream, - remaining: n, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream + pub(super) fn new(stream: St, n: usize) -> Self { + Self { stream, remaining: n } } - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Skip { @@ -68,28 +34,25 @@ impl FusedStream for Skip { impl Stream for Skip { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - while self.remaining > 0 { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(_) => *self.as_mut().remaining() -= 1, - None => return Poll::Ready(None), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + while *this.remaining > 0 { + if ready!(this.stream.as_mut().poll_next(cx)).is_some() { + *this.remaining -= 1; + } else { + return Poll::Ready(None); } } - self.as_mut().stream().poll_next(cx) + this.stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { let (lower, upper) = self.stream.size_hint(); - let lower = lower.saturating_sub(self.remaining as usize); - let upper = match upper { - Some(x) => Some(x.saturating_sub(self.remaining as usize)), - None => None, - }; + let lower = lower.saturating_sub(self.remaining); + let upper = upper.map(|x| x.saturating_sub(self.remaining)); (lower, upper) } diff --git a/futures-util/src/stream/stream/skip_while.rs b/futures-util/src/stream/stream/skip_while.rs index 666d9deabc..50a21a21ae 100644 --- a/futures-util/src/stream/stream/skip_while.rs +++ b/futures-util/src/stream/stream/skip_while.rs @@ -1,24 +1,27 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`skip_while`](super::StreamExt::skip_while) method. -#[must_use = "streams do nothing unless polled"] -pub struct SkipWhile where St: Stream { - stream: St, - f: F, - pending_fut: Option, - pending_item: Option, - done_skipping: bool, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`skip_while`](super::StreamExt::skip_while) method. + #[must_use = "streams do nothing unless polled"] + pub struct SkipWhile where St: Stream { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + done_skipping: bool, + } } -impl Unpin for SkipWhile {} - impl fmt::Debug for SkipWhile where St: Stream + fmt::Debug, @@ -36,63 +39,23 @@ where } impl SkipWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - - pub(super) fn new(stream: St, f: F) -> SkipWhile { - SkipWhile { - stream, - f, - pending_fut: None, - pending_item: None, - done_skipping: false, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending_fut: None, pending_item: None, done_skipping: false } } - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for SkipWhile - where St: FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_item.is_none() && self.stream.is_terminated() @@ -100,59 +63,60 @@ impl FusedStream for SkipWhile } impl Stream for SkipWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().poll_next(cx); - } - - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *this.done_skipping { + return this.stream.poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(item)) + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let skipped = ready!(fut.poll(cx)); + let item = this.pending_item.take(); + this.pending_fut.set(None); + if !skipped { + *this.done_skipping = true; + break item; + } + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + this.pending_fut.set(Some((this.f)(&item))); + *this.pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; - let (_, upper) = self.stream.size_hint(); - let upper = match upper { - Some(x) => x.checked_add(pending_len), - None => None, - }; - (0, upper) // can't know a lower bound, due to the predicate + if self.done_skipping { + self.stream.size_hint() + } else { + let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let (_, upper) = self.stream.size_hint(); + let upper = match upper { + Some(x) => x.checked_add(pending_len), + None => None, + }; + (0, upper) // can't know a lower bound, due to the predicate + } } } // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for SkipWhile - where S: Stream + Sink, - F: FnMut(&S::Item) -> Fut, - Fut: Future, +where + S: Stream + Sink, + F: FnMut(&S::Item) -> Fut, + Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/split.rs b/futures-util/src/stream/stream/split.rs index 4118b33462..3a72fee30b 100644 --- a/futures-util/src/stream/stream/split.rs +++ b/futures-util/src/stream/stream/split.rs @@ -1,14 +1,16 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use core::fmt; -use core::pin::Pin; use crate::lock::BiLock; /// A `Stream` part of the split pair #[derive(Debug)] #[must_use = "streams do nothing unless polled"] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub struct SplitStream(BiLock); impl Unpin for SplitStream {} @@ -16,9 +18,10 @@ impl Unpin for SplitStream {} impl SplitStream { /// Attempts to put the two "halves" of a split `Stream + Sink` back /// together. Succeeds only if the `SplitStream` and `SplitSink` are - /// a matching pair originating from the same call to `Stream::split`. + /// a matching pair originating from the same call to `StreamExt::split`. pub fn reunite(self, other: SplitSink) -> Result> - where S: Sink, + where + S: Sink, { other.reunite(self) } @@ -34,15 +37,13 @@ impl Stream for SplitStream { #[allow(bad_style)] fn SplitSink, Item>(lock: BiLock) -> SplitSink { - SplitSink { - lock, - slot: None, - } + SplitSink { lock, slot: None } } /// A `Sink` part of the split pair #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub struct SplitSink { lock: BiLock, slot: Option, @@ -53,16 +54,18 @@ impl Unpin for SplitSink {} impl + Unpin, Item> SplitSink { /// Attempts to put the two "halves" of a split `Stream + Sink` back /// together. Succeeds only if the `SplitStream` and `SplitSink` are - /// a matching pair originating from the same call to `Stream::split`. + /// a matching pair originating from the same call to `StreamExt::split`. pub fn reunite(self, other: SplitStream) -> Result> { - self.lock.reunite(other.0).map_err(|err| { - ReuniteError(SplitSink(err.0), SplitStream(err.1)) - }) + self.lock.reunite(other.0).map_err(|err| ReuniteError(SplitSink(err.0), SplitStream(err.1))) } } impl, Item> SplitSink { - fn poll_flush_slot(mut inner: Pin<&mut S>, slot: &mut Option, cx: &mut Context<'_>) -> Poll> { + fn poll_flush_slot( + mut inner: Pin<&mut S>, + slot: &mut Option, + cx: &mut Context<'_>, + ) -> Poll> { if slot.is_some() { ready!(inner.as_mut().poll_ready(cx))?; Poll::Ready(inner.start_send(slot.take().unwrap())) @@ -71,7 +74,10 @@ impl, Item> SplitSink { } } - fn poll_lock_and_flush_slot(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_lock_and_flush_slot( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = &mut *self; let mut inner = ready!(this.lock.poll_lock(cx)); Self::poll_flush_slot(inner.as_pin_mut(), &mut this.slot, cx) @@ -119,13 +125,12 @@ pub(super) fn split, Item>(s: S) -> (SplitSink, /// Error indicating a `SplitSink` and `SplitStream` were not two halves /// of a `Stream + Split`, and thus could not be `reunite`d. +#[cfg_attr(docsrs, doc(cfg(feature = "sink")))] pub struct ReuniteError(pub SplitSink, pub SplitStream); impl fmt::Debug for ReuniteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ReuniteError") - .field(&"...") - .finish() + f.debug_tuple("ReuniteError").field(&"...").finish() } } diff --git a/futures-util/src/stream/stream/take.rs b/futures-util/src/stream/stream/take.rs index 1109a4a0f0..b1c728e333 100644 --- a/futures-util/src/stream/stream/take.rs +++ b/futures-util/src/stream/stream/take.rs @@ -1,81 +1,47 @@ use core::cmp; use core::pin::Pin; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`take`](super::StreamExt::take) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Take { - stream: St, - remaining: usize, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`take`](super::StreamExt::take) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Take { + #[pin] + stream: St, + remaining: usize, + } } -impl Unpin for Take {} - impl Take { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - - pub(super) fn new(stream: St, n: usize) -> Take { - Take { - stream, - remaining: n, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream + pub(super) fn new(stream: St, n: usize) -> Self { + Self { stream, remaining: n } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Take - where St: Stream, +where + St: Stream, { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.remaining == 0 { Poll::Ready(None) } else { - let next = ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + let this = self.project(); + let next = ready!(this.stream.poll_next(cx)); + if next.is_some() { + *this.remaining -= 1; + } else { + *this.remaining = 0; } Poll::Ready(next) } @@ -92,7 +58,7 @@ impl Stream for Take let upper = match upper { Some(x) if x < self.remaining as usize => Some(x), - _ => Some(self.remaining as usize) + _ => Some(self.remaining as usize), }; (lower, upper) @@ -100,7 +66,8 @@ impl Stream for Take } impl FusedStream for Take - where St: FusedStream, +where + St: FusedStream, { fn is_terminated(&self) -> bool { self.remaining == 0 || self.stream.is_terminated() @@ -110,7 +77,8 @@ impl FusedStream for Take // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Take - where S: Stream + Sink, +where + S: Stream + Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/take_until.rs b/futures-util/src/stream/stream/take_until.rs new file mode 100644 index 0000000000..d14f9ce100 --- /dev/null +++ b/futures-util/src/stream/stream/take_until.rs @@ -0,0 +1,170 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::Future; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +// FIXME: docs, tests + +pin_project! { + /// Stream for the [`take_until`](super::StreamExt::take_until) method. + #[must_use = "streams do nothing unless polled"] + pub struct TakeUntil { + #[pin] + stream: St, + // Contains the inner Future on start and None once the inner Future is resolved + // or taken out by the user. + #[pin] + fut: Option, + // Contains fut's return value once fut is resolved + fut_result: Option, + // Whether the future was taken out by the user. + free: bool, + } +} + +impl fmt::Debug for TakeUntil +where + St: Stream + fmt::Debug, + St::Item: fmt::Debug, + Fut: Future + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TakeUntil").field("stream", &self.stream).field("fut", &self.fut).finish() + } +} + +impl TakeUntil +where + St: Stream, + Fut: Future, +{ + pub(super) fn new(stream: St, fut: Fut) -> Self { + Self { stream, fut: Some(fut), fut_result: None, free: false } + } + + delegate_access_inner!(stream, St, ()); + + /// Extract the stopping future out of the combinator. + /// The future is returned only if it isn't resolved yet, ie. if the stream isn't stopped yet. + /// Taking out the future means the combinator will be yielding + /// elements from the wrapped stream without ever stopping it. + pub fn take_future(&mut self) -> Option { + if self.fut.is_some() { + self.free = true; + } + + self.fut.take() + } + + /// Once the stopping future is resolved, this method can be used + /// to extract the value returned by the stopping future. + /// + /// This may be used to retrieve arbitrary data from the stopping + /// future, for example a reason why the stream was stopped. + /// + /// This method will return `None` if the future isn't resolved yet, + /// or if the result was already taken out. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future; + /// use futures::stream::{self, StreamExt}; + /// use futures::task::Poll; + /// + /// let stream = stream::iter(1..=10); + /// + /// let mut i = 0; + /// let stop_fut = future::poll_fn(|_cx| { + /// i += 1; + /// if i <= 5 { + /// Poll::Pending + /// } else { + /// Poll::Ready("reason") + /// } + /// }); + /// + /// let mut stream = stream.take_until(stop_fut); + /// let _ = stream.by_ref().collect::>().await; + /// + /// let result = stream.take_result().unwrap(); + /// assert_eq!(result, "reason"); + /// # }); + /// ``` + pub fn take_result(&mut self) -> Option { + self.fut_result.take() + } + + /// Whether the stream was stopped yet by the stopping future + /// being resolved. + pub fn is_stopped(&self) -> bool { + !self.free && self.fut.is_none() + } +} + +impl Stream for TakeUntil +where + St: Stream, + Fut: Future, +{ + type Item = St::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if let Some(f) = this.fut.as_mut().as_pin_mut() { + if let Poll::Ready(result) = f.poll(cx) { + this.fut.set(None); + *this.fut_result = Some(result); + } + } + + if !*this.free && this.fut.is_none() { + // Future resolved, inner stream stopped + Poll::Ready(None) + } else { + // Future either not resolved yet or taken out by the user + let item = ready!(this.stream.poll_next(cx)); + if item.is_none() { + this.fut.set(None); + } + Poll::Ready(item) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.is_stopped() { + return (0, Some(0)); + } + + self.stream.size_hint() + } +} + +impl FusedStream for TakeUntil +where + St: Stream, + Fut: Future, +{ + fn is_terminated(&self) -> bool { + self.is_stopped() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TakeUntil +where + S: Stream + Sink, + Fut: Future, +{ + type Error = S::Error; + + delegate_sink!(stream, Item); +} diff --git a/futures-util/src/stream/stream/take_while.rs b/futures-util/src/stream/stream/take_while.rs index 68606ec263..01b27654b8 100644 --- a/futures-util/src/stream/stream/take_while.rs +++ b/futures-util/src/stream/stream/take_while.rs @@ -1,24 +1,27 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`take_while`](super::StreamExt::take_while) method. -#[must_use = "streams do nothing unless polled"] -pub struct TakeWhile { - stream: St, - f: F, - pending_fut: Option, - pending_item: Option, - done_taking: bool, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`take_while`](super::StreamExt::take_while) method. + #[must_use = "streams do nothing unless polled"] + pub struct TakeWhile { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + done_taking: bool, + } } -impl Unpin for TakeWhile {} - impl fmt::Debug for TakeWhile where St: Stream + fmt::Debug, @@ -35,97 +38,52 @@ where } } -impl TakeWhile { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_taking: bool); -} - impl TakeWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { - pub(super) fn new(stream: St, f: F) -> TakeWhile { - TakeWhile { - stream, - f, - pending_fut: None, - pending_item: None, - done_taking: false, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending_fut: None, pending_item: None, done_taking: false } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TakeWhile - where St: Stream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { type Item = St::Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.done_taking { return Poll::Ready(None); } - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let take = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if take { - Poll::Ready(Some(item)) - } else { - *self.as_mut().done_taking() = true; - Poll::Ready(None) - } + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let take = ready!(fut.poll(cx)); + let item = this.pending_item.take(); + this.pending_fut.set(None); + if take { + break item; + } else { + *this.done_taking = true; + break None; + } + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + this.pending_fut.set(Some((this.f)(&item))); + *this.pending_item = Some(item); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { @@ -144,9 +102,10 @@ impl Stream for TakeWhile } impl FusedStream for TakeWhile - where St: FusedStream, - F: FnMut(&St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(&St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.done_taking || self.pending_item.is_none() && self.stream.is_terminated() @@ -156,7 +115,8 @@ impl FusedStream for TakeWhile // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TakeWhile - where S: Stream + Sink, +where + S: Stream + Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/then.rs b/futures-util/src/stream/stream/then.rs index 39843b2504..d4531d4b94 100644 --- a/futures-util/src/stream/stream/then.rs +++ b/futures-util/src/stream/stream/then.rs @@ -1,90 +1,52 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`then`](super::StreamExt::then) method. -#[must_use = "streams do nothing unless polled"] -pub struct Then { - stream: St, - future: Option, - f: F, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`then`](super::StreamExt::then) method. + #[must_use = "streams do nothing unless polled"] + pub struct Then { + #[pin] + stream: St, + #[pin] + future: Option, + f: F, + } } -impl Unpin for Then {} - impl fmt::Debug for Then where St: fmt::Debug, Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Then") - .field("stream", &self.stream) - .field("future", &self.future) - .finish() + f.debug_struct("Then").field("stream", &self.stream).field("future", &self.future).finish() } } -impl Then { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl Then - where St: Stream, - F: FnMut(St::Item) -> Fut, +where + St: Stream, + F: FnMut(St::Item) -> Fut, { - pub(super) fn new(stream: St, f: F) -> Then { - Then { - stream, - future: None, - f, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, future: None, f } } - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Then - where St: FusedStream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: FusedStream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -92,28 +54,27 @@ impl FusedStream for Then } impl Stream for Then - where St: Stream, - F: FnMut(St::Item) -> Fut, - Fut: Future, +where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, { type Item = Fut::Output; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + fn poll_next(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() { + let item = ready!(fut.poll(cx)); + this.future.set(None); + break Some(item); + } else if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) { + this.future.set(Some((this.f)(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { @@ -131,7 +92,8 @@ impl Stream for Then // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for Then - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/unzip.rs b/futures-util/src/stream/stream/unzip.rs new file mode 100644 index 0000000000..15f22e80b0 --- /dev/null +++ b/futures-util/src/stream/stream/unzip.rs @@ -0,0 +1,63 @@ +use core::mem; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`unzip`](super::StreamExt::unzip) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Unzip { + #[pin] + stream: St, + left: FromA, + right: FromB, + } +} + +impl Unzip { + fn finish(self: Pin<&mut Self>) -> (FromA, FromB) { + let this = self.project(); + (mem::replace(this.left, Default::default()), mem::replace(this.right, Default::default())) + } + + pub(super) fn new(stream: St) -> Self { + Self { stream, left: Default::default(), right: Default::default() } + } +} + +impl FusedFuture for Unzip +where + St: FusedStream, + FromA: Default + Extend, + FromB: Default + Extend, +{ + fn is_terminated(&self) -> bool { + self.stream.is_terminated() + } +} + +impl Future for Unzip +where + St: Stream, + FromA: Default + Extend, + FromB: Default + Extend, +{ + type Output = (FromA, FromB); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<(FromA, FromB)> { + let mut this = self.as_mut().project(); + loop { + match ready!(this.stream.as_mut().poll_next(cx)) { + Some(e) => { + this.left.extend(Some(e.0)); + this.right.extend(Some(e.1)); + } + None => return Poll::Ready(self.finish()), + } + } + } +} diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index f97ac17d35..360a8b63bb 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -1,42 +1,27 @@ -use crate::stream::{StreamExt, Fuse}; +use crate::stream::{Fuse, StreamExt}; use core::cmp; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`zip`](super::StreamExt::zip) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct Zip { - stream1: Fuse, - stream2: Fuse, - queued1: Option, - queued2: Option, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`zip`](super::StreamExt::zip) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct Zip { + #[pin] + stream1: Fuse, + #[pin] + stream2: Fuse, + queued1: Option, + queued2: Option, + } } -#[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4323 -impl Unpin for Zip -where - St1: Stream, - Fuse: Unpin, - St2: Stream, - Fuse: Unpin, -{} - impl Zip { - unsafe_pinned!(stream1: Fuse); - unsafe_pinned!(stream2: Fuse); - unsafe_unpinned!(queued1: Option); - unsafe_unpinned!(queued2: Option); - - pub(super) fn new(stream1: St1, stream2: St2) -> Zip { - Zip { - stream1: stream1.fuse(), - stream2: stream2.fuse(), - queued1: None, - queued2: None, - } + pub(super) fn new(stream1: St1, stream2: St2) -> Self { + Self { stream1: stream1.fuse(), stream2: stream2.fuse(), queued1: None, queued2: None } } /// Acquires a reference to the underlying streams that this combinator is @@ -60,10 +45,8 @@ impl Zip { /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - unsafe { - let Self { stream1, stream2, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(stream1).get_pin_mut(), Pin::new_unchecked(stream2).get_pin_mut()) - } + let this = self.project(); + (this.stream1.get_pin_mut(), this.stream2.get_pin_mut()) } /// Consumes this combinator, returning the underlying streams. @@ -76,7 +59,9 @@ impl Zip { } impl FusedStream for Zip - where St1: Stream, St2: Stream, +where + St1: Stream, + St2: Stream, { fn is_terminated(&self) -> bool { self.stream1.is_terminated() && self.stream2.is_terminated() @@ -84,32 +69,32 @@ impl FusedStream for Zip } impl Stream for Zip - where St1: Stream, St2: Stream +where + St1: Stream, + St2: Stream, { type Item = (St1::Item, St2::Item); - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.queued1.is_none() { - match self.as_mut().stream1().poll_next(cx) { - Poll::Ready(Some(item1)) => *self.as_mut().queued1() = Some(item1), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if this.queued1.is_none() { + match this.stream1.as_mut().poll_next(cx) { + Poll::Ready(Some(item1)) => *this.queued1 = Some(item1), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued2.is_none() { - match self.as_mut().stream2().poll_next(cx) { - Poll::Ready(Some(item2)) => *self.as_mut().queued2() = Some(item2), + if this.queued2.is_none() { + match this.stream2.as_mut().poll_next(cx) { + Poll::Ready(Some(item2)) => *this.queued2 = Some(item2), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued1.is_some() && self.queued2.is_some() { - let pair = (self.as_mut().queued1().take().unwrap(), - self.as_mut().queued2().take().unwrap()); + if this.queued1.is_some() && this.queued2.is_some() { + let pair = (this.queued1.take().unwrap(), this.queued2.take().unwrap()); Poll::Ready(Some(pair)) - } else if self.stream1.is_done() || self.stream2.is_done() { + } else if this.stream1.is_done() || this.stream2.is_done() { Poll::Ready(None) } else { Poll::Pending @@ -135,7 +120,7 @@ impl Stream for Zip } (Some(x), None) => x.checked_add(queued1_len), (None, Some(y)) => y.checked_add(queued2_len), - (None, None) => None + (None, None) => None, }; (lower, upper) diff --git a/futures-util/src/stream/try_stream/and_then.rs b/futures-util/src/stream/try_stream/and_then.rs index 809c32a94b..a7b50db0b1 100644 --- a/futures-util/src/stream/try_stream/and_then.rs +++ b/futures-util/src/stream/try_stream/and_then.rs @@ -1,22 +1,25 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; -use futures_core::stream::{Stream, TryStream, FusedStream}; +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_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`and_then`](super::TryStreamExt::and_then) method. -#[must_use = "streams do nothing unless polled"] -pub struct AndThen { - stream: St, - future: Option, - f: F, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`and_then`](super::TryStreamExt::and_then) method. + #[must_use = "streams do nothing unless polled"] + pub struct AndThen { + #[pin] + stream: St, + #[pin] + future: Option, + f: F, + } } -impl Unpin for AndThen {} - impl fmt::Debug for AndThen where St: fmt::Debug, @@ -30,77 +33,41 @@ where } } -impl AndThen { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl AndThen - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for AndThen - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + fn poll_next(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() { + let item = ready!(fut.try_poll(cx)); + this.future.set(None); + break Some(item); + } else if let Some(item) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + this.future.set(Some((this.f)(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { @@ -116,9 +83,10 @@ impl Stream for AndThen } impl FusedStream for AndThen - where St: TryStream + FusedStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -128,7 +96,8 @@ impl FusedStream for AndThen // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for AndThen - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/err_into.rs b/futures-util/src/stream/try_stream/err_into.rs deleted file mode 100644 index f5d92945f3..0000000000 --- a/futures-util/src/stream/try_stream/err_into.rs +++ /dev/null @@ -1,98 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`err_into`](super::TryStreamExt::err_into) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct ErrInto { - stream: St, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(stream: St); - - pub(super) fn new(stream: St) -> Self { - ErrInto { stream, _marker: PhantomData } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for ErrInto -where - St: TryStream + FusedStream, - St::Error: Into, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for ErrInto -where - St: TryStream, - St::Error: Into, -{ - type Item = Result; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().try_poll_next(cx) - .map(|res| res.map(|some| some.map_err(Into::into))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for ErrInto -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_err.rs b/futures-util/src/stream/try_stream/inspect_err.rs deleted file mode 100644 index 3c23ae0395..0000000000 --- a/futures-util/src/stream/try_stream/inspect_err.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectErr { - stream: St, - f: F, -} - -impl Unpin for InspectErr {} - -impl fmt::Debug for InspectErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectErr") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectErr -where - St: TryStream + FusedStream, - F: FnMut(&St::Error), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_ok.rs b/futures-util/src/stream/try_stream/inspect_ok.rs deleted file mode 100644 index 89fb459be9..0000000000 --- a/futures-util/src/stream/try_stream/inspect_ok.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectOk { - stream: St, - f: F, -} - -impl Unpin for InspectOk {} - -impl fmt::Debug for InspectOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectOk") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectOk -where - St: TryStream + FusedStream, - F: FnMut(&St::Ok), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/into_async_read.rs b/futures-util/src/stream/try_stream/into_async_read.rs index 71bbfd1c72..914b277a02 100644 --- a/futures-util/src/stream/try_stream/into_async_read.rs +++ b/futures-util/src/stream/try_stream/into_async_read.rs @@ -1,14 +1,16 @@ use crate::stream::TryStreamExt; use core::pin::Pin; +use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use futures_io::{AsyncRead, AsyncWrite, AsyncBufRead}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncWrite}; use std::cmp; use std::io::{Error, Result}; /// Reader for the [`into_async_read`](super::TryStreamExt::into_async_read) method. #[derive(Debug)] #[must_use = "readers do nothing unless polled"] +#[cfg_attr(docsrs, doc(cfg(feature = "io")))] pub struct IntoAsyncRead where St: TryStream + Unpin, @@ -38,10 +40,7 @@ where St::Ok: AsRef<[u8]>, { pub(super) fn new(stream: St) -> Self { - IntoAsyncRead { - stream, - state: ReadState::PendingChunk, - } + Self { stream, state: ReadState::PendingChunk } } } @@ -61,9 +60,7 @@ where let chunk = chunk.as_ref(); let len = cmp::min(buf.len(), chunk.len() - *chunk_start); - buf[..len].copy_from_slice( - &chunk[*chunk_start..*chunk_start + len], - ); + buf[..len].copy_from_slice(&chunk[*chunk_start..*chunk_start + len]); *chunk_start += len; if chunk.len() == *chunk_start { @@ -72,26 +69,21 @@ where return Poll::Ready(Ok(len)); } - ReadState::PendingChunk => { - match ready!(self.stream.try_poll_next_unpin(cx)) { - Some(Ok(chunk)) => { - if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { - chunk, - chunk_start: 0, - }; - } - } - Some(Err(err)) => { - self.state = ReadState::Eof; - return Poll::Ready(Err(err)); - } - None => { - self.state = ReadState::Eof; - return Poll::Ready(Ok(0)); + ReadState::PendingChunk => match ready!(self.stream.try_poll_next_unpin(cx)) { + Some(Ok(chunk)) => { + if !chunk.as_ref().is_empty() { + self.state = ReadState::Ready { chunk, chunk_start: 0 }; } } - } + Some(Err(err)) => { + self.state = ReadState::Eof; + return Poll::Ready(Err(err)); + } + None => { + self.state = ReadState::Eof; + return Poll::Ready(Ok(0)); + } + }, ReadState::Eof => { return Poll::Ready(Ok(0)); } @@ -108,23 +100,17 @@ where fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - buf: &[u8] + buf: &[u8], ) -> Poll> { - Pin::new( &mut self.stream ).poll_write( cx, buf ) + Pin::new(&mut self.stream).poll_write(cx, buf) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_> - ) -> Poll> { - Pin::new( &mut self.stream ).poll_flush( cx ) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_flush(cx) } - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_> - ) -> Poll> { - Pin::new( &mut self.stream ).poll_close( cx ) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.stream).poll_close(cx) } } @@ -133,18 +119,12 @@ where St: TryStream + Unpin, St::Ok: AsRef<[u8]>, { - fn poll_fill_buf( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { while let ReadState::PendingChunk = self.state { match ready!(self.stream.try_poll_next_unpin(cx)) { Some(Ok(chunk)) => { if !chunk.as_ref().is_empty() { - self.state = ReadState::Ready { - chunk, - chunk_start: 0, - }; + self.state = ReadState::Ready { chunk, chunk_start: 0 }; } } Some(Err(err)) => { @@ -167,12 +147,11 @@ where Poll::Ready(Ok(&[])) } - fn consume( - mut self: Pin<&mut Self>, - amount: usize, - ) { - // https://github.com/rust-lang/futures-rs/pull/1556#discussion_r281644295 - if amount == 0 { return } + fn consume(mut self: Pin<&mut Self>, amount: usize) { + // https://github.com/rust-lang/futures-rs/pull/1556#discussion_r281644295 + if amount == 0 { + return; + } if let ReadState::Ready { chunk, chunk_start } = &mut self.state { *chunk_start += amount; debug_assert!(*chunk_start <= chunk.as_ref().len()); diff --git a/futures-util/src/stream/try_stream/into_stream.rs b/futures-util/src/stream/try_stream/into_stream.rs index b0fa07aa79..2126258af7 100644 --- a/futures-util/src/stream/try_stream/into_stream.rs +++ b/futures-util/src/stream/try_stream/into_stream.rs @@ -3,54 +3,25 @@ use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Stream for the [`into_stream`](super::TryStreamExt::into_stream) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct IntoStream { - stream: St, +pin_project! { + /// Stream for the [`into_stream`](super::TryStreamExt::into_stream) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct IntoStream { + #[pin] + stream: St, + } } impl IntoStream { - unsafe_pinned!(stream: St); - #[inline] pub(super) fn new(stream: St) -> Self { - IntoStream { stream } + Self { stream } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for IntoStream { @@ -63,11 +34,8 @@ impl Stream for IntoStream { type Item = Result; #[inline] - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().try_poll_next(cx) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().stream.try_poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/map_err.rs b/futures-util/src/stream/try_stream/map_err.rs deleted file mode 100644 index 1b98d6b4bc..0000000000 --- a/futures-util/src/stream/try_stream/map_err.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_err`](super::TryStreamExt::map_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapErr { - stream: St, - f: F, -} - -impl Unpin for MapErr {} - -impl fmt::Debug for MapErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapErr") - .field("stream", &self.stream) - .finish() - } -} - -impl MapErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapErr. - pub(super) fn new(stream: St, f: F) -> Self { - MapErr { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapErr -where - St: TryStream + FusedStream, - F: FnMut(St::Error) -> E, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapErr -where - St: TryStream, - F: FnMut(St::Error) -> E, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| self.as_mut().f()(e)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/map_ok.rs b/futures-util/src/stream/try_stream/map_ok.rs deleted file mode 100644 index 19d01be459..0000000000 --- a/futures-util/src/stream/try_stream/map_ok.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapOk { - stream: St, - f: F, -} - -impl Unpin for MapOk {} - -impl fmt::Debug for MapOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapOk") - .field("stream", &self.stream) - .finish() - } -} - -impl MapOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapOk. - pub(super) fn new(stream: St, f: F) -> Self { - MapOk { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapOk -where - St: TryStream + FusedStream, - F: FnMut(St::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapOk -where - St: TryStream, - F: FnMut(St::Ok) -> T, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|x| self.as_mut().f()(x)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 6a7ced4f8c..6bf2cb74a5 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -5,6 +5,15 @@ #[cfg(feature = "compat")] use crate::compat::Compat; +use crate::fns::{ + inspect_err_fn, inspect_ok_fn, into_fn, map_err_fn, map_ok_fn, InspectErrFn, InspectOkFn, + IntoFn, MapErrFn, MapOkFn, +}; +use crate::future::assert_future; +use crate::stream::assert_stream; +use crate::stream::{Inspect, Map}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::pin::Pin; use futures_core::{ future::{Future, TryFuture}, @@ -16,29 +25,44 @@ mod and_then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::and_then::AndThen; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; +delegate_all!( + /// Stream for the [`err_into`](super::TryStreamExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] +); -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; +delegate_all!( + /// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] +); -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; +delegate_all!( + /// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] +); mod into_stream; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_stream::IntoStream; -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; +delegate_all!( + /// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] +); -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; +delegate_all!( + /// Stream for the [`map_err`](super::TryStreamExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] +); mod or_else; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -72,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; @@ -84,24 +114,39 @@ mod try_skip_while; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::try_skip_while::TrySkipWhile; -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; +mod try_take_while; +#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +pub use self::try_take_while::TryTakeWhile; - #[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_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(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(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")] mod into_async_read; #[cfg(feature = "io")] +#[cfg_attr(docsrs, doc(cfg(feature = "io")))] #[cfg(feature = "std")] #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_async_read::IntoAsyncRead; @@ -132,7 +177,7 @@ pub trait TryStreamExt: TryStream { Self: Sized, Self::Error: Into, { - ErrInto::new(self) + assert_stream::, _>(ErrInto::new(self)) } /// Wraps the current stream in a new stream which maps the success value @@ -157,7 +202,7 @@ pub trait TryStreamExt: TryStream { Self: Sized, F: FnMut(Self::Ok) -> T, { - MapOk::new(self, f) + assert_stream::, _>(MapOk::new(self, f)) } /// Wraps the current stream in a new stream which maps the error value @@ -182,7 +227,7 @@ pub trait TryStreamExt: TryStream { Self: Sized, F: FnMut(Self::Error) -> E, { - MapErr::new(self, f) + assert_stream::, _>(MapErr::new(self, f)) } /// Chain on a computation for when a value is ready, passing the successful @@ -229,7 +274,7 @@ pub trait TryStreamExt: TryStream { Fut: TryFuture, Self: Sized, { - AndThen::new(self, f) + assert_stream::, _>(AndThen::new(self, f)) } /// Chain on a computation for when an error happens, passing the @@ -255,7 +300,7 @@ pub trait TryStreamExt: TryStream { Fut: TryFuture, Self: Sized, { - OrElse::new(self, f) + assert_stream::, _>(OrElse::new(self, f)) } /// Do something with the success value of this stream, afterwards passing @@ -269,7 +314,7 @@ pub trait TryStreamExt: TryStream { F: FnMut(&Self::Ok), Self: Sized, { - InspectOk::new(self, f) + assert_stream::, _>(InspectOk::new(self, f)) } /// Do something with the error value of this stream, afterwards passing it on. @@ -282,7 +327,7 @@ pub trait TryStreamExt: TryStream { F: FnMut(&Self::Error), Self: Sized, { - InspectErr::new(self, f) + assert_stream::, _>(InspectErr::new(self, f)) } /// Wraps a [`TryStream`] into a type that implements @@ -310,7 +355,7 @@ pub trait TryStreamExt: TryStream { where Self: Sized, { - IntoStream::new(self) + assert_stream::, _>(IntoStream::new(self)) } /// Creates a future that attempts to resolve the next item in the stream. @@ -337,7 +382,7 @@ pub trait TryStreamExt: TryStream { where Self: Unpin, { - TryNext::new(self) + assert_future::, Self::Error>, _>(TryNext::new(self)) } /// Attempts to run this stream to completion, executing the provided @@ -379,14 +424,15 @@ pub trait TryStreamExt: TryStream { Fut: TryFuture, Self: Sized, { - TryForEach::new(self, f) + assert_future::, _>(TryForEach::new(self, f)) } /// Skip elements on this stream while the provided asynchronous predicate /// resolves to `true`. /// - /// This function is similar to [`StreamExt::skip_while`](crate::stream::StreamExt::skip_while) - /// but exits early if an error occurs. + /// This function is similar to + /// [`StreamExt::skip_while`](crate::stream::StreamExt::skip_while) but exits + /// early if an error occurs. /// /// # Examples /// @@ -408,7 +454,37 @@ pub trait TryStreamExt: TryStream { Fut: TryFuture, Self: Sized, { - TrySkipWhile::new(self, f) + assert_stream::, _>(TrySkipWhile::new(self, f)) + } + + /// Take elements on this stream while the provided asynchronous predicate + /// resolves to `true`. + /// + /// This function is similar to + /// [`StreamExt::take_while`](crate::stream::StreamExt::take_while) but exits + /// early if an error occurs. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future; + /// use futures::stream::{self, TryStreamExt}; + /// + /// let stream = stream::iter(vec![Ok::(1), Ok(2), Ok(3), Ok(2)]); + /// let stream = stream.try_take_while(|x| future::ready(Ok(*x < 3))); + /// + /// let output: Result, i32> = stream.try_collect().await; + /// assert_eq!(output, Ok(vec![1, 2])); + /// # }) + /// ``` + fn try_take_while(self, f: F) -> TryTakeWhile + where + F: FnMut(&Self::Ok) -> Fut, + Fut: TryFuture, + Self: Sized, + { + assert_stream::, _>(TryTakeWhile::new(self, f)) } /// Attempts to run this stream to completion, executing the provided asynchronous @@ -452,7 +528,7 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(Err(oneshot::Canceled), fut.await); /// # }) /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn try_for_each_concurrent( self, @@ -464,7 +540,11 @@ pub trait TryStreamExt: TryStream { Fut: Future>, Self: Sized, { - TryForEachConcurrent::new(self, limit.into(), f) + assert_future::, _>(TryForEachConcurrent::new( + self, + limit.into(), + f, + )) } /// Attempt to transform a stream into a collection, @@ -501,7 +581,54 @@ pub trait TryStreamExt: TryStream { where Self: Sized, { - TryCollect::new(self) + 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 @@ -540,7 +667,7 @@ pub trait TryStreamExt: TryStream { F: FnMut(&Self::Ok) -> Fut, Self: Sized, { - TryFilter::new(self, f) + assert_stream::, _>(TryFilter::new(self, f)) } /// Attempt to filter the values produced by this stream while @@ -581,7 +708,7 @@ pub trait TryStreamExt: TryStream { F: FnMut(Self::Ok) -> Fut, Self: Sized, { - TryFilterMap::new(self, f) + assert_stream::, _>(TryFilterMap::new(self, f)) } /// Flattens a stream of streams into just one continuous stream. @@ -609,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 @@ -628,7 +759,9 @@ pub trait TryStreamExt: TryStream { ::Error: From, Self: Sized, { - TryFlatten::new(self) + assert_stream::::Ok, ::Error>, _>( + TryFlatten::new(self), + ) } /// Attempt to execute an accumulating asynchronous computation over a @@ -665,7 +798,7 @@ pub trait TryStreamExt: TryStream { Fut: TryFuture, Self: Sized, { - TryFold::new(self, f, init) + assert_future::, _>(TryFold::new(self, f, init)) } /// Attempt to concatenate all items of a stream into a single @@ -707,10 +840,10 @@ pub trait TryStreamExt: TryStream { Self: Sized, Self::Ok: Extend<<::Ok as IntoIterator>::Item> + IntoIterator + Default, { - TryConcat::new(self) + assert_future::, _>(TryConcat::new(self)) } - /// Attempt to execute several futures from a stream concurrently. + /// Attempt to execute several futures from a stream concurrently (unordered). /// /// This stream's `Ok` type must be a [`TryFuture`](futures_core::future::TryFuture) with an `Error` type /// that matches the stream's `Error` type. @@ -767,14 +900,92 @@ pub trait TryStreamExt: TryStream { /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); /// # Ok::<(), Box>(()) }).unwrap(); /// ``` - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] + #[cfg(not(futures_no_atomic_cas))] #[cfg(feature = "alloc")] fn try_buffer_unordered(self, n: usize) -> TryBufferUnordered where Self::Ok: TryFuture, Self: Sized, { - TryBufferUnordered::new(self, n) + assert_stream::::Ok, Self::Error>, _>( + TryBufferUnordered::new(self, n), + ) + } + + /// Attempt to execute several futures from a stream concurrently. + /// + /// This stream's `Ok` type must be a [`TryFuture`](futures_core::future::TryFuture) with an `Error` type + /// that matches the stream's `Error` type. + /// + /// This adaptor will buffer up to `n` futures and then return their + /// outputs in the order. If the underlying stream returns an error, it will + /// be immediately propagated. + /// + /// The returned stream will be a stream of results, each containing either + /// an error or a future's output. An error can be produced either by the + /// underlying stream itself or by one of the futures it yielded. + /// + /// This method is only available when the `std` or `alloc` feature of this + /// library is activated, and it is activated by default. + /// + /// # Examples + /// + /// Results are returned in the order of addition: + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::oneshot; + /// use futures::future::lazy; + /// use futures::stream::{self, StreamExt, TryStreamExt}; + /// + /// let (send_one, recv_one) = oneshot::channel(); + /// let (send_two, recv_two) = oneshot::channel(); + /// + /// let mut buffered = lazy(move |cx| { + /// let stream_of_futures = stream::iter(vec![Ok(recv_one), Ok(recv_two)]); + /// + /// let mut buffered = stream_of_futures.try_buffered(10); + /// + /// assert!(buffered.try_poll_next_unpin(cx).is_pending()); + /// + /// send_two.send(2i32)?; + /// assert!(buffered.try_poll_next_unpin(cx).is_pending()); + /// Ok::<_, i32>(buffered) + /// }).await?; + /// + /// send_one.send(1i32)?; + /// assert_eq!(buffered.next().await, Some(Ok(1i32))); + /// assert_eq!(buffered.next().await, Some(Ok(2i32))); + /// + /// assert_eq!(buffered.next().await, None); + /// # Ok::<(), i32>(()) }).unwrap(); + /// ``` + /// + /// Errors from the underlying stream itself are propagated: + /// ``` + /// # futures::executor::block_on(async { + /// use futures::channel::mpsc; + /// use futures::stream::{StreamExt, TryStreamExt}; + /// + /// let (sink, stream_of_futures) = mpsc::unbounded(); + /// let mut buffered = stream_of_futures.try_buffered(10); + /// + /// sink.unbounded_send(Ok(async { Ok(7i32) }))?; + /// assert_eq!(buffered.next().await, Some(Ok(7i32))); + /// + /// sink.unbounded_send(Err("error in the stream"))?; + /// assert_eq!(buffered.next().await, Some(Err("error in the stream"))); + /// # Ok::<(), Box>(()) }).unwrap(); + /// ``` + #[cfg(not(futures_no_atomic_cas))] + #[cfg(feature = "alloc")] + fn try_buffered(self, n: usize) -> TryBuffered + where + Self::Ok: TryFuture, + Self: Sized, + { + assert_stream::::Ok, Self::Error>, _>(TryBuffered::new( + self, n, + )) } // TODO: false positive warning from rustdoc. Verify once #43466 settles @@ -794,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(); /// @@ -811,6 +1023,7 @@ pub trait TryStreamExt: TryStream { /// # assert_eq!(42, futures::executor::block_on(rx).unwrap()); /// ``` #[cfg(feature = "compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "compat")))] fn compat(self) -> Compat where Self: Sized + Unpin, @@ -844,12 +1057,13 @@ pub trait TryStreamExt: TryStream { /// # }) /// ``` #[cfg(feature = "io")] + #[cfg_attr(docsrs, doc(cfg(feature = "io")))] #[cfg(feature = "std")] fn into_async_read(self) -> IntoAsyncRead where Self: Sized + TryStreamExt + Unpin, Self::Ok: AsRef<[u8]>, { - IntoAsyncRead::new(self) + crate::io::assert_read(IntoAsyncRead::new(self)) } } diff --git a/futures-util/src/stream/try_stream/or_else.rs b/futures-util/src/stream/try_stream/or_else.rs index 33310d1ce3..cb69e81323 100644 --- a/futures-util/src/stream/try_stream/or_else.rs +++ b/futures-util/src/stream/try_stream/or_else.rs @@ -1,22 +1,25 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; -use futures_core::stream::{Stream, TryStream, FusedStream}; +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_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`or_else`](super::TryStreamExt::or_else) method. -#[must_use = "streams do nothing unless polled"] -pub struct OrElse { - stream: St, - future: Option, - f: F, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`or_else`](super::TryStreamExt::or_else) method. + #[must_use = "streams do nothing unless polled"] + pub struct OrElse { + #[pin] + stream: St, + #[pin] + future: Option, + f: F, + } } -impl Unpin for OrElse {} - impl fmt::Debug for OrElse where St: fmt::Debug, @@ -30,78 +33,45 @@ where } } -impl OrElse { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl OrElse - where St: TryStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { pub(super) fn new(stream: St, f: F) -> Self { Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for OrElse - where St: TryStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - None => return Poll::Ready(None), - Some(Ok(e)) => return Poll::Ready(Some(Ok(e))), - Some(Err(e)) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + fn poll_next(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() { + let item = ready!(fut.try_poll(cx)); + this.future.set(None); + break Some(item); + } else { + match ready!(this.stream.as_mut().try_poll_next(cx)) { + Some(Ok(item)) => break Some(Ok(item)), + Some(Err(e)) => { + this.future.set(Some((this.f)(e))); + } + None => break None, + } + } + }) } fn size_hint(&self) -> (usize, Option) { @@ -117,9 +87,10 @@ impl Stream for OrElse } impl FusedStream for OrElse - where St: TryStream + FusedStream, - F: FnMut(St::Error) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(St::Error) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.future.is_none() && self.stream.is_terminated() @@ -129,7 +100,8 @@ impl FusedStream for OrElse // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for OrElse - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/try_buffer_unordered.rs b/futures-util/src/stream/try_stream/try_buffer_unordered.rs index d11e1b4bae..9a899d4ea6 100644 --- a/futures-util/src/stream/try_stream/try_buffer_unordered.rs +++ b/futures-util/src/stream/try_stream/try_buffer_unordered.rs @@ -1,104 +1,71 @@ -use crate::stream::{Fuse, FuturesUnordered, StreamExt, IntoStream}; use crate::future::{IntoFuture, TryFutureExt}; +use crate::stream::{Fuse, FuturesUnordered, IntoStream, StreamExt}; +use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; -use core::pin::Pin; +use pin_project_lite::pin_project; -/// Stream for the -/// [`try_buffer_unordered`](super::TryStreamExt::try_buffer_unordered) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct TryBufferUnordered - where St: TryStream -{ - stream: Fuse>, - in_progress_queue: FuturesUnordered>, - max: usize, +pin_project! { + /// Stream for the + /// [`try_buffer_unordered`](super::TryStreamExt::try_buffer_unordered) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryBufferUnordered + where St: TryStream + { + #[pin] + stream: Fuse>, + in_progress_queue: FuturesUnordered>, + max: usize, + } } -impl Unpin for TryBufferUnordered - where St: TryStream + Unpin -{} - impl TryBufferUnordered - where St: TryStream, - St::Ok: TryFuture, +where + St: TryStream, + St::Ok: TryFuture, { - unsafe_pinned!(stream: Fuse>); - unsafe_unpinned!(in_progress_queue: FuturesUnordered>); - pub(super) fn new(stream: St, n: usize) -> Self { - TryBufferUnordered { + Self { stream: IntoStream::new(stream).fuse(), in_progress_queue: FuturesUnordered::new(), max: n, } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref().get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut().get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner().into_inner() - } + delegate_access_inner!(stream, St, (. .)); } impl Stream for TryBufferUnordered - where St: TryStream, - St::Ok: TryFuture, +where + St: TryStream, + St::Ok: TryFuture, { type Item = Result<::Ok, St::Error>; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. Propagate errors from the stream immediately. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut.into_future()), + while this.in_progress_queue.len() < *this.max { + match this.stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut.into_future()), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match this.in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if this.stream.is_done() { Poll::Ready(None) } else { Poll::Pending @@ -109,8 +76,9 @@ impl Stream for TryBufferUnordered // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryBufferUnordered - where S: TryStream + Sink, - S::Ok: TryFuture, +where + S: TryStream + Sink, + S::Ok: TryFuture, { type Error = E; diff --git a/futures-util/src/stream/try_stream/try_buffered.rs b/futures-util/src/stream/try_stream/try_buffered.rs new file mode 100644 index 0000000000..45bd3f8c7a --- /dev/null +++ b/futures-util/src/stream/try_stream/try_buffered.rs @@ -0,0 +1,87 @@ +use crate::future::{IntoFuture, TryFutureExt}; +use crate::stream::{Fuse, FuturesOrdered, IntoStream, StreamExt}; +use core::pin::Pin; +use futures_core::future::TryFuture; +use futures_core::stream::{Stream, TryStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_buffered`](super::TryStreamExt::try_buffered) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryBuffered + where + St: TryStream, + St::Ok: TryFuture, + { + #[pin] + stream: Fuse>, + in_progress_queue: FuturesOrdered>, + max: usize, + } +} + +impl TryBuffered +where + St: TryStream, + St::Ok: TryFuture, +{ + pub(super) fn new(stream: St, n: usize) -> Self { + Self { + stream: IntoStream::new(stream).fuse(), + in_progress_queue: FuturesOrdered::new(), + max: n, + } + } + + delegate_access_inner!(stream, St, (. .)); +} + +impl Stream for TryBuffered +where + St: TryStream, + St::Ok: TryFuture, +{ + type Item = Result<::Ok, St::Error>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + // First up, try to spawn off as many futures as possible by filling up + // our queue of futures. Propagate errors from the stream immediately. + while this.in_progress_queue.len() < *this.max { + match this.stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(fut)) => this.in_progress_queue.push(fut.into_future()), + Poll::Ready(None) | Poll::Pending => break, + } + } + + // Attempt to pull the next value from the in_progress_queue + match this.in_progress_queue.poll_next_unpin(cx) { + x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, + Poll::Ready(None) => {} + } + + // If more values are still coming from the stream, we're not done yet + if this.stream.is_done() { + Poll::Ready(None) + } else { + Poll::Pending + } + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TryBuffered +where + S: TryStream + Sink, + S::Ok: TryFuture, +{ + type Error = E; + + delegate_sink!(stream, Item); +} 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/stream/try_stream/try_collect.rs b/futures-util/src/stream/try_stream/try_collect.rs index d22e8e8543..5d3b3d7668 100644 --- a/futures-util/src/stream/try_stream/try_collect.rs +++ b/futures-util/src/stream/try_stream/try_collect.rs @@ -1,36 +1,28 @@ use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::ready; use futures_core::stream::{FusedStream, TryStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`try_collect`](super::TryStreamExt::try_collect) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct TryCollect { - stream: St, - items: C, +use pin_project_lite::pin_project; + +pin_project! { + /// Future for the [`try_collect`](super::TryStreamExt::try_collect) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryCollect { + #[pin] + stream: St, + items: C, + } } impl TryCollect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(items: C); - - pub(super) fn new(s: St) -> TryCollect { - TryCollect { - stream: s, - items: Default::default(), - } - } - - fn finish(self: Pin<&mut Self>) -> C { - mem::replace(self.items(), Default::default()) + pub(super) fn new(s: St) -> Self { + Self { stream: s, items: Default::default() } } } -impl Unpin for TryCollect {} - impl FusedFuture for TryCollect where St: TryStream + FusedStream, @@ -48,15 +40,13 @@ where { type Output = Result; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => self.as_mut().items().extend(Some(x)), - None => return Poll::Ready(Ok(self.as_mut().finish())), + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Poll::Ready(Ok(loop { + match ready!(this.stream.as_mut().try_poll_next(cx)?) { + Some(x) => this.items.extend(Some(x)), + None => break mem::replace(this.items, Default::default()), } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_concat.rs b/futures-util/src/stream/try_stream/try_concat.rs index 395f166c6b..58fb6a5413 100644 --- a/futures-util/src/stream/try_stream/try_concat.rs +++ b/futures-util/src/stream/try_stream/try_concat.rs @@ -1,32 +1,28 @@ use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`try_concat`](super::TryStreamExt::try_concat) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct TryConcat { - stream: St, - accum: Option, +pin_project! { + /// Future for the [`try_concat`](super::TryStreamExt::try_concat) method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryConcat { + #[pin] + stream: St, + accum: Option, + } } -impl Unpin for TryConcat {} - impl TryConcat where St: TryStream, St::Ok: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - - pub(super) fn new(stream: St) -> TryConcat { - TryConcat { - stream, - accum: None, - } + pub(super) fn new(stream: St) -> Self { + Self { stream, accum: None } } } @@ -37,21 +33,19 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => { - let accum = self.as_mut().accum(); - if let Some(a) = accum { - a.extend(x) - } else { - *accum = Some(x) - } - }, - None => { - return Poll::Ready(Ok(self.as_mut().accum().take().unwrap_or_default())) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + Poll::Ready(Ok(loop { + if let Some(x) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + if let Some(a) = this.accum { + a.extend(x) + } else { + *this.accum = Some(x) } + } else { + break this.accum.take().unwrap_or_default(); } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 24a9c3275a..61e6105c37 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -1,28 +1,29 @@ use core::fmt; use core::pin::Pin; use futures_core::future::Future; -use futures_core::stream::{Stream, TryStream, FusedStream}; +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_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`try_filter`](super::TryStreamExt::try_filter) -/// method. -#[must_use = "streams do nothing unless polled"] -pub struct TryFilter - where St: TryStream -{ - stream: St, - f: F, - pending_fut: Option, - pending_item: Option, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_filter`](super::TryStreamExt::try_filter) + /// method. + #[must_use = "streams do nothing unless polled"] + pub struct TryFilter + where St: TryStream + { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + } } -impl Unpin for TryFilter - where St: TryStream + Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilter where St: TryStream + fmt::Debug, @@ -39,59 +40,21 @@ where } impl TryFilter - where St: TryStream +where + St: TryStream, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - pub(super) fn new(stream: St, f: F) -> Self { - TryFilter { - stream, - f, - pending_fut: None, - pending_item: None, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream + Self { stream, f, pending_fut: None, pending_item: None } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilter - where St: TryStream + FusedStream, - F: FnMut(&St::Ok) -> Fut, - Fut: Future, +where + St: TryStream + FusedStream, + F: FnMut(&St::Ok) -> Fut, + Fut: Future, { fn is_terminated(&self) -> bool { self.pending_fut.is_none() && self.stream.is_terminated() @@ -99,35 +62,31 @@ impl FusedStream for TryFilter } impl Stream for TryFilter - where St: TryStream, - Fut: Future, - F: FnMut(&St::Ok) -> Fut, +where + St: TryStream, + Fut: Future, + F: FnMut(&St::Ok) -> Fut, { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let res = ready!(fut.poll(cx)); + this.pending_fut.set(None); + if res { + break this.pending_item.take().map(Ok); + } + *this.pending_item = None; + } else if let Some(item) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + this.pending_fut.set(Some((this.f)(&item))); + *this.pending_item = Some(item); + } else { + break None; } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(Ok(item))); - } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -144,7 +103,8 @@ impl Stream for TryFilter // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryFilter - where S: TryStream + Sink, +where + S: TryStream + Sink, { type Error = E; diff --git a/futures-util/src/stream/try_stream/try_filter_map.rs b/futures-util/src/stream/try_stream/try_filter_map.rs index ed7eeb227e..bb1b5b9db6 100644 --- a/futures-util/src/stream/try_stream/try_filter_map.rs +++ b/futures-util/src/stream/try_stream/try_filter_map.rs @@ -1,25 +1,26 @@ use core::fmt; use core::pin::Pin; -use futures_core::future::{TryFuture}; -use futures_core::stream::{Stream, TryStream, FusedStream}; +use futures_core::future::TryFuture; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`try_filter_map`](super::TryStreamExt::try_filter_map) -/// method. -#[must_use = "streams do nothing unless polled"] -pub struct TryFilterMap { - stream: St, - f: F, - pending: Option, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_filter_map`](super::TryStreamExt::try_filter_map) + /// method. + #[must_use = "streams do nothing unless polled"] + pub struct TryFilterMap { + #[pin] + stream: St, + f: F, + #[pin] + pending: Option, + } } -impl Unpin for TryFilterMap - where St: Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilterMap where St: fmt::Debug, @@ -34,51 +35,18 @@ where } impl TryFilterMap { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - pub(super) fn new(stream: St, f: F) -> Self { - TryFilterMap { stream, f, pending: None } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream + Self { stream, f, pending: None } } - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilterMap - where St: TryStream + FusedStream, - Fut: TryFuture, Error = St::Error>, - F: FnMut(St::Ok) -> Fut, +where + St: TryStream + FusedStream, + Fut: TryFuture, Error = St::Error>, + F: FnMut(St::Ok) -> Fut, { fn is_terminated(&self) -> bool { self.pending.is_none() && self.stream.is_terminated() @@ -86,32 +54,33 @@ impl FusedStream for TryFilterMap } impl Stream for TryFilterMap - where St: TryStream, - Fut: TryFuture, Error = St::Error>, - F: FnMut(St::Ok) -> Fut, +where + St: TryStream, + Fut: TryFuture, Error = St::Error>, + F: FnMut(St::Ok) -> Fut, { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let result = ready!(self.as_mut().pending().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().pending().set(None); - if let Some(x) = result? { - return Poll::Ready(Some(Ok(x))); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + Poll::Ready(loop { + if let Some(p) = this.pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let res = ready!(p.try_poll(cx)); + this.pending.set(None); + let item = res?; + if item.is_some() { + break item.map(Ok); + } + } else if let Some(item) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + // No item in progress, but the stream is still going + this.pending.set(Some((this.f)(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -128,7 +97,8 @@ impl Stream for TryFilterMap // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TryFilterMap - where S: Sink, +where + S: Sink, { type Error = S::Error; diff --git a/futures-util/src/stream/try_stream/try_flatten.rs b/futures-util/src/stream/try_stream/try_flatten.rs index 5f81b22b4c..4fc04a07bb 100644 --- a/futures-util/src/stream/try_stream/try_flatten.rs +++ b/futures-util/src/stream/try_stream/try_flatten.rs @@ -1,34 +1,24 @@ use core::pin::Pin; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -/// Stream for the [`try_flatten`](super::TryStreamExt::try_flatten) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct TryFlatten -where - St: TryStream, -{ - stream: St, - next: Option, -} - -impl Unpin for TryFlatten -where - St: TryStream + Unpin, - St::Ok: Unpin, -{ -} - -impl TryFlatten -where - St: TryStream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); +pin_project! { + /// Stream for the [`try_flatten`](super::TryStreamExt::try_flatten) method. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct TryFlatten + where + St: TryStream, + { + #[pin] + stream: St, + #[pin] + next: Option, + } } impl TryFlatten @@ -41,37 +31,7 @@ where Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFlatten @@ -93,27 +53,22 @@ where { type Item = Result<::Ok, ::Error>; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), - } - } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - if let Some(item) = ready!(self - .as_mut() - .next() - .as_pin_mut() - .unwrap() - .try_poll_next(cx)?) - { - return Poll::Ready(Some(Ok(item))); + Poll::Ready(loop { + if let Some(s) = this.next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.try_poll_next(cx)?) { + break Some(Ok(item)); + } else { + this.next.set(None); + } + } else if let Some(s) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + this.next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_fold.rs b/futures-util/src/stream/try_stream/try_fold.rs index b8b8dc24f1..d344d96e7d 100644 --- a/futures-util/src/stream/try_stream/try_fold.rs +++ b/futures-util/src/stream/try_stream/try_fold.rs @@ -1,21 +1,24 @@ use core::fmt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`try_fold`](super::TryStreamExt::try_fold) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct TryFold { - stream: St, - f: F, - accum: Option, - future: Option, +pin_project! { + /// Future for the [`try_fold`](super::TryStreamExt::try_fold) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryFold { + #[pin] + stream: St, + f: F, + accum: Option, + #[pin] + future: Option, + } } -impl Unpin for TryFold {} - impl fmt::Debug for TryFold where St: fmt::Debug, @@ -32,29 +35,21 @@ where } impl TryFold -where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - - pub(super) fn new(stream: St, f: F, t: T) -> TryFold { - TryFold { - stream, - f, - accum: Some(t), - future: None, - } + pub(super) fn new(stream: St, f: F, t: T) -> Self { + Self { stream, f, accum: Some(t), future: None } } } impl FusedFuture for TryFold - where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.accum.is_none() && self.future.is_none() @@ -62,49 +57,37 @@ impl FusedFuture for TryFold } impl Future for TryFold - where St: TryStream, - F: FnMut(T, St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(T, St::Ok) -> Fut, + Fut: TryFuture, { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = match ready!( - self.as_mut().future().as_pin_mut() - .expect("TryFold polled after completion") - .try_poll(cx) - ) { - Ok(accum) => accum, - Err(e) => { - // Indicate that the future can no longer be polled. - self.as_mut().future().set(None); - return Poll::Ready(Err(e)); - } - }; - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - Some(Ok(item)) => Some(item), - Some(Err(e)) => { - // Indicate that the future can no longer be polled. - *self.as_mut().accum() = None; - return Poll::Ready(Err(e)); + 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 res = ready!(fut.try_poll(cx)); + this.future.set(None); + match res { + Ok(a) => *this.accum = Some(a), + Err(e) => break Err(e), + } + } else if this.accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(this.stream.as_mut().try_poll_next(cx)); + let a = this.accum.take().unwrap(); + match res { + Some(Ok(item)) => this.future.set(Some((this.f)(a, item))), + Some(Err(e)) => break Err(e), + None => break Ok(a), } - None => None, - }; - let accum = self.as_mut().accum().take().unwrap(); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); } else { - return Poll::Ready(Ok(accum)) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_for_each.rs b/futures-util/src/stream/try_stream/try_for_each.rs index 2c71107646..6a081d84e7 100644 --- a/futures-util/src/stream/try_stream/try_for_each.rs +++ b/futures-util/src/stream/try_stream/try_for_each.rs @@ -1,20 +1,23 @@ use core::fmt; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; +use futures_core::ready; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the [`try_for_each`](super::TryStreamExt::try_for_each) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct TryForEach { - stream: St, - f: F, - future: Option, +pin_project! { + /// Future for the [`try_for_each`](super::TryStreamExt::try_for_each) method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryForEach { + #[pin] + stream: St, + f: F, + #[pin] + future: Option, + } } -impl Unpin for TryForEach {} - impl fmt::Debug for TryForEach where St: fmt::Debug, @@ -29,44 +32,37 @@ where } impl TryForEach -where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - - pub(super) fn new(stream: St, f: F) -> TryForEach { - TryForEach { - stream, - f, - future: None, - } + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, future: None } } } impl Future for TryForEach - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: TryFuture, { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.try_poll(cx))?; - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); + if let Some(fut) = this.future.as_mut().as_pin_mut() { + ready!(fut.try_poll(cx))?; + this.future.set(None); + } else { + match ready!(this.stream.as_mut().try_poll_next(cx)?) { + Some(e) => this.future.set(Some((this.f)(e))), + None => break, } - None => return Poll::Ready(Ok(())), } } + Poll::Ready(Ok(())) } } diff --git a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs index 19c3e5b89a..62734c746b 100644 --- a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs +++ b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs @@ -1,29 +1,27 @@ use crate::stream::{FuturesUnordered, StreamExt}; use core::fmt; use core::mem; -use core::pin::Pin; use core::num::NonZeroUsize; +use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; -/// Future for the -/// [`try_for_each_concurrent`](super::TryStreamExt::try_for_each_concurrent) -/// method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct TryForEachConcurrent { - stream: Option, - f: F, - futures: FuturesUnordered, - limit: Option, +pin_project! { + /// Future for the + /// [`try_for_each_concurrent`](super::TryStreamExt::try_for_each_concurrent) + /// method. + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct TryForEachConcurrent { + #[pin] + stream: Option, + f: F, + futures: FuturesUnordered, + limit: Option, + } } -impl Unpin for TryForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for TryForEachConcurrent where St: fmt::Debug, @@ -39,9 +37,10 @@ where } impl FusedFuture for TryForEachConcurrent - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { fn is_terminated(&self) -> bool { self.stream.is_none() && self.futures.is_empty() @@ -49,17 +48,13 @@ impl FusedFuture for TryForEachConcurrent } impl TryForEachConcurrent -where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - - pub(super) fn new(stream: St, limit: Option, f: F) -> TryForEachConcurrent { - TryForEachConcurrent { + pub(super) fn new(stream: St, limit: Option, f: F) -> Self { + Self { stream: Some(stream), // Note: `limit` = 0 gets ignored. limit: limit.and_then(NonZeroUsize::new), @@ -70,21 +65,21 @@ where St: TryStream, } impl Future for TryForEachConcurrent - where St: TryStream, - F: FnMut(St::Ok) -> Fut, - Fut: Future>, +where + St: TryStream, + F: FnMut(St::Ok) -> Fut, + Fut: Future>, { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { - let poll_res = match self.as_mut().stream().as_pin_mut() { + if this.limit.map(|limit| limit.get() > this.futures.len()).unwrap_or(true) { + let poll_res = match this.stream.as_mut().as_pin_mut() { Some(stream) => stream.try_poll_next(cx), None => Poll::Ready(None), }; @@ -93,40 +88,39 @@ impl Future for TryForEachConcurrent Poll::Ready(Some(Ok(elem))) => { made_progress_this_iter = true; Some(elem) - }, + } Poll::Ready(None) => { - self.as_mut().stream().set(None); + this.stream.set(None); None } Poll::Pending => None, Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + this.stream.set(None); + drop(mem::replace(this.futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } }; if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + this.futures.push((this.f)(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match this.futures.poll_next_unpin(cx) { Poll::Ready(Some(Ok(()))) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { - return Poll::Ready(Ok(())) + if this.stream.is_none() { + return Poll::Ready(Ok(())); } - }, + } Poll::Pending => {} Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + this.stream.set(None); + drop(mem::replace(this.futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } } diff --git a/futures-util/src/stream/try_stream/try_next.rs b/futures-util/src/stream/try_stream/try_next.rs index 78599ad109..13fcf80cae 100644 --- a/futures-util/src/stream/try_stream/try_next.rs +++ b/futures-util/src/stream/try_stream/try_next.rs @@ -15,7 +15,7 @@ impl Unpin for TryNext<'_, St> {} impl<'a, St: ?Sized + TryStream + Unpin> TryNext<'a, St> { pub(super) fn new(stream: &'a mut St) -> Self { - TryNext { stream } + Self { stream } } } @@ -28,10 +28,7 @@ impl FusedFuture for TryNext<'_, S impl Future for TryNext<'_, St> { type Output = Result, St::Error>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.stream.try_poll_next_unpin(cx)?.map(Ok) } } diff --git a/futures-util/src/stream/try_stream/try_skip_while.rs b/futures-util/src/stream/try_stream/try_skip_while.rs index a3d6803a1b..a424b6c5b1 100644 --- a/futures-util/src/stream/try_stream/try_skip_while.rs +++ b/futures-util/src/stream/try_stream/try_skip_while.rs @@ -1,25 +1,28 @@ use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; -use futures_core::stream::{Stream, TryStream, FusedStream}; +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_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`try_skip_while`](super::TryStreamExt::try_skip_while) -/// method. -#[must_use = "streams do nothing unless polled"] -pub struct TrySkipWhile where St: TryStream { - stream: St, - f: F, - pending_fut: Option, - pending_item: Option, - done_skipping: bool, +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_skip_while`](super::TryStreamExt::try_skip_while) + /// method. + #[must_use = "streams do nothing unless polled"] + pub struct TrySkipWhile where St: TryStream { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + done_skipping: bool, + } } -impl Unpin for TrySkipWhile {} - impl fmt::Debug for TrySkipWhile where St: TryStream + fmt::Debug, @@ -37,99 +40,50 @@ where } impl TrySkipWhile - where St: TryStream, -{ - unsafe_pinned!(stream: St); -} - -impl TrySkipWhile - where St: TryStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - - pub(super) fn new(stream: St, f: F) -> TrySkipWhile { - TrySkipWhile { - stream, - f, - pending_fut: None, - pending_item: None, - done_skipping: false, - } + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending_fut: None, pending_item: None, done_skipping: false } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TrySkipWhile - where St: TryStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().try_poll_next(cx); - } - - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().try_poll(cx)?); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *this.done_skipping { + return this.stream.try_poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(Ok(item))) + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let res = ready!(fut.try_poll(cx)); + this.pending_fut.set(None); + let skipped = res?; + let item = this.pending_item.take(); + if !skipped { + *this.done_skipping = true; + break item.map(Ok); + } + } else if let Some(item) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + this.pending_fut.set(Some((this.f)(&item))); + *this.pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -144,9 +98,10 @@ impl Stream for TrySkipWhile } impl FusedStream for TrySkipWhile - where St: TryStream + FusedStream, - F: FnMut(&St::Ok) -> Fut, - Fut: TryFuture, +where + St: TryStream + FusedStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, { fn is_terminated(&self) -> bool { self.pending_item.is_none() && self.stream.is_terminated() @@ -156,7 +111,8 @@ impl FusedStream for TrySkipWhile // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] impl Sink for TrySkipWhile - where S: TryStream + Sink, +where + S: TryStream + Sink, { type Error = E; diff --git a/futures-util/src/stream/try_stream/try_take_while.rs b/futures-util/src/stream/try_stream/try_take_while.rs new file mode 100644 index 0000000000..3375960ef4 --- /dev/null +++ b/futures-util/src/stream/try_stream/try_take_while.rs @@ -0,0 +1,129 @@ +use core::fmt; +use core::pin::Pin; +use futures_core::future::TryFuture; +use futures_core::ready; +use futures_core::stream::{FusedStream, Stream, TryStream}; +use futures_core::task::{Context, Poll}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + /// Stream for the [`try_take_while`](super::TryStreamExt::try_take_while) + /// method. + #[must_use = "streams do nothing unless polled"] + pub struct TryTakeWhile + where + St: TryStream, + { + #[pin] + stream: St, + f: F, + #[pin] + pending_fut: Option, + pending_item: Option, + done_taking: bool, + } +} + +impl fmt::Debug for TryTakeWhile +where + St: TryStream + fmt::Debug, + St::Ok: fmt::Debug, + Fut: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TryTakeWhile") + .field("stream", &self.stream) + .field("pending_fut", &self.pending_fut) + .field("pending_item", &self.pending_item) + .field("done_taking", &self.done_taking) + .finish() + } +} + +impl TryTakeWhile +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, +{ + pub(super) fn new(stream: St, f: F) -> Self { + Self { stream, f, pending_fut: None, pending_item: None, done_taking: false } + } + + delegate_access_inner!(stream, St, ()); +} + +impl Stream for TryTakeWhile +where + St: TryStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if *this.done_taking { + return Poll::Ready(None); + } + + Poll::Ready(loop { + if let Some(fut) = this.pending_fut.as_mut().as_pin_mut() { + let res = ready!(fut.try_poll(cx)); + this.pending_fut.set(None); + let take = res?; + let item = this.pending_item.take(); + if take { + break item.map(Ok); + } else { + *this.done_taking = true; + break None; + } + } else if let Some(item) = ready!(this.stream.as_mut().try_poll_next(cx)?) { + this.pending_fut.set(Some((this.f)(&item))); + *this.pending_item = Some(item); + } else { + break None; + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + if self.done_taking { + return (0, Some(0)); + } + + let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let (_, upper) = self.stream.size_hint(); + let upper = match upper { + Some(x) => x.checked_add(pending_len), + None => None, + }; + (0, upper) // can't know a lower bound, due to the predicate + } +} + +impl FusedStream for TryTakeWhile +where + St: TryStream + FusedStream, + F: FnMut(&St::Ok) -> Fut, + Fut: TryFuture, +{ + fn is_terminated(&self) -> bool { + self.done_taking || self.pending_item.is_none() && self.stream.is_terminated() + } +} + +// Forwarding impl of Sink from the underlying stream +#[cfg(feature = "sink")] +impl Sink for TryTakeWhile +where + S: TryStream + Sink, +{ + type Error = E; + + delegate_sink!(stream, Item); +} diff --git a/futures-util/src/stream/try_stream/try_unfold.rs b/futures-util/src/stream/try_stream/try_unfold.rs index 6266274cd5..fd9cdf1d8c 100644 --- a/futures-util/src/stream/try_stream/try_unfold.rs +++ b/futures-util/src/stream/try_stream/try_unfold.rs @@ -1,9 +1,11 @@ +use super::assert_stream; use core::fmt; use core::pin::Pin; use futures_core::future::TryFuture; +use futures_core::ready; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; /// Creates a `TryStream` from a seed and a closure returning a `TryFuture`. /// @@ -59,42 +61,30 @@ where F: FnMut(T) -> Fut, Fut: TryFuture>, { - TryUnfold { - f, - state: Some(init), - fut: None, - } + assert_stream::, _>(TryUnfold { f, state: Some(init), fut: None }) } -/// Stream for the [`try_unfold`] function. -#[must_use = "streams do nothing unless polled"] -pub struct TryUnfold { - f: F, - state: Option, - fut: Option, +pin_project! { + /// Stream for the [`try_unfold`] function. + #[must_use = "streams do nothing unless polled"] + pub struct TryUnfold { + f: F, + state: Option, + #[pin] + fut: Option, + } } -impl Unpin for TryUnfold {} - impl fmt::Debug for TryUnfold where T: fmt::Debug, Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryUnfold") - .field("state", &self.state) - .field("fut", &self.fut) - .finish() + f.debug_struct("TryUnfold").field("state", &self.state).field("fut", &self.fut).finish() } } -impl TryUnfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl Stream for TryUnfold where F: FnMut(T) -> Fut, @@ -102,27 +92,25 @@ where { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if let Some(state) = this.state.take() { + this.fut.set(Some((this.f)(state))); } - match self.as_mut().fut().as_pin_mut() { + match this.fut.as_mut().as_pin_mut() { None => { // The future previously errored Poll::Ready(None) } - Some(fut) => { - let step = ready!(fut.try_poll(cx)); - self.as_mut().fut().set(None); + Some(future) => { + let step = ready!(future.try_poll(cx)); + this.fut.set(None); match step { Ok(Some((item, next_state))) => { - *self.as_mut().state() = Some(next_state); + *this.state = Some(next_state); Poll::Ready(Some(Ok(item))) } Ok(None) => Poll::Ready(None), diff --git a/futures-util/src/stream/unfold.rs b/futures-util/src/stream/unfold.rs index 3153f83711..7d8ef6babc 100644 --- a/futures-util/src/stream/unfold.rs +++ b/futures-util/src/stream/unfold.rs @@ -1,9 +1,12 @@ +use super::assert_stream; +use crate::unfold_state::UnfoldState; use core::fmt; use core::pin::Pin; use futures_core::future::Future; +use futures_core::ready; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project_lite::pin_project; /// Creates a `Stream` from a seed and a closure returning a `Future`. /// @@ -45,77 +48,71 @@ use pin_utils::{unsafe_pinned, unsafe_unpinned}; /// # }); /// ``` pub fn unfold(init: T, f: F) -> Unfold - where F: FnMut(T) -> Fut, - Fut: Future>, +where + F: FnMut(T) -> Fut, + Fut: Future>, { - Unfold { - f, - state: Some(init), - fut: None, - } + assert_stream::(Unfold { f, state: UnfoldState::Value { value: init } }) } -/// Stream for the [`unfold`] function. -#[must_use = "streams do nothing unless polled"] -pub struct Unfold { - f: F, - state: Option, - fut: Option, +pin_project! { + /// Stream for the [`unfold`] function. + #[must_use = "streams do nothing unless polled"] + pub struct Unfold { + f: F, + #[pin] + state: UnfoldState, + } } -impl Unpin for Unfold {} - impl fmt::Debug for Unfold where T: fmt::Debug, Fut: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Unfold") - .field("state", &self.state) - .field("fut", &self.fut) - .finish() + f.debug_struct("Unfold").field("state", &self.state).finish() } } -impl Unfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl FusedStream for Unfold - where F: FnMut(T) -> Fut, - Fut: Future>, +where + F: FnMut(T) -> Fut, + Fut: Future>, { fn is_terminated(&self) -> bool { - self.state.is_none() && self.fut.is_none() + if let UnfoldState::Empty = self.state { + true + } else { + false + } } } impl Stream for Unfold - where F: FnMut(T) -> Fut, - Fut: Future>, +where + F: FnMut(T) -> Fut, + Fut: Future>, { type Item = Item; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + if let Some(state) = this.state.as_mut().take_value() { + this.state.set(UnfoldState::Future { future: (this.f)(state) }); } - let step = ready!(self.as_mut().fut().as_pin_mut() - .expect("Unfold must not be polled after it returned `Poll::Ready(None)`").poll(cx)); - self.as_mut().fut().set(None); + let step = match this.state.as_mut().project_future() { + Some(fut) => ready!(fut.poll(cx)), + None => panic!("Unfold must not be polled after it returned `Poll::Ready(None)`"), + }; if let Some((item, next_state)) = step { - *self.as_mut().state() = Some(next_state); + this.state.set(UnfoldState::Value { value: next_state }); Poll::Ready(Some(item)) } else { + this.state.set(UnfoldState::Empty); Poll::Ready(None) } } diff --git a/futures-util/src/task/mod.rs b/futures-util/src/task/mod.rs index fb3b7adacd..0a31eeac14 100644 --- a/futures-util/src/task/mod.rs +++ b/futures-util/src/task/mod.rs @@ -1,28 +1,37 @@ -//! Task notification +//! Tools for working with tasks. +//! +//! This module contains: +//! +//! - [`Spawn`], a trait for spawning new tasks. +//! - [`Context`], a context of an asynchronous task, +//! including a handle for waking up the task. +//! - [`Waker`], a handle for waking up a task. +//! +//! The remaining types and traits in the module are used for implementing +//! executors or dealing with synchronization issues around task wakeup. + +#[doc(no_inline)] +pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +pub use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, UnsafeFutureObj}; -cfg_target_has_atomic! { - #[cfg(feature = "alloc")] - pub use futures_task::ArcWake; - - #[cfg(feature = "alloc")] - pub use futures_task::waker; - - #[cfg(feature = "alloc")] - pub use futures_task::{waker_ref, WakerRef}; +pub use futures_task::noop_waker; +pub use futures_task::noop_waker_ref; - pub use futures_core::task::__internal::AtomicWaker; -} +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::ArcWake; -mod spawn; -pub use self::spawn::{SpawnExt, LocalSpawnExt}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::waker; -pub use futures_core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; +#[cfg(not(futures_no_atomic_cas))] +#[cfg(feature = "alloc")] +pub use futures_task::{waker_ref, WakerRef}; -pub use futures_task::{ - Spawn, LocalSpawn, SpawnError, - FutureObj, LocalFutureObj, UnsafeFutureObj, -}; +#[cfg(not(futures_no_atomic_cas))] +pub use futures_core::task::__internal::AtomicWaker; -pub use futures_task::noop_waker; -#[cfg(feature = "std")] -pub use futures_task::noop_waker_ref; +mod spawn; +pub use self::spawn::{LocalSpawnExt, SpawnExt}; diff --git a/futures-util/src/task/spawn.rs b/futures-util/src/task/spawn.rs index f34445a030..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; @@ -69,6 +71,7 @@ pub trait SpawnExt: Spawn { /// assert_eq!(block_on(join_handle_fut), 1); /// ``` #[cfg(feature = "channel")] + #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] fn spawn_with_handle(&self, future: Fut) -> Result, SpawnError> where @@ -83,6 +86,7 @@ pub trait SpawnExt: Spawn { /// Wraps a [`Spawn`] and makes it usable as a futures 0.1 `Executor`. /// Requires the `compat` feature to enable. #[cfg(feature = "compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "compat")))] fn compat(self) -> Compat where Self: Sized, @@ -134,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; /// @@ -145,6 +150,7 @@ pub trait LocalSpawnExt: LocalSpawn { /// assert_eq!(executor.run_until(join_handle_fut), 1); /// ``` #[cfg(feature = "channel")] + #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] #[cfg(feature = "std")] fn spawn_local_with_handle( &self, diff --git a/futures-util/src/unfold_state.rs b/futures-util/src/unfold_state.rs new file mode 100644 index 0000000000..0edc15e437 --- /dev/null +++ b/futures-util/src/unfold_state.rs @@ -0,0 +1,39 @@ +use core::pin::Pin; + +use pin_project_lite::pin_project; + +pin_project! { + /// UnfoldState used for stream and sink unfolds + #[project = UnfoldStateProj] + #[project_replace = UnfoldStateProjReplace] + #[derive(Debug)] + pub(crate) enum UnfoldState { + Value { + value: T, + }, + Future { + #[pin] + future: R, + }, + Empty, + } +} + +impl UnfoldState { + pub(crate) fn project_future(self: Pin<&mut Self>) -> Option> { + match self.project() { + UnfoldStateProj::Future { future } => Some(future), + _ => None, + } + } + + pub(crate) fn take_value(self: Pin<&mut Self>) -> Option { + match &*self { + UnfoldState::Value { .. } => match self.project_replace(UnfoldState::Empty) { + UnfoldStateProjReplace::Value { value } => Some(value), + _ => unreachable!(), + }, + _ => None, + } + } +} diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 6619121fa4..6871f47eaa 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,38 +1,36 @@ [package] name = "futures" +version = "0.3.21" edition = "2018" -version = "0.3.4" -authors = ["Alex Crichton "] +rust-version = "1.45" license = "MIT OR Apache-2.0" readme = "../README.md" keywords = ["futures", "async", "future"] repository = "https://github.com/rust-lang/futures-rs" homepage = "https://rust-lang.github.io/futures-rs" -documentation = "https://docs.rs/futures/0.3.0" description = """ An implementation of futures and streams featuring zero allocations, composability, and iterator-like interfaces. """ categories = ["asynchronous"] -[badges] -travis-ci = { repository = "rust-lang/futures-rs" } - [dependencies] -futures-core = { path = "../futures-core", version = "0.3.4", default-features = false } -futures-task = { path = "../futures-task", version = "0.3.4", default-features = false } -futures-channel = { path = "../futures-channel", version = "0.3.4", default-features = false, features = ["sink"] } -futures-executor = { path = "../futures-executor", version = "0.3.4", default-features = false, optional = true } -futures-io = { path = "../futures-io", version = "0.3.4", default-features = false } -futures-sink = { path = "../futures-sink", version = "0.3.4", default-features = false } -futures-util = { path = "../futures-util", version = "0.3.4", default-features = false, features = ["sink"] } +futures-core = { path = "../futures-core", version = "0.3.21", default-features = false } +futures-task = { path = "../futures-task", version = "0.3.21", default-features = false } +futures-channel = { path = "../futures-channel", version = "0.3.21", default-features = false, features = ["sink"] } +futures-executor = { path = "../futures-executor", version = "0.3.21", default-features = false, optional = true } +futures-io = { path = "../futures-io", version = "0.3.21", default-features = false } +futures-sink = { path = "../futures-sink", version = "0.3.21", default-features = false } +futures-util = { path = "../futures-util", version = "0.3.21", default-features = false, features = ["sink"] } [dev-dependencies] -pin-utils = "0.1.0-alpha.4" -futures-executor = { path = "../futures-executor", version = "0.3.4", features = ["thread-pool"] } -futures-test = { path = "../futures-test", version = "0.3.4" } -tokio = "0.1.11" +futures-executor = { path = "../futures-executor", features = ["thread-pool"] } +futures-test = { path = "../futures-test" } assert_matches = "1.3.0" +pin-project = "1.0.1" +pin-utils = "0.1.0" +static_assertions = "1" +tokio = "0.1.11" [features] default = ["std", "async-await", "executor"] @@ -48,12 +46,16 @@ thread-pool = ["executor", "futures-executor/thread-pool"] # These features are outside of the normal semver guarantees and require the # `unstable` feature as an explicit opt-in to unstable API. unstable = ["futures-core/unstable", "futures-task/unstable", "futures-channel/unstable", "futures-io/unstable", "futures-util/unstable"] -cfg-target-has-atomic = ["futures-core/cfg-target-has-atomic", "futures-task/cfg-target-has-atomic", "futures-channel/cfg-target-has-atomic", "futures-util/cfg-target-has-atomic"] bilock = ["futures-util/bilock"] -read-initializer = ["futures-io/read-initializer", "futures-util/read-initializer"] +write-all-vectored = ["futures-util/write-all-vectored"] + +# These features are no longer used. +# TODO: remove in the next major version. +cfg-target-has-atomic = [] [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] features = ["std", "async-await", "compat", "io-compat", "executor", "thread-pool"] diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 3cdb3d34ee..b8ebc614e2 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -3,12 +3,12 @@ //! This crate provides a number of core abstractions for writing asynchronous //! code: //! -//! - [Futures](crate::future::Future) are single eventual values produced by +//! - [Futures](crate::future) are single eventual values produced by //! asynchronous computations. Some programming languages (e.g. JavaScript) //! call this concept "promise". -//! - [Streams](crate::stream::Stream) represent a series of values +//! - [Streams](crate::stream) represent a series of values //! produced asynchronously. -//! - [Sinks](crate::sink::Sink) provide support for asynchronous writing of +//! - [Sinks](crate::sink) provide support for asynchronous writing of //! data. //! - [Executors](crate::executor) are responsible for running asynchronous //! tasks. @@ -25,11 +25,12 @@ //! within macros and keywords such as async and await!. //! //! ```rust +//! # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038 //! # use futures::channel::mpsc; //! # use futures::executor; ///standard executors to provide a context for futures and streams //! # use futures::executor::ThreadPool; //! # use futures::StreamExt; -//! +//! # //! fn main() { //! let pool = ThreadPool::new().expect("Failed to build pool"); //! let (tx, rx) = mpsc::unbounded::(); @@ -67,7 +68,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); //! @@ -78,278 +79,144 @@ //! The majority of examples and code snippets in this crate assume that they are //! inside an async block as written above. -#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] -#![cfg_attr(feature = "read-initializer", feature(read_initializer))] - #![cfg_attr(not(feature = "std"), no_std)] - -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// 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(html_root_url = "https://docs.rs/futures/0.3.0")] - -#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))] -compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features"); +#![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")))] compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features"); -#[cfg(all(feature = "read-initializer", not(feature = "unstable")))] -compile_error!("The `read-initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features"); - -#[doc(hidden)] pub use futures_core::future::{Future, TryFuture}; -#[doc(hidden)] pub use futures_util::future::{FutureExt, TryFutureExt}; +#[doc(no_inline)] +pub use futures_core::future::{Future, TryFuture}; +#[doc(no_inline)] +pub use futures_util::future::{FutureExt, TryFutureExt}; -#[doc(hidden)] pub use futures_core::stream::{Stream, TryStream}; -#[doc(hidden)] pub use futures_util::stream::{StreamExt, TryStreamExt}; +#[doc(no_inline)] +pub use futures_core::stream::{Stream, TryStream}; +#[doc(no_inline)] +pub use futures_util::stream::{StreamExt, TryStreamExt}; -#[doc(hidden)] pub use futures_sink::Sink; -#[doc(hidden)] pub use futures_util::sink::SinkExt; +#[doc(no_inline)] +pub use futures_sink::Sink; +#[doc(no_inline)] +pub use futures_util::sink::SinkExt; #[cfg(feature = "std")] -#[doc(hidden)] pub use futures_io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead}; +#[doc(no_inline)] +pub use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; #[cfg(feature = "std")] -#[doc(hidden)] pub use futures_util::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt}; +#[doc(no_inline)] +pub use futures_util::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; // Macro reexports pub use futures_core::ready; // Readiness propagation pub use futures_util::pin_mut; #[cfg(feature = "std")] #[cfg(feature = "async-await")] -pub use futures_util::{pending, poll}; // Async-await - -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] -#[cfg(feature = "alloc")] -pub mod channel { - //! Cross-task communication. - //! - //! Like threads, concurrent tasks sometimes need to communicate with each - //! other. This module contains two basic abstractions for doing so: - //! - //! - [oneshot](crate::channel::oneshot), a way of sending a single value - //! from one task to another. - //! - [mpsc](crate::channel::mpsc), a multi-producer, single-consumer - //! channel for sending values between tasks, analogous to the - //! similarly-named structure in the standard library. - //! - //! This module is only available when the `std` or `alloc` feature of this - //! library is activated, and it is activated by default. +pub use futures_util::select; +#[cfg(feature = "async-await")] +pub use futures_util::{join, pending, poll, select_biased, try_join}; // Async-await - pub use futures_channel::oneshot; +// Module reexports +#[doc(inline)] +pub use futures_util::{future, never, sink, stream, task}; - #[cfg(feature = "std")] - pub use futures_channel::mpsc; -} - -#[cfg(feature = "compat")] -pub mod compat { - //! Interop between `futures` 0.1 and 0.3. - //! - //! This module is only available when the `compat` feature of this - //! library is activated. +#[cfg(feature = "std")] +#[cfg(feature = "async-await")] +pub use futures_util::stream_select; - pub use futures_util::compat::{ - Compat, - CompatSink, - Compat01As03, - Compat01As03Sink, - Executor01Future, - Executor01As03, - Executor01CompatExt, - Future01CompatExt, - Stream01CompatExt, - Sink01CompatExt, - }; +#[cfg(feature = "alloc")] +#[doc(inline)] +pub use futures_channel as channel; +#[cfg(feature = "alloc")] +#[doc(inline)] +pub use futures_util::lock; - #[cfg(feature = "io-compat")] - pub use futures_util::compat::{ - AsyncRead01CompatExt, - AsyncWrite01CompatExt, - }; -} +#[cfg(feature = "std")] +#[doc(inline)] +pub use futures_util::io; #[cfg(feature = "executor")] +#[cfg_attr(docsrs, doc(cfg(feature = "executor")))] pub mod executor { - //! Task execution. + //! Built-in executors and related tools. //! //! All asynchronous computation occurs within an executor, which is //! capable of spawning futures as tasks. This module provides several //! built-in executors, as well as tools for building your own. //! + //! //! This module is only available when the `executor` feature of this - //! library is activated, and it is activated by default. + //! library is activated. //! //! # Using a thread pool (M:N task scheduling) //! - //! Most of the time tasks should be executed on a [thread - //! pool](crate::executor::ThreadPool). A small set of worker threads can - //! handle a very large set of spawned tasks (which are much lighter weight - //! than threads). Tasks spawned onto the pool with the - //! [`spawn_ok()`](crate::executor::ThreadPool::spawn_ok) - //! function will run ambiently on the created threads. + //! Most of the time tasks should be executed on a [thread pool](ThreadPool). + //! A small set of worker threads can handle a very large set of spawned tasks + //! (which are much lighter weight than threads). Tasks spawned onto the pool + //! with the [`spawn_ok`](ThreadPool::spawn_ok) function will run ambiently on + //! the created threads. //! //! # Spawning additional tasks //! - //! Tasks can be spawned onto a spawner by calling its - //! [`spawn_obj`](crate::task::Spawn::spawn_obj) method directly. - //! In the case of `!Send` futures, - //! [`spawn_local_obj`](crate::task::LocalSpawn::spawn_local_obj) - //! can be used instead. + //! Tasks can be spawned onto a spawner by calling its [`spawn_obj`] method + //! directly. In the case of `!Send` futures, [`spawn_local_obj`] can be used + //! instead. //! //! # Single-threaded execution //! //! In addition to thread pools, it's possible to run a task (and the tasks - //! it spawns) entirely within a single thread via the - //! [`LocalPool`](crate::executor::LocalPool) executor. Aside from cutting - //! down on synchronization costs, this executor also makes it possible to - //! spawn non-`Send` tasks, via - //! [`spawn_local_obj`](crate::task::LocalSpawn::spawn_local_obj). - //! The `LocalPool` is best suited for running I/O-bound tasks that do - //! relatively little work between I/O operations. + //! it spawns) entirely within a single thread via the [`LocalPool`] executor. + //! Aside from cutting down on synchronization costs, this executor also makes + //! it possible to spawn non-`Send` tasks, via [`spawn_local_obj`]. The + //! [`LocalPool`] is best suited for running I/O-bound tasks that do relatively + //! little work between I/O operations. + //! + //! There is also a convenience function [`block_on`] for simply running a + //! future to completion on the current thread. //! - //! There is also a convenience function - //! [`block_on`](crate::executor::block_on) for simply running a future to - //! completion on the current thread. + //! [`spawn_obj`]: https://docs.rs/futures/0.3/futures/task/trait.Spawn.html#tymethod.spawn_obj + //! [`spawn_local_obj`]: https://docs.rs/futures/0.3/futures/task/trait.LocalSpawn.html#tymethod.spawn_local_obj pub use futures_executor::{ - BlockingStream, - Enter, EnterError, - LocalSpawner, LocalPool, - block_on, block_on_stream, enter, + block_on, block_on_stream, enter, BlockingStream, Enter, EnterError, LocalPool, + LocalSpawner, }; #[cfg(feature = "thread-pool")] + #[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))] pub use futures_executor::{ThreadPool, ThreadPoolBuilder}; } -pub mod future { - //! Asynchronous values. - //! - //! This module contains: - //! - //! - The [`Future` trait](crate::future::Future). - //! - The [`FutureExt`](crate::future::FutureExt) trait, which provides - //! adapters for chaining and composing futures. - //! - Top-level future combinators like [`lazy`](crate::future::lazy) which - //! creates a future from a closure that defines its return value, and - //! [`ready`](crate::future::ready), which constructs a future with an - //! immediate defined value. - - pub use futures_core::future::{ - Future, TryFuture, FusedFuture, - }; - - #[cfg(feature = "alloc")] - pub use futures_core::future::{BoxFuture, LocalBoxFuture}; - - pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj}; - - pub use futures_util::future::{ - lazy, Lazy, - maybe_done, MaybeDone, - pending, Pending, - poll_fn, PollFn, - ready, ok, err, Ready, - join, join3, join4, join5, - Join, Join3, Join4, Join5, - select, Select, - try_join, try_join3, try_join4, try_join5, - TryJoin, TryJoin3, TryJoin4, TryJoin5, - try_select, TrySelect, - Either, - OptionFuture, - - FutureExt, - FlattenStream, Flatten, Fuse, Inspect, IntoStream, Map, Then, UnitError, - NeverError, - - TryFutureExt, - AndThen, ErrInto, FlattenSink, IntoFuture, MapErr, MapOk, OrElse, - InspectOk, InspectErr, TryFlattenStream, UnwrapOrElse, - }; - - #[cfg(feature = "alloc")] - pub use futures_util::future::{ - join_all, JoinAll, - select_all, SelectAll, - try_join_all, TryJoinAll, - select_ok, SelectOk, - }; - - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - #[cfg(feature = "alloc")] - pub use futures_util::future::{ - abortable, Abortable, AbortHandle, AbortRegistration, Aborted, - }; - - #[cfg(feature = "std")] - pub use futures_util::future::{ - Remote, RemoteHandle, - CatchUnwind, Shared, - }; -} - -#[cfg(feature = "std")] -pub mod io { - //! Asynchronous I/O. - //! - //! This module is the asynchronous version of `std::io`. It defines four - //! traits, [`AsyncRead`](crate::io::AsyncRead), - //! [`AsyncWrite`](crate::io::AsyncWrite), - //! [`AsyncSeek`](crate::io::AsyncSeek), and - //! [`AsyncBufRead`](crate::io::AsyncBufRead), which mirror the `Read`, - //! `Write`, `Seek`, and `BufRead` traits of the standard library. However, - //! these traits integrate - //! with the asynchronous task system, so that if an I/O object isn't ready - //! for reading (or writing), the thread is not blocked, and instead the - //! current task is queued to be woken when I/O is ready. - //! - //! In addition, the [`AsyncReadExt`](crate::io::AsyncReadExt), - //! [`AsyncWriteExt`](crate::io::AsyncWriteExt), - //! [`AsyncSeekExt`](crate::io::AsyncSeekExt), and - //! [`AsyncBufReadExt`](crate::io::AsyncBufReadExt) extension traits offer a - //! variety of useful combinators for operating with asynchronous I/O - //! objects, including ways to work with them using futures, streams and - //! sinks. +#[cfg(feature = "compat")] +#[cfg_attr(docsrs, doc(cfg(feature = "compat")))] +pub mod compat { + //! Interop between `futures` 0.1 and 0.3. //! - //! This module is only available when the `std` feature of this - //! library is activated, and it is activated by default. - - pub use futures_io::{ - AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, Error, ErrorKind, - IoSlice, IoSliceMut, Result, SeekFrom, - }; - - #[cfg(feature = "read-initializer")] - pub use futures_io::Initializer; + //! This module is only available when the `compat` feature of this + //! library is activated. - pub use futures_util::io::{ - AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt, AllowStdIo, - BufReader, BufWriter, Cursor, Chain, Close, copy, Copy, copy_buf, CopyBuf, - empty, Empty, Flush, IntoSink, Lines, Read, ReadExact, ReadHalf, - ReadLine, ReadToEnd, ReadToString, ReadUntil, ReadVectored, repeat, - Repeat, Seek, sink, Sink, Take, Window, Write, WriteAll, WriteHalf, - WriteVectored, + pub use futures_util::compat::{ + Compat, Compat01As03, Compat01As03Sink, CompatSink, Executor01As03, Executor01CompatExt, + Executor01Future, Future01CompatExt, Sink01CompatExt, Stream01CompatExt, }; -} - -#[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] -#[cfg(feature = "alloc")] -pub mod lock { - //! Futures-powered synchronization primitives. - //! - //! This module is only available when the `std` or `alloc` feature of this - //! library is activated, and it is activated by default. - #[cfg(feature = "bilock")] - pub use futures_util::lock::{BiLock, BiLockAcquire, BiLockGuard, ReuniteError}; - - #[cfg(feature = "std")] - pub use futures_util::lock::{MappedMutexGuard, Mutex, MutexLockFuture, MutexGuard}; + #[cfg(feature = "io-compat")] + #[cfg_attr(docsrs, doc(cfg(feature = "io-compat")))] + pub use futures_util::compat::{AsyncRead01CompatExt, AsyncWrite01CompatExt}; } pub mod prelude { @@ -367,239 +234,25 @@ pub mod prelude { //! The prelude may grow over time as additional items see ubiquitous use. pub use crate::future::{self, Future, TryFuture}; - pub use crate::stream::{self, Stream, TryStream}; pub use crate::sink::{self, Sink}; + 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::stream::{StreamExt as _, TryStreamExt 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")] - pub use crate::io::{ - AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead, - }; + pub use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; #[cfg(feature = "std")] #[doc(no_inline)] + #[allow(unreachable_pub)] pub use crate::io::{ - AsyncReadExt as _, AsyncWriteExt as _, AsyncSeekExt as _, AsyncBufReadExt as _, - }; -} - -pub mod sink { - //! Asynchronous sinks. - //! - //! This module contains: - //! - //! - The [`Sink` trait](crate::sink::Sink), which allows you to - //! asynchronously write data. - //! - The [`SinkExt`](crate::sink::SinkExt) trait, which provides adapters - //! for chaining and composing sinks. - - pub use futures_sink::Sink; - - pub use futures_util::sink::{ - Close, Flush, Send, SendAll, SinkErrInto, SinkMapErr, With, - SinkExt, Fanout, Drain, drain, - WithFlatMap, - }; - - #[cfg(feature = "alloc")] - pub use futures_util::sink::Buffer; -} - -pub mod stream { - //! Asynchronous streams. - //! - //! This module contains: - //! - //! - The [`Stream` trait](crate::stream::Stream), for objects that can - //! asynchronously produce a sequence of values. - //! - The [`StreamExt`](crate::stream::StreamExt) trait, which provides - //! adapters for chaining and composing streams. - //! - Top-level stream contructors like [`iter`](crate::stream::iter) - //! which creates a stream from an iterator. - - pub use futures_core::stream::{ - Stream, TryStream, FusedStream, - }; - - #[cfg(feature = "alloc")] - pub use futures_core::stream::{BoxStream, LocalBoxStream}; - - pub use futures_util::stream::{ - iter, Iter, - repeat, Repeat, - empty, Empty, - pending, Pending, - once, Once, - poll_fn, PollFn, - select, Select, - unfold, Unfold, - try_unfold, TryUnfold, - - StreamExt, - Chain, Collect, Concat, Enumerate, Filter, FilterMap, Flatten, Fold, - Forward, ForEach, Fuse, StreamFuture, Inspect, Map, Next, - SelectNextSome, Peek, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile, - Then, Zip, - - TryStreamExt, - AndThen, ErrInto, MapOk, MapErr, OrElse, - InspectOk, InspectErr, - TryNext, TryForEach, TryFilter, TryFilterMap, TryFlatten, - TryCollect, TryConcat, TryFold, TrySkipWhile, - IntoStream, - }; - - #[cfg(feature = "alloc")] - pub use futures_util::stream::{ - // For StreamExt: - Chunks, - }; - - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - #[cfg(feature = "alloc")] - pub use futures_util::stream::{ - FuturesOrdered, - futures_unordered, FuturesUnordered, - - // For StreamExt: - BufferUnordered, Buffered, ForEachConcurrent, SplitStream, SplitSink, - ReuniteError, - - select_all, SelectAll, - }; - - #[cfg(feature = "std")] - pub use futures_util::stream::{ - // For StreamExt: - CatchUnwind, - }; - - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - #[cfg(feature = "alloc")] - pub use futures_util::stream::{ - // For TryStreamExt: - TryBufferUnordered, TryForEachConcurrent, + AsyncBufReadExt as _, AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _, }; - - #[cfg(feature = "std")] - pub use futures_util::stream::IntoAsyncRead; -} - -pub mod task { - //! Tools for working with tasks. - //! - //! This module contains: - //! - //! - [`Spawn`](crate::task::Spawn), a trait for spawning new tasks. - //! - [`Context`](crate::task::Context), a context of an asynchronous task, - //! including a handle for waking up the task. - //! - [`Waker`](crate::task::Waker), a handle for waking up a task. - //! - //! The remaining types and traits in the module are used for implementing - //! executors or dealing with synchronization issues around task wakeup. - - pub use futures_core::task::{Context, Poll, Waker, RawWaker, RawWakerVTable}; - - pub use futures_task::{ - Spawn, LocalSpawn, SpawnError, - FutureObj, LocalFutureObj, UnsafeFutureObj, - }; - - pub use futures_util::task::noop_waker; - - #[cfg(feature = "std")] - pub use futures_util::task::noop_waker_ref; - - #[cfg(feature = "alloc")] - pub use futures_util::task::{SpawnExt, LocalSpawnExt}; - - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - #[cfg(feature = "alloc")] - pub use futures_util::task::{waker, waker_ref, WakerRef, ArcWake}; - - #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))] - pub use futures_util::task::AtomicWaker; -} - -pub mod never { - //! This module contains the `Never` type. - //! - //! Values of this type can never be created and will never exist. - - pub use futures_util::never::Never; -} - -// proc-macro re-export -------------------------------------- - -// Not public API. -#[doc(hidden)] -pub use futures_core::core_reexport; - -// Not public API. -#[cfg(feature = "async-await")] -#[doc(hidden)] -pub use futures_util::async_await; - -// Not public API. -#[cfg(feature = "async-await")] -#[doc(hidden)] -pub mod inner_macro { - pub use futures_util::join; - pub use futures_util::try_join; - #[cfg(feature = "std")] - pub use futures_util::select; - pub use futures_util::select_biased; -} - -#[cfg(feature = "async-await")] -futures_util::document_join_macro! { - #[macro_export] - macro_rules! join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! try_join { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::try_join! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } -} - -#[cfg(feature = "async-await")] -futures_util::document_select_macro! { - #[cfg(feature = "std")] - #[macro_export] - macro_rules! select { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } - - #[macro_export] - macro_rules! select_biased { // replace `::futures_util` with `::futures` as the crate path - ($($tokens:tt)*) => { - $crate::inner_macro::select_biased! { - futures_crate_path ( ::futures ) - $( $tokens )* - } - } - } } diff --git a/futures/tests/_require_features.rs b/futures/tests/_require_features.rs new file mode 100644 index 0000000000..8046cc99a4 --- /dev/null +++ b/futures/tests/_require_features.rs @@ -0,0 +1,13 @@ +#[cfg(not(all( + feature = "std", + feature = "alloc", + feature = "async-await", + feature = "compat", + feature = "io-compat", + feature = "executor", + feature = "thread-pool", +)))] +compile_error!( + "`futures` tests must have all stable features activated: \ + use `--all-features` or `--features default,thread-pool,io-compat`" +); diff --git a/futures/tests/async_await_macros.rs b/futures/tests/async_await_macros.rs index bc717df535..ce1f3a3379 100644 --- a/futures/tests/async_await_macros.rs +++ b/futures/tests/async_await_macros.rs @@ -1,12 +1,13 @@ -#![recursion_limit="128"] - -use futures::{pending, pin_mut, poll, join, try_join, select}; use futures::channel::{mpsc, oneshot}; use futures::executor::block_on; -use futures::future::{self, FutureExt, poll_fn}; +use futures::future::{self, poll_fn, FutureExt}; use futures::sink::SinkExt; use futures::stream::StreamExt; use futures::task::{Context, Poll}; +use futures::{ + join, pending, pin_mut, poll, select, select_biased, stream, stream_select, try_join, +}; +use std::mem; #[test] fn poll_and_pending() { @@ -58,8 +59,6 @@ fn select() { #[test] fn select_biased() { - use futures::select_biased; - let (tx1, rx1) = oneshot::channel::(); let (_tx2, rx2) = oneshot::channel::(); tx1.send(1).unwrap(); @@ -161,6 +160,7 @@ fn select_nested() { assert_eq!(res, 3); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn select_size() { let fut = async { @@ -169,7 +169,7 @@ fn select_size() { _ = ready => {}, } }; - assert_eq!(::std::mem::size_of_val(&fut), 24); + assert_eq!(mem::size_of_val(&fut), 24); let fut = async { let mut ready1 = future::ready(0i32); @@ -179,21 +179,19 @@ fn select_size() { _ = ready2 => {}, } }; - assert_eq!(::std::mem::size_of_val(&fut), 40); + assert_eq!(mem::size_of_val(&fut), 40); } #[test] fn select_on_non_unpin_expressions() { // The returned Future is !Unpin - let make_non_unpin_fut = || { async { - 5 - }}; + let make_non_unpin_fut = || async { 5 }; let res = block_on(async { let select_res; select! { - value_1 = make_non_unpin_fut().fuse() => { select_res = value_1 }, - value_2 = make_non_unpin_fut().fuse() => { select_res = value_2 }, + value_1 = make_non_unpin_fut().fuse() => select_res = value_1, + value_2 = make_non_unpin_fut().fuse() => select_res = value_2, }; select_res }); @@ -203,47 +201,44 @@ fn select_on_non_unpin_expressions() { #[test] fn select_on_non_unpin_expressions_with_default() { // The returned Future is !Unpin - let make_non_unpin_fut = || { async { - 5 - }}; + let make_non_unpin_fut = || async { 5 }; let res = block_on(async { let select_res; select! { - value_1 = make_non_unpin_fut().fuse() => { select_res = value_1 }, - value_2 = make_non_unpin_fut().fuse() => { select_res = value_2 }, - default => { select_res = 7 }, + value_1 = make_non_unpin_fut().fuse() => select_res = value_1, + value_2 = make_non_unpin_fut().fuse() => select_res = value_2, + default => select_res = 7, }; select_res }); assert_eq!(res, 5); } +#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[test] fn select_on_non_unpin_size() { // The returned Future is !Unpin - let make_non_unpin_fut = || { async { - 5 - }}; + let make_non_unpin_fut = || async { 5 }; let fut = async { let select_res; select! { - value_1 = make_non_unpin_fut().fuse() => { select_res = value_1 }, - value_2 = make_non_unpin_fut().fuse() => { select_res = value_2 }, + value_1 = make_non_unpin_fut().fuse() => select_res = value_1, + value_2 = make_non_unpin_fut().fuse() => select_res = value_2, }; select_res }; - assert_eq!(48, std::mem::size_of_val(&fut)); + assert_eq!(32, mem::size_of_val(&fut)); } #[test] fn select_can_be_used_as_expression() { block_on(async { let res = select! { - x = future::ready(7) => { x }, - y = future::ready(3) => { y + 1 }, + x = future::ready(7) => x, + y = future::ready(3) => y + 1, }; assert!(res == 7 || res == 4); }); @@ -258,7 +253,7 @@ fn select_with_default_can_be_used_as_expression() { block_on(async { let res = select! { x = poll_fn(poll_always_pending::).fuse() => x, - y = poll_fn(poll_always_pending::).fuse() => { y + 1 }, + y = poll_fn(poll_always_pending::).fuse() => y + 1, default => 99, }; assert_eq!(res, 99); @@ -269,8 +264,8 @@ fn select_with_default_can_be_used_as_expression() { fn select_with_complete_can_be_used_as_expression() { block_on(async { let res = select! { - x = future::pending::() => { x }, - y = future::pending::() => { y + 1 }, + x = future::pending::() => x, + y = future::pending::() => y + 1, default => 99, complete => 237, }; @@ -278,16 +273,17 @@ fn select_with_complete_can_be_used_as_expression() { }); } -async fn require_mutable(_: &mut i32) {} -async fn async_noop() {} - #[test] +#[allow(unused_assignments)] fn select_on_mutable_borrowing_future_with_same_borrow_in_block() { + async fn require_mutable(_: &mut i32) {} + async fn async_noop() {} + block_on(async { let mut value = 234; select! { - x = require_mutable(&mut value).fuse() => { }, - y = async_noop().fuse() => { + _ = require_mutable(&mut value).fuse() => { }, + _ = async_noop().fuse() => { value += 5; }, } @@ -295,12 +291,16 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block() { } #[test] +#[allow(unused_assignments)] fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { + async fn require_mutable(_: &mut i32) {} + async fn async_noop() {} + block_on(async { let mut value = 234; select! { - x = require_mutable(&mut value).fuse() => { }, - y = async_noop().fuse() => { + _ = require_mutable(&mut value).fuse() => { }, + _ = async_noop().fuse() => { value += 5; }, default => { @@ -310,20 +310,56 @@ fn select_on_mutable_borrowing_future_with_same_borrow_in_block_and_default() { }); } +#[test] +#[allow(unused_assignments)] +fn stream_select() { + // stream_select! macro + block_on(async { + let endless_ints = |i| stream::iter(vec![i].into_iter().cycle()); + + let mut endless_ones = stream_select!(endless_ints(1i32), stream::pending()); + assert_eq!(endless_ones.next().await, Some(1)); + assert_eq!(endless_ones.next().await, Some(1)); + + let mut finite_list = + stream_select!(stream::iter(vec![1].into_iter()), stream::iter(vec![1].into_iter())); + assert_eq!(finite_list.next().await, Some(1)); + assert_eq!(finite_list.next().await, Some(1)); + assert_eq!(finite_list.next().await, None); + + let endless_mixed = stream_select!(endless_ints(1i32), endless_ints(2), endless_ints(3)); + // Take 1000, and assert a somewhat even distribution of values. + // The fairness is randomized, but over 1000 samples we should be pretty close to even. + // This test may be a bit flaky. Feel free to adjust the margins as you see fit. + let mut count = 0; + let results = endless_mixed + .take_while(move |_| { + count += 1; + let ret = count < 1000; + async move { ret } + }) + .collect::>() + .await; + assert!(results.iter().filter(|x| **x == 1).count() >= 299); + assert!(results.iter().filter(|x| **x == 2).count() >= 299); + assert!(results.iter().filter(|x| **x == 3).count() >= 299); + }); +} + #[test] fn join_size() { let fut = async { let ready = future::ready(0i32); join!(ready) }; - assert_eq!(::std::mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 16); let fut = async { let ready1 = future::ready(0i32); let ready2 = future::ready(0i32); join!(ready1, ready2) }; - assert_eq!(::std::mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 28); } #[test] @@ -332,29 +368,22 @@ fn try_join_size() { let ready = future::ready(Ok::(0)); try_join!(ready) }; - assert_eq!(::std::mem::size_of_val(&fut), 16); + assert_eq!(mem::size_of_val(&fut), 16); let fut = async { let ready1 = future::ready(Ok::(0)); let ready2 = future::ready(Ok::(0)); try_join!(ready1, ready2) }; - assert_eq!(::std::mem::size_of_val(&fut), 28); + assert_eq!(mem::size_of_val(&fut), 28); } #[test] fn join_doesnt_require_unpin() { - let _ = async { - join!(async {}, async {}) - }; + let _ = async { join!(async {}, async {}) }; } #[test] fn try_join_doesnt_require_unpin() { - let _ = async { - try_join!( - async { Ok::<(), ()>(()) }, - async { Ok::<(), ()>(()) }, - ) - }; + let _ = async { try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) },) }; } diff --git a/futures/tests/auto_traits.rs b/futures/tests/auto_traits.rs new file mode 100644 index 0000000000..b3d8b00773 --- /dev/null +++ b/futures/tests/auto_traits.rs @@ -0,0 +1,1891 @@ +#![cfg(feature = "compat")] + +//! Assert Send/Sync/Unpin for all public types. + +use futures::{ + future::Future, + sink::Sink, + stream::Stream, + task::{Context, Poll}, +}; +use static_assertions::{assert_impl_all as assert_impl, assert_not_impl_all as assert_not_impl}; +use std::marker::PhantomPinned; +use std::{marker::PhantomData, pin::Pin}; + +pub type LocalFuture = Pin>>; +pub type LocalTryFuture = LocalFuture>; +pub type SendFuture = Pin + Send>>; +pub type SendTryFuture = SendFuture>; +pub type SyncFuture = Pin + Sync>>; +pub type SyncTryFuture = SyncFuture>; +pub type UnpinFuture = LocalFuture; +pub type UnpinTryFuture = UnpinFuture>; +pub struct PinnedFuture(PhantomPinned, PhantomData); +impl Future for PinnedFuture { + type Output = T; + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + unimplemented!() + } +} +pub type PinnedTryFuture = PinnedFuture>; + +pub type LocalStream = Pin>>; +pub type LocalTryStream = LocalStream>; +pub type SendStream = Pin + Send>>; +pub type SendTryStream = SendStream>; +pub type SyncStream = Pin + Sync>>; +pub type SyncTryStream = SyncStream>; +pub type UnpinStream = LocalStream; +pub type UnpinTryStream = UnpinStream>; +pub struct PinnedStream(PhantomPinned, PhantomData); +impl Stream for PinnedStream { + type Item = T; + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + unimplemented!() + } +} +pub type PinnedTryStream = PinnedStream>; + +pub type LocalSink = Pin>>; +pub type SendSink = Pin + Send>>; +pub type SyncSink = Pin + Sync>>; +pub type UnpinSink = LocalSink; +pub struct PinnedSink(PhantomPinned, PhantomData<(T, E)>); +impl Sink for PinnedSink { + type Error = E; + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + unimplemented!() + } + fn start_send(self: Pin<&mut Self>, _: T) -> Result<(), Self::Error> { + unimplemented!() + } + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + unimplemented!() + } + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + unimplemented!() + } +} + +/// Assert Send/Sync/Unpin for all public types in `futures::channel`. +pub mod channel { + use super::*; + use futures::channel::*; + + assert_impl!(mpsc::Receiver<()>: Send); + assert_not_impl!(mpsc::Receiver<*const ()>: Send); + assert_impl!(mpsc::Receiver<()>: Sync); + assert_not_impl!(mpsc::Receiver<*const ()>: Sync); + assert_impl!(mpsc::Receiver: Unpin); + + assert_impl!(mpsc::SendError: Send); + assert_impl!(mpsc::SendError: Sync); + assert_impl!(mpsc::SendError: Unpin); + + assert_impl!(mpsc::Sender<()>: Send); + assert_not_impl!(mpsc::Sender<*const ()>: Send); + assert_impl!(mpsc::Sender<()>: Sync); + assert_not_impl!(mpsc::Sender<*const ()>: Sync); + assert_impl!(mpsc::Sender: Unpin); + + assert_impl!(mpsc::TryRecvError: Send); + assert_impl!(mpsc::TryRecvError: Sync); + assert_impl!(mpsc::TryRecvError: Unpin); + + assert_impl!(mpsc::TrySendError<()>: Send); + assert_not_impl!(mpsc::TrySendError<*const ()>: Send); + assert_impl!(mpsc::TrySendError<()>: Sync); + assert_not_impl!(mpsc::TrySendError<*const ()>: Sync); + assert_impl!(mpsc::TrySendError<()>: Unpin); + assert_not_impl!(mpsc::TrySendError: Unpin); + + assert_impl!(mpsc::UnboundedReceiver<()>: Send); + assert_not_impl!(mpsc::UnboundedReceiver<*const ()>: Send); + assert_impl!(mpsc::UnboundedReceiver<()>: Sync); + assert_not_impl!(mpsc::UnboundedReceiver<*const ()>: Sync); + assert_impl!(mpsc::UnboundedReceiver: Unpin); + + assert_impl!(mpsc::UnboundedReceiver<()>: Send); + assert_not_impl!(mpsc::UnboundedReceiver<*const ()>: Send); + assert_impl!(mpsc::UnboundedReceiver<()>: Sync); + assert_not_impl!(mpsc::UnboundedReceiver<*const ()>: Sync); + assert_impl!(mpsc::UnboundedReceiver: Unpin); + + assert_impl!(oneshot::Canceled: Send); + assert_impl!(oneshot::Canceled: Sync); + assert_impl!(oneshot::Canceled: Unpin); + + assert_impl!(oneshot::Cancellation<()>: Send); + assert_not_impl!(oneshot::Cancellation<*const ()>: Send); + assert_impl!(oneshot::Cancellation<()>: Sync); + assert_not_impl!(oneshot::Cancellation<*const ()>: Sync); + assert_impl!(oneshot::Cancellation: Unpin); + + assert_impl!(oneshot::Receiver<()>: Send); + assert_not_impl!(oneshot::Receiver<*const ()>: Send); + assert_impl!(oneshot::Receiver<()>: Sync); + assert_not_impl!(oneshot::Receiver<*const ()>: Sync); + assert_impl!(oneshot::Receiver: Unpin); + + assert_impl!(oneshot::Sender<()>: Send); + assert_not_impl!(oneshot::Sender<*const ()>: Send); + assert_impl!(oneshot::Sender<()>: Sync); + assert_not_impl!(oneshot::Sender<*const ()>: Sync); + assert_impl!(oneshot::Sender: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::compat`. +pub mod compat { + use super::*; + use futures::compat::*; + + assert_impl!(Compat<()>: Send); + assert_not_impl!(Compat<*const ()>: Send); + assert_impl!(Compat<()>: Sync); + assert_not_impl!(Compat<*const ()>: Sync); + assert_impl!(Compat<()>: Unpin); + assert_not_impl!(Compat: Unpin); + + assert_impl!(Compat01As03<()>: Send); + assert_not_impl!(Compat01As03<*const ()>: Send); + assert_not_impl!(Compat01As03<()>: Sync); + assert_impl!(Compat01As03: Unpin); + + assert_impl!(Compat01As03Sink<(), ()>: Send); + assert_not_impl!(Compat01As03Sink<(), *const ()>: Send); + assert_not_impl!(Compat01As03Sink<*const (), ()>: Send); + assert_not_impl!(Compat01As03Sink<(), ()>: Sync); + assert_impl!(Compat01As03Sink: Unpin); + + assert_impl!(CompatSink<(), *const ()>: Send); + assert_not_impl!(CompatSink<*const (), ()>: Send); + assert_impl!(CompatSink<(), *const ()>: Sync); + assert_not_impl!(CompatSink<*const (), ()>: Sync); + assert_impl!(CompatSink<(), PhantomPinned>: Unpin); + assert_not_impl!(CompatSink: Unpin); + + assert_impl!(Executor01As03<()>: Send); + assert_not_impl!(Executor01As03<*const ()>: Send); + assert_impl!(Executor01As03<()>: Sync); + assert_not_impl!(Executor01As03<*const ()>: Sync); + assert_impl!(Executor01As03<()>: Unpin); + assert_not_impl!(Executor01As03: Unpin); + + assert_impl!(Executor01Future: Send); + assert_not_impl!(Executor01Future: Sync); + assert_impl!(Executor01Future: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::executor`. +pub mod executor { + use super::*; + use futures::executor::*; + + assert_impl!(BlockingStream: Send); + assert_not_impl!(BlockingStream: Send); + assert_impl!(BlockingStream: Sync); + assert_not_impl!(BlockingStream: Sync); + assert_impl!(BlockingStream: Unpin); + // BlockingStream requires `S: Unpin` + // assert_not_impl!(BlockingStream: Unpin); + + assert_impl!(Enter: Send); + assert_impl!(Enter: Sync); + assert_impl!(Enter: Unpin); + + assert_impl!(EnterError: Send); + assert_impl!(EnterError: Sync); + assert_impl!(EnterError: Unpin); + + assert_not_impl!(LocalPool: Send); + assert_not_impl!(LocalPool: Sync); + assert_impl!(LocalPool: Unpin); + + assert_not_impl!(LocalSpawner: Send); + assert_not_impl!(LocalSpawner: Sync); + assert_impl!(LocalSpawner: Unpin); + + assert_impl!(ThreadPool: Send); + assert_impl!(ThreadPool: Sync); + assert_impl!(ThreadPool: Unpin); + + assert_impl!(ThreadPoolBuilder: Send); + assert_impl!(ThreadPoolBuilder: Sync); + assert_impl!(ThreadPoolBuilder: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::future`. +pub mod future { + use super::*; + use futures::future::*; + + assert_impl!(AbortHandle: Send); + assert_impl!(AbortHandle: Sync); + assert_impl!(AbortHandle: Unpin); + + assert_impl!(AbortRegistration: Send); + assert_impl!(AbortRegistration: Sync); + assert_impl!(AbortRegistration: Unpin); + + assert_impl!(Abortable: Send); + assert_not_impl!(Abortable: Send); + assert_impl!(Abortable: Sync); + assert_not_impl!(Abortable: Sync); + assert_impl!(Abortable: Unpin); + assert_not_impl!(Abortable: Unpin); + + assert_impl!(Aborted: Send); + assert_impl!(Aborted: Sync); + assert_impl!(Aborted: Unpin); + + assert_impl!(AndThen: Send); + assert_not_impl!(AndThen: Send); + assert_not_impl!(AndThen: Send); + assert_not_impl!(AndThen: Send); + assert_impl!(AndThen: Sync); + assert_not_impl!(AndThen: Sync); + assert_not_impl!(AndThen: Sync); + assert_not_impl!(AndThen: Sync); + assert_impl!(AndThen: Unpin); + assert_not_impl!(AndThen: Unpin); + assert_not_impl!(AndThen: Unpin); + + assert_impl!(CatchUnwind: Send); + assert_not_impl!(CatchUnwind: Send); + assert_impl!(CatchUnwind: Sync); + assert_not_impl!(CatchUnwind: Sync); + assert_impl!(CatchUnwind: Unpin); + assert_not_impl!(CatchUnwind: Unpin); + + assert_impl!(ErrInto: Send); + assert_not_impl!(ErrInto: Send); + assert_impl!(ErrInto: Sync); + assert_not_impl!(ErrInto: Sync); + assert_impl!(ErrInto: Unpin); + assert_not_impl!(ErrInto: Unpin); + + assert_impl!(Flatten>: Send); + assert_not_impl!(Flatten: Send); + assert_not_impl!(Flatten: Send); + assert_impl!(Flatten>: Sync); + assert_not_impl!(Flatten: Sync); + assert_not_impl!(Flatten: Sync); + assert_impl!(Flatten>: Unpin); + assert_not_impl!(Flatten: Unpin); + assert_not_impl!(Flatten: Unpin); + + assert_impl!(FlattenSink: Send); + assert_not_impl!(FlattenSink: Send); + assert_not_impl!(FlattenSink: Send); + assert_impl!(FlattenSink: Sync); + assert_not_impl!(FlattenSink: Sync); + assert_not_impl!(FlattenSink: Sync); + assert_impl!(FlattenSink: Unpin); + assert_not_impl!(FlattenSink: Unpin); + assert_not_impl!(FlattenSink: Unpin); + + assert_impl!(FlattenStream>: Send); + assert_not_impl!(FlattenStream: Send); + assert_not_impl!(FlattenStream: Send); + assert_impl!(FlattenStream>: Sync); + assert_not_impl!(FlattenStream: Sync); + assert_not_impl!(FlattenStream: Sync); + assert_impl!(FlattenStream>: Unpin); + assert_not_impl!(FlattenStream: Unpin); + assert_not_impl!(FlattenStream: Unpin); + + assert_impl!(Fuse: Send); + assert_not_impl!(Fuse: Send); + assert_impl!(Fuse: Sync); + assert_not_impl!(Fuse: Sync); + assert_impl!(Fuse: Unpin); + assert_not_impl!(Fuse: Unpin); + + assert_impl!(FutureObj<*const ()>: Send); + assert_not_impl!(FutureObj<()>: Sync); + assert_impl!(FutureObj: Unpin); + + assert_impl!(Inspect: Send); + assert_not_impl!(Inspect: Send); + assert_not_impl!(Inspect: Send); + assert_impl!(Inspect: Sync); + assert_not_impl!(Inspect: Sync); + assert_not_impl!(Inspect: Sync); + assert_impl!(Inspect: Unpin); + assert_not_impl!(Inspect: Unpin); + + assert_impl!(InspectErr: Send); + assert_not_impl!(InspectErr: Send); + assert_not_impl!(InspectErr: Send); + assert_impl!(InspectErr: Sync); + assert_not_impl!(InspectErr: Sync); + assert_not_impl!(InspectErr: Sync); + assert_impl!(InspectErr: Unpin); + assert_not_impl!(InspectErr: Unpin); + + assert_impl!(InspectOk: Send); + assert_not_impl!(InspectOk: Send); + assert_not_impl!(InspectOk: Send); + assert_impl!(InspectOk: Sync); + assert_not_impl!(InspectOk: Sync); + assert_not_impl!(InspectOk: Sync); + assert_impl!(InspectOk: Unpin); + assert_not_impl!(InspectOk: Unpin); + + assert_impl!(IntoFuture: Send); + assert_not_impl!(IntoFuture: Send); + assert_impl!(IntoFuture: Sync); + assert_not_impl!(IntoFuture: Sync); + assert_impl!(IntoFuture: Unpin); + assert_not_impl!(IntoFuture: Unpin); + + assert_impl!(IntoStream: Send); + assert_not_impl!(IntoStream: Send); + assert_impl!(IntoStream: Sync); + assert_not_impl!(IntoStream: Sync); + assert_impl!(IntoStream: Unpin); + assert_not_impl!(IntoStream: Unpin); + + assert_impl!(Join, SendFuture<()>>: Send); + assert_not_impl!(Join, SendFuture>: Send); + assert_not_impl!(Join>: Send); + assert_not_impl!(Join: Send); + assert_not_impl!(Join: Send); + assert_impl!(Join, SyncFuture<()>>: Sync); + assert_not_impl!(Join, SyncFuture>: Sync); + assert_not_impl!(Join>: Sync); + assert_not_impl!(Join: Sync); + assert_not_impl!(Join: Sync); + assert_impl!(Join: Unpin); + assert_not_impl!(Join: Unpin); + assert_not_impl!(Join: Unpin); + + // Join3, Join4, Join5 are the same as Join + + assert_impl!(JoinAll>: Send); + assert_not_impl!(JoinAll: Send); + assert_not_impl!(JoinAll: Send); + assert_impl!(JoinAll>: Sync); + assert_not_impl!(JoinAll: Sync); + assert_not_impl!(JoinAll: Sync); + assert_impl!(JoinAll: Unpin); + + assert_impl!(Lazy<()>: Send); + assert_not_impl!(Lazy<*const ()>: Send); + assert_impl!(Lazy<()>: Sync); + assert_not_impl!(Lazy<*const ()>: Sync); + assert_impl!(Lazy: Unpin); + + assert_not_impl!(LocalFutureObj<()>: Send); + assert_not_impl!(LocalFutureObj<()>: Sync); + assert_impl!(LocalFutureObj: Unpin); + + assert_impl!(Map: Send); + assert_not_impl!(Map: Send); + assert_not_impl!(Map: Send); + assert_impl!(Map: Sync); + assert_not_impl!(Map: Sync); + assert_not_impl!(Map: Sync); + assert_impl!(Map: Unpin); + assert_not_impl!(Map: Unpin); + + assert_impl!(MapErr: Send); + assert_not_impl!(MapErr: Send); + assert_not_impl!(MapErr: Send); + assert_impl!(MapErr: Sync); + assert_not_impl!(MapErr: Sync); + assert_not_impl!(MapErr: Sync); + assert_impl!(MapErr: Unpin); + assert_not_impl!(MapErr: Unpin); + + assert_impl!(MapInto: Send); + assert_not_impl!(MapInto: Send); + assert_impl!(MapInto: Sync); + assert_not_impl!(MapInto: Sync); + assert_impl!(MapInto: Unpin); + assert_not_impl!(MapInto: Unpin); + + assert_impl!(MapOk: Send); + assert_not_impl!(MapOk: Send); + assert_not_impl!(MapOk: Send); + assert_impl!(MapOk: Sync); + assert_not_impl!(MapOk: Sync); + assert_not_impl!(MapOk: Sync); + assert_impl!(MapOk: Unpin); + assert_not_impl!(MapOk: Unpin); + + assert_impl!(MapOkOrElse: Send); + assert_not_impl!(MapOkOrElse: Send); + assert_not_impl!(MapOkOrElse: Send); + assert_not_impl!(MapOkOrElse: Send); + assert_impl!(MapOkOrElse: Sync); + assert_not_impl!(MapOkOrElse: Sync); + assert_not_impl!(MapOkOrElse: Sync); + assert_not_impl!(MapOkOrElse: Sync); + assert_impl!(MapOkOrElse: Unpin); + assert_not_impl!(MapOkOrElse: Unpin); + + assert_impl!(NeverError: Send); + assert_not_impl!(NeverError: Send); + assert_impl!(NeverError: Sync); + assert_not_impl!(NeverError: Sync); + assert_impl!(NeverError: Unpin); + assert_not_impl!(NeverError: Unpin); + + assert_impl!(OkInto: Send); + assert_not_impl!(OkInto: Send); + assert_impl!(OkInto: Sync); + assert_not_impl!(OkInto: Sync); + assert_impl!(OkInto: Unpin); + assert_not_impl!(OkInto: Unpin); + + assert_impl!(OptionFuture: Send); + assert_not_impl!(OptionFuture: Send); + assert_impl!(OptionFuture: Sync); + assert_not_impl!(OptionFuture: Sync); + assert_impl!(OptionFuture: Unpin); + assert_not_impl!(OptionFuture: Unpin); + + assert_impl!(OrElse: Send); + assert_not_impl!(OrElse: Send); + assert_not_impl!(OrElse: Send); + assert_not_impl!(OrElse: Send); + assert_impl!(OrElse: Sync); + assert_not_impl!(OrElse: Sync); + assert_not_impl!(OrElse: Sync); + assert_not_impl!(OrElse: Sync); + assert_impl!(OrElse: Unpin); + assert_not_impl!(OrElse: Unpin); + assert_not_impl!(OrElse: Unpin); + + assert_impl!(Pending<()>: Send); + assert_not_impl!(Pending<*const ()>: Send); + assert_impl!(Pending<()>: Sync); + assert_not_impl!(Pending<*const ()>: Sync); + assert_impl!(Pending: Unpin); + + assert_impl!(PollFn<()>: Send); + assert_not_impl!(PollFn<*const ()>: Send); + assert_impl!(PollFn<()>: Sync); + assert_not_impl!(PollFn<*const ()>: Sync); + assert_impl!(PollFn: Unpin); + + assert_impl!(PollImmediate: Send); + assert_not_impl!(PollImmediate>: Send); + assert_impl!(PollImmediate: Sync); + assert_not_impl!(PollImmediate>: Sync); + assert_impl!(PollImmediate: Unpin); + assert_not_impl!(PollImmediate: Unpin); + + assert_impl!(Ready<()>: Send); + assert_not_impl!(Ready<*const ()>: Send); + assert_impl!(Ready<()>: Sync); + assert_not_impl!(Ready<*const ()>: Sync); + assert_impl!(Ready: Unpin); + + assert_impl!(Remote>: Send); + assert_not_impl!(Remote: Send); + assert_not_impl!(Remote: Send); + assert_impl!(Remote>: Sync); + assert_not_impl!(Remote: Sync); + assert_not_impl!(Remote: Sync); + assert_impl!(Remote: Unpin); + assert_not_impl!(Remote: Unpin); + + assert_impl!(RemoteHandle<()>: Send); + assert_not_impl!(RemoteHandle<*const ()>: Send); + assert_impl!(RemoteHandle<()>: Sync); + assert_not_impl!(RemoteHandle<*const ()>: Sync); + assert_impl!(RemoteHandle: Unpin); + + assert_impl!(Select: Send); + assert_not_impl!(Select: Send); + assert_not_impl!(Select: Send); + assert_impl!(Select: Sync); + assert_not_impl!(Select: Sync); + assert_not_impl!(Select: Sync); + assert_impl!(Select: Unpin); + assert_not_impl!(Select: Unpin); + assert_not_impl!(Select: Unpin); + + assert_impl!(SelectAll: Send); + assert_not_impl!(SelectAll: Send); + assert_impl!(SelectAll: Sync); + assert_not_impl!(SelectAll: Sync); + assert_impl!(SelectAll: Unpin); + assert_not_impl!(SelectAll: Unpin); + + assert_impl!(SelectOk: Send); + assert_not_impl!(SelectOk: Send); + assert_impl!(SelectOk: Sync); + assert_not_impl!(SelectOk: Sync); + assert_impl!(SelectOk: Unpin); + assert_not_impl!(SelectOk: Unpin); + + assert_impl!(Shared>: Send); + assert_not_impl!(Shared: Send); + assert_not_impl!(Shared: Send); + assert_not_impl!(Shared>: Sync); + assert_impl!(Shared: Unpin); + + assert_impl!(Then: Send); + assert_not_impl!(Then: Send); + assert_not_impl!(Then: Send); + assert_not_impl!(Then: Send); + assert_impl!(Then: Sync); + assert_not_impl!(Then: Sync); + assert_not_impl!(Then: Sync); + assert_not_impl!(Then: Sync); + assert_impl!(Then: Unpin); + assert_not_impl!(Then: Unpin); + assert_not_impl!(Then: Unpin); + + assert_impl!(TryFlatten, ()>: Send); + assert_not_impl!(TryFlatten: Send); + assert_not_impl!(TryFlatten: Send); + assert_impl!(TryFlatten, ()>: Sync); + assert_not_impl!(TryFlatten: Sync); + assert_not_impl!(TryFlatten: Sync); + assert_impl!(TryFlatten, ()>: Unpin); + assert_not_impl!(TryFlatten: Unpin); + assert_not_impl!(TryFlatten: Unpin); + + assert_impl!(TryFlattenStream>: Send); + assert_not_impl!(TryFlattenStream: Send); + assert_not_impl!(TryFlattenStream: Send); + assert_impl!(TryFlattenStream>: Sync); + assert_not_impl!(TryFlattenStream: Sync); + assert_not_impl!(TryFlattenStream: Sync); + assert_impl!(TryFlattenStream>: Unpin); + assert_not_impl!(TryFlattenStream: Unpin); + assert_not_impl!(TryFlattenStream: Unpin); + + assert_impl!(TryJoin, SendTryFuture<()>>: Send); + assert_not_impl!(TryJoin, SendTryFuture>: Send); + assert_not_impl!(TryJoin>: Send); + assert_not_impl!(TryJoin: Send); + assert_not_impl!(TryJoin: Send); + assert_impl!(TryJoin, SyncTryFuture<()>>: Sync); + assert_not_impl!(TryJoin, SyncTryFuture>: Sync); + assert_not_impl!(TryJoin>: Sync); + assert_not_impl!(TryJoin: Sync); + assert_not_impl!(TryJoin: Sync); + assert_impl!(TryJoin: Unpin); + assert_not_impl!(TryJoin: Unpin); + assert_not_impl!(TryJoin: Unpin); + + // TryJoin3, TryJoin4, TryJoin5 are the same as TryJoin + + assert_impl!(TryJoinAll>: Send); + assert_not_impl!(TryJoinAll: Send); + assert_not_impl!(TryJoinAll: Send); + assert_impl!(TryJoinAll>: Sync); + assert_not_impl!(TryJoinAll: Sync); + assert_not_impl!(TryJoinAll: Sync); + assert_impl!(TryJoinAll: Unpin); + + assert_impl!(TrySelect: Send); + assert_not_impl!(TrySelect: Send); + assert_not_impl!(TrySelect: Send); + assert_impl!(TrySelect: Sync); + assert_not_impl!(TrySelect: Sync); + assert_not_impl!(TrySelect: Sync); + assert_impl!(TrySelect: Unpin); + assert_not_impl!(TrySelect: Unpin); + assert_not_impl!(TrySelect: Unpin); + + assert_impl!(UnitError: Send); + assert_not_impl!(UnitError: Send); + assert_impl!(UnitError: Sync); + assert_not_impl!(UnitError: Sync); + assert_impl!(UnitError: Unpin); + assert_not_impl!(UnitError: Unpin); + + assert_impl!(UnwrapOrElse: Send); + assert_not_impl!(UnwrapOrElse: Send); + assert_not_impl!(UnwrapOrElse: Send); + assert_impl!(UnwrapOrElse: Sync); + assert_not_impl!(UnwrapOrElse: Sync); + assert_not_impl!(UnwrapOrElse: Sync); + assert_impl!(UnwrapOrElse: Unpin); + assert_not_impl!(UnwrapOrElse: Unpin); + + assert_impl!(WeakShared>: Send); + assert_not_impl!(WeakShared: Send); + assert_not_impl!(WeakShared: Send); + assert_not_impl!(WeakShared>: Sync); + assert_impl!(WeakShared: Unpin); + + assert_impl!(Either: Send); + assert_not_impl!(Either: Send); + assert_not_impl!(Either: Send); + assert_impl!(Either: Sync); + assert_not_impl!(Either: Sync); + assert_not_impl!(Either: Sync); + assert_impl!(Either: Unpin); + assert_not_impl!(Either: Unpin); + assert_not_impl!(Either: Unpin); + + assert_impl!(MaybeDone>: Send); + assert_not_impl!(MaybeDone: Send); + assert_not_impl!(MaybeDone: Send); + assert_impl!(MaybeDone>: Sync); + assert_not_impl!(MaybeDone: Sync); + assert_not_impl!(MaybeDone: Sync); + assert_impl!(MaybeDone: Unpin); + assert_not_impl!(MaybeDone: Unpin); + + assert_impl!(TryMaybeDone>: Send); + assert_not_impl!(TryMaybeDone: Send); + assert_not_impl!(TryMaybeDone: Send); + assert_impl!(TryMaybeDone>: Sync); + assert_not_impl!(TryMaybeDone: Sync); + assert_not_impl!(TryMaybeDone: Sync); + assert_impl!(TryMaybeDone: Unpin); + assert_not_impl!(TryMaybeDone: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::io`. +pub mod io { + use super::*; + use futures::io::{Sink, *}; + + assert_impl!(AllowStdIo<()>: Send); + assert_not_impl!(AllowStdIo<*const ()>: Send); + assert_impl!(AllowStdIo<()>: Sync); + assert_not_impl!(AllowStdIo<*const ()>: Sync); + assert_impl!(AllowStdIo: Unpin); + + assert_impl!(BufReader<()>: Send); + assert_not_impl!(BufReader<*const ()>: Send); + assert_impl!(BufReader<()>: Sync); + assert_not_impl!(BufReader<*const ()>: Sync); + assert_impl!(BufReader<()>: Unpin); + assert_not_impl!(BufReader: Unpin); + + assert_impl!(BufWriter<()>: Send); + assert_not_impl!(BufWriter<*const ()>: Send); + assert_impl!(BufWriter<()>: Sync); + assert_not_impl!(BufWriter<*const ()>: Sync); + assert_impl!(BufWriter<()>: Unpin); + assert_not_impl!(BufWriter: Unpin); + + assert_impl!(Chain<(), ()>: Send); + assert_not_impl!(Chain<(), *const ()>: Send); + assert_not_impl!(Chain<*const (), ()>: Send); + assert_impl!(Chain<(), ()>: Sync); + assert_not_impl!(Chain<(), *const ()>: Sync); + assert_not_impl!(Chain<*const (), ()>: Sync); + assert_impl!(Chain<(), ()>: Unpin); + assert_not_impl!(Chain<(), PhantomPinned>: Unpin); + assert_not_impl!(Chain: Unpin); + + assert_impl!(Close<'_, ()>: Send); + assert_not_impl!(Close<'_, *const ()>: Send); + assert_impl!(Close<'_, ()>: Sync); + assert_not_impl!(Close<'_, *const ()>: Sync); + assert_impl!(Close<'_, ()>: Unpin); + assert_not_impl!(Close<'_, PhantomPinned>: Unpin); + + assert_impl!(Copy<(), ()>: Send); + assert_not_impl!(Copy<(), *const ()>: Send); + assert_not_impl!(Copy<*const (), ()>: Send); + assert_impl!(Copy<(), ()>: Sync); + assert_not_impl!(Copy<(), *const ()>: Sync); + assert_not_impl!(Copy<*const (), ()>: Sync); + assert_impl!(Copy<(), PhantomPinned>: Unpin); + assert_not_impl!(Copy: Unpin); + + assert_impl!(CopyBuf<(), ()>: Send); + assert_not_impl!(CopyBuf<(), *const ()>: Send); + assert_not_impl!(CopyBuf<*const (), ()>: Send); + assert_impl!(CopyBuf<(), ()>: Sync); + assert_not_impl!(CopyBuf<(), *const ()>: Sync); + assert_not_impl!(CopyBuf<*const (), ()>: Sync); + assert_impl!(CopyBuf<(), PhantomPinned>: Unpin); + assert_not_impl!(CopyBuf: Unpin); + + assert_impl!(Cursor<()>: Send); + assert_not_impl!(Cursor<*const ()>: Send); + assert_impl!(Cursor<()>: Sync); + assert_not_impl!(Cursor<*const ()>: Sync); + assert_impl!(Cursor<()>: Unpin); + assert_not_impl!(Cursor: Unpin); + + assert_impl!(Empty: Send); + assert_impl!(Empty: Sync); + assert_impl!(Empty: Unpin); + + assert_impl!(FillBuf<'_, ()>: Send); + assert_not_impl!(FillBuf<'_, *const ()>: Send); + assert_impl!(FillBuf<'_, ()>: Sync); + assert_not_impl!(FillBuf<'_, *const ()>: Sync); + assert_impl!(FillBuf<'_, PhantomPinned>: Unpin); + + assert_impl!(Flush<'_, ()>: Send); + assert_not_impl!(Flush<'_, *const ()>: Send); + assert_impl!(Flush<'_, ()>: Sync); + assert_not_impl!(Flush<'_, *const ()>: Sync); + assert_impl!(Flush<'_, ()>: Unpin); + assert_not_impl!(Flush<'_, PhantomPinned>: Unpin); + + assert_impl!(IntoSink<(), ()>: Send); + assert_not_impl!(IntoSink<(), *const ()>: Send); + assert_not_impl!(IntoSink<*const (), ()>: Send); + assert_impl!(IntoSink<(), ()>: Sync); + assert_not_impl!(IntoSink<(), *const ()>: Sync); + assert_not_impl!(IntoSink<*const (), ()>: Sync); + assert_impl!(IntoSink<(), PhantomPinned>: Unpin); + assert_not_impl!(IntoSink: Unpin); + + assert_impl!(Lines<()>: Send); + assert_not_impl!(Lines<*const ()>: Send); + assert_impl!(Lines<()>: Sync); + assert_not_impl!(Lines<*const ()>: Sync); + assert_impl!(Lines<()>: Unpin); + assert_not_impl!(Lines: Unpin); + + assert_impl!(Read<'_, ()>: Send); + assert_not_impl!(Read<'_, *const ()>: Send); + assert_impl!(Read<'_, ()>: Sync); + assert_not_impl!(Read<'_, *const ()>: Sync); + assert_impl!(Read<'_, ()>: Unpin); + assert_not_impl!(Read<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadExact<'_, ()>: Send); + assert_not_impl!(ReadExact<'_, *const ()>: Send); + assert_impl!(ReadExact<'_, ()>: Sync); + assert_not_impl!(ReadExact<'_, *const ()>: Sync); + assert_impl!(ReadExact<'_, ()>: Unpin); + assert_not_impl!(ReadExact<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadHalf<()>: Send); + assert_not_impl!(ReadHalf<*const ()>: Send); + assert_impl!(ReadHalf<()>: Sync); + assert_not_impl!(ReadHalf<*const ()>: Sync); + assert_impl!(ReadHalf: Unpin); + + assert_impl!(ReadLine<'_, ()>: Send); + assert_not_impl!(ReadLine<'_, *const ()>: Send); + assert_impl!(ReadLine<'_, ()>: Sync); + assert_not_impl!(ReadLine<'_, *const ()>: Sync); + assert_impl!(ReadLine<'_, ()>: Unpin); + assert_not_impl!(ReadLine<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadToEnd<'_, ()>: Send); + assert_not_impl!(ReadToEnd<'_, *const ()>: Send); + assert_impl!(ReadToEnd<'_, ()>: Sync); + assert_not_impl!(ReadToEnd<'_, *const ()>: Sync); + assert_impl!(ReadToEnd<'_, ()>: Unpin); + assert_not_impl!(ReadToEnd<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadToString<'_, ()>: Send); + assert_not_impl!(ReadToString<'_, *const ()>: Send); + assert_impl!(ReadToString<'_, ()>: Sync); + assert_not_impl!(ReadToString<'_, *const ()>: Sync); + assert_impl!(ReadToString<'_, ()>: Unpin); + assert_not_impl!(ReadToString<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadUntil<'_, ()>: Send); + assert_not_impl!(ReadUntil<'_, *const ()>: Send); + assert_impl!(ReadUntil<'_, ()>: Sync); + assert_not_impl!(ReadUntil<'_, *const ()>: Sync); + assert_impl!(ReadUntil<'_, ()>: Unpin); + assert_not_impl!(ReadUntil<'_, PhantomPinned>: Unpin); + + assert_impl!(ReadVectored<'_, ()>: Send); + assert_not_impl!(ReadVectored<'_, *const ()>: Send); + assert_impl!(ReadVectored<'_, ()>: Sync); + assert_not_impl!(ReadVectored<'_, *const ()>: Sync); + assert_impl!(ReadVectored<'_, ()>: Unpin); + assert_not_impl!(ReadVectored<'_, PhantomPinned>: Unpin); + + assert_impl!(Repeat: Send); + assert_impl!(Repeat: Sync); + assert_impl!(Repeat: Unpin); + + assert_impl!(ReuniteError<()>: Send); + assert_not_impl!(ReuniteError<*const ()>: Send); + assert_impl!(ReuniteError<()>: Sync); + assert_not_impl!(ReuniteError<*const ()>: Sync); + assert_impl!(ReuniteError: Unpin); + + assert_impl!(Seek<'_, ()>: Send); + assert_not_impl!(Seek<'_, *const ()>: Send); + assert_impl!(Seek<'_, ()>: Sync); + assert_not_impl!(Seek<'_, *const ()>: Sync); + assert_impl!(Seek<'_, ()>: Unpin); + assert_not_impl!(Seek<'_, PhantomPinned>: Unpin); + + assert_impl!(SeeKRelative<'_, ()>: Send); + assert_not_impl!(SeeKRelative<'_, *const ()>: Send); + assert_impl!(SeeKRelative<'_, ()>: Sync); + assert_not_impl!(SeeKRelative<'_, *const ()>: Sync); + assert_impl!(SeeKRelative<'_, PhantomPinned>: Unpin); + + assert_impl!(Sink: Send); + assert_impl!(Sink: Sync); + assert_impl!(Sink: Unpin); + + assert_impl!(Take<()>: Send); + assert_not_impl!(Take<*const ()>: Send); + assert_impl!(Take<()>: Sync); + assert_not_impl!(Take<*const ()>: Sync); + assert_impl!(Take<()>: Unpin); + assert_not_impl!(Take: Unpin); + + assert_impl!(Window<()>: Send); + assert_not_impl!(Window<*const ()>: Send); + assert_impl!(Window<()>: Sync); + assert_not_impl!(Window<*const ()>: Sync); + assert_impl!(Window<()>: Unpin); + assert_not_impl!(Window: Unpin); + + assert_impl!(Write<'_, ()>: Send); + assert_not_impl!(Write<'_, *const ()>: Send); + assert_impl!(Write<'_, ()>: Sync); + assert_not_impl!(Write<'_, *const ()>: Sync); + assert_impl!(Write<'_, ()>: Unpin); + assert_not_impl!(Write<'_, PhantomPinned>: Unpin); + + assert_impl!(WriteAll<'_, ()>: Send); + assert_not_impl!(WriteAll<'_, *const ()>: Send); + assert_impl!(WriteAll<'_, ()>: Sync); + assert_not_impl!(WriteAll<'_, *const ()>: Sync); + assert_impl!(WriteAll<'_, ()>: Unpin); + assert_not_impl!(WriteAll<'_, PhantomPinned>: Unpin); + + #[cfg(feature = "write-all-vectored")] + assert_impl!(WriteAllVectored<'_, ()>: Send); + #[cfg(feature = "write-all-vectored")] + assert_not_impl!(WriteAllVectored<'_, *const ()>: Send); + #[cfg(feature = "write-all-vectored")] + assert_impl!(WriteAllVectored<'_, ()>: Sync); + #[cfg(feature = "write-all-vectored")] + assert_not_impl!(WriteAllVectored<'_, *const ()>: Sync); + #[cfg(feature = "write-all-vectored")] + assert_impl!(WriteAllVectored<'_, ()>: Unpin); + // WriteAllVectored requires `W: Unpin` + // #[cfg(feature = "write-all-vectored")] + // assert_not_impl!(WriteAllVectored<'_, PhantomPinned>: Unpin); + + assert_impl!(WriteHalf<()>: Send); + assert_not_impl!(WriteHalf<*const ()>: Send); + assert_impl!(WriteHalf<()>: Sync); + assert_not_impl!(WriteHalf<*const ()>: Sync); + assert_impl!(WriteHalf: Unpin); + + assert_impl!(WriteVectored<'_, ()>: Send); + assert_not_impl!(WriteVectored<'_, *const ()>: Send); + assert_impl!(WriteVectored<'_, ()>: Sync); + assert_not_impl!(WriteVectored<'_, *const ()>: Sync); + assert_impl!(WriteVectored<'_, ()>: Unpin); + assert_not_impl!(WriteVectored<'_, PhantomPinned>: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::lock`. +pub mod lock { + use super::*; + use futures::lock::*; + + #[cfg(feature = "bilock")] + assert_impl!(BiLock<()>: Send); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLock<*const ()>: Send); + #[cfg(feature = "bilock")] + assert_impl!(BiLock<()>: Sync); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLock<*const ()>: Sync); + #[cfg(feature = "bilock")] + assert_impl!(BiLock: Unpin); + + #[cfg(feature = "bilock")] + assert_impl!(BiLockAcquire<'_, ()>: Send); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLockAcquire<'_, *const ()>: Send); + #[cfg(feature = "bilock")] + assert_impl!(BiLockAcquire<'_, ()>: Sync); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLockAcquire<'_, *const ()>: Sync); + #[cfg(feature = "bilock")] + assert_impl!(BiLockAcquire<'_, PhantomPinned>: Unpin); + + #[cfg(feature = "bilock")] + assert_impl!(BiLockGuard<'_, ()>: Send); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLockGuard<'_, *const ()>: Send); + #[cfg(feature = "bilock")] + assert_impl!(BiLockGuard<'_, ()>: Sync); + #[cfg(feature = "bilock")] + assert_not_impl!(BiLockGuard<'_, *const ()>: Sync); + #[cfg(feature = "bilock")] + assert_impl!(BiLockGuard<'_, PhantomPinned>: Unpin); + + assert_impl!(MappedMutexGuard<'_, (), ()>: Send); + assert_not_impl!(MappedMutexGuard<'_, (), *const ()>: Send); + assert_not_impl!(MappedMutexGuard<'_, *const (), ()>: Send); + assert_impl!(MappedMutexGuard<'_, (), ()>: Sync); + assert_not_impl!(MappedMutexGuard<'_, (), *const ()>: Sync); + assert_not_impl!(MappedMutexGuard<'_, *const (), ()>: Sync); + assert_impl!(MappedMutexGuard<'_, PhantomPinned, PhantomPinned>: Unpin); + + assert_impl!(Mutex<()>: Send); + assert_not_impl!(Mutex<*const ()>: Send); + assert_impl!(Mutex<()>: Sync); + assert_not_impl!(Mutex<*const ()>: Sync); + assert_impl!(Mutex<()>: Unpin); + assert_not_impl!(Mutex: Unpin); + + assert_impl!(MutexGuard<'_, ()>: Send); + assert_not_impl!(MutexGuard<'_, *const ()>: Send); + assert_impl!(MutexGuard<'_, ()>: Sync); + assert_not_impl!(MutexGuard<'_, *const ()>: Sync); + assert_impl!(MutexGuard<'_, PhantomPinned>: Unpin); + + assert_impl!(MutexLockFuture<'_, ()>: Send); + assert_not_impl!(MutexLockFuture<'_, *const ()>: Send); + assert_impl!(MutexLockFuture<'_, *const ()>: Sync); + assert_impl!(MutexLockFuture<'_, PhantomPinned>: Unpin); + + #[cfg(feature = "bilock")] + assert_impl!(ReuniteError<()>: Send); + #[cfg(feature = "bilock")] + assert_not_impl!(ReuniteError<*const ()>: Send); + #[cfg(feature = "bilock")] + assert_impl!(ReuniteError<()>: Sync); + #[cfg(feature = "bilock")] + assert_not_impl!(ReuniteError<*const ()>: Sync); + #[cfg(feature = "bilock")] + assert_impl!(ReuniteError: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::sink`. +pub mod sink { + use super::*; + use futures::sink::{self, *}; + use std::marker::Send; + + assert_impl!(Buffer<(), ()>: Send); + assert_not_impl!(Buffer<(), *const ()>: Send); + assert_not_impl!(Buffer<*const (), ()>: Send); + assert_impl!(Buffer<(), ()>: Sync); + assert_not_impl!(Buffer<(), *const ()>: Sync); + assert_not_impl!(Buffer<*const (), ()>: Sync); + assert_impl!(Buffer<(), PhantomPinned>: Unpin); + assert_not_impl!(Buffer: Unpin); + + assert_impl!(Close<'_, (), *const ()>: Send); + assert_not_impl!(Close<'_, *const (), ()>: Send); + assert_impl!(Close<'_, (), *const ()>: Sync); + assert_not_impl!(Close<'_, *const (), ()>: Sync); + assert_impl!(Close<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(Close<'_, PhantomPinned, ()>: Unpin); + + assert_impl!(Drain<()>: Send); + assert_not_impl!(Drain<*const ()>: Send); + assert_impl!(Drain<()>: Sync); + assert_not_impl!(Drain<*const ()>: Sync); + assert_impl!(Drain: Unpin); + + assert_impl!(Fanout<(), ()>: Send); + assert_not_impl!(Fanout<(), *const ()>: Send); + assert_not_impl!(Fanout<*const (), ()>: Send); + assert_impl!(Fanout<(), ()>: Sync); + assert_not_impl!(Fanout<(), *const ()>: Sync); + assert_not_impl!(Fanout<*const (), ()>: Sync); + assert_impl!(Fanout<(), ()>: Unpin); + assert_not_impl!(Fanout<(), PhantomPinned>: Unpin); + assert_not_impl!(Fanout: Unpin); + + assert_impl!(Feed<'_, (), ()>: Send); + assert_not_impl!(Feed<'_, (), *const ()>: Send); + assert_not_impl!(Feed<'_, *const (), ()>: Send); + assert_impl!(Feed<'_, (), ()>: Sync); + assert_not_impl!(Feed<'_, (), *const ()>: Sync); + assert_not_impl!(Feed<'_, *const (), ()>: Sync); + assert_impl!(Feed<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(Feed<'_, PhantomPinned, ()>: Unpin); + + assert_impl!(Flush<'_, (), *const ()>: Send); + assert_not_impl!(Flush<'_, *const (), ()>: Send); + assert_impl!(Flush<'_, (), *const ()>: Sync); + assert_not_impl!(Flush<'_, *const (), ()>: Sync); + assert_impl!(Flush<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(Flush<'_, PhantomPinned, ()>: Unpin); + + assert_impl!(sink::Send<'_, (), ()>: Send); + assert_not_impl!(sink::Send<'_, (), *const ()>: Send); + assert_not_impl!(sink::Send<'_, *const (), ()>: Send); + assert_impl!(sink::Send<'_, (), ()>: Sync); + assert_not_impl!(sink::Send<'_, (), *const ()>: Sync); + assert_not_impl!(sink::Send<'_, *const (), ()>: Sync); + assert_impl!(sink::Send<'_, (), PhantomPinned>: Unpin); + assert_not_impl!(sink::Send<'_, PhantomPinned, ()>: Unpin); + + assert_impl!(SendAll<'_, (), SendTryStream<()>>: Send); + assert_not_impl!(SendAll<'_, (), SendTryStream>: Send); + assert_not_impl!(SendAll<'_, (), LocalTryStream>: Send); + assert_not_impl!(SendAll<'_, *const (), SendTryStream<()>>: Send); + assert_impl!(SendAll<'_, (), SyncTryStream<()>>: Sync); + assert_not_impl!(SendAll<'_, (), SyncTryStream>: Sync); + assert_not_impl!(SendAll<'_, (), LocalTryStream>: Sync); + assert_not_impl!(SendAll<'_, *const (), SyncTryStream<()>>: Sync); + assert_impl!(SendAll<'_, (), UnpinTryStream>: Unpin); + assert_not_impl!(SendAll<'_, PhantomPinned, UnpinTryStream>: Unpin); + assert_not_impl!(SendAll<'_, (), PinnedTryStream>: Unpin); + + assert_impl!(SinkErrInto: Send); + assert_not_impl!(SinkErrInto, (), ()>: Send); + assert_impl!(SinkErrInto: Sync); + assert_not_impl!(SinkErrInto, (), ()>: Sync); + assert_impl!(SinkErrInto: Unpin); + assert_not_impl!(SinkErrInto, (), ()>: Unpin); + + assert_impl!(SinkMapErr: Send); + assert_not_impl!(SinkMapErr: Send); + assert_not_impl!(SinkMapErr, ()>: Send); + assert_impl!(SinkMapErr: Sync); + assert_not_impl!(SinkMapErr: Sync); + assert_not_impl!(SinkMapErr, ()>: Sync); + assert_impl!(SinkMapErr: Unpin); + assert_not_impl!(SinkMapErr, ()>: Unpin); + + assert_impl!(Unfold<(), (), ()>: Send); + assert_not_impl!(Unfold<*const (), (), ()>: Send); + assert_not_impl!(Unfold<(), *const (), ()>: Send); + assert_not_impl!(Unfold<(), (), *const ()>: Send); + assert_impl!(Unfold<(), (), ()>: Sync); + assert_not_impl!(Unfold<*const (), (), ()>: Sync); + assert_not_impl!(Unfold<(), *const (), ()>: Sync); + assert_not_impl!(Unfold<(), (), *const ()>: Sync); + assert_impl!(Unfold: Unpin); + assert_not_impl!(Unfold, (), PhantomPinned>: Unpin); + + assert_impl!(With<(), *const (), *const (), (), ()>: Send); + assert_not_impl!(With<*const (), (), (), (), ()>: Send); + assert_not_impl!(With<(), (), (), *const (), ()>: Send); + assert_not_impl!(With<(), (), (), (), *const ()>: Send); + assert_impl!(With<(), *const (), *const (), (), ()>: Sync); + assert_not_impl!(With<*const (), (), (), (), ()>: Sync); + assert_not_impl!(With<(), (), (), *const (), ()>: Sync); + assert_not_impl!(With<(), (), (), (), *const ()>: Sync); + assert_impl!(With<(), PhantomPinned, PhantomPinned, (), PhantomPinned>: Unpin); + assert_not_impl!(With: Unpin); + assert_not_impl!(With<(), (), (), PhantomPinned, ()>: Unpin); + + assert_impl!(WithFlatMap<(), (), *const (), (), ()>: Send); + assert_not_impl!(WithFlatMap<*const (), (), (), (), ()>: Send); + assert_not_impl!(WithFlatMap<(), *const (), (), (), ()>: Send); + assert_not_impl!(WithFlatMap<(), (), (), *const (), ()>: Send); + assert_not_impl!(WithFlatMap<(), (), (), (), *const ()>: Send); + assert_impl!(WithFlatMap<(), (), *const (), (), ()>: Sync); + assert_not_impl!(WithFlatMap<*const (), (), (), (), ()>: Sync); + assert_not_impl!(WithFlatMap<(), *const (), (), (), ()>: Sync); + assert_not_impl!(WithFlatMap<(), (), (), *const (), ()>: Sync); + assert_not_impl!(WithFlatMap<(), (), (), (), *const ()>: Sync); + assert_impl!(WithFlatMap<(), PhantomPinned, PhantomPinned, (), PhantomPinned>: Unpin); + assert_not_impl!(WithFlatMap: Unpin); + assert_not_impl!(WithFlatMap<(), (), (), PhantomPinned, ()>: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::stream`. +pub mod stream { + use super::*; + use futures::{io, stream::*}; + + assert_impl!(AndThen<(), (), ()>: Send); + assert_not_impl!(AndThen<*const (), (), ()>: Send); + assert_not_impl!(AndThen<(), *const (), ()>: Send); + assert_not_impl!(AndThen<(), (), *const ()>: Send); + assert_impl!(AndThen<(), (), ()>: Sync); + assert_not_impl!(AndThen<*const (), (), ()>: Sync); + assert_not_impl!(AndThen<(), *const (), ()>: Sync); + assert_not_impl!(AndThen<(), (), *const ()>: Sync); + assert_impl!(AndThen<(), (), PhantomPinned>: Unpin); + assert_not_impl!(AndThen: Unpin); + assert_not_impl!(AndThen<(), PhantomPinned, ()>: Unpin); + + assert_impl!(BufferUnordered>: Send); + assert_not_impl!(BufferUnordered: Send); + assert_not_impl!(BufferUnordered: Send); + assert_impl!(BufferUnordered>: Sync); + assert_not_impl!(BufferUnordered: Sync); + assert_not_impl!(BufferUnordered: Sync); + assert_impl!(BufferUnordered: Unpin); + assert_not_impl!(BufferUnordered: Unpin); + + assert_impl!(Buffered>>: Send); + assert_not_impl!(Buffered>: Send); + assert_not_impl!(Buffered>: Send); + assert_not_impl!(Buffered>>: Send); + assert_impl!(Buffered>>: Sync); + assert_not_impl!(Buffered>: Sync); + assert_not_impl!(Buffered>: Sync); + assert_not_impl!(Buffered>>: Sync); + assert_impl!(Buffered>: Unpin); + assert_not_impl!(Buffered>: Unpin); + + assert_impl!(CatchUnwind: Send); + assert_not_impl!(CatchUnwind: Send); + assert_impl!(CatchUnwind: Sync); + assert_not_impl!(CatchUnwind: Sync); + assert_impl!(CatchUnwind: Unpin); + assert_not_impl!(CatchUnwind: Unpin); + + assert_impl!(Chain<(), ()>: Send); + assert_not_impl!(Chain<(), *const ()>: Send); + assert_not_impl!(Chain<*const (), ()>: Send); + assert_impl!(Chain<(), ()>: Sync); + assert_not_impl!(Chain<(), *const ()>: Sync); + assert_not_impl!(Chain<*const (), ()>: Sync); + assert_impl!(Chain<(), ()>: Unpin); + assert_not_impl!(Chain<(), PhantomPinned>: Unpin); + assert_not_impl!(Chain: Unpin); + + assert_impl!(Chunks>: Send); + assert_not_impl!(Chunks: Send); + assert_not_impl!(Chunks: Send); + assert_impl!(Chunks>: Sync); + assert_not_impl!(Chunks: Sync); + assert_not_impl!(Chunks: Sync); + assert_impl!(Chunks: Unpin); + assert_not_impl!(Chunks: Unpin); + + assert_impl!(Collect<(), ()>: Send); + assert_not_impl!(Collect<*const (), ()>: Send); + assert_not_impl!(Collect<(), *const ()>: Send); + assert_impl!(Collect<(), ()>: Sync); + assert_not_impl!(Collect<*const (), ()>: Sync); + assert_not_impl!(Collect<(), *const ()>: Sync); + assert_impl!(Collect<(), PhantomPinned>: Unpin); + assert_not_impl!(Collect: Unpin); + + assert_impl!(Concat>: Send); + assert_not_impl!(Concat: Send); + assert_not_impl!(Concat: Send); + assert_impl!(Concat>: Sync); + assert_not_impl!(Concat: Sync); + assert_not_impl!(Concat: Sync); + assert_impl!(Concat: Unpin); + assert_not_impl!(Concat: Unpin); + + assert_impl!(Cycle<()>: Send); + assert_not_impl!(Cycle<*const ()>: Send); + assert_impl!(Cycle<()>: Sync); + assert_not_impl!(Cycle<*const ()>: Sync); + assert_impl!(Cycle<()>: Unpin); + assert_not_impl!(Cycle: Unpin); + + assert_impl!(Empty<()>: Send); + assert_not_impl!(Empty<*const ()>: Send); + assert_impl!(Empty<()>: Sync); + assert_not_impl!(Empty<*const ()>: Sync); + assert_impl!(Empty: Unpin); + + assert_impl!(Enumerate<()>: Send); + assert_not_impl!(Enumerate<*const ()>: Send); + assert_impl!(Enumerate<()>: Sync); + assert_not_impl!(Enumerate<*const ()>: Sync); + assert_impl!(Enumerate<()>: Unpin); + assert_not_impl!(Enumerate: Unpin); + + assert_impl!(ErrInto<(), *const ()>: Send); + assert_not_impl!(ErrInto<*const (), ()>: Send); + assert_impl!(ErrInto<(), *const ()>: Sync); + assert_not_impl!(ErrInto<*const (), ()>: Sync); + assert_impl!(ErrInto<(), PhantomPinned>: Unpin); + assert_not_impl!(ErrInto: Unpin); + + assert_impl!(Filter, (), ()>: Send); + assert_not_impl!(Filter, (), ()>: Send); + assert_not_impl!(Filter: Send); + assert_not_impl!(Filter, *const (), ()>: Send); + assert_not_impl!(Filter, (), *const ()>: Send); + assert_impl!(Filter, (), ()>: Sync); + assert_not_impl!(Filter, (), ()>: Sync); + assert_not_impl!(Filter: Sync); + assert_not_impl!(Filter, *const (), ()>: Sync); + assert_not_impl!(Filter, (), *const ()>: Sync); + assert_impl!(Filter: Unpin); + assert_not_impl!(Filter: Unpin); + assert_not_impl!(Filter: Unpin); + + assert_impl!(FilterMap<(), (), ()>: Send); + assert_not_impl!(FilterMap<*const (), (), ()>: Send); + assert_not_impl!(FilterMap<(), *const (), ()>: Send); + assert_not_impl!(FilterMap<(), (), *const ()>: Send); + assert_impl!(FilterMap<(), (), ()>: Sync); + assert_not_impl!(FilterMap<*const (), (), ()>: Sync); + assert_not_impl!(FilterMap<(), *const (), ()>: Sync); + assert_not_impl!(FilterMap<(), (), *const ()>: Sync); + assert_impl!(FilterMap<(), (), PhantomPinned>: Unpin); + assert_not_impl!(FilterMap: Unpin); + assert_not_impl!(FilterMap<(), PhantomPinned, ()>: Unpin); + + assert_impl!(FlatMap<(), (), ()>: Send); + assert_not_impl!(FlatMap<*const (), (), ()>: Send); + assert_not_impl!(FlatMap<(), *const (), ()>: Send); + assert_not_impl!(FlatMap<(), (), *const ()>: Send); + assert_impl!(FlatMap<(), (), ()>: Sync); + assert_not_impl!(FlatMap<*const (), (), ()>: Sync); + assert_not_impl!(FlatMap<(), *const (), ()>: Sync); + assert_not_impl!(FlatMap<(), (), *const ()>: Sync); + assert_impl!(FlatMap<(), (), PhantomPinned>: Unpin); + assert_not_impl!(FlatMap: Unpin); + assert_not_impl!(FlatMap<(), PhantomPinned, ()>: Unpin); + + assert_impl!(Flatten>: Send); + assert_not_impl!(Flatten: Send); + assert_not_impl!(Flatten: Send); + assert_impl!(Flatten>: Sync); + assert_not_impl!(Flatten>: Sync); + assert_not_impl!(Flatten>: Sync); + assert_impl!(Flatten>: Unpin); + assert_not_impl!(Flatten: Unpin); + assert_not_impl!(Flatten: Unpin); + + assert_impl!(Fold<(), (), (), ()>: Send); + assert_not_impl!(Fold<*const (), (), (), ()>: Send); + assert_not_impl!(Fold<(), *const (), (), ()>: Send); + assert_not_impl!(Fold<(), (), *const (), ()>: Send); + assert_not_impl!(Fold<(), (), (), *const ()>: Send); + assert_impl!(Fold<(), (), (), ()>: Sync); + assert_not_impl!(Fold<*const (), (), (), ()>: Sync); + assert_not_impl!(Fold<(), *const (), (), ()>: Sync); + assert_not_impl!(Fold<(), (), *const (), ()>: Sync); + assert_not_impl!(Fold<(), (), (), *const ()>: Sync); + assert_impl!(Fold<(), (), PhantomPinned, PhantomPinned>: Unpin); + assert_not_impl!(Fold: Unpin); + assert_not_impl!(Fold<(), PhantomPinned, (), ()>: Unpin); + + assert_impl!(ForEach<(), (), ()>: Send); + assert_not_impl!(ForEach<*const (), (), ()>: Send); + assert_not_impl!(ForEach<(), *const (), ()>: Send); + assert_not_impl!(ForEach<(), (), *const ()>: Send); + assert_impl!(ForEach<(), (), ()>: Sync); + assert_not_impl!(ForEach<*const (), (), ()>: Sync); + assert_not_impl!(ForEach<(), *const (), ()>: Sync); + assert_not_impl!(ForEach<(), (), *const ()>: Sync); + assert_impl!(ForEach<(), (), PhantomPinned>: Unpin); + assert_not_impl!(ForEach: Unpin); + assert_not_impl!(ForEach<(), PhantomPinned, ()>: Unpin); + + assert_impl!(ForEachConcurrent<(), (), ()>: Send); + assert_not_impl!(ForEachConcurrent<*const (), (), ()>: Send); + assert_not_impl!(ForEachConcurrent<(), *const (), ()>: Send); + assert_not_impl!(ForEachConcurrent<(), (), *const ()>: Send); + assert_impl!(ForEachConcurrent<(), (), ()>: Sync); + assert_not_impl!(ForEachConcurrent<*const (), (), ()>: Sync); + assert_not_impl!(ForEachConcurrent<(), *const (), ()>: Sync); + assert_not_impl!(ForEachConcurrent<(), (), *const ()>: Sync); + assert_impl!(ForEachConcurrent<(), PhantomPinned, PhantomPinned>: Unpin); + assert_not_impl!(ForEachConcurrent: Unpin); + + assert_impl!(Forward, ()>: Send); + assert_not_impl!(Forward: Send); + assert_not_impl!(Forward, *const ()>: Send); + assert_not_impl!(Forward: Send); + assert_impl!(Forward, ()>: Sync); + assert_not_impl!(Forward: Sync); + assert_not_impl!(Forward, *const ()>: Sync); + assert_not_impl!(Forward: Sync); + assert_impl!(Forward: Unpin); + assert_not_impl!(Forward: Unpin); + assert_not_impl!(Forward: Unpin); + + assert_impl!(Fuse<()>: Send); + assert_not_impl!(Fuse<*const ()>: Send); + assert_impl!(Fuse<()>: Sync); + assert_not_impl!(Fuse<*const ()>: Sync); + assert_impl!(Fuse<()>: Unpin); + assert_not_impl!(Fuse: Unpin); + + assert_impl!(FuturesOrdered>: Send); + assert_not_impl!(FuturesOrdered: Send); + assert_not_impl!(FuturesOrdered: Send); + assert_impl!(FuturesOrdered>: Sync); + assert_not_impl!(FuturesOrdered>: Sync); + assert_not_impl!(FuturesOrdered>: Sync); + assert_impl!(FuturesOrdered: Unpin); + + assert_impl!(FuturesUnordered<()>: Send); + assert_not_impl!(FuturesUnordered<*const ()>: Send); + assert_impl!(FuturesUnordered<()>: Sync); + assert_not_impl!(FuturesUnordered<*const ()>: Sync); + assert_impl!(FuturesUnordered: Unpin); + + assert_impl!(Inspect<(), ()>: Send); + assert_not_impl!(Inspect<*const (), ()>: Send); + assert_not_impl!(Inspect<(), *const ()>: Send); + assert_impl!(Inspect<(), ()>: Sync); + assert_not_impl!(Inspect<*const (), ()>: Sync); + assert_not_impl!(Inspect<(), *const ()>: Sync); + assert_impl!(Inspect<(), PhantomPinned>: Unpin); + assert_not_impl!(Inspect: Unpin); + + assert_impl!(InspectErr<(), ()>: Send); + assert_not_impl!(InspectErr<*const (), ()>: Send); + assert_not_impl!(InspectErr<(), *const ()>: Send); + assert_impl!(InspectErr<(), ()>: Sync); + assert_not_impl!(InspectErr<*const (), ()>: Sync); + assert_not_impl!(InspectErr<(), *const ()>: Sync); + assert_impl!(InspectErr<(), PhantomPinned>: Unpin); + assert_not_impl!(InspectErr: Unpin); + + assert_impl!(InspectOk<(), ()>: Send); + assert_not_impl!(InspectOk<*const (), ()>: Send); + assert_not_impl!(InspectOk<(), *const ()>: Send); + assert_impl!(InspectOk<(), ()>: Sync); + assert_not_impl!(InspectOk<*const (), ()>: Sync); + assert_not_impl!(InspectOk<(), *const ()>: Sync); + assert_impl!(InspectOk<(), PhantomPinned>: Unpin); + assert_not_impl!(InspectOk: Unpin); + + assert_impl!(IntoAsyncRead, io::Error>>: Send); + assert_not_impl!(IntoAsyncRead, io::Error>>: Send); + assert_impl!(IntoAsyncRead, io::Error>>: Sync); + assert_not_impl!(IntoAsyncRead, io::Error>>: Sync); + assert_impl!(IntoAsyncRead, io::Error>>: Unpin); + // IntoAsyncRead requires `St: Unpin` + // assert_not_impl!(IntoAsyncRead, io::Error>>: Unpin); + + assert_impl!(IntoStream<()>: Send); + assert_not_impl!(IntoStream<*const ()>: Send); + assert_impl!(IntoStream<()>: Sync); + assert_not_impl!(IntoStream<*const ()>: Sync); + assert_impl!(IntoStream<()>: Unpin); + assert_not_impl!(IntoStream: Unpin); + + assert_impl!(Iter<()>: Send); + assert_not_impl!(Iter<*const ()>: Send); + assert_impl!(Iter<()>: Sync); + assert_not_impl!(Iter<*const ()>: Sync); + assert_impl!(Iter: Unpin); + + assert_impl!(Map<(), ()>: Send); + assert_not_impl!(Map<*const (), ()>: Send); + assert_not_impl!(Map<(), *const ()>: Send); + assert_impl!(Map<(), ()>: Sync); + assert_not_impl!(Map<*const (), ()>: Sync); + assert_not_impl!(Map<(), *const ()>: Sync); + assert_impl!(Map<(), PhantomPinned>: Unpin); + assert_not_impl!(Map: Unpin); + + assert_impl!(MapErr<(), ()>: Send); + assert_not_impl!(MapErr<*const (), ()>: Send); + assert_not_impl!(MapErr<(), *const ()>: Send); + assert_impl!(MapErr<(), ()>: Sync); + assert_not_impl!(MapErr<*const (), ()>: Sync); + assert_not_impl!(MapErr<(), *const ()>: Sync); + assert_impl!(MapErr<(), PhantomPinned>: Unpin); + assert_not_impl!(MapErr: Unpin); + + assert_impl!(MapOk<(), ()>: Send); + assert_not_impl!(MapOk<*const (), ()>: Send); + assert_not_impl!(MapOk<(), *const ()>: Send); + assert_impl!(MapOk<(), ()>: Sync); + assert_not_impl!(MapOk<*const (), ()>: Sync); + assert_not_impl!(MapOk<(), *const ()>: Sync); + assert_impl!(MapOk<(), PhantomPinned>: Unpin); + assert_not_impl!(MapOk: Unpin); + + assert_impl!(Next<'_, ()>: Send); + assert_not_impl!(Next<'_, *const ()>: Send); + assert_impl!(Next<'_, ()>: Sync); + assert_not_impl!(Next<'_, *const ()>: Sync); + assert_impl!(Next<'_, ()>: Unpin); + assert_not_impl!(Next<'_, PhantomPinned>: Unpin); + + assert_impl!(NextIf<'_, SendStream<()>, ()>: Send); + assert_not_impl!(NextIf<'_, SendStream<()>, *const ()>: Send); + assert_not_impl!(NextIf<'_, SendStream, ()>: Send); + assert_not_impl!(NextIf<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIf<'_, SyncStream<()>, ()>: Sync); + assert_not_impl!(NextIf<'_, SyncStream<()>, *const ()>: Sync); + assert_not_impl!(NextIf<'_, SyncStream, ()>: Sync); + assert_not_impl!(NextIf<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIf<'_, PinnedStream, PhantomPinned>: Unpin); + + assert_impl!(NextIfEq<'_, SendStream<()>, ()>: Send); + assert_not_impl!(NextIfEq<'_, SendStream<()>, *const ()>: Send); + assert_not_impl!(NextIfEq<'_, SendStream, ()>: Send); + assert_not_impl!(NextIfEq<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIfEq<'_, SyncStream<()>, ()>: Sync); + assert_not_impl!(NextIfEq<'_, SyncStream<()>, *const ()>: Sync); + assert_not_impl!(NextIfEq<'_, SyncStream, ()>: Sync); + assert_not_impl!(NextIfEq<'_, LocalStream<()>, ()>: Send); + assert_impl!(NextIfEq<'_, PinnedStream, PhantomPinned>: Unpin); + + assert_impl!(Once<()>: Send); + assert_not_impl!(Once<*const ()>: Send); + assert_impl!(Once<()>: Sync); + assert_not_impl!(Once<*const ()>: Sync); + assert_impl!(Once<()>: Unpin); + assert_not_impl!(Once: Unpin); + + assert_impl!(OrElse<(), (), ()>: Send); + assert_not_impl!(OrElse<*const (), (), ()>: Send); + assert_not_impl!(OrElse<(), *const (), ()>: Send); + assert_not_impl!(OrElse<(), (), *const ()>: Send); + assert_impl!(OrElse<(), (), ()>: Sync); + assert_not_impl!(OrElse<*const (), (), ()>: Sync); + assert_not_impl!(OrElse<(), *const (), ()>: Sync); + assert_not_impl!(OrElse<(), (), *const ()>: Sync); + assert_impl!(OrElse<(), (), PhantomPinned>: Unpin); + assert_not_impl!(OrElse: Unpin); + assert_not_impl!(OrElse<(), PhantomPinned, ()>: Unpin); + + assert_impl!(Peek<'_, SendStream<()>>: Send); + assert_not_impl!(Peek<'_, SendStream>: Send); + assert_not_impl!(Peek<'_, LocalStream<()>>: Send); + assert_impl!(Peek<'_, SyncStream<()>>: Sync); + assert_not_impl!(Peek<'_, SyncStream>: Sync); + assert_not_impl!(Peek<'_, LocalStream<()>>: Sync); + assert_impl!(Peek<'_, PinnedStream>: Unpin); + + assert_impl!(PeekMut<'_, SendStream<()>>: Send); + assert_not_impl!(PeekMut<'_, SendStream>: Send); + assert_not_impl!(PeekMut<'_, LocalStream<()>>: Send); + assert_impl!(PeekMut<'_, SyncStream<()>>: Sync); + assert_not_impl!(PeekMut<'_, SyncStream>: Sync); + assert_not_impl!(PeekMut<'_, LocalStream<()>>: Sync); + assert_impl!(PeekMut<'_, PinnedStream>: Unpin); + + assert_impl!(Peekable>: Send); + assert_not_impl!(Peekable: Send); + assert_not_impl!(Peekable: Send); + assert_impl!(Peekable>: Sync); + assert_not_impl!(Peekable: Sync); + assert_not_impl!(Peekable: Sync); + assert_impl!(Peekable: Unpin); + assert_not_impl!(Peekable: Unpin); + + assert_impl!(Pending<()>: Send); + assert_not_impl!(Pending<*const ()>: Send); + assert_impl!(Pending<()>: Sync); + assert_not_impl!(Pending<*const ()>: Sync); + assert_impl!(Pending: Unpin); + + assert_impl!(PollFn<()>: Send); + assert_not_impl!(PollFn<*const ()>: Send); + assert_impl!(PollFn<()>: Sync); + assert_not_impl!(PollFn<*const ()>: Sync); + assert_impl!(PollFn: Unpin); + + assert_impl!(PollImmediate: Send); + assert_not_impl!(PollImmediate>: Send); + assert_impl!(PollImmediate: Sync); + assert_not_impl!(PollImmediate>: Sync); + assert_impl!(PollImmediate: Unpin); + assert_not_impl!(PollImmediate: Unpin); + + assert_impl!(ReadyChunks>: Send); + assert_not_impl!(ReadyChunks: Send); + assert_not_impl!(ReadyChunks: Send); + assert_impl!(ReadyChunks>: Sync); + assert_not_impl!(ReadyChunks: Sync); + assert_not_impl!(ReadyChunks: Sync); + assert_impl!(ReadyChunks: Unpin); + assert_not_impl!(ReadyChunks: Unpin); + + assert_impl!(Repeat<()>: Send); + assert_not_impl!(Repeat<*const ()>: Send); + assert_impl!(Repeat<()>: Sync); + assert_not_impl!(Repeat<*const ()>: Sync); + assert_impl!(Repeat: Unpin); + + assert_impl!(RepeatWith<()>: Send); + assert_not_impl!(RepeatWith<*const ()>: Send); + assert_impl!(RepeatWith<()>: Sync); + assert_not_impl!(RepeatWith<*const ()>: Sync); + // RepeatWith requires `F: FnMut() -> A` + assert_impl!(RepeatWith ()>: Unpin); + // assert_impl!(RepeatWith: Unpin); + + assert_impl!(ReuniteError<(), ()>: Send); + assert_not_impl!(ReuniteError<*const (), ()>: Send); + assert_not_impl!(ReuniteError<(), *const ()>: Send); + assert_impl!(ReuniteError<(), ()>: Sync); + assert_not_impl!(ReuniteError<*const (), ()>: Sync); + assert_not_impl!(ReuniteError<(), *const ()>: Sync); + assert_impl!(ReuniteError: Unpin); + + assert_impl!(Scan: Send); + assert_not_impl!(Scan, (), (), ()>: Send); + assert_not_impl!(Scan, *const (), (), ()>: Send); + assert_not_impl!(Scan, (), *const (), ()>: Send); + assert_not_impl!(Scan, (), (), *const ()>: Send); + assert_impl!(Scan: Sync); + assert_not_impl!(Scan, (), (), ()>: Sync); + assert_not_impl!(Scan, *const (), (), ()>: Sync); + assert_not_impl!(Scan, (), *const (), ()>: Sync); + assert_not_impl!(Scan, (), (), *const ()>: Sync); + assert_impl!(Scan: Unpin); + assert_not_impl!(Scan: Unpin); + assert_not_impl!(Scan: Unpin); + + assert_impl!(Select<(), ()>: Send); + assert_not_impl!(Select<*const (), ()>: Send); + assert_not_impl!(Select<(), *const ()>: Send); + assert_impl!(Select<(), ()>: Sync); + assert_not_impl!(Select<*const (), ()>: Sync); + assert_not_impl!(Select<(), *const ()>: Sync); + assert_impl!(Select<(), ()>: Unpin); + assert_not_impl!(Select: Unpin); + assert_not_impl!(Select<(), PhantomPinned>: Unpin); + + assert_impl!(SelectAll<()>: Send); + assert_not_impl!(SelectAll<*const ()>: Send); + assert_impl!(SelectAll<()>: Sync); + assert_not_impl!(SelectAll<*const ()>: Sync); + assert_impl!(SelectAll: Unpin); + + assert_impl!(SelectNextSome<'_, ()>: Send); + assert_not_impl!(SelectNextSome<'_, *const ()>: Send); + assert_impl!(SelectNextSome<'_, ()>: Sync); + assert_not_impl!(SelectNextSome<'_, *const ()>: Sync); + assert_impl!(SelectNextSome<'_, PhantomPinned>: Unpin); + + assert_impl!(Skip<()>: Send); + assert_not_impl!(Skip<*const ()>: Send); + assert_impl!(Skip<()>: Sync); + assert_not_impl!(Skip<*const ()>: Sync); + assert_impl!(Skip<()>: Unpin); + assert_not_impl!(Skip: Unpin); + + assert_impl!(SkipWhile, (), ()>: Send); + assert_not_impl!(SkipWhile, (), ()>: Send); + assert_not_impl!(SkipWhile: Send); + assert_not_impl!(SkipWhile, *const (), ()>: Send); + assert_not_impl!(SkipWhile, (), *const ()>: Send); + assert_impl!(SkipWhile, (), ()>: Sync); + assert_not_impl!(SkipWhile, (), ()>: Sync); + assert_not_impl!(SkipWhile: Sync); + assert_not_impl!(SkipWhile, *const (), ()>: Sync); + assert_not_impl!(SkipWhile, (), *const ()>: Sync); + assert_impl!(SkipWhile: Unpin); + assert_not_impl!(SkipWhile: Unpin); + assert_not_impl!(SkipWhile: Unpin); + + assert_impl!(SplitSink<(), ()>: Send); + assert_not_impl!(SplitSink<*const (), ()>: Send); + assert_not_impl!(SplitSink<(), *const ()>: Send); + assert_impl!(SplitSink<(), ()>: Sync); + assert_not_impl!(SplitSink<*const (), ()>: Sync); + assert_not_impl!(SplitSink<(), *const ()>: Sync); + assert_impl!(SplitSink: Unpin); + + assert_impl!(SplitStream<()>: Send); + assert_not_impl!(SplitStream<*const ()>: Send); + assert_impl!(SplitStream<()>: Sync); + assert_not_impl!(SplitStream<*const ()>: Sync); + assert_impl!(SplitStream: Unpin); + + assert_impl!(StreamFuture<()>: Send); + assert_not_impl!(StreamFuture<*const ()>: Send); + assert_impl!(StreamFuture<()>: Sync); + assert_not_impl!(StreamFuture<*const ()>: Sync); + assert_impl!(StreamFuture<()>: Unpin); + assert_not_impl!(StreamFuture: Unpin); + + assert_impl!(Take<()>: Send); + assert_not_impl!(Take<*const ()>: Send); + assert_impl!(Take<()>: Sync); + assert_not_impl!(Take<*const ()>: Sync); + assert_impl!(Take<()>: Unpin); + assert_not_impl!(Take: Unpin); + + assert_impl!(TakeUntil>: Send); + assert_not_impl!(TakeUntil: Send); + assert_not_impl!(TakeUntil>: Send); + assert_not_impl!(TakeUntil>: Send); + assert_impl!(TakeUntil>: Sync); + assert_not_impl!(TakeUntil: Sync); + assert_not_impl!(TakeUntil>: Sync); + assert_not_impl!(TakeUntil>: Sync); + assert_impl!(TakeUntil: Unpin); + assert_not_impl!(TakeUntil: Unpin); + assert_not_impl!(TakeUntil: Unpin); + + assert_impl!(TakeWhile, (), ()>: Send); + assert_not_impl!(TakeWhile, (), ()>: Send); + assert_not_impl!(TakeWhile: Send); + assert_not_impl!(TakeWhile, *const (), ()>: Send); + assert_not_impl!(TakeWhile, (), *const ()>: Send); + assert_impl!(TakeWhile, (), ()>: Sync); + assert_not_impl!(TakeWhile, (), ()>: Sync); + assert_not_impl!(TakeWhile: Sync); + assert_not_impl!(TakeWhile, *const (), ()>: Sync); + assert_not_impl!(TakeWhile, (), *const ()>: Sync); + assert_impl!(TakeWhile: Unpin); + assert_not_impl!(TakeWhile: Unpin); + assert_not_impl!(TakeWhile: Unpin); + + assert_impl!(Then: Send); + assert_not_impl!(Then, (), ()>: Send); + assert_not_impl!(Then, *const (), ()>: Send); + assert_not_impl!(Then, (), *const ()>: Send); + assert_impl!(Then: Sync); + assert_not_impl!(Then, (), ()>: Sync); + assert_not_impl!(Then, *const (), ()>: Sync); + assert_not_impl!(Then, (), *const ()>: Sync); + assert_impl!(Then: Unpin); + assert_not_impl!(Then: Unpin); + assert_not_impl!(Then: Unpin); + + assert_impl!(TryBufferUnordered>: Send); + assert_not_impl!(TryBufferUnordered: Send); + assert_not_impl!(TryBufferUnordered: Send); + assert_impl!(TryBufferUnordered>: Sync); + assert_not_impl!(TryBufferUnordered: Sync); + assert_not_impl!(TryBufferUnordered: Sync); + assert_impl!(TryBufferUnordered: Unpin); + assert_not_impl!(TryBufferUnordered: Unpin); + + assert_impl!(TryBuffered>>: Send); + assert_not_impl!(TryBuffered>>: Send); + assert_not_impl!(TryBuffered>>: Send); + assert_not_impl!(TryBuffered>>: Send); + assert_not_impl!(TryBuffered>>: Send); + assert_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_not_impl!(TryBuffered>>: Sync); + assert_impl!(TryBuffered>: Unpin); + assert_not_impl!(TryBuffered>: Unpin); + + assert_impl!(TryCollect<(), ()>: Send); + assert_not_impl!(TryCollect<*const (), ()>: Send); + assert_not_impl!(TryCollect<(), *const ()>: Send); + assert_impl!(TryCollect<(), ()>: Sync); + assert_not_impl!(TryCollect<*const (), ()>: Sync); + assert_not_impl!(TryCollect<(), *const ()>: Sync); + assert_impl!(TryCollect<(), PhantomPinned>: Unpin); + assert_not_impl!(TryCollect: Unpin); + + assert_impl!(TryConcat>: Send); + assert_not_impl!(TryConcat: Send); + assert_not_impl!(TryConcat: Send); + assert_impl!(TryConcat>: Sync); + assert_not_impl!(TryConcat: Sync); + assert_not_impl!(TryConcat: Sync); + assert_impl!(TryConcat: Unpin); + assert_not_impl!(TryConcat: Unpin); + + assert_impl!(TryFilter, (), ()>: Send); + assert_not_impl!(TryFilter, (), ()>: Send); + assert_not_impl!(TryFilter: Send); + assert_not_impl!(TryFilter, *const (), ()>: Send); + assert_not_impl!(TryFilter, (), *const ()>: Send); + assert_impl!(TryFilter, (), ()>: Sync); + assert_not_impl!(TryFilter, (), ()>: Sync); + assert_not_impl!(TryFilter: Sync); + assert_not_impl!(TryFilter, *const (), ()>: Sync); + assert_not_impl!(TryFilter, (), *const ()>: Sync); + assert_impl!(TryFilter: Unpin); + assert_not_impl!(TryFilter: Unpin); + assert_not_impl!(TryFilter: Unpin); + + assert_impl!(TryFilterMap<(), (), ()>: Send); + assert_not_impl!(TryFilterMap<*const (), (), ()>: Send); + assert_not_impl!(TryFilterMap<(), *const (), ()>: Send); + assert_not_impl!(TryFilterMap<(), (), *const ()>: Send); + assert_impl!(TryFilterMap<(), (), ()>: Sync); + assert_not_impl!(TryFilterMap<*const (), (), ()>: Sync); + assert_not_impl!(TryFilterMap<(), *const (), ()>: Sync); + assert_not_impl!(TryFilterMap<(), (), *const ()>: Sync); + assert_impl!(TryFilterMap<(), (), PhantomPinned>: Unpin); + assert_not_impl!(TryFilterMap: Unpin); + assert_not_impl!(TryFilterMap<(), PhantomPinned, ()>: Unpin); + + assert_impl!(TryFlatten>: Send); + assert_not_impl!(TryFlatten: Send); + assert_not_impl!(TryFlatten: Send); + assert_impl!(TryFlatten>: Sync); + assert_not_impl!(TryFlatten>: Sync); + assert_not_impl!(TryFlatten>: Sync); + assert_impl!(TryFlatten>: Unpin); + assert_not_impl!(TryFlatten: Unpin); + assert_not_impl!(TryFlatten: Unpin); + + assert_impl!(TryFold<(), (), (), ()>: Send); + assert_not_impl!(TryFold<*const (), (), (), ()>: Send); + assert_not_impl!(TryFold<(), *const (), (), ()>: Send); + assert_not_impl!(TryFold<(), (), *const (), ()>: Send); + assert_not_impl!(TryFold<(), (), (), *const ()>: Send); + assert_impl!(TryFold<(), (), (), ()>: Sync); + assert_not_impl!(TryFold<*const (), (), (), ()>: Sync); + assert_not_impl!(TryFold<(), *const (), (), ()>: Sync); + assert_not_impl!(TryFold<(), (), *const (), ()>: Sync); + assert_not_impl!(TryFold<(), (), (), *const ()>: Sync); + assert_impl!(TryFold<(), (), PhantomPinned, PhantomPinned>: Unpin); + assert_not_impl!(TryFold: Unpin); + assert_not_impl!(TryFold<(), PhantomPinned, (), ()>: Unpin); + + assert_impl!(TryForEach<(), (), ()>: Send); + assert_not_impl!(TryForEach<*const (), (), ()>: Send); + assert_not_impl!(TryForEach<(), *const (), ()>: Send); + assert_not_impl!(TryForEach<(), (), *const ()>: Send); + assert_impl!(TryForEach<(), (), ()>: Sync); + assert_not_impl!(TryForEach<*const (), (), ()>: Sync); + assert_not_impl!(TryForEach<(), *const (), ()>: Sync); + assert_not_impl!(TryForEach<(), (), *const ()>: Sync); + assert_impl!(TryForEach<(), (), PhantomPinned>: Unpin); + assert_not_impl!(TryForEach: Unpin); + assert_not_impl!(TryForEach<(), PhantomPinned, ()>: Unpin); + + assert_impl!(TryForEachConcurrent<(), (), ()>: Send); + assert_not_impl!(TryForEachConcurrent<*const (), (), ()>: Send); + assert_not_impl!(TryForEachConcurrent<(), *const (), ()>: Send); + assert_not_impl!(TryForEachConcurrent<(), (), *const ()>: Send); + assert_impl!(TryForEachConcurrent<(), (), ()>: Sync); + assert_not_impl!(TryForEachConcurrent<*const (), (), ()>: Sync); + assert_not_impl!(TryForEachConcurrent<(), *const (), ()>: Sync); + assert_not_impl!(TryForEachConcurrent<(), (), *const ()>: Sync); + assert_impl!(TryForEachConcurrent<(), PhantomPinned, PhantomPinned>: Unpin); + assert_not_impl!(TryForEachConcurrent: Unpin); + + assert_impl!(TryNext<'_, ()>: Send); + assert_not_impl!(TryNext<'_, *const ()>: Send); + assert_impl!(TryNext<'_, ()>: Sync); + assert_not_impl!(TryNext<'_, *const ()>: Sync); + assert_impl!(TryNext<'_, ()>: Unpin); + assert_not_impl!(TryNext<'_, PhantomPinned>: Unpin); + + assert_impl!(TrySkipWhile, (), ()>: Send); + assert_not_impl!(TrySkipWhile, (), ()>: Send); + assert_not_impl!(TrySkipWhile: Send); + assert_not_impl!(TrySkipWhile, *const (), ()>: Send); + assert_not_impl!(TrySkipWhile, (), *const ()>: Send); + assert_impl!(TrySkipWhile, (), ()>: Sync); + assert_not_impl!(TrySkipWhile, (), ()>: Sync); + assert_not_impl!(TrySkipWhile: Sync); + assert_not_impl!(TrySkipWhile, *const (), ()>: Sync); + assert_not_impl!(TrySkipWhile, (), *const ()>: Sync); + assert_impl!(TrySkipWhile: Unpin); + assert_not_impl!(TrySkipWhile: Unpin); + assert_not_impl!(TrySkipWhile: Unpin); + + assert_impl!(TryTakeWhile, (), ()>: Send); + assert_not_impl!(TryTakeWhile, (), ()>: Send); + assert_not_impl!(TryTakeWhile: Send); + assert_not_impl!(TryTakeWhile, *const (), ()>: Send); + assert_not_impl!(TryTakeWhile, (), *const ()>: Send); + assert_impl!(TryTakeWhile, (), ()>: Sync); + assert_not_impl!(TryTakeWhile, (), ()>: Sync); + assert_not_impl!(TryTakeWhile: Sync); + assert_not_impl!(TryTakeWhile, *const (), ()>: Sync); + assert_not_impl!(TryTakeWhile, (), *const ()>: Sync); + assert_impl!(TryTakeWhile: Unpin); + assert_not_impl!(TryTakeWhile: Unpin); + assert_not_impl!(TryTakeWhile: Unpin); + + assert_impl!(TryUnfold<(), (), ()>: Send); + assert_not_impl!(TryUnfold<*const (), (), ()>: Send); + assert_not_impl!(TryUnfold<(), *const (), ()>: Send); + assert_not_impl!(TryUnfold<(), (), *const ()>: Send); + assert_impl!(TryUnfold<(), (), ()>: Sync); + assert_not_impl!(TryUnfold<*const (), (), ()>: Sync); + assert_not_impl!(TryUnfold<(), *const (), ()>: Sync); + assert_not_impl!(TryUnfold<(), (), *const ()>: Sync); + assert_impl!(TryUnfold: Unpin); + assert_not_impl!(TryUnfold<(), (), PhantomPinned>: Unpin); + + assert_impl!(Unfold<(), (), ()>: Send); + assert_not_impl!(Unfold<*const (), (), ()>: Send); + assert_not_impl!(Unfold<(), *const (), ()>: Send); + assert_not_impl!(Unfold<(), (), *const ()>: Send); + assert_impl!(Unfold<(), (), ()>: Sync); + assert_not_impl!(Unfold<*const (), (), ()>: Sync); + assert_not_impl!(Unfold<(), *const (), ()>: Sync); + assert_not_impl!(Unfold<(), (), *const ()>: Sync); + assert_impl!(Unfold: Unpin); + assert_not_impl!(Unfold<(), (), PhantomPinned>: Unpin); + + assert_impl!(Unzip<(), (), ()>: Send); + assert_not_impl!(Unzip<*const (), (), ()>: Send); + assert_not_impl!(Unzip<(), *const (), ()>: Send); + assert_not_impl!(Unzip<(), (), *const ()>: Send); + assert_impl!(Unzip<(), (), ()>: Sync); + assert_not_impl!(Unzip<*const (), (), ()>: Sync); + assert_not_impl!(Unzip<(), *const (), ()>: Sync); + assert_not_impl!(Unzip<(), (), *const ()>: Sync); + assert_impl!(Unzip<(), PhantomPinned, PhantomPinned>: Unpin); + assert_not_impl!(Unzip: Unpin); + + assert_impl!(Zip, SendStream<()>>: Send); + assert_not_impl!(Zip>: Send); + assert_not_impl!(Zip, SendStream>: Send); + assert_not_impl!(Zip>: Send); + assert_not_impl!(Zip, LocalStream>: Send); + assert_impl!(Zip, SyncStream<()>>: Sync); + assert_not_impl!(Zip>: Sync); + assert_not_impl!(Zip, SyncStream>: Sync); + assert_not_impl!(Zip>: Sync); + assert_not_impl!(Zip, LocalStream>: Sync); + assert_impl!(Zip: Unpin); + assert_not_impl!(Zip: Unpin); + assert_not_impl!(Zip: Unpin); + + assert_impl!(futures_unordered::Iter<()>: Send); + assert_not_impl!(futures_unordered::Iter<*const ()>: Send); + assert_impl!(futures_unordered::Iter<()>: Sync); + assert_not_impl!(futures_unordered::Iter<*const ()>: Sync); + assert_impl!(futures_unordered::Iter<()>: Unpin); + // The definition of futures_unordered::Iter has `Fut: Unpin` bounds. + // assert_not_impl!(futures_unordered::Iter: Unpin); + + assert_impl!(futures_unordered::IterMut<()>: Send); + assert_not_impl!(futures_unordered::IterMut<*const ()>: Send); + assert_impl!(futures_unordered::IterMut<()>: Sync); + assert_not_impl!(futures_unordered::IterMut<*const ()>: Sync); + assert_impl!(futures_unordered::IterMut<()>: Unpin); + // The definition of futures_unordered::IterMut has `Fut: Unpin` bounds. + // assert_not_impl!(futures_unordered::IterMut: Unpin); + + assert_impl!(futures_unordered::IterPinMut<()>: Send); + assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Send); + assert_impl!(futures_unordered::IterPinMut<()>: Sync); + assert_not_impl!(futures_unordered::IterPinMut<*const ()>: Sync); + assert_impl!(futures_unordered::IterPinMut: Unpin); + + assert_impl!(futures_unordered::IterPinRef<()>: Send); + assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Send); + assert_impl!(futures_unordered::IterPinRef<()>: Sync); + assert_not_impl!(futures_unordered::IterPinRef<*const ()>: Sync); + assert_impl!(futures_unordered::IterPinRef: Unpin); + + assert_impl!(futures_unordered::IntoIter<()>: Send); + assert_not_impl!(futures_unordered::IntoIter<*const ()>: Send); + assert_impl!(futures_unordered::IntoIter<()>: Sync); + assert_not_impl!(futures_unordered::IntoIter<*const ()>: Sync); + // The definition of futures_unordered::IntoIter has `Fut: Unpin` bounds. + // assert_not_impl!(futures_unordered::IntoIter: Unpin); +} + +/// Assert Send/Sync/Unpin for all public types in `futures::task`. +pub mod task { + use super::*; + use futures::task::*; + + assert_impl!(AtomicWaker: Send); + assert_impl!(AtomicWaker: Sync); + assert_impl!(AtomicWaker: Unpin); + + assert_impl!(FutureObj<*const ()>: Send); + assert_not_impl!(FutureObj<()>: Sync); + assert_impl!(FutureObj: Unpin); + + assert_not_impl!(LocalFutureObj<()>: Send); + assert_not_impl!(LocalFutureObj<()>: Sync); + assert_impl!(LocalFutureObj: Unpin); + + assert_impl!(SpawnError: Send); + assert_impl!(SpawnError: Sync); + assert_impl!(SpawnError: Unpin); + + assert_impl!(WakerRef<'_>: Send); + assert_impl!(WakerRef<'_>: Sync); + assert_impl!(WakerRef<'_>: Unpin); +} diff --git a/futures/tests/compat.rs b/futures/tests/compat.rs index 39adc7cba4..ac04a95ea8 100644 --- a/futures/tests/compat.rs +++ b/futures/tests/compat.rs @@ -1,16 +1,15 @@ #![cfg(feature = "compat")] +#![cfg(not(miri))] // Miri does not support epoll -use tokio::timer::Delay; -use tokio::runtime::Runtime; -use std::time::Instant; -use futures::prelude::*; use futures::compat::Future01CompatExt; +use futures::prelude::*; +use std::time::Instant; +use tokio::runtime::Runtime; +use tokio::timer::Delay; #[test] fn can_use_01_futures_in_a_03_future_running_on_a_01_executor() { - let f = async { - Delay::new(Instant::now()).compat().await - }; + let f = async { Delay::new(Instant::now()).compat().await }; let mut runtime = Runtime::new().unwrap(); runtime.block_on(f.boxed().compat()).unwrap(); diff --git a/futures/tests/eager_drop.rs b/futures/tests/eager_drop.rs index 674e40121d..992507774c 100644 --- a/futures/tests/eager_drop.rs +++ b/futures/tests/eager_drop.rs @@ -2,7 +2,7 @@ use futures::channel::oneshot; use futures::future::{self, Future, FutureExt, TryFutureExt}; use futures::task::{Context, Poll}; use futures_test::future::FutureTestExt; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; use std::pin::Pin; use std::sync::mpsc; @@ -14,7 +14,10 @@ fn map_ok() { let (tx2, rx2) = mpsc::channel::<()>(); future::ready::>(Err(1)) - .map_ok(move |_| { let _tx1 = tx1; panic!("should not run"); }) + .map_ok(move |_| { + let _tx1 = tx1; + panic!("should not run"); + }) .map(move |_| { assert!(rx1.recv().is_err()); tx2.send(()).unwrap() @@ -32,7 +35,10 @@ fn map_err() { let (tx2, rx2) = mpsc::channel::<()>(); future::ready::>(Ok(1)) - .map_err(move |_| { let _tx1 = tx1; panic!("should not run"); }) + .map_err(move |_| { + let _tx1 = tx1; + panic!("should not run"); + }) .map(move |_| { assert!(rx1.recv().is_err()); tx2.send(()).unwrap() @@ -42,20 +48,18 @@ fn map_err() { rx2.recv().unwrap(); } +#[pin_project] struct FutureData { _data: T, + #[pin] future: F, } -impl FutureData { - unsafe_pinned!(future: F); -} - impl Future for FutureData { type Output = F::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.future().poll(cx) + self.project().future.poll(cx) } } @@ -65,7 +69,7 @@ fn then_drops_eagerly() { let (tx1, rx1) = mpsc::channel::<()>(); let (tx2, rx2) = mpsc::channel::<()>(); - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| panic!()) } .then(move |_| { assert!(rx1.recv().is_err()); // tx1 should have been dropped tx2.send(()).unwrap(); @@ -84,7 +88,7 @@ fn and_then_drops_eagerly() { let (tx1, rx1) = mpsc::channel::<()>(); let (tx2, rx2) = mpsc::channel::<()>(); - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| panic!()) } .and_then(move |_| { assert!(rx1.recv().is_err()); // tx1 should have been dropped tx2.send(()).unwrap(); @@ -103,7 +107,7 @@ fn or_else_drops_eagerly() { let (tx1, rx1) = mpsc::channel::<()>(); let (tx2, rx2) = mpsc::channel::<()>(); - FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| { panic!() }) } + FutureData { _data: tx1, future: rx0.unwrap_or_else(|_| panic!()) } .or_else(move |_| { assert!(rx1.recv().is_err()); // tx1 should have been dropped tx2.send(()).unwrap(); 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/abortable.rs b/futures/tests/future_abortable.rs similarity index 90% rename from futures/tests/abortable.rs rename to futures/tests/future_abortable.rs index 5925c9a27b..e119f0b719 100644 --- a/futures/tests/abortable.rs +++ b/futures/tests/future_abortable.rs @@ -10,6 +10,7 @@ fn abortable_works() { let (abortable_rx, abort_handle) = abortable(a_rx); abort_handle.abort(); + assert!(abortable_rx.is_aborted()); assert_eq!(Err(Aborted), block_on(abortable_rx)); } @@ -20,11 +21,14 @@ fn abortable_awakens() { let (waker, counter) = new_count_waker(); let mut cx = Context::from_waker(&waker); + assert_eq!(counter, 0); assert_eq!(Poll::Pending, abortable_rx.poll_unpin(&mut cx)); assert_eq!(counter, 0); + abort_handle.abort(); assert_eq!(counter, 1); + assert!(abortable_rx.is_aborted()); assert_eq!(Poll::Ready(Err(Aborted)), abortable_rx.poll_unpin(&mut cx)); } @@ -35,5 +39,6 @@ fn abortable_resolves() { tx.send(()).unwrap(); + assert!(!abortable_rx.is_aborted()); assert_eq!(Ok(Ok(())), block_on(abortable_rx)); } diff --git a/futures/tests/basic_combinators.rs b/futures/tests/future_basic_combinators.rs similarity index 92% rename from futures/tests/basic_combinators.rs rename to futures/tests/future_basic_combinators.rs index fa65b6f5f1..372ab48b79 100644 --- a/futures/tests/basic_combinators.rs +++ b/futures/tests/future_basic_combinators.rs @@ -13,17 +13,21 @@ fn basic_future_combinators() { tx1.send(x).unwrap(); // Send 1 tx1.send(2).unwrap(); // Send 2 future::ready(3) - }).map(move |x| { + }) + .map(move |x| { tx2.send(x).unwrap(); // Send 3 tx2.send(4).unwrap(); // Send 4 5 - }).map(move |x| { + }) + .map(move |x| { tx3.send(x).unwrap(); // Send 5 }); assert!(rx.try_recv().is_err()); // Not started yet fut.run_in_background(); // Start it - for i in 1..=5 { assert_eq!(rx.recv(), Ok(i)); } // Check it + for i in 1..=5 { + assert_eq!(rx.recv(), Ok(i)); + } // Check it assert!(rx.recv().is_err()); // Should be done } @@ -93,6 +97,8 @@ fn basic_try_future_combinators() { assert!(rx.try_recv().is_err()); // Not started yet fut.run_in_background(); // Start it - for i in 1..=12 { assert_eq!(rx.recv(), Ok(i)); } // Check it + for i in 1..=12 { + assert_eq!(rx.recv(), Ok(i)); + } // Check it assert!(rx.recv().is_err()); // Should be done } diff --git a/futures/tests/fuse.rs b/futures/tests/future_fuse.rs similarity index 100% rename from futures/tests/fuse.rs rename to futures/tests/future_fuse.rs diff --git a/futures/tests/inspect.rs b/futures/tests/future_inspect.rs similarity index 66% rename from futures/tests/inspect.rs rename to futures/tests/future_inspect.rs index 42f6f73634..eacd1f78a2 100644 --- a/futures/tests/inspect.rs +++ b/futures/tests/future_inspect.rs @@ -6,7 +6,9 @@ fn smoke() { let mut counter = 0; { - let work = future::ready::(40).inspect(|val| { counter += *val; }); + let work = future::ready::(40).inspect(|val| { + counter += *val; + }); assert_eq!(block_on(work), 40); } diff --git a/futures/tests/future_join_all.rs b/futures/tests/future_join_all.rs new file mode 100644 index 0000000000..44486e1ca3 --- /dev/null +++ b/futures/tests/future_join_all.rs @@ -0,0 +1,41 @@ +use futures::executor::block_on; +use futures::future::{join_all, ready, Future, JoinAll}; +use futures::pin_mut; +use std::fmt::Debug; + +#[track_caller] +fn assert_done(actual_fut: impl Future, expected: T) +where + T: PartialEq + Debug, +{ + pin_mut!(actual_fut); + let output = block_on(actual_fut); + assert_eq!(output, expected); +} + +#[test] +fn collect_collects() { + 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(join_all(Vec::::new()), vec![]); + + // TODO: needs more tests +} + +#[test] +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]>) -> impl Future> { + let iter = bufs.into_iter().map(|b| ready::(b.len())); + join_all(iter) + } + + assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]); +} + +#[test] +fn join_all_from_iter() { + assert_done(vec![ready(1), ready(2)].into_iter().collect::>(), vec![1, 2]) +} diff --git a/futures/tests/future_obj.rs b/futures/tests/future_obj.rs index c6b18fc85c..0e5253464e 100644 --- a/futures/tests/future_obj.rs +++ b/futures/tests/future_obj.rs @@ -1,6 +1,6 @@ -use futures::future::{Future, FutureObj, FutureExt}; -use std::pin::Pin; +use futures::future::{Future, FutureExt, FutureObj}; use futures::task::{Context, Poll}; +use std::pin::Pin; #[test] fn dropping_does_not_segfault() { diff --git a/futures/tests/select_all.rs b/futures/tests/future_select_all.rs similarity index 87% rename from futures/tests/select_all.rs rename to futures/tests/future_select_all.rs index aad977d351..299b479044 100644 --- a/futures/tests/select_all.rs +++ b/futures/tests/future_select_all.rs @@ -4,11 +4,7 @@ use std::collections::HashSet; #[test] fn smoke() { - let v = vec![ - ready(1), - ready(2), - ready(3), - ]; + let v = vec![ready(1), ready(2), ready(3)]; let mut c = vec![1, 2, 3].into_iter().collect::>(); diff --git a/futures/tests/select_ok.rs b/futures/tests/future_select_ok.rs similarity index 76% rename from futures/tests/select_ok.rs rename to futures/tests/future_select_ok.rs index db88a95c7a..8aec00362d 100644 --- a/futures/tests/select_ok.rs +++ b/futures/tests/future_select_ok.rs @@ -3,12 +3,7 @@ use futures::future::{err, ok, select_ok}; #[test] fn ignore_err() { - let v = vec![ - err(1), - err(2), - ok(3), - ok(4), - ]; + let v = vec![err(1), err(2), ok(3), ok(4)]; let (i, v) = block_on(select_ok(v)).ok().unwrap(); assert_eq!(i, 3); @@ -23,11 +18,7 @@ fn ignore_err() { #[test] fn last_err() { - let v = vec![ - ok(1), - err(2), - err(3), - ]; + let v = vec![ok(1), err(2), err(3)]; let (i, v) = block_on(select_ok(v)).ok().unwrap(); assert_eq!(i, 1); diff --git a/futures/tests/shared.rs b/futures/tests/future_shared.rs similarity index 69% rename from futures/tests/shared.rs rename to futures/tests/future_shared.rs index 8402bfe10b..3ceaebb5c8 100644 --- a/futures/tests/shared.rs +++ b/futures/tests/future_shared.rs @@ -1,11 +1,21 @@ use futures::channel::oneshot; use futures::executor::{block_on, LocalPool}; -use futures::future::{self, FutureExt, TryFutureExt, LocalFutureObj}; +use futures::future::{self, FutureExt, LocalFutureObj, TryFutureExt}; use futures::task::LocalSpawn; use std::cell::{Cell, RefCell}; use std::rc::Rc; +use std::task::Poll; use std::thread; +struct CountClone(Rc>); + +impl Clone for CountClone { + fn clone(&self) -> Self { + self.0.set(self.0.get() + 1); + Self(self.0.clone()) + } +} + fn send_shared_oneshot_and_wait_on_multiple_threads(threads_number: u32) { let (tx, rx) = oneshot::channel::(); let f = rx.shared(); @@ -77,7 +87,8 @@ fn drop_in_poll() { let future1 = future::lazy(move |_| { slot2.replace(None); // Drop future 1 - }).shared(); + }) + .shared(); let future2 = LocalFutureObj::new(Box::new(future1.clone())); slot1.replace(Some(future2)); @@ -85,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(); @@ -115,13 +127,28 @@ fn peek() { } } -struct CountClone(Rc>); - -impl Clone for CountClone { - fn clone(&self) -> Self { - self.0.set(self.0.get() + 1); - CountClone(self.0.clone()) - } +#[test] +fn downgrade() { + let (tx, rx) = oneshot::channel::(); + let shared = rx.shared(); + // Since there are outstanding `Shared`s, we can get a `WeakShared`. + let weak = shared.downgrade().unwrap(); + // It should upgrade fine right now. + let mut shared2 = weak.upgrade().unwrap(); + + tx.send(42).unwrap(); + assert_eq!(block_on(shared).unwrap(), 42); + + // We should still be able to get a new `WeakShared` and upgrade it + // because `shared2` is outstanding. + assert!(shared2.downgrade().is_some()); + assert!(weak.upgrade().is_some()); + + assert_eq!(block_on(&mut shared2).unwrap(), 42); + // Now that all `Shared`s have been exhausted, we should not be able + // to get a new `WeakShared` or upgrade an existing one. + assert!(weak.upgrade().is_none()); + assert!(shared2.downgrade().is_none()); } #[test] @@ -149,3 +176,21 @@ fn dont_do_unnecessary_clones_on_output() { assert_eq!(block_on(rx.clone()).unwrap().0.get(), 2); assert_eq!(block_on(rx).unwrap().0.get(), 2); } + +#[test] +fn shared_future_that_wakes_itself_until_pending_is_returned() { + let proceed = Cell::new(false); + let fut = futures::future::poll_fn(|cx| { + if proceed.get() { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + }) + .shared(); + + // The join future can only complete if the second future gets a chance to run after the first + // has returned pending + assert_eq!(block_on(futures::future::join(fut, async { proceed.set(true) })), ((), ())); +} diff --git a/futures/tests/future_try_flatten_stream.rs b/futures/tests/future_try_flatten_stream.rs index 082c5efa9a..82ae1baf2c 100644 --- a/futures/tests/future_try_flatten_stream.rs +++ b/futures/tests/future_try_flatten_stream.rs @@ -1,10 +1,11 @@ -use core::marker::PhantomData; -use core::pin::Pin; use futures::executor::block_on_stream; -use futures::future::{ok, err, TryFutureExt}; +use futures::future::{err, ok, TryFutureExt}; use futures::sink::Sink; -use futures::stream::{self, Stream, StreamExt}; +use futures::stream::Stream; +use futures::stream::{self, StreamExt}; use futures::task::{Context, Poll}; +use std::marker::PhantomData; +use std::pin::Pin; #[test] fn successful_future() { @@ -19,20 +20,20 @@ fn successful_future() { assert_eq!(None, iter.next()); } -struct PanickingStream { - _marker: PhantomData<(T, E)> -} +#[test] +fn failed_future() { + struct PanickingStream { + _marker: PhantomData<(T, E)>, + } -impl Stream for PanickingStream { - type Item = Result; + impl Stream for PanickingStream { + type Item = Result; - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } -} -#[test] -fn failed_future() { let future_of_a_stream = err::, _>(10); let stream = future_of_a_stream.try_flatten_stream(); let mut iter = block_on_stream(stream); @@ -40,37 +41,37 @@ fn failed_future() { assert_eq!(None, iter.next()); } -struct StreamSink(PhantomData<(T, E, Item)>); +#[test] +fn assert_impls() { + struct StreamSink(PhantomData<(T, E, Item)>); -impl Stream for StreamSink { - type Item = Result; - fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() + impl Stream for StreamSink { + type Item = Result; + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } -} -impl Sink for StreamSink { - type Error = E; - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() - } - fn start_send(self: Pin<&mut Self>, _: Item) -> Result<(), Self::Error> { - panic!() - } - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() + impl Sink for StreamSink { + type Error = E; + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } + fn start_send(self: Pin<&mut Self>, _: Item) -> Result<(), Self::Error> { + panic!() + } + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + panic!() + } } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - panic!() - } -} -fn assert_stream(_: &S) {} -fn assert_sink, Item>(_: &S) {} -fn assert_stream_sink, Item>(_: &S) {} + fn assert_stream(_: &S) {} + fn assert_sink, Item>(_: &S) {} + fn assert_stream_sink, Item>(_: &S) {} -#[test] -fn assert_impls() { let s = ok(StreamSink::<(), (), ()>(PhantomData)).try_flatten_stream(); assert_stream(&s); assert_sink(&s); diff --git a/futures/tests/future_try_join_all.rs b/futures/tests/future_try_join_all.rs new file mode 100644 index 0000000000..9a824872f7 --- /dev/null +++ b/futures/tests/future_try_join_all.rs @@ -0,0 +1,46 @@ +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; + +#[track_caller] +fn assert_done(actual_fut: impl Future, expected: T) +where + T: PartialEq + Debug, +{ + pin_mut!(actual_fut); + let output = block_on(actual_fut); + assert_eq!(output, expected); +} + +#[test] +fn collect_collects() { + 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(try_join_all(Vec::::new()), Ok(vec![])); + + // TODO: needs more tests +} + +#[test] +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]>) -> impl Future, ()>> { + let iter = bufs.into_iter().map(|b| ok::(b.len())); + try_join_all(iter) + } + + assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1])); +} + +#[test] +fn try_join_all_from_iter() { + assert_done( + vec![ok(1), ok(2)].into_iter().collect::>(), + Ok::<_, usize>(vec![1, 2]), + ) +} diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index a3d723a691..717297ccea 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -1,98 +1,240 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; use futures::io::{ - AsyncSeek, AsyncSeekExt, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, - AllowStdIo, BufReader, Cursor, SeekFrom, + AllowStdIo, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, + BufReader, SeekFrom, }; +use futures::pin_mut; use futures::task::{Context, Poll}; use futures_test::task::noop_context; +use pin_project::pin_project; use std::cmp; use std::io; use std::pin::Pin; -/// A dummy reader intended at testing short-reads propagation. -struct ShortReader { - lengths: Vec, +// helper for maybe_pending_* tests +fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } +} + +// https://github.com/rust-lang/futures-rs/pull/2489#discussion_r697865719 +#[pin_project(!Unpin)] +struct Cursor { + #[pin] + inner: futures::io::Cursor, +} + +impl Cursor { + fn new(inner: T) -> Self { + Self { inner: futures::io::Cursor::new(inner) } + } +} + +impl AsyncRead for Cursor<&[u8]> { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + self.project().inner.poll_read(cx, buf) + } +} + +impl AsyncBufRead for Cursor<&[u8]> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_fill_buf(cx) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.project().inner.consume(amt) + } } -impl io::Read for ShortReader { - fn read(&mut self, _: &mut [u8]) -> io::Result { - if self.lengths.is_empty() { - Ok(0) +impl AsyncSeek for Cursor<&[u8]> { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + self.project().inner.poll_seek(cx, pos) + } +} + +struct MaybePending<'a> { + inner: &'a [u8], + ready_read: bool, + ready_fill_buf: bool, +} + +impl<'a> MaybePending<'a> { + fn new(inner: &'a [u8]) -> Self { + Self { inner, ready_read: false, ready_fill_buf: false } + } +} + +impl AsyncRead for MaybePending<'_> { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + if self.ready_read { + self.ready_read = false; + Pin::new(&mut self.inner).poll_read(cx, buf) } else { - Ok(self.lengths.remove(0)) + self.ready_read = true; + Poll::Pending } } } -macro_rules! run_fill_buf { - ($reader:expr) => {{ - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = Pin::new(&mut $reader).poll_fill_buf(&mut cx) { - break x; +impl AsyncBufRead for MaybePending<'_> { + fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + if self.ready_fill_buf { + self.ready_fill_buf = false; + if self.inner.is_empty() { + return Poll::Ready(Ok(&[])); } + let len = cmp::min(2, self.inner.len()); + Poll::Ready(Ok(&self.inner[0..len])) + } else { + self.ready_fill_buf = true; + Poll::Pending } - }}; + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + self.inner = &self.inner[amt..]; + } } #[test] fn test_buffered_reader() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 3); - assert_eq!(buf, [5, 6, 7]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 2); - assert_eq!(buf, [0, 1]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [2]); - assert_eq!(reader.buffer(), [3]); + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 3); + assert_eq!(buf, [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 2); + assert_eq!(buf, [0, 1]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [2]); + assert_eq!(reader.buffer(), [3]); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [3, 0, 0]); + assert_eq!(reader.buffer(), []); + + let nread = reader.read(&mut buf).await.unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf, [4, 0, 0]); + assert_eq!(reader.buffer(), []); + + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + }); +} - let mut buf = [0, 0, 0]; - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [3, 0, 0]); - assert_eq!(reader.buffer(), []); +#[test] +fn test_buffered_reader_seek() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(2, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.seek(SeekFrom::Start(3)).await.unwrap(), 3); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.seek(SeekFrom::Current(i64::MIN)).await.is_err()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert_eq!(reader.seek(SeekFrom::Current(1)).await.unwrap(), 4); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[1, 2][..]); + reader.as_mut().consume(1); + assert_eq!(reader.seek(SeekFrom::Current(-2)).await.unwrap(), 3); + }); +} - let nread = block_on(reader.read(&mut buf)); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [4, 0, 0]); - assert_eq!(reader.buffer(), []); +#[test] +fn test_buffered_reader_seek_relative() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(2, Cursor::new(inner)); + pin_mut!(reader); + + assert!(reader.as_mut().seek_relative(3).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(0).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(1).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[1][..]); + assert!(reader.as_mut().seek_relative(-1).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1][..]); + assert!(reader.as_mut().seek_relative(2).await.is_ok()); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[2, 3][..]); + }); +} - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); +#[test] +fn test_buffered_reader_invalidated_after_read() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(3, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[5, 6, 7][..]); + reader.as_mut().consume(3); + + let mut buffer = [0, 0, 0, 0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 5); + assert_eq!(buffer, [0, 1, 2, 3, 4]); + + assert!(reader.as_mut().seek_relative(-2).await.is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 2); + assert_eq!(buffer, [3, 4]); + }); } #[test] -fn test_buffered_reader_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, Cursor::new(inner)); - - assert_eq!(block_on(reader.seek(SeekFrom::Start(3))).ok(), Some(3)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(block_on(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); - Pin::new(&mut reader).consume(1); - assert_eq!(block_on(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); +fn test_buffered_reader_invalidated_after_seek() { + block_on(async { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let reader = BufReader::with_capacity(3, Cursor::new(inner)); + pin_mut!(reader); + + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[5, 6, 7][..]); + reader.as_mut().consume(3); + + assert!(reader.seek(SeekFrom::Current(5)).await.is_ok()); + + assert!(reader.as_mut().seek_relative(-2).await.is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).await.unwrap(), 2); + assert_eq!(buffer, [3, 4]); + }); } #[test] fn test_buffered_reader_seek_underflow() { // gimmick reader that yields its position modulo 256 for each byte struct PositionReader { - pos: u64 + pos: u64, } impl io::Read for PositionReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { @@ -114,93 +256,58 @@ fn test_buffered_reader_seek_underflow() { self.pos = self.pos.wrapping_add(n as u64); } SeekFrom::End(n) => { - self.pos = u64::max_value().wrapping_add(n as u64); + self.pos = u64::MAX.wrapping_add(n as u64); } } Ok(self.pos) } } - let mut reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(block_on(reader.seek(SeekFrom::End(-5))).ok(), Some(u64::max_value()-5)); - assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); - // the following seek will require two underlying seeks - let expected = 9_223_372_036_854_775_802; - assert_eq!(block_on(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), Some(expected)); - assert_eq!(run_fill_buf!(reader).ok().map(|s| s.len()), Some(5)); - // seeking to 0 should empty the buffer. - assert_eq!(block_on(reader.seek(SeekFrom::Current(0))).ok(), Some(expected)); - assert_eq!(reader.get_ref().get_ref().pos, expected); + block_on(async { + let reader = BufReader::with_capacity(5, AllowStdIo::new(PositionReader { pos: 0 })); + pin_mut!(reader); + assert_eq!(reader.as_mut().fill_buf().await.unwrap(), &[0, 1, 2, 3, 4][..]); + assert_eq!(reader.seek(SeekFrom::End(-5)).await.unwrap(), u64::MAX - 5); + assert_eq!(reader.as_mut().fill_buf().await.unwrap().len(), 5); + // the following seek will require two underlying seeks + let expected = 9_223_372_036_854_775_802; + assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).await.unwrap(), expected); + assert_eq!(reader.as_mut().fill_buf().await.unwrap().len(), 5); + // seeking to 0 should empty the buffer. + assert_eq!(reader.seek(SeekFrom::Current(0)).await.unwrap(), expected); + assert_eq!(reader.get_ref().get_ref().pos, expected); + }); } #[test] fn test_short_reads() { - let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; - let mut reader = BufReader::new(AllowStdIo::new(inner)); - let mut buf = [0, 0]; - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 1); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 2); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 1); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); - assert_eq!(block_on(reader.read(&mut buf)).unwrap(), 0); -} - -struct MaybePending<'a> { - inner: &'a [u8], - ready_read: bool, - ready_fill_buf: bool, -} - -impl<'a> MaybePending<'a> { - fn new(inner: &'a [u8]) -> Self { - Self { inner, ready_read: false, ready_fill_buf: false } + /// A dummy reader intended at testing short-reads propagation. + struct ShortReader { + lengths: Vec, } -} -impl AsyncRead for MaybePending<'_> { - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { - if self.ready_read { - self.ready_read = false; - Pin::new(&mut self.inner).poll_read(cx, buf) - } else { - self.ready_read = true; - Poll::Pending - } - } -} - -impl AsyncBufRead for MaybePending<'_> { - fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) - -> Poll> - { - if self.ready_fill_buf { - self.ready_fill_buf = false; - if self.inner.is_empty() { return Poll::Ready(Ok(&[])) } - let len = cmp::min(2, self.inner.len()); - Poll::Ready(Ok(&self.inner[0..len])) - } else { - self.ready_fill_buf = true; - Poll::Pending + impl io::Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + if self.lengths.is_empty() { + Ok(0) + } else { + Ok(self.lengths.remove(0)) + } } } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - self.inner = &self.inner[amt..]; - } -} - -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } + block_on(async { + let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; + let mut reader = BufReader::new(AllowStdIo::new(inner)); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 1); + assert_eq!(reader.read(&mut buf).await.unwrap(), 2); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 1); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + assert_eq!(reader.read(&mut buf).await.unwrap(), 0); + }); } #[test] @@ -258,64 +365,68 @@ fn maybe_pending_buf_read() { assert_eq!(v, []); } -struct MaybePendingSeek<'a> { - inner: Cursor<&'a [u8]>, - ready: bool, -} - -impl<'a> MaybePendingSeek<'a> { - fn new(inner: &'a [u8]) -> Self { - Self { inner: Cursor::new(inner), ready: true } +// https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 +#[test] +fn maybe_pending_seek() { + #[pin_project] + struct MaybePendingSeek<'a> { + #[pin] + inner: Cursor<&'a [u8]>, + ready: bool, } -} -impl AsyncRead for MaybePendingSeek<'_> { - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) - -> Poll> - { - Pin::new(&mut self.inner).poll_read(cx, buf) + impl<'a> MaybePendingSeek<'a> { + fn new(inner: &'a [u8]) -> Self { + Self { inner: Cursor::new(inner), ready: true } + } } -} -impl AsyncBufRead for MaybePendingSeek<'_> { - fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) - -> Poll> - { - let this: *mut Self = &mut *self as *mut _; - Pin::new(&mut unsafe { &mut *this }.inner).poll_fill_buf(cx) + impl AsyncRead for MaybePendingSeek<'_> { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + self.project().inner.poll_read(cx, buf) + } } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + impl AsyncBufRead for MaybePendingSeek<'_> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_fill_buf(cx) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.project().inner.consume(amt) + } } -} -impl AsyncSeek for MaybePendingSeek<'_> { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { - if self.ready { - self.ready = false; - Pin::new(&mut self.inner).poll_seek(cx, pos) - } else { - self.ready = true; - Poll::Pending + impl AsyncSeek for MaybePendingSeek<'_> { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + if self.ready { + *self.as_mut().project().ready = false; + self.project().inner.poll_seek(cx, pos) + } else { + *self.project().ready = true; + Poll::Pending + } } } -} -// https://github.com/rust-lang/futures-rs/pull/1573#discussion_r281162309 -#[test] -fn maybe_pending_seek() { let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, MaybePendingSeek::new(inner)); + let reader = BufReader::with_capacity(2, MaybePendingSeek::new(inner)); + pin_mut!(reader); assert_eq!(run(reader.seek(SeekFrom::Current(3))).ok(), Some(3)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); - assert_eq!(run(reader.seek(SeekFrom::Current(i64::min_value()))).ok(), None); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[0, 1][..])); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[0, 1][..])); + assert_eq!(run(reader.seek(SeekFrom::Current(i64::MIN))).ok(), None); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[0, 1][..])); assert_eq!(run(reader.seek(SeekFrom::Current(1))).ok(), Some(4)); - assert_eq!(run_fill_buf!(reader).ok(), Some(&[1, 2][..])); + assert_eq!(run(reader.as_mut().fill_buf()).ok(), Some(&[1, 2][..])); Pin::new(&mut reader).consume(1); assert_eq!(run(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); } diff --git a/futures/tests/io_buf_writer.rs b/futures/tests/io_buf_writer.rs index 7bdcd16df0..b264cd54c2 100644 --- a/futures/tests/io_buf_writer.rs +++ b/futures/tests/io_buf_writer.rs @@ -1,11 +1,57 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::io::{AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter, Cursor, SeekFrom}; +use futures::io::{ + AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter, Cursor, SeekFrom, +}; use futures::task::{Context, Poll}; use futures_test::task::noop_context; use std::io; use std::pin::Pin; +struct MaybePending { + inner: Vec, + ready: bool, +} + +impl MaybePending { + fn new(inner: Vec) -> Self { + Self { inner, ready: false } + } +} + +impl AsyncWrite for MaybePending { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if self.ready { + self.ready = false; + Pin::new(&mut self.inner).poll_write(cx, buf) + } else { + self.ready = true; + Poll::Pending + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } +} + +fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } +} + #[test] fn buf_writer() { let mut writer = BufWriter::with_capacity(2, Vec::new()); @@ -73,50 +119,6 @@ fn buf_writer_seek() { assert_eq!(&w.into_inner().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); } -struct MaybePending { - inner: Vec, - ready: bool, -} - -impl MaybePending { - fn new(inner: Vec) -> Self { - Self { inner, ready: false } - } -} - -impl AsyncWrite for MaybePending { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - if self.ready { - self.ready = false; - Pin::new(&mut self.inner).poll_write(cx, buf) - } else { - self.ready = true; - Poll::Pending - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx) - } -} - -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - #[test] fn maybe_pending_buf_writer() { let mut writer = BufWriter::with_capacity(2, MaybePending::new(Vec::new())); @@ -169,59 +171,60 @@ fn maybe_pending_buf_writer_inner_flushes() { assert_eq!(w, [0, 1]); } - -struct MaybePendingSeek { - inner: Cursor>, - ready_write: bool, - ready_seek: bool, -} - -impl MaybePendingSeek { - fn new(inner: Vec) -> Self { - Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } +#[test] +fn maybe_pending_buf_writer_seek() { + struct MaybePendingSeek { + inner: Cursor>, + ready_write: bool, + ready_seek: bool, } -} -impl AsyncWrite for MaybePendingSeek { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - if self.ready_write { - self.ready_write = false; - Pin::new(&mut self.inner).poll_write(cx, buf) - } else { - self.ready_write = true; - Poll::Pending + impl MaybePendingSeek { + fn new(inner: Vec) -> Self { + Self { inner: Cursor::new(inner), ready_write: false, ready_seek: false } } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } + impl AsyncWrite for MaybePendingSeek { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if self.ready_write { + self.ready_write = false; + Pin::new(&mut self.inner).poll_write(cx, buf) + } else { + self.ready_write = true; + Poll::Pending + } + } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } } -} -impl AsyncSeek for MaybePendingSeek { - fn poll_seek(mut self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) - -> Poll> - { - if self.ready_seek { - self.ready_seek = false; - Pin::new(&mut self.inner).poll_seek(cx, pos) - } else { - self.ready_seek = true; - Poll::Pending + impl AsyncSeek for MaybePendingSeek { + fn poll_seek( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + if self.ready_seek { + self.ready_seek = false; + Pin::new(&mut self.inner).poll_seek(cx, pos) + } else { + self.ready_seek = true; + Poll::Pending + } } } -} -#[test] -fn maybe_pending_buf_writer_seek() { // FIXME: when https://github.com/rust-lang/futures-rs/issues/1510 fixed, // use `Vec::new` instead of `vec![0; 8]`. let mut w = BufWriter::with_capacity(3, MaybePendingSeek::new(vec![0; 8])); diff --git a/futures/tests/io_cursor.rs b/futures/tests/io_cursor.rs index 4f80a75a37..435ea5a155 100644 --- a/futures/tests/io_cursor.rs +++ b/futures/tests/io_cursor.rs @@ -1,4 +1,5 @@ use assert_matches::assert_matches; +use futures::executor::block_on; use futures::future::lazy; use futures::io::{AsyncWrite, Cursor}; use futures::task::Poll; @@ -7,7 +8,7 @@ use std::pin::Pin; #[test] fn cursor_asyncwrite_vec() { let mut cursor = Cursor::new(vec![0; 5]); - futures::executor::block_on(lazy(|cx| { + block_on(lazy(|cx| { assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(2))); @@ -19,7 +20,7 @@ fn cursor_asyncwrite_vec() { #[test] fn cursor_asyncwrite_box() { let mut cursor = Cursor::new(vec![0; 5].into_boxed_slice()); - futures::executor::block_on(lazy(|cx| { + block_on(lazy(|cx| { assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[1, 2]), Poll::Ready(Ok(2))); assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[3, 4]), Poll::Ready(Ok(2))); assert_matches!(Pin::new(&mut cursor).poll_write(cx, &[5, 6]), Poll::Ready(Ok(1))); diff --git a/futures/tests/io_line_writer.rs b/futures/tests/io_line_writer.rs new file mode 100644 index 0000000000..b483e0ff77 --- /dev/null +++ b/futures/tests/io_line_writer.rs @@ -0,0 +1,73 @@ +use futures::executor::block_on; +use futures::io::{AsyncWriteExt, LineWriter}; +use std::io; + +#[test] +fn line_writer() { + let mut writer = LineWriter::new(Vec::new()); + + block_on(writer.write(&[0])).unwrap(); + assert_eq!(*writer.get_ref(), []); + + block_on(writer.write(&[1])).unwrap(); + assert_eq!(*writer.get_ref(), []); + + block_on(writer.flush()).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + block_on(writer.write(&[0, b'\n', 1, b'\n', 2])).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + + block_on(writer.flush()).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + + block_on(writer.write(&[3, b'\n'])).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); +} + +#[test] +fn line_vectored() { + let mut line_writer = LineWriter::new(Vec::new()); + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(b"\n"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"a"), + ])) + .unwrap(), + 2 + ); + assert_eq!(line_writer.get_ref(), b"\n"); + + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(b"b"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"a"), + io::IoSlice::new(&[]), + io::IoSlice::new(b"c"), + ])) + .unwrap(), + 3 + ); + assert_eq!(line_writer.get_ref(), b"\n"); + block_on(line_writer.flush()).unwrap(); + assert_eq!(line_writer.get_ref(), b"\nabac"); + assert_eq!(block_on(line_writer.write_vectored(&[])).unwrap(), 0); + + assert_eq!( + block_on(line_writer.write_vectored(&[ + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + io::IoSlice::new(&[]), + ])) + .unwrap(), + 0 + ); + + assert_eq!(block_on(line_writer.write_vectored(&[io::IoSlice::new(b"a\nb")])).unwrap(), 3); + assert_eq!(line_writer.get_ref(), b"\nabaca\nb"); +} diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs index 39eafa9a66..5ce01a6945 100644 --- a/futures/tests/io_lines.rs +++ b/futures/tests/io_lines.rs @@ -1,17 +1,32 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; use futures_test::io::AsyncReadTestExt; use futures_test::task::noop_context; +fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } +} + macro_rules! block_on_next { ($expr:expr) => { block_on($expr.next()).unwrap().unwrap() }; } +macro_rules! run_next { + ($expr:expr) => { + run($expr.next()).unwrap().unwrap() + }; +} + #[test] fn lines() { let buf = Cursor::new(&b"12\r"[..]); @@ -26,27 +41,10 @@ fn lines() { assert!(block_on(s.next()).is_none()); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - -macro_rules! run_next { - ($expr:expr) => { - run($expr.next()).unwrap().unwrap() - }; -} - #[test] fn maybe_pending() { - let buf = stream::iter(vec![&b"12"[..], &b"\r"[..]]) - .map(Ok) - .into_async_read() - .interleave_pending(); + let buf = + stream::iter(vec![&b"12"[..], &b"\r"[..]]).map(Ok).into_async_read().interleave_pending(); let mut s = buf.lines(); assert_eq!(run_next!(s), "12\r".to_string()); assert!(run(s.next()).is_none()); diff --git a/futures/tests/io_read.rs b/futures/tests/io_read.rs index f99c4edbb6..d39a6ea790 100644 --- a/futures/tests/io_read.rs +++ b/futures/tests/io_read.rs @@ -9,8 +9,8 @@ struct MockReader { } impl MockReader { - pub fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { - MockReader { fun: Box::new(fun) } + fn new(fun: impl FnMut(&mut [u8]) -> Poll> + 'static) -> Self { + Self { fun: Box::new(fun) } } } @@ -18,7 +18,7 @@ impl AsyncRead for MockReader { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, - buf: &mut [u8] + buf: &mut [u8], ) -> Poll> { (self.get_mut().fun)(buf) } @@ -52,7 +52,7 @@ fn read_vectored_first_non_empty() { let cx = &mut panic_context(); let mut buf = [0; 4]; let bufs = &mut [ - io::IoSliceMut::new(&mut []), + io::IoSliceMut::new(&mut []), io::IoSliceMut::new(&mut []), io::IoSliceMut::new(&mut buf), ]; @@ -62,4 +62,3 @@ fn read_vectored_first_non_empty() { assert_eq!(res, Poll::Ready(Ok(4))); assert_eq!(buf, b"four"[..]); } - diff --git a/futures/tests/io_read_exact.rs b/futures/tests/io_read_exact.rs index 4941773cb5..6582e50b80 100644 --- a/futures/tests/io_read_exact.rs +++ b/futures/tests/io_read_exact.rs @@ -8,7 +8,7 @@ fn read_exact() { let res = block_on(reader.read_exact(&mut out)); // read 3 bytes out assert!(res.is_ok()); - assert_eq!(out, [1,2,3]); + assert_eq!(out, [1, 2, 3]); assert_eq!(reader.len(), 2); let res = block_on(reader.read_exact(&mut out)); // read another 3 bytes, but only 2 bytes left diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs index d1dba5e6d2..88a877928a 100644 --- a/futures/tests/io_read_line.rs +++ b/futures/tests/io_read_line.rs @@ -1,11 +1,20 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; use futures_test::io::AsyncReadTestExt; use futures_test::task::noop_context; +fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } +} + #[test] fn read_line() { let mut buf = Cursor::new(b"12"); @@ -25,15 +34,6 @@ fn read_line() { assert_eq!(v, ""); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - #[test] fn maybe_pending() { let mut buf = b"12".interleave_pending(); @@ -41,10 +41,8 @@ fn maybe_pending() { assert_eq!(run(buf.read_line(&mut v)).unwrap(), 2); assert_eq!(v, "12"); - let mut buf = stream::iter(vec![&b"12"[..], &b"\n\n"[..]]) - .map(Ok) - .into_async_read() - .interleave_pending(); + let mut buf = + stream::iter(vec![&b"12"[..], &b"\n\n"[..]]).map(Ok).into_async_read().interleave_pending(); let mut v = String::new(); assert_eq!(run(buf.read_line(&mut v)).unwrap(), 3); assert_eq!(v, "12\n"); diff --git a/futures/tests/io_read_to_end.rs b/futures/tests/io_read_to_end.rs new file mode 100644 index 0000000000..7122511fcb --- /dev/null +++ b/futures/tests/io_read_to_end.rs @@ -0,0 +1,65 @@ +use futures::{ + executor::block_on, + io::{self, AsyncRead, AsyncReadExt}, + task::{Context, Poll}, +}; +use std::pin::Pin; + +#[test] +#[should_panic(expected = "assertion failed: n <= buf.len()")] +fn issue2310() { + struct MyRead { + first: bool, + } + + impl MyRead { + fn new() -> Self { + MyRead { first: false } + } + } + + impl AsyncRead for MyRead { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context, + _buf: &mut [u8], + ) -> Poll> { + Poll::Ready(if !self.first { + self.first = true; + // First iteration: return more than the buffer size + Ok(64) + } else { + // Second iteration: indicate that we are done + Ok(0) + }) + } + } + + struct VecWrapper { + inner: Vec, + } + + impl VecWrapper { + fn new() -> Self { + VecWrapper { inner: Vec::new() } + } + } + + impl Drop for VecWrapper { + fn drop(&mut self) { + // Observe uninitialized bytes + println!("{:?}", &self.inner); + // Overwrite heap contents + for b in &mut self.inner { + *b = 0x90; + } + } + } + + block_on(async { + let mut vec = VecWrapper::new(); + let mut read = MyRead::new(); + + read.read_to_end(&mut vec.inner).await.unwrap(); + }) +} diff --git a/futures/tests/io_read_to_string.rs b/futures/tests/io_read_to_string.rs index db825af21c..ae6aaa21d8 100644 --- a/futures/tests/io_read_to_string.rs +++ b/futures/tests/io_read_to_string.rs @@ -1,7 +1,7 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; use futures::io::{AsyncReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; use futures_test::io::AsyncReadTestExt; use futures_test::task::noop_context; @@ -23,17 +23,16 @@ fn read_to_string() { assert!(block_on(c.read_to_string(&mut v)).is_err()); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - #[test] fn interleave_pending() { + fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } + } let mut buf = stream::iter(vec![&b"12"[..], &b"33"[..], &b"3"[..]]) .map(Ok) .into_async_read() diff --git a/futures/tests/io_read_until.rs b/futures/tests/io_read_until.rs index 5152281795..71f857f4b0 100644 --- a/futures/tests/io_read_until.rs +++ b/futures/tests/io_read_until.rs @@ -1,11 +1,20 @@ use futures::executor::block_on; use futures::future::{Future, FutureExt}; -use futures::stream::{self, StreamExt, TryStreamExt}; use futures::io::{AsyncBufReadExt, Cursor}; +use futures::stream::{self, StreamExt, TryStreamExt}; use futures::task::Poll; use futures_test::io::AsyncReadTestExt; use futures_test::task::noop_context; +fn run(mut f: F) -> F::Output { + let mut cx = noop_context(); + loop { + if let Poll::Ready(x) = f.poll_unpin(&mut cx) { + return x; + } + } +} + #[test] fn read_until() { let mut buf = Cursor::new(b"12"); @@ -25,15 +34,6 @@ fn read_until() { assert_eq!(v, []); } -fn run(mut f: F) -> F::Output { - let mut cx = noop_context(); - loop { - if let Poll::Ready(x) = f.poll_unpin(&mut cx) { - return x; - } - } -} - #[test] fn maybe_pending() { let mut buf = b"12".interleave_pending(); diff --git a/futures/tests/io_window.rs b/futures/tests/io_window.rs index 98df69c83b..8f0d48bc94 100644 --- a/futures/tests/io_window.rs +++ b/futures/tests/io_window.rs @@ -1,13 +1,17 @@ +#![allow(clippy::reversed_empty_ranges)] // This is intentional. + use futures::io::Window; #[test] fn set() { let mut buffer = Window::new(&[1, 2, 3]); buffer.set(..3); + assert_eq!(buffer.as_ref(), &[1, 2, 3]); buffer.set(3..3); + assert_eq!(buffer.as_ref(), &[]); buffer.set(3..=2); // == 3..3 + assert_eq!(buffer.as_ref(), &[]); buffer.set(0..2); - assert_eq!(buffer.as_ref(), &[1, 2]); } diff --git a/futures/tests/io_write.rs b/futures/tests/io_write.rs index b96344446c..6af27553cb 100644 --- a/futures/tests/io_write.rs +++ b/futures/tests/io_write.rs @@ -9,8 +9,8 @@ struct MockWriter { } impl MockWriter { - pub fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { - MockWriter { fun: Box::new(fun) } + fn new(fun: impl FnMut(&[u8]) -> Poll> + 'static) -> Self { + Self { fun: Box::new(fun) } } } @@ -57,14 +57,9 @@ fn write_vectored_first_non_empty() { Poll::Ready(Ok(4)) }); let cx = &mut panic_context(); - let bufs = &mut [ - io::IoSlice::new(&[]), - io::IoSlice::new(&[]), - io::IoSlice::new(b"four") - ]; + let bufs = &mut [io::IoSlice::new(&[]), io::IoSlice::new(&[]), io::IoSlice::new(b"four")]; let res = Pin::new(&mut writer).poll_write_vectored(cx, bufs); let res = res.map_err(|e| e.kind()); assert_eq!(res, Poll::Ready(Ok(4))); } - diff --git a/futures/tests/join_all.rs b/futures/tests/join_all.rs deleted file mode 100644 index 63967bf987..0000000000 --- a/futures/tests/join_all.rs +++ /dev/null @@ -1,43 +0,0 @@ -use futures_util::future::*; -use std::future::Future; -use futures::executor::block_on; -use std::fmt::Debug; - -fn assert_done(actual_fut: F, expected: T) -where - T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, -{ - let output = block_on(actual_fut()); - assert_eq!(output, expected); -} - -#[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]); - // REVIEW: should this be implemented? - // assert_done(|| Box::new(join_all(Vec::::new())), vec![]); - - // TODO: needs more tests -} - -#[test] -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<'a>(bufs: Vec<&'a [u8]>) -> Box> + Unpin> { - let iter = bufs.into_iter().map(|b| ready::(b.len())); - Box::new(join_all(iter)) - } - - assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), vec![3 as usize, 0, 1]); -} - -#[test] -fn join_all_from_iter() { - assert_done( - || Box::new(vec![ready(1), ready(2)].into_iter().collect::>()), - vec![1, 2], - ) -} diff --git a/futures/tests/mutex.rs b/futures/tests/lock_mutex.rs similarity index 89% rename from futures/tests/mutex.rs rename to futures/tests/lock_mutex.rs index bad53a9b8f..c92ef50ad8 100644 --- a/futures/tests/mutex.rs +++ b/futures/tests/lock_mutex.rs @@ -1,5 +1,5 @@ use futures::channel::mpsc; -use futures::executor::block_on; +use futures::executor::{block_on, ThreadPool}; use futures::future::{ready, FutureExt}; use futures::lock::Mutex; use futures::stream::StreamExt; @@ -34,13 +34,11 @@ fn mutex_wakes_waiters() { assert!(waiter.poll_unpin(&mut panic_context()).is_ready()); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn mutex_contested() { let (tx, mut rx) = mpsc::unbounded(); - let pool = futures::executor::ThreadPool::builder() - .pool_size(16) - .create() - .unwrap(); + let pool = ThreadPool::builder().pool_size(16).create().unwrap(); let tx = Arc::new(tx); let mutex = Arc::new(Mutex::new(0)); diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml new file mode 100644 index 0000000000..a648ee54a5 --- /dev/null +++ b/futures/tests/macro-reexport/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "macro-reexport" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } diff --git a/futures/tests/macro-reexport/src/lib.rs b/futures/tests/macro-reexport/src/lib.rs new file mode 100644 index 0000000000..82f939b343 --- /dev/null +++ b/futures/tests/macro-reexport/src/lib.rs @@ -0,0 +1,7 @@ +// normal reexport +pub use futures03::{join, select, select_biased, try_join}; + +// reexport + rename +pub use futures03::{ + join as join2, select as select2, select_biased as select_biased2, try_join as try_join2, +}; diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml new file mode 100644 index 0000000000..963da731ac --- /dev/null +++ b/futures/tests/macro-tests/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "macro-tests" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +futures03 = { path = "../..", package = "futures" } +macro-reexport = { path = "../macro-reexport" } diff --git a/futures/tests/macro-tests/src/main.rs b/futures/tests/macro-tests/src/main.rs new file mode 100644 index 0000000000..c654e0c92c --- /dev/null +++ b/futures/tests/macro-tests/src/main.rs @@ -0,0 +1,68 @@ +// Check that it works even if proc-macros are reexported. + +fn main() { + use futures03::{executor::block_on, future}; + + // join! macro + let _ = block_on(async { + let _ = futures03::join!(async {}, async {}); + let _ = macro_reexport::join!(async {}, async {}); + let _ = macro_reexport::join2!(async {}, async {}); + }); + + // try_join! macro + let _ = block_on(async { + let _ = futures03::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + let _ = macro_reexport::try_join2!(async { Ok::<(), ()>(()) }, async { Ok::<(), ()>(()) }); + Ok::<(), ()>(()) + }); + + // select! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); + + // select_biased! macro + let _ = block_on(async { + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = futures03::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased! { + _ = a => {}, + _ = b => unreachable!(), + }; + + let mut a = future::ready(()); + let mut b = future::pending::<()>(); + let _ = macro_reexport::select_biased2! { + _ = a => {}, + _ = b => unreachable!(), + }; + }); +} diff --git a/futures/tests/macro_comma_support.rs b/futures/tests/macro_comma_support.rs index 111f65af4e..3b082d211f 100644 --- a/futures/tests/macro_comma_support.rs +++ b/futures/tests/macro_comma_support.rs @@ -1,10 +1,9 @@ -#[macro_use] -extern crate futures; - use futures::{ executor::block_on, future::{self, FutureExt}, + join, ready, task::Poll, + try_join, }; #[test] @@ -15,8 +14,11 @@ fn ready() { })) } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn poll() { + use futures::poll; + block_on(async { let _ = poll!(async {}.boxed(),); }) diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml new file mode 100644 index 0000000000..ed5d0c146a --- /dev/null +++ b/futures/tests/no-std/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "no-std" +version = "0.1.0" +edition = "2018" +publish = false + +[features] +futures-core-alloc = ["futures-core/alloc"] +futures-task-alloc = ["futures-task/alloc"] +futures-channel-alloc = ["futures-channel/alloc"] +futures-util-alloc = ["futures-util/alloc"] +futures-util-async-await = ["futures-util/async-await"] +futures-alloc = ["futures/alloc"] +futures-async-await = ["futures/async-await"] + +[dependencies] +futures-core = { path = "../../../futures-core", optional = true, default-features = false } +futures-task = { path = "../../../futures-task", optional = true, default-features = false } +futures-channel = { path = "../../../futures-channel", optional = true, default-features = false } +futures-util = { path = "../../../futures-util", optional = true, default-features = false } +futures = { path = "../..", optional = true, default-features = false } diff --git a/futures/tests/no-std/build.rs b/futures/tests/no-std/build.rs new file mode 100644 index 0000000000..a96a68274b --- /dev/null +++ b/futures/tests/no-std/build.rs @@ -0,0 +1,14 @@ +use std::{env, process::Command}; + +fn main() { + if is_nightly() { + println!("cargo:rustc-cfg=nightly"); + } +} + +fn is_nightly() -> bool { + env::var_os("RUSTC") + .and_then(|rustc| Command::new(rustc).arg("--version").output().ok()) + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map_or(false, |version| version.contains("nightly") || version.contains("dev")) +} diff --git a/futures/tests/no-std/src/lib.rs b/futures/tests/no-std/src/lib.rs new file mode 100644 index 0000000000..308218d6b7 --- /dev/null +++ b/futures/tests/no-std/src/lib.rs @@ -0,0 +1,31 @@ +#![cfg(nightly)] +#![no_std] +#![feature(cfg_target_has_atomic)] + +#[cfg(feature = "futures-core-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_core::task::__internal::AtomicWaker as _; + +#[cfg(feature = "futures-task-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_task::ArcWake as _; + +#[cfg(feature = "futures-channel-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_channel::oneshot as _; + +#[cfg(any(feature = "futures", feature = "futures-alloc"))] +#[cfg(target_has_atomic = "ptr")] +pub use futures::task::AtomicWaker as _; + +#[cfg(feature = "futures-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures::stream::FuturesOrdered as _; + +#[cfg(any(feature = "futures-util", feature = "futures-util-alloc"))] +#[cfg(target_has_atomic = "ptr")] +pub use futures_util::task::AtomicWaker as _; + +#[cfg(feature = "futures-util-alloc")] +#[cfg(target_has_atomic = "ptr")] +pub use futures_util::stream::FuturesOrdered as _; diff --git a/futures/tests/oneshot.rs b/futures/tests/oneshot.rs index 58951ec581..34b78a33fb 100644 --- a/futures/tests/oneshot.rs +++ b/futures/tests/oneshot.rs @@ -64,3 +64,15 @@ fn oneshot_drop_rx() { drop(rx); assert_eq!(Err(2), tx.send(2)); } + +#[test] +fn oneshot_debug() { + let (tx, rx) = oneshot::channel::(); + assert_eq!(format!("{:?}", tx), "Sender { complete: false }"); + assert_eq!(format!("{:?}", rx), "Receiver { complete: false }"); + drop(rx); + assert_eq!(format!("{:?}", tx), "Sender { complete: true }"); + let (tx, rx) = oneshot::channel::(); + drop(tx); + assert_eq!(format!("{:?}", rx), "Receiver { complete: true }"); +} diff --git a/futures/tests/ready_queue.rs b/futures/tests/ready_queue.rs index 15a0befda8..afba8f28b3 100644 --- a/futures/tests/ready_queue.rs +++ b/futures/tests/ready_queue.rs @@ -8,9 +8,6 @@ use std::panic::{self, AssertUnwindSafe}; use std::sync::{Arc, Barrier}; use std::thread; -trait AssertSendSync: Send + Sync {} -impl AssertSendSync for FuturesUnordered<()> {} - #[test] fn basic_usage() { block_on(future::lazy(move |cx| { @@ -96,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 { @@ -126,7 +126,7 @@ fn stress() { assert_eq!(rx.len(), n); - rx.sort(); + rx.sort_unstable(); for (i, x) in rx.into_iter().enumerate() { assert_eq!(i, x); diff --git a/futures/tests/recurse.rs b/futures/tests/recurse.rs index 2920a41a59..f06524f85a 100644 --- a/futures/tests/recurse.rs +++ b/futures/tests/recurse.rs @@ -1,10 +1,16 @@ use futures::executor::block_on; -use futures::future::{self, FutureExt, BoxFuture}; +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))] + const N: i32 = 1_000; + #[cfg(futures_sanitizer)] // If N is many, asan reports stack-overflow: https://gist.github.com/taiki-e/099446d21cbec69d4acbacf7a9646136 + const N: i32 = 100; + fn do_it(input: (i32, i32)) -> BoxFuture<'static, i32> { let (n, x) = input; if n == 0 { @@ -15,8 +21,6 @@ fn lots() { } let (tx, rx) = mpsc::channel(); - thread::spawn(|| { - block_on(do_it((1_000, 0)).map(move |x| tx.send(x).unwrap())) - }); - assert_eq!(500_500, rx.recv().unwrap()); + thread::spawn(|| block_on(do_it((N, 0)).map(move |x| tx.send(x).unwrap()))); + assert_eq!((0..=N).sum::(), rx.recv().unwrap()); } diff --git a/futures/tests/sink.rs b/futures/tests/sink.rs index f967e1b643..dc826bda98 100644 --- a/futures/tests/sink.rs +++ b/futures/tests/sink.rs @@ -1,9 +1,9 @@ use futures::channel::{mpsc, oneshot}; use futures::executor::block_on; -use futures::future::{self, Future, FutureExt, TryFutureExt}; +use futures::future::{self, poll_fn, Future, FutureExt, TryFutureExt}; use futures::never::Never; use futures::ready; -use futures::sink::{Sink, SinkErrInto, SinkExt}; +use futures::sink::{self, Sink, SinkErrInto, SinkExt}; use futures::stream::{self, Stream, StreamExt}; use futures::task::{self, ArcWake, Context, Poll, Waker}; use futures_test::task::panic_context; @@ -13,8 +13,8 @@ use std::fmt; use std::mem; use std::pin::Pin; use std::rc::Rc; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; fn sassert_next(s: &mut S, item: S::Item) where @@ -36,66 +36,6 @@ fn unwrap(x: Poll>) -> T { } } -#[test] -fn either_sink() { - let mut s = if true { - Vec::::new().left_sink() - } else { - VecDeque::::new().right_sink() - }; - - Pin::new(&mut s).start_send(0).unwrap(); -} - -#[test] -fn vec_sink() { - let mut v = Vec::new(); - Pin::new(&mut v).start_send(0).unwrap(); - Pin::new(&mut v).start_send(1).unwrap(); - assert_eq!(v, vec![0, 1]); - block_on(v.flush()).unwrap(); - assert_eq!(v, vec![0, 1]); -} - -#[test] -fn vecdeque_sink() { - let mut deque = VecDeque::new(); - Pin::new(&mut deque).start_send(2).unwrap(); - Pin::new(&mut deque).start_send(3).unwrap(); - - assert_eq!(deque.pop_front(), Some(2)); - assert_eq!(deque.pop_front(), Some(3)); - assert_eq!(deque.pop_front(), None); -} - -#[test] -fn send() { - let mut v = Vec::new(); - - block_on(v.send(0)).unwrap(); - assert_eq!(v, vec![0]); - - block_on(v.send(1)).unwrap(); - assert_eq!(v, vec![0, 1]); - - block_on(v.send(2)).unwrap(); - assert_eq!(v, vec![0, 1, 2]); -} - -#[test] -fn send_all() { - let mut v = Vec::new(); - - block_on(v.send_all(&mut stream::iter(vec![0, 1]).map(Ok))).unwrap(); - assert_eq!(v, vec![0, 1]); - - block_on(v.send_all(&mut stream::iter(vec![2, 3]).map(Ok))).unwrap(); - assert_eq!(v, vec![0, 1, 2, 3]); - - block_on(v.send_all(&mut stream::iter(vec![4, 5]).map(Ok))).unwrap(); - assert_eq!(v, vec![0, 1, 2, 3, 4, 5]); -} - // An Unpark struct that records unpark events for inspection struct Flag(AtomicBool); @@ -152,6 +92,177 @@ impl + Unpin, Item: Unpin> Future for StartSendFut { } } +// Immediately accepts all requests to start pushing, but completion is managed +// by manually flushing +struct ManualFlush { + data: Vec, + waiting_tasks: Vec, +} + +impl Sink> for ManualFlush { + type Error = (); + + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn start_send(mut self: Pin<&mut Self>, item: Option) -> Result<(), Self::Error> { + if let Some(item) = item { + self.data.push(item); + } else { + self.force_flush(); + } + Ok(()) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.data.is_empty() { + Poll::Ready(Ok(())) + } else { + self.waiting_tasks.push(cx.waker().clone()); + Poll::Pending + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl ManualFlush { + fn new() -> Self { + Self { data: Vec::new(), waiting_tasks: Vec::new() } + } + + fn force_flush(&mut self) -> Vec { + for task in self.waiting_tasks.drain(..) { + task.wake() + } + mem::replace(&mut self.data, Vec::new()) + } +} + +struct ManualAllow { + data: Vec, + allow: Rc, +} + +struct Allow { + flag: Cell, + tasks: RefCell>, +} + +impl Allow { + fn new() -> Self { + Self { flag: Cell::new(false), tasks: RefCell::new(Vec::new()) } + } + + fn check(&self, cx: &mut Context<'_>) -> bool { + if self.flag.get() { + true + } else { + self.tasks.borrow_mut().push(cx.waker().clone()); + false + } + } + + fn start(&self) { + self.flag.set(true); + let mut tasks = self.tasks.borrow_mut(); + for task in tasks.drain(..) { + task.wake(); + } + } +} + +impl Sink for ManualAllow { + type Error = (); + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.allow.check(cx) { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + + fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { + self.data.push(item); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +fn manual_allow() -> (ManualAllow, Rc) { + let allow = Rc::new(Allow::new()); + let manual_allow = ManualAllow { data: Vec::new(), allow: allow.clone() }; + (manual_allow, allow) +} + +#[test] +fn either_sink() { + let mut s = + if true { Vec::::new().left_sink() } else { VecDeque::::new().right_sink() }; + + Pin::new(&mut s).start_send(0).unwrap(); +} + +#[test] +fn vec_sink() { + let mut v = Vec::new(); + Pin::new(&mut v).start_send(0).unwrap(); + Pin::new(&mut v).start_send(1).unwrap(); + assert_eq!(v, vec![0, 1]); + block_on(v.flush()).unwrap(); + assert_eq!(v, vec![0, 1]); +} + +#[test] +fn vecdeque_sink() { + let mut deque = VecDeque::new(); + Pin::new(&mut deque).start_send(2).unwrap(); + Pin::new(&mut deque).start_send(3).unwrap(); + + assert_eq!(deque.pop_front(), Some(2)); + assert_eq!(deque.pop_front(), Some(3)); + assert_eq!(deque.pop_front(), None); +} + +#[test] +fn send() { + let mut v = Vec::new(); + + block_on(v.send(0)).unwrap(); + assert_eq!(v, vec![0]); + + block_on(v.send(1)).unwrap(); + assert_eq!(v, vec![0, 1]); + + block_on(v.send(2)).unwrap(); + assert_eq!(v, vec![0, 1, 2]); +} + +#[test] +fn send_all() { + let mut v = Vec::new(); + + block_on(v.send_all(&mut stream::iter(vec![0, 1]).map(Ok))).unwrap(); + assert_eq!(v, vec![0, 1]); + + block_on(v.send_all(&mut stream::iter(vec![2, 3]).map(Ok))).unwrap(); + assert_eq!(v, vec![0, 1, 2, 3]); + + block_on(v.send_all(&mut stream::iter(vec![4, 5]).map(Ok))).unwrap(); + assert_eq!(v, vec![0, 1, 2, 3, 4, 5]); +} + // Test that `start_send` on an `mpsc` channel does indeed block when the // channel is full #[test] @@ -177,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(); @@ -249,59 +361,6 @@ fn with_propagates_poll_ready() { })); } -// Immediately accepts all requests to start pushing, but completion is managed -// by manually flushing -struct ManualFlush { - data: Vec, - waiting_tasks: Vec, -} - -impl Sink> for ManualFlush { - type Error = (); - - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn start_send(mut self: Pin<&mut Self>, item: Option) -> Result<(), Self::Error> { - if let Some(item) = item { - self.data.push(item); - } else { - self.force_flush(); - } - Ok(()) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.data.is_empty() { - Poll::Ready(Ok(())) - } else { - self.waiting_tasks.push(cx.waker().clone()); - Poll::Pending - } - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } -} - -impl ManualFlush { - fn new() -> Self { - Self { - data: Vec::new(), - waiting_tasks: Vec::new(), - } - } - - fn force_flush(&mut self) -> Vec { - for task in self.waiting_tasks.drain(..) { - task.wake() - } - mem::replace(&mut self.data, Vec::new()) - } -} - // test that the `with` sink doesn't require the underlying sink to flush, // but doesn't claim to be flushed until the underlying sink is #[test] @@ -324,6 +383,30 @@ fn with_flush_propagate() { }) } +// test that `Clone` is implemented on `with` sinks +#[test] +fn with_implements_clone() { + let (mut tx, rx) = mpsc::channel(5); + + { + let mut is_positive = tx.clone().with(|item| future::ok::(item > 0)); + + let mut is_long = + tx.clone().with(|item: &str| future::ok::(item.len() > 5)); + + block_on(is_positive.clone().send(-1)).unwrap(); + block_on(is_long.clone().send("123456")).unwrap(); + block_on(is_long.send("123")).unwrap(); + block_on(is_positive.send(1)).unwrap(); + } + + block_on(tx.send(false)).unwrap(); + + block_on(tx.close()).unwrap(); + + assert_eq!(block_on(rx.collect::>()), vec![false, true, false, true, false]); +} + // test that a buffer is a no-nop around a sink that always accepts sends #[test] fn buffer_noop() { @@ -338,76 +421,6 @@ fn buffer_noop() { assert_eq!(sink.get_ref(), &[0, 1]); } -struct ManualAllow { - data: Vec, - allow: Rc, -} - -struct Allow { - flag: Cell, - tasks: RefCell>, -} - -impl Allow { - fn new() -> Self { - Self { - flag: Cell::new(false), - tasks: RefCell::new(Vec::new()), - } - } - - fn check(&self, cx: &mut Context<'_>) -> bool { - if self.flag.get() { - true - } else { - self.tasks.borrow_mut().push(cx.waker().clone()); - false - } - } - - fn start(&self) { - self.flag.set(true); - let mut tasks = self.tasks.borrow_mut(); - for task in tasks.drain(..) { - task.wake(); - } - } -} - -impl Sink for ManualAllow { - type Error = (); - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.allow.check(cx) { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - - fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { - self.data.push(item); - Ok(()) - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -fn manual_allow() -> (ManualAllow, Rc) { - let allow = Rc::new(Allow::new()); - let manual_allow = ManualAllow { - data: Vec::new(), - allow: allow.clone(), - }; - (manual_allow, allow) -} - // test basic buffer functionality, including both filling up to capacity, // and writing out when the underlying sink is ready #[test] @@ -483,23 +496,52 @@ fn sink_map_err() { } let tx = mpsc::channel(0).0; - assert_eq!( - Pin::new(&mut tx.sink_map_err(|_| ())).start_send(()), - Err(()) - ); + assert_eq!(Pin::new(&mut tx.sink_map_err(|_| ())).start_send(()), Err(())); } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct ErrIntoTest; - -impl From for ErrIntoTest { - fn from(_: mpsc::SendError) -> Self { - Self - } +#[test] +fn sink_unfold() { + block_on(poll_fn(|cx| { + let (tx, mut rx) = mpsc::channel(1); + let unfold = sink::unfold((), |(), i: i32| { + let mut tx = tx.clone(); + async move { + tx.send(i).await.unwrap(); + Ok::<_, String>(()) + } + }); + futures::pin_mut!(unfold); + assert_eq!(unfold.as_mut().start_send(1), Ok(())); + assert_eq!(unfold.as_mut().poll_flush(cx), Poll::Ready(Ok(()))); + assert_eq!(rx.try_next().unwrap(), Some(1)); + + assert_eq!(unfold.as_mut().poll_ready(cx), Poll::Ready(Ok(()))); + assert_eq!(unfold.as_mut().start_send(2), Ok(())); + assert_eq!(unfold.as_mut().poll_ready(cx), Poll::Ready(Ok(()))); + assert_eq!(unfold.as_mut().start_send(3), Ok(())); + assert_eq!(rx.try_next().unwrap(), Some(2)); + assert!(rx.try_next().is_err()); + assert_eq!(unfold.as_mut().poll_ready(cx), Poll::Ready(Ok(()))); + assert_eq!(unfold.as_mut().start_send(4), Ok(())); + assert_eq!(unfold.as_mut().poll_flush(cx), Poll::Pending); // Channel full + assert_eq!(rx.try_next().unwrap(), Some(3)); + assert_eq!(rx.try_next().unwrap(), Some(4)); + + Poll::Ready(()) + })) } #[test] fn err_into() { + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + struct ErrIntoTest; + + impl From for ErrIntoTest { + fn from(_: mpsc::SendError) -> Self { + Self + } + } + { let cx = &mut panic_context(); let (tx, _rx) = mpsc::channel(1); @@ -509,8 +551,5 @@ fn err_into() { } let tx = mpsc::channel(0).0; - assert_eq!( - Pin::new(&mut tx.sink_err_into()).start_send(()), - Err(ErrIntoTest) - ); + assert_eq!(Pin::new(&mut tx.sink_err_into()).start_send(()), Err(ErrIntoTest)); } diff --git a/futures/tests/split.rs b/futures/tests/split.rs deleted file mode 100644 index 9f4f1a07e2..0000000000 --- a/futures/tests/split.rs +++ /dev/null @@ -1,77 +0,0 @@ -use futures::executor::block_on; -use futures::sink::{Sink, SinkExt}; -use futures::stream::{self, Stream, StreamExt}; -use futures::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use std::pin::Pin; - -struct Join { - stream: T, - sink: U -} - -impl Join { - unsafe_pinned!(stream: T); - unsafe_pinned!(sink: U); -} - -impl Stream for Join { - type Item = T::Item; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().poll_next(cx) - } -} - -impl, Item> Sink for Join { - type Error = U::Error; - - fn poll_ready( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_ready(cx) - } - - fn start_send( - self: Pin<&mut Self>, - item: Item, - ) -> Result<(), Self::Error> { - self.sink().start_send(item) - } - - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_flush(cx) - } - - fn poll_close( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_close(cx) - } -} - -#[test] -fn test_split() { - let mut dest: Vec = Vec::new(); - { - let join = Join { - stream: stream::iter(vec![10, 20, 30]), - sink: &mut dest - }; - - let (sink, stream) = join.split(); - let join = sink.reunite(stream).expect("test_split: reunite error"); - let (mut sink, stream) = join.split(); - let mut stream = stream.map(Ok); - block_on(sink.send_all(&mut stream)).unwrap(); - } - assert_eq!(dest, vec![10, 20, 30]); -} diff --git a/futures/tests/stream.rs b/futures/tests/stream.rs index fd6a8b6da7..71ec654bfb 100644 --- a/futures/tests/stream.rs +++ b/futures/tests/stream.rs @@ -1,5 +1,15 @@ +use std::iter; +use std::sync::Arc; + +use futures::channel::mpsc; use futures::executor::block_on; +use futures::future::{self, Future}; +use futures::lock::Mutex; +use futures::sink::SinkExt; use futures::stream::{self, StreamExt}; +use futures::task::Poll; +use futures::{ready, FutureExt}; +use futures_test::task::noop_context; #[test] fn select() { @@ -15,18 +25,397 @@ fn select() { select_and_compare(vec![1, 2], vec![4, 5, 6], vec![1, 4, 2, 5, 6]); } +#[test] +fn flat_map() { + block_on(async { + let st = + stream::iter(vec![stream::iter(0..=4u8), stream::iter(6..=10), stream::iter(0..=2)]); + + let values: Vec<_> = + st.flat_map(|s| s.filter(|v| futures::future::ready(v % 2 == 0))).collect().await; + + assert_eq!(values, vec![0, 2, 4, 6, 8, 10, 0, 2]); + }); +} + #[test] fn scan() { - futures::executor::block_on(async { - assert_eq!( - stream::iter(vec![1u8, 2, 3, 4, 6, 8, 2]) - .scan(1, |acc, e| { - *acc += 1; - futures::future::ready(if e < *acc { Some(e) } else { None }) - }) + block_on(async { + let values = stream::iter(vec![1u8, 2, 3, 4, 6, 8, 2]) + .scan(1, |state, e| { + *state += 1; + futures::future::ready(if e < *state { Some(e) } else { None }) + }) + .collect::>() + .await; + + assert_eq!(values, vec![1u8, 2, 3, 4]); + }); +} + +#[test] +fn flatten_unordered() { + use futures::executor::block_on; + use futures::stream::*; + use futures::task::*; + use std::convert::identity; + use std::pin::Pin; + use std::thread; + use std::time::Duration; + + struct DataStream { + data: Vec, + polled: bool, + wake_immediately: bool, + } + + impl Stream for DataStream { + type Item = u8; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if !self.polled { + if !self.wake_immediately { + let waker = ctx.waker().clone(); + let sleep_time = + Duration::from_millis(*self.data.first().unwrap_or(&0) as u64 / 10); + thread::spawn(move || { + thread::sleep(sleep_time); + waker.wake_by_ref(); + }); + } else { + ctx.waker().wake_by_ref(); + } + self.polled = true; + Poll::Pending + } else { + self.polled = false; + Poll::Ready(self.data.pop()) + } + } + } + + struct Interchanger { + polled: bool, + base: u8, + wake_immediately: bool, + } + + impl Stream for Interchanger { + type Item = DataStream; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if !self.polled { + self.polled = true; + if !self.wake_immediately { + let waker = ctx.waker().clone(); + let sleep_time = Duration::from_millis(self.base as u64); + thread::spawn(move || { + thread::sleep(sleep_time); + waker.wake_by_ref(); + }); + } else { + ctx.waker().wake_by_ref(); + } + Poll::Pending + } else { + let data: Vec<_> = (0..6).rev().map(|v| v + self.base * 6).collect(); + self.base += 1; + self.polled = false; + Poll::Ready(Some(DataStream { + polled: false, + data, + wake_immediately: self.wake_immediately && self.base % 2 == 0, + })) + } + } + } + + // basic behaviour + { + block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(10..=12), + ]); + + let fl_unordered = st.flatten_unordered(3).collect::>().await; + + assert_eq!(fl_unordered, vec![0, 6, 10, 1, 7, 11, 2, 8, 12, 3, 9, 4, 10]); + }); + + block_on(async { + let st = stream::iter(vec![ + stream::iter(0..=4u8), + stream::iter(6..=10), + stream::iter(0..=2), + ]); + + let mut fm_unordered = st + .flat_map_unordered(1, |s| s.filter(|v| futures::future::ready(v % 2 == 0))) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, vec![0, 0, 2, 2, 4, 6, 8, 10]); + }); + } + + // wake up immediately + { + block_on(async { + let mut fl_unordered = Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) .collect::>() - .await, - vec![1u8, 2, 3, 4] + .await; + + fl_unordered.sort_unstable(); + + assert_eq!(fl_unordered, (0..60).collect::>()); + }); + + block_on(async { + let mut fm_unordered = Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + } + + // wake up after delay + { + block_on(async { + let mut fl_unordered = Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) + .collect::>() + .await; + + fl_unordered.sort_unstable(); + + assert_eq!(fl_unordered, (0..60).collect::>()); + }); + + block_on(async { + let mut fm_unordered = Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>() + .await; + + fm_unordered.sort_unstable(); + + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + + block_on(async { + let (mut fm_unordered, mut fl_unordered) = futures_util::join!( + Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)) + .collect::>(), + Interchanger { polled: false, base: 0, wake_immediately: false } + .take(10) + .map(|s| s.map(identity)) + .flatten_unordered(10) + .collect::>() + ); + + fm_unordered.sort_unstable(); + fl_unordered.sort_unstable(); + + assert_eq!(fm_unordered, fl_unordered); + assert_eq!(fm_unordered, (0..60).collect::>()); + }); + } + + // waker panics + { + let stream = Arc::new(Mutex::new( + Interchanger { polled: false, base: 0, wake_immediately: true } + .take(10) + .flat_map_unordered(10, |s| s.map(identity)), + )); + + struct PanicWaker; + + impl ArcWake for PanicWaker { + fn wake_by_ref(_arc_self: &Arc) { + panic!("WAKE UP"); + } + } + + std::thread::spawn({ + let stream = stream.clone(); + move || { + let mut st = poll_fn(|cx| { + let mut lock = ready!(stream.lock().poll_unpin(cx)); + + let panic_waker = waker(Arc::new(PanicWaker)); + let mut panic_cx = Context::from_waker(&panic_waker); + let _ = ready!(lock.poll_next_unpin(&mut panic_cx)); + + Poll::Ready(Some(())) + }); + + block_on(st.next()) + } + }) + .join() + .unwrap_err(); + + block_on(async move { + let mut values: Vec<_> = stream.lock().await.by_ref().collect().await; + values.sort_unstable(); + + assert_eq!(values, (0..60).collect::>()); + }); + } + + // stream panics + { + let st = stream::iter(iter::once( + once(Box::pin(async { panic!("Polled") })).left_stream::(), + )) + .chain( + Interchanger { polled: false, base: 0, wake_immediately: true } + .map(|stream| stream.right_stream()) + .take(10), ); + + let stream = Arc::new(Mutex::new(st.flatten_unordered(10))); + + std::thread::spawn({ + let stream = stream.clone(); + move || { + let mut st = poll_fn(|cx| { + let mut lock = ready!(stream.lock().poll_unpin(cx)); + let data = ready!(lock.poll_next_unpin(cx)); + + Poll::Ready(data) + }); + + block_on(st.next()) + } + }) + .join() + .unwrap_err(); + + block_on(async move { + let mut values: Vec<_> = stream.lock().await.by_ref().collect().await; + values.sort_unstable(); + + assert_eq!(values, (0..60).collect::>()); + }); + } +} + +#[test] +fn take_until() { + fn make_stop_fut(stop_on: u32) -> impl Future { + let mut i = 0; + future::poll_fn(move |_cx| { + i += 1; + if i <= stop_on { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + } + + block_on(async { + // Verify stopping works: + let stream = stream::iter(1u32..=10); + let stop_fut = make_stop_fut(5); + + let stream = stream.take_until(stop_fut); + let last = stream.fold(0, |_, i| async move { i }).await; + assert_eq!(last, 5); + + // Verify take_future() works: + let stream = stream::iter(1..=10); + let stop_fut = make_stop_fut(5); + + let mut stream = stream.take_until(stop_fut); + + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, Some(2)); + + stream.take_future(); + + let last = stream.fold(0, |_, i| async move { i }).await; + assert_eq!(last, 10); + + // Verify take_future() returns None if stream is stopped: + let stream = stream::iter(1u32..=10); + let stop_fut = make_stop_fut(1); + let mut stream = stream.take_until(stop_fut); + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, None); + assert!(stream.take_future().is_none()); + + // Verify TakeUntil is fused: + let mut i = 0; + let stream = stream::poll_fn(move |_cx| { + i += 1; + match i { + 1 => Poll::Ready(Some(1)), + 2 => Poll::Ready(None), + _ => panic!("TakeUntil not fused"), + } + }); + + let stop_fut = make_stop_fut(1); + let mut stream = stream.take_until(stop_fut); + assert_eq!(stream.next().await, Some(1)); + assert_eq!(stream.next().await, None); + assert_eq!(stream.next().await, None); + }); +} + +#[test] +#[should_panic] +fn chunks_panic_on_cap_zero() { + let (_, rx1) = mpsc::channel::<()>(1); + + let _ = rx1.chunks(0); +} + +#[test] +#[should_panic] +fn ready_chunks_panic_on_cap_zero() { + let (_, rx1) = mpsc::channel::<()>(1); + + let _ = rx1.ready_chunks(0); +} + +#[test] +fn ready_chunks() { + let (mut tx, rx1) = mpsc::channel::(16); + + let mut s = rx1.ready_chunks(2); + + let mut cx = noop_context(); + assert!(s.next().poll_unpin(&mut cx).is_pending()); + + block_on(async { + tx.send(1).await.unwrap(); + + assert_eq!(s.next().await.unwrap(), vec![1]); + tx.send(2).await.unwrap(); + tx.send(3).await.unwrap(); + tx.send(4).await.unwrap(); + assert_eq!(s.next().await.unwrap(), vec![2, 3]); + assert_eq!(s.next().await.unwrap(), vec![4]); }); } diff --git a/futures/tests/stream_abortable.rs b/futures/tests/stream_abortable.rs new file mode 100644 index 0000000000..2339dd0522 --- /dev/null +++ b/futures/tests/stream_abortable.rs @@ -0,0 +1,46 @@ +use futures::channel::mpsc; +use futures::executor::block_on; +use futures::stream::{abortable, Stream, StreamExt}; +use futures::task::{Context, Poll}; +use futures::SinkExt; +use futures_test::task::new_count_waker; +use std::pin::Pin; + +#[test] +fn abortable_works() { + let (_tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, abort_handle) = abortable(a_rx); + + abort_handle.abort(); + assert!(abortable_rx.is_aborted()); + assert_eq!(None, block_on(abortable_rx.next())); +} + +#[test] +fn abortable_awakens() { + let (_tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, abort_handle) = abortable(a_rx); + + let (waker, counter) = new_count_waker(); + let mut cx = Context::from_waker(&waker); + + assert_eq!(counter, 0); + assert_eq!(Poll::Pending, Pin::new(&mut abortable_rx).poll_next(&mut cx)); + assert_eq!(counter, 0); + + abort_handle.abort(); + assert_eq!(counter, 1); + assert!(abortable_rx.is_aborted()); + assert_eq!(Poll::Ready(None), Pin::new(&mut abortable_rx).poll_next(&mut cx)); +} + +#[test] +fn abortable_resolves() { + let (mut tx, a_rx) = mpsc::channel::<()>(1); + let (mut abortable_rx, _abort_handle) = abortable(a_rx); + + block_on(tx.send(())).unwrap(); + + assert!(!abortable_rx.is_aborted()); + assert_eq!(Some(()), block_on(abortable_rx.next())); +} diff --git a/futures/tests/buffer_unordered.rs b/futures/tests/stream_buffer_unordered.rs similarity index 98% rename from futures/tests/buffer_unordered.rs rename to futures/tests/stream_buffer_unordered.rs index 1c559c8544..9a2ee174ed 100644 --- a/futures/tests/buffer_unordered.rs +++ b/futures/tests/stream_buffer_unordered.rs @@ -1,4 +1,4 @@ -use futures::channel::{oneshot, mpsc}; +use futures::channel::{mpsc, oneshot}; use futures::executor::{block_on, block_on_stream}; use futures::sink::SinkExt; use futures::stream::StreamExt; diff --git a/futures/tests/futures_ordered.rs b/futures/tests/stream_futures_ordered.rs similarity index 70% rename from futures/tests/futures_ordered.rs rename to futures/tests/stream_futures_ordered.rs index d06b62f76c..84e0bcc1df 100644 --- a/futures/tests/futures_ordered.rs +++ b/futures/tests/stream_futures_ordered.rs @@ -1,7 +1,7 @@ use futures::channel::oneshot; use futures::executor::{block_on, block_on_stream}; use futures::future::{self, join, Future, FutureExt, TryFutureExt}; -use futures::stream::{StreamExt, FuturesOrdered}; +use futures::stream::{FuturesOrdered, StreamExt}; use futures_test::task::noop_context; use std::any::Any; @@ -26,16 +26,16 @@ 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::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![ - a_rx.boxed(), - join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed(), - ].into_iter().collect::>(); + let mut stream = vec![a_rx.boxed(), join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed()] + .into_iter() + .collect::>(); let mut cx = noop_context(); a_tx.send(33).unwrap(); @@ -48,15 +48,14 @@ fn works_2() { #[test] fn from_iterator() { - let stream = vec![ - future::ready::(1), - future::ready::(2), - future::ready::(3) - ].into_iter().collect::>(); + let stream = vec![future::ready::(1), future::ready::(2), future::ready::(3)] + .into_iter() + .collect::>(); assert_eq!(stream.len(), 3); - assert_eq!(block_on(stream.collect::>()), vec![1,2,3]); + 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::>(); @@ -65,10 +64,14 @@ fn queue_never_unblocked() { let mut stream = vec![ Box::new(a_rx) as Box + Unpin>, - Box::new(future::try_select(b_rx, c_rx) - .map_err(|e| e.factor_first().0) - .and_then(|e| future::ok(Box::new(e) as Box))) as _, - ].into_iter().collect::>(); + Box::new( + future::try_select(b_rx, c_rx) + .map_err(|e| e.factor_first().0) + .and_then(|e| future::ok(Box::new(e) as Box)), + ) as _, + ] + .into_iter() + .collect::>(); let cx = &mut noop_context(); for _ in 0..10 { diff --git a/futures/tests/futures_unordered.rs b/futures/tests/stream_futures_unordered.rs similarity index 65% rename from futures/tests/futures_unordered.rs rename to futures/tests/stream_futures_unordered.rs index 57eb98fd1b..f62f733610 100644 --- a/futures/tests/futures_unordered.rs +++ b/futures/tests/stream_futures_unordered.rs @@ -1,7 +1,3 @@ -use std::marker::Unpin; -use std::pin::Pin; -use std::sync::atomic::{AtomicBool, Ordering}; - use futures::channel::oneshot; use futures::executor::{block_on, block_on_stream}; use futures::future::{self, join, Future, FutureExt}; @@ -9,7 +5,10 @@ use futures::stream::{FusedStream, FuturesUnordered, StreamExt}; use futures::task::{Context, Poll}; use futures_test::future::FutureTestExt; use futures_test::task::noop_context; -use futures_test::{assert_stream_done, assert_stream_next}; +use futures_test::{assert_stream_done, assert_stream_next, assert_stream_pending}; +use std::iter::FromIterator; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; #[test] fn is_terminated() { @@ -44,11 +43,8 @@ fn works_1() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut iter = block_on_stream( - vec![a_rx, b_rx, c_rx] - .into_iter() - .collect::>(), - ); + let mut iter = + block_on_stream(vec![a_rx, b_rx, c_rx].into_iter().collect::>()); b_tx.send(99).unwrap(); assert_eq!(Some(Ok(99)), iter.next()); @@ -60,18 +56,16 @@ 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::(); let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![ - a_rx.boxed(), - join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed(), - ] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx.boxed(), join(b_rx, c_rx).map(|(a, b)| Ok(a? + b?)).boxed()] + .into_iter() + .collect::>(); a_tx.send(9).unwrap(); b_tx.send(10).unwrap(); @@ -85,17 +79,14 @@ fn works_2() { #[test] fn from_iterator() { - let stream = vec![ - future::ready::(1), - future::ready::(2), - future::ready::(3), - ] - .into_iter() - .collect::>(); + let stream = vec![future::ready::(1), future::ready::(2), future::ready::(3)] + .into_iter() + .collect::>(); assert_eq!(stream.len(), 3); assert_eq!(block_on(stream.collect::>()), vec![1, 2, 3]); } +#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038 #[test] fn finished_future() { let (_a_tx, a_rx) = oneshot::channel::(); @@ -127,9 +118,7 @@ fn iter_mut_cancel() { let (b_tx, b_rx) = oneshot::channel::(); let (c_tx, c_rx) = oneshot::channel::(); - let mut stream = vec![a_rx, b_rx, c_rx] - .into_iter() - .collect::>(); + let mut stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); for rx in stream.iter_mut() { rx.close(); @@ -149,13 +138,10 @@ fn iter_mut_cancel() { #[test] fn iter_mut_len() { - let mut stream = vec![ - future::pending::<()>(), - future::pending::<()>(), - future::pending::<()>(), - ] - .into_iter() - .collect::>(); + let mut stream = + vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); let mut iter_mut = stream.iter_mut(); assert_eq!(iter_mut.len(), 3); @@ -215,13 +201,9 @@ fn iter_cancel() { #[test] fn iter_len() { - let stream = vec![ - future::pending::<()>(), - future::pending::<()>(), - future::pending::<()>(), - ] - .into_iter() - .collect::>(); + let stream = vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); let mut iter = stream.iter(); assert_eq!(iter.len(), 3); @@ -234,12 +216,58 @@ fn iter_len() { assert!(iter.next().is_none()); } +#[test] +fn into_iter_cancel() { + let (a_tx, a_rx) = oneshot::channel::(); + let (b_tx, b_rx) = oneshot::channel::(); + let (c_tx, c_rx) = oneshot::channel::(); + + let stream = vec![a_rx, b_rx, c_rx].into_iter().collect::>(); + + let stream = stream + .into_iter() + .map(|mut rx| { + rx.close(); + rx + }) + .collect::>(); + + let mut iter = block_on_stream(stream); + + assert!(a_tx.is_canceled()); + assert!(b_tx.is_canceled()); + assert!(c_tx.is_canceled()); + + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), Some(Err(futures::channel::oneshot::Canceled))); + assert_eq!(iter.next(), None); +} + +#[test] +fn into_iter_len() { + let stream = vec![future::pending::<()>(), future::pending::<()>(), future::pending::<()>()] + .into_iter() + .collect::>(); + + let mut into_iter = stream.into_iter(); + assert_eq!(into_iter.len(), 3); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 2); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 1); + assert!(into_iter.next().is_some()); + assert_eq!(into_iter.len(), 0); + assert!(into_iter.next().is_none()); +} + #[test] fn futures_not_moved_after_poll() { // Future that will be ready after being polled twice, // asserting that it does not move. let fut = future::ready(()).pending_once().assert_unmoved(); let mut stream = vec![fut; 3].into_iter().collect::>(); + assert_stream_pending!(stream); assert_stream_next!(stream, ()); assert_stream_next!(stream, ()); assert_stream_next!(stream, ()); @@ -285,3 +313,59 @@ fn len_valid_during_out_of_order_completion() { assert_eq!(stream.poll_next_unpin(&mut cx), Poll::Ready(Some(Ok(7)))); assert_eq!(stream.len(), 0); } + +#[test] +fn polled_only_once_at_most_per_iteration() { + #[derive(Debug, Clone, Copy, Default)] + struct F { + polled: bool, + } + + impl Future for F { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + if self.polled { + panic!("polled twice") + } else { + self.polled = true; + Poll::Pending + } + } + } + + let cx = &mut noop_context(); + + let mut tasks = FuturesUnordered::from_iter(vec![F::default(); 10]); + assert!(tasks.poll_next_unpin(cx).is_pending()); + assert_eq!(10, tasks.iter().filter(|f| f.polled).count()); + + let mut tasks = FuturesUnordered::from_iter(vec![F::default(); 33]); + assert!(tasks.poll_next_unpin(cx).is_pending()); + assert_eq!(33, tasks.iter().filter(|f| f.polled).count()); + + let mut tasks = FuturesUnordered::::new(); + assert_eq!(Poll::Ready(None), tasks.poll_next_unpin(cx)); +} + +#[test] +fn clear() { + let mut tasks = FuturesUnordered::from_iter(vec![future::ready(1), future::ready(2)]); + + assert_eq!(block_on(tasks.next()), Some(1)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + tasks.push(future::ready(3)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + assert_eq!(block_on(tasks.next()), None); + assert!(tasks.is_terminated()); + tasks.clear(); + assert!(!tasks.is_terminated()); +} diff --git a/futures/tests/stream_into_async_read.rs b/futures/tests/stream_into_async_read.rs index c528af03f0..60188d3e58 100644 --- a/futures/tests/stream_into_async_read.rs +++ b/futures/tests/stream_into_async_read.rs @@ -1,8 +1,8 @@ use core::pin::Pin; -use futures::io::{AsyncRead, AsyncBufRead}; +use futures::io::{AsyncBufRead, AsyncRead}; use futures::stream::{self, TryStreamExt}; use futures::task::Poll; -use futures_test::{task::noop_context, stream::StreamTestExt}; +use futures_test::{stream::StreamTestExt, task::noop_context}; macro_rules! assert_read { ($reader:expr, $buf:expr, $item:expr) => { @@ -72,7 +72,7 @@ fn test_into_async_read() { } #[test] -fn test_into_async_bufread() -> std::io::Result<()> { +fn test_into_async_bufread() { let stream = stream::iter((1..=2).flat_map(|_| vec![Ok(vec![]), Ok(vec![1, 2, 3, 4, 5])])); let mut reader = stream.interleave_pending().into_async_read(); @@ -91,6 +91,4 @@ fn test_into_async_bufread() -> std::io::Result<()> { reader.as_mut().consume(3); assert_fill_buf!(reader, &[][..]); - - Ok(()) } diff --git a/futures/tests/stream_peekable.rs b/futures/tests/stream_peekable.rs index b65a0572cb..153fcc25b4 100644 --- a/futures/tests/stream_peekable.rs +++ b/futures/tests/stream_peekable.rs @@ -9,5 +9,50 @@ fn peekable() { pin_mut!(peekable); assert_eq!(peekable.as_mut().peek().await, Some(&1u8)); assert_eq!(peekable.collect::>().await, vec![1, 2, 3]); + + let s = stream::once(async { 1 }).peekable(); + pin_mut!(s); + assert_eq!(s.as_mut().peek().await, Some(&1u8)); + assert_eq!(s.collect::>().await, vec![1]); + }); +} + +#[test] +fn peekable_mut() { + block_on(async { + let s = stream::iter(vec![1u8, 2, 3]).peekable(); + pin_mut!(s); + if let Some(p) = s.as_mut().peek_mut().await { + if *p == 1 { + *p = 5; + } + } + assert_eq!(s.collect::>().await, vec![5, 2, 3]); + }); +} + +#[test] +fn peekable_next_if_eq() { + block_on(async { + // first, try on references + let s = stream::iter(vec!["Heart", "of", "Gold"]).peekable(); + pin_mut!(s); + // try before `peek()` + assert_eq!(s.as_mut().next_if_eq(&"trillian").await, None); + assert_eq!(s.as_mut().next_if_eq(&"Heart").await, Some("Heart")); + // try after peek() + assert_eq!(s.as_mut().peek().await, Some(&"of")); + assert_eq!(s.as_mut().next_if_eq(&"of").await, Some("of")); + assert_eq!(s.as_mut().next_if_eq(&"zaphod").await, None); + // make sure `next()` still behaves + assert_eq!(s.next().await, Some("Gold")); + + // make sure comparison works for owned values + let s = stream::iter(vec![String::from("Ludicrous"), "speed".into()]).peekable(); + pin_mut!(s); + // make sure basic functionality works + assert_eq!(s.as_mut().next_if_eq("Ludicrous").await, Some("Ludicrous".into())); + assert_eq!(s.as_mut().next_if_eq("speed").await, Some("speed".into())); + assert_eq!(s.as_mut().next_if_eq("").await, None); }); } diff --git a/futures/tests/stream_select_all.rs b/futures/tests/stream_select_all.rs index eb711dda0c..4ae0735762 100644 --- a/futures/tests/stream_select_all.rs +++ b/futures/tests/stream_select_all.rs @@ -1,5 +1,5 @@ use futures::channel::mpsc; -use futures::executor::block_on_stream; +use futures::executor::{block_on, block_on_stream}; use futures::future::{self, FutureExt}; use futures::stream::{self, select_all, FusedStream, SelectAll, StreamExt}; use futures::task::Poll; @@ -76,3 +76,122 @@ fn works_1() { drop((a_tx, b_tx, c_tx)); assert_eq!(None, stream.next()); } + +#[test] +fn clear() { + let mut tasks = + select_all(vec![stream::iter(vec![1].into_iter()), stream::iter(vec![2].into_iter())]); + + assert_eq!(block_on(tasks.next()), Some(1)); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + tasks.push(stream::iter(vec![3].into_iter())); + assert!(!tasks.is_empty()); + + tasks.clear(); + assert!(tasks.is_empty()); + + assert_eq!(block_on(tasks.next()), None); + assert!(tasks.is_terminated()); + tasks.clear(); + assert!(!tasks.is_terminated()); +} + +#[test] +fn iter_mut() { + let mut stream = + vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + let mut stream = vec![stream::iter(vec![]), stream::iter(vec![1]), stream::iter(vec![2])] + .into_iter() + .collect::>(); + + assert_eq!(stream.len(), 3); + assert_eq!(block_on(stream.next()), Some(1)); + assert_eq!(stream.len(), 2); + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + assert_eq!(block_on(stream.next()), Some(2)); + assert_eq!(stream.len(), 2); + assert_eq!(block_on(stream.next()), None); + let mut iter = stream.iter_mut(); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} + +#[test] +fn iter() { + let stream = vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.iter(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + let mut stream = vec![stream::iter(vec![]), stream::iter(vec![1]), stream::iter(vec![2])] + .into_iter() + .collect::>(); + + assert_eq!(stream.len(), 3); + assert_eq!(block_on(stream.next()), Some(1)); + assert_eq!(stream.len(), 2); + let mut iter = stream.iter(); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); + + assert_eq!(block_on(stream.next()), Some(2)); + assert_eq!(stream.len(), 2); + assert_eq!(block_on(stream.next()), None); + let mut iter = stream.iter(); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} + +#[test] +fn into_iter() { + let stream = vec![stream::pending::<()>(), stream::pending::<()>(), stream::pending::<()>()] + .into_iter() + .collect::>(); + + let mut iter = stream.into_iter(); + assert_eq!(iter.len(), 3); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 2); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 1); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 0); + assert!(iter.next().is_none()); +} diff --git a/futures/tests/stream_select_next_some.rs b/futures/tests/stream_select_next_some.rs index 09d7e895c1..8252ad7b54 100644 --- a/futures/tests/stream_select_next_some.rs +++ b/futures/tests/stream_select_next_some.rs @@ -1,5 +1,6 @@ -use futures::{future, select}; -use futures::future::{FusedFuture, FutureExt}; +use futures::executor::block_on; +use futures::future::{self, FusedFuture, FutureExt}; +use futures::select; use futures::stream::{FuturesUnordered, StreamExt}; use futures::task::{Context, Poll}; use futures_test::future::FutureTestExt; @@ -35,7 +36,7 @@ fn select() { // `is_terminated() == true` during the first poll, it manages to toggle // back to having items after a future is pushed into it during the second // poll (after pending_once completes). - futures::executor::block_on(async { + block_on(async { let mut fut = future::ready(1).pending_once(); let mut async_tasks = FuturesUnordered::new(); let mut total = 0; @@ -64,7 +65,7 @@ fn futures_util_select() { // `is_terminated() == true` during the first poll, it manages to toggle // back to having items after a future is pushed into it during the second // poll (after pending_once completes). - futures::executor::block_on(async { + block_on(async { let mut fut = future::ready(1).pending_once(); let mut async_tasks = FuturesUnordered::new(); let mut total = 0; diff --git a/futures/tests/stream_split.rs b/futures/tests/stream_split.rs new file mode 100644 index 0000000000..694c151807 --- /dev/null +++ b/futures/tests/stream_split.rs @@ -0,0 +1,57 @@ +use futures::executor::block_on; +use futures::sink::{Sink, SinkExt}; +use futures::stream::{self, Stream, StreamExt}; +use futures::task::{Context, Poll}; +use pin_project::pin_project; +use std::pin::Pin; + +#[test] +fn test_split() { + #[pin_project] + struct Join { + #[pin] + stream: T, + #[pin] + sink: U, + } + + impl Stream for Join { + type Item = T::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().stream.poll_next(cx) + } + } + + impl, Item> Sink for Join { + type Error = U::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().sink.poll_ready(cx) + } + + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + self.project().sink.start_send(item) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().sink.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().sink.poll_close(cx) + } + } + + let mut dest: Vec = Vec::new(); + { + let join = Join { stream: stream::iter(vec![10, 20, 30]), sink: &mut dest }; + + let (sink, stream) = join.split(); + let join = sink.reunite(stream).expect("test_split: reunite error"); + let (mut sink, stream) = join.split(); + let mut stream = stream.map(Ok); + block_on(sink.send_all(&mut stream)).unwrap(); + } + assert_eq!(dest, vec![10, 20, 30]); +} diff --git a/futures/tests/stream_try_stream.rs b/futures/tests/stream_try_stream.rs new file mode 100644 index 0000000000..d83fc54b1c --- /dev/null +++ b/futures/tests/stream_try_stream.rs @@ -0,0 +1,40 @@ +#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038 + +use futures::{ + stream::{self, StreamExt, TryStreamExt}, + task::Poll, +}; +use futures_test::task::noop_context; + +#[test] +fn try_filter_map_after_err() { + let cx = &mut noop_context(); + let mut s = stream::iter(1..=3) + .map(Ok) + .try_filter_map(|v| async move { Err::, _>(v) }) + .filter_map(|r| async move { r.ok() }) + .boxed(); + assert_eq!(Poll::Ready(None), s.poll_next_unpin(cx)); +} + +#[test] +fn try_skip_while_after_err() { + let cx = &mut noop_context(); + let mut s = stream::iter(1..=3) + .map(Ok) + .try_skip_while(|_| async move { Err::<_, ()>(()) }) + .filter_map(|r| async move { r.ok() }) + .boxed(); + assert_eq!(Poll::Ready(None), s.poll_next_unpin(cx)); +} + +#[test] +fn try_take_while_after_err() { + let cx = &mut noop_context(); + let mut s = stream::iter(1..=3) + .map(Ok) + .try_take_while(|_| async move { Err::<_, ()>(()) }) + .filter_map(|r| async move { r.ok() }) + .boxed(); + assert_eq!(Poll::Ready(None), s.poll_next_unpin(cx)); +} diff --git a/futures/tests/unfold.rs b/futures/tests/stream_unfold.rs similarity index 89% rename from futures/tests/unfold.rs rename to futures/tests/stream_unfold.rs index 95722cf8a6..16b10813b1 100644 --- a/futures/tests/unfold.rs +++ b/futures/tests/stream_unfold.rs @@ -1,10 +1,7 @@ use futures::future; use futures::stream; - use futures_test::future::FutureTestExt; -use futures_test::{ - assert_stream_done, assert_stream_next, assert_stream_pending, -}; +use futures_test::{assert_stream_done, assert_stream_next, assert_stream_pending}; #[test] fn unfold1() { diff --git a/futures/tests/arc_wake.rs b/futures/tests/task_arc_wake.rs similarity index 79% rename from futures/tests/arc_wake.rs rename to futures/tests/task_arc_wake.rs index 1940e4f98b..aedc15bcb8 100644 --- a/futures/tests/arc_wake.rs +++ b/futures/tests/task_arc_wake.rs @@ -1,4 +1,5 @@ use futures::task::{self, ArcWake, Waker}; +use std::panic; use std::sync::{Arc, Mutex}; struct CountingWaker { @@ -6,10 +7,8 @@ struct CountingWaker { } impl CountingWaker { - fn new() -> CountingWaker { - CountingWaker { - nr_wake: Mutex::new(0), - } + fn new() -> Self { + Self { nr_wake: Mutex::new(0) } } fn wakes(&self) -> i32 { @@ -25,7 +24,7 @@ impl ArcWake for CountingWaker { } #[test] -fn create_waker_from_arc() { +fn create_from_arc() { let some_w = Arc::new(CountingWaker::new()); let w1: Waker = task::waker(some_w.clone()); @@ -45,33 +44,36 @@ fn create_waker_from_arc() { assert_eq!(1, Arc::strong_count(&some_w)); } -struct PanicWaker; +#[test] +fn ref_wake_same() { + let some_w = Arc::new(CountingWaker::new()); -impl ArcWake for PanicWaker { - fn wake_by_ref(_arc_self: &Arc) { - panic!("WAKE UP"); - } + let w1: Waker = task::waker(some_w.clone()); + let w2 = task::waker_ref(&some_w); + let w3 = w2.clone(); + + assert!(w1.will_wake(&w2)); + assert!(w2.will_wake(&w3)); } #[test] fn proper_refcount_on_wake_panic() { + struct PanicWaker; + + impl ArcWake for PanicWaker { + fn wake_by_ref(_arc_self: &Arc) { + panic!("WAKE UP"); + } + } + let some_w = Arc::new(PanicWaker); let w1: Waker = task::waker(some_w.clone()); - assert_eq!("WAKE UP", *std::panic::catch_unwind(|| w1.wake_by_ref()).unwrap_err().downcast::<&str>().unwrap()); + assert_eq!( + "WAKE UP", + *panic::catch_unwind(|| w1.wake_by_ref()).unwrap_err().downcast::<&str>().unwrap() + ); assert_eq!(2, Arc::strong_count(&some_w)); // some_w + w1 drop(w1); assert_eq!(1, Arc::strong_count(&some_w)); // some_w } - -#[test] -fn waker_ref_wake_same() { - let some_w = Arc::new(CountingWaker::new()); - - let w1: Waker = task::waker(some_w.clone()); - let w2 = task::waker_ref(&some_w); - let w3 = w2.clone(); - - assert!(w1.will_wake(&w2)); - assert!(w2.will_wake(&w3)); -} diff --git a/futures/tests/atomic_waker.rs b/futures/tests/task_atomic_waker.rs similarity index 96% rename from futures/tests/atomic_waker.rs rename to futures/tests/task_atomic_waker.rs index d9ce753701..2d1612a45d 100644 --- a/futures/tests/atomic_waker.rs +++ b/futures/tests/task_atomic_waker.rs @@ -1,12 +1,12 @@ +use futures::executor::block_on; +use futures::future::poll_fn; +use futures::task::{AtomicWaker, Poll}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::sync::Arc; use std::thread; -use futures::executor::block_on; -use futures::future::poll_fn; -use futures::task::{AtomicWaker, Poll}; - +#[cfg_attr(miri, ignore)] // Miri is too slow #[test] fn basic() { let atomic_waker = Arc::new(AtomicWaker::new()); diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs new file mode 100644 index 0000000000..6adf51d8bb --- /dev/null +++ b/futures/tests/test_macro.rs @@ -0,0 +1,20 @@ +#[futures_test::test] +async fn it_works() { + let fut = async { true }; + assert!(fut.await); + + let fut = async { false }; + assert!(!fut.await); +} + +#[should_panic] +#[futures_test::test] +async fn it_is_being_run() { + let fut = async { false }; + assert!(fut.await); +} + +#[futures_test::test] +async fn return_ty() -> Result<(), ()> { + Ok(()) +} diff --git a/futures/tests/try_join.rs b/futures/tests/try_join.rs index 6c6d0843d5..0281ab897d 100644 --- a/futures/tests/try_join.rs +++ b/futures/tests/try_join.rs @@ -1,9 +1,9 @@ #![deny(unreachable_code)] -use futures::{try_join, executor::block_on}; +use futures::{executor::block_on, try_join}; // TODO: This abuses https://github.com/rust-lang/rust/issues/58733 in order to -// test behaviour of the `try_join!` macro with the never type before it is +// 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 { @@ -14,7 +14,6 @@ impl MyTrait for fn() -> T { } type Never = ! as MyTrait>::Output; - #[test] fn try_join_never_error() { block_on(async { diff --git a/futures/tests/try_join_all.rs b/futures/tests/try_join_all.rs deleted file mode 100644 index 662b866ec8..0000000000 --- a/futures/tests/try_join_all.rs +++ /dev/null @@ -1,44 +0,0 @@ -use futures_util::future::*; -use std::future::Future; -use futures::executor::block_on; -use std::fmt::Debug; - -fn assert_done(actual_fut: F, expected: T) -where - T: PartialEq + Debug, - F: FnOnce() -> Box + Unpin>, -{ - let output = block_on(actual_fut()); - assert_eq!(output, expected); -} - -#[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])); - // REVIEW: should this be implemented? - // assert_done(|| Box::new(try_join_all(Vec::::new())), Ok(vec![])); - - // TODO: needs more tests -} - -#[test] -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<'a>(bufs: Vec<&'a [u8]>) -> Box, ()>> + Unpin> { - let iter = bufs.into_iter().map(|b| ok::(b.len())); - Box::new(try_join_all(iter)) - } - - assert_done(|| sizes(vec![&[1,2,3], &[], &[0]]), Ok(vec![3 as usize, 0, 1])); -} - -#[test] -fn try_join_all_from_iter() { - assert_done( - || Box::new(vec![ok(1), ok(2)].into_iter().collect::>()), - Ok::<_, usize>(vec![1, 2]), - ) -} diff --git a/futures/tests_disabled/all.rs b/futures/tests_disabled/all.rs index 6c7e11cf7b..a7a571040a 100644 --- a/futures/tests_disabled/all.rs +++ b/futures/tests_disabled/all.rs @@ -1,27 +1,27 @@ -use futures::future; -use futures::executor::block_on; use futures::channel::oneshot::{self, Canceled}; +use futures::executor::block_on; +use futures::future; use std::sync::mpsc::{channel, TryRecvError}; -mod support; -use support::*; +// mod support; +// use support::*; fn unselect(r: Result, Either<(E, B), (E, A)>>) -> Result { match r { - Ok(Either::Left((t, _))) | - Ok(Either::Right((t, _))) => Ok(t), - Err(Either::Left((e, _))) | - Err(Either::Right((e, _))) => Err(e), + Ok(Either::Left((t, _))) | Ok(Either::Right((t, _))) => Ok(t), + Err(Either::Left((e, _))) | Err(Either::Right((e, _))) => Err(e), } } #[test] fn result_smoke() { fn is_future_v(_: C) - where A: Send + 'static, - B: Send + 'static, - C: Future - {} + where + A: Send + 'static, + B: Send + 'static, + C: Future, + { + } is_future_v::(f_ok(1).map(|a| a + 1)); is_future_v::(f_ok(1).map_err(|a| a + 1)); @@ -64,7 +64,9 @@ fn result_smoke() { #[test] fn test_empty() { - fn empty() -> Empty { future::empty() } + fn empty() -> Empty { + future::empty() + } assert_empty(|| empty()); assert_empty(|| empty().select(empty())); @@ -105,16 +107,22 @@ fn flatten() { #[test] fn smoke_oneshot() { - assert_done(|| { - let (c, p) = oneshot::channel(); - c.send(1).unwrap(); - p - }, Ok(1)); - assert_done(|| { - let (c, p) = oneshot::channel::(); - drop(c); - p - }, Err(Canceled)); + assert_done( + || { + let (c, p) = oneshot::channel(); + c.send(1).unwrap(); + p + }, + Ok(1), + ); + assert_done( + || { + let (c, p) = oneshot::channel::(); + drop(c); + p + }, + Err(Canceled), + ); let mut completes = Vec::new(); assert_empty(|| { let (a, b) = oneshot::channel::(); @@ -129,9 +137,7 @@ fn smoke_oneshot() { let (c, p) = oneshot::channel::(); drop(c); let (tx, rx) = channel(); - p.then(move |_| { - tx.send(()) - }).forget(); + p.then(move |_| tx.send(())).forget(); rx.recv().unwrap(); } @@ -139,8 +145,14 @@ fn smoke_oneshot() { fn select_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.select(d).then(unselect); // assert!(f.poll(&mut Task::new()).is_pending()); @@ -156,8 +168,14 @@ fn select_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.select(d).then(unselect); assert!(f.poll(lw).ok().unwrap().is_pending()); @@ -173,8 +191,14 @@ fn select_cancels() { fn join_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let mut f = b.join(d); drop(a); @@ -185,8 +209,14 @@ fn join_cancels() { let ((a, b), (c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, _brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |b| { btx.send(b).unwrap(); b }); - let d = d.map(move |d| { dtx.send(d).unwrap(); d }); + let b = b.map(move |b| { + btx.send(b).unwrap(); + b + }); + let d = d.map(move |d| { + dtx.send(d).unwrap(); + d + }); let (tx, rx) = channel(); let f = b.join(d); @@ -194,7 +224,8 @@ fn join_cancels() { tx.send(()).unwrap(); let res: Result<(), ()> = Ok(()); res - }).forget(); + }) + .forget(); assert!(rx.try_recv().is_err()); drop(a); rx.recv().unwrap(); @@ -243,7 +274,6 @@ fn join_incomplete() { }) } - #[test] fn select2() { assert_done(|| f_ok(2).select(empty()).then(unselect), Ok(2)); @@ -251,14 +281,15 @@ fn select2() { assert_done(|| f_err(2).select(empty()).then(unselect), Err(2)); assert_done(|| empty().select(f_err(2)).then(unselect), Err(2)); - assert_done(|| { - f_ok(1).select(f_ok(2)) - .map_err(|_| 0) - .and_then(|either_tup| { - let (a, b) = either_tup.into_inner(); - b.map(move |b| a + b) - }) - }, Ok(3)); + assert_done( + || { + f_ok(1).select(f_ok(2)).map_err(|_| 0).and_then(|either_tup| { + let (a, b) = either_tup.into_inner(); + b.map(move |b| a + b) + }) + }, + Ok(3), + ); // Finish one half of a select and then fail the second, ensuring that we // get the notification of the second one. @@ -297,8 +328,14 @@ fn select2() { { let ((_a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let f = b.select(d); drop(f); assert!(drx.recv().is_err()); @@ -309,8 +346,14 @@ fn select2() { { let ((_a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let mut f = b.select(d); let _res = noop_waker_lw(|lw| f.poll(lw)); drop(f); @@ -322,8 +365,14 @@ fn select2() { { let ((a, b), (_c, d)) = (oneshot::channel::(), oneshot::channel::()); let ((btx, brx), (dtx, drx)) = (channel(), channel()); - let b = b.map(move |v| { btx.send(v).unwrap(); v }); - let d = d.map(move |v| { dtx.send(v).unwrap(); v }); + let b = b.map(move |v| { + btx.send(v).unwrap(); + v + }); + let d = d.map(move |v| { + dtx.send(v).unwrap(); + v + }); let (tx, rx) = channel(); b.select(d).map(move |_| tx.send(()).unwrap()).forget(); drop(a); diff --git a/futures/tests_disabled/bilock.rs b/futures/tests_disabled/bilock.rs index c1bc33f507..0166ca48ba 100644 --- a/futures/tests_disabled/bilock.rs +++ b/futures/tests_disabled/bilock.rs @@ -1,11 +1,11 @@ -use futures::task; -use futures::stream; use futures::future; +use futures::stream; +use futures::task; use futures_util::lock::BiLock; use std::thread; -mod support; -use support::*; +// mod support; +// use support::*; #[test] fn smoke() { @@ -41,9 +41,9 @@ fn smoke() { }); assert!(task::spawn(future) - .poll_future_notify(¬ify_noop(), 0) - .expect("failure in poll") - .is_ready()); + .poll_future_notify(¬ify_noop(), 0) + .expect("failure in poll") + .is_ready()); } #[test] @@ -51,10 +51,7 @@ fn concurrent() { const N: usize = 10000; let (a, b) = BiLock::new(0); - let a = Increment { - a: Some(a), - remaining: N, - }; + let a = Increment { a: Some(a), remaining: N }; let b = stream::iter_ok(0..N).fold(b, |b, _n| { b.lock().map(|mut b| { *b += 1; @@ -89,7 +86,7 @@ fn concurrent() { fn poll(&mut self) -> Poll, ()> { loop { if self.remaining == 0 { - return Ok(self.a.take().unwrap().into()) + return Ok(self.a.take().unwrap().into()); } let a = self.a.as_ref().unwrap(); diff --git a/futures/tests_disabled/stream.rs b/futures/tests_disabled/stream.rs index 4eaf12e0d9..854dbad829 100644 --- a/futures/tests_disabled/stream.rs +++ b/futures/tests_disabled/stream.rs @@ -1,26 +1,26 @@ +use futures::channel::mpsc; +use futures::channel::oneshot; use futures::executor::{block_on, block_on_stream}; use futures::future::{err, ok}; use futures::stream::{empty, iter_ok, poll_fn, Peekable}; -use futures::channel::oneshot; -use futures::channel::mpsc; -mod support; -use support::*; +// mod support; +// use support::*; pub struct Iter { iter: I, } pub fn iter(i: J) -> Iter - where J: IntoIterator>, +where + J: IntoIterator>, { - Iter { - iter: i.into_iter(), - } + Iter { iter: i.into_iter() } } impl Stream for Iter - where I: Iterator>, +where + I: Iterator>, { type Item = T; type Error = E; @@ -34,21 +34,15 @@ impl Stream for Iter } } -fn list() -> Box + Send> { +fn list() -> Box + Send> { let (tx, rx) = mpsc::channel(1); - tx.send(Ok(1)) - .and_then(|tx| tx.send(Ok(2))) - .and_then(|tx| tx.send(Ok(3))) - .forget(); + tx.send(Ok(1)).and_then(|tx| tx.send(Ok(2))).and_then(|tx| tx.send(Ok(3))).forget(); Box::new(rx.then(|r| r.unwrap())) } -fn err_list() -> Box + Send> { +fn err_list() -> Box + Send> { let (tx, rx) = mpsc::channel(1); - tx.send(Ok(1)) - .and_then(|tx| tx.send(Ok(2))) - .and_then(|tx| tx.send(Err(3))) - .forget(); + tx.send(Ok(1)).and_then(|tx| tx.send(Ok(2))).and_then(|tx| tx.send(Err(3))).forget(); Box::new(rx.then(|r| r.unwrap())) } @@ -66,8 +60,8 @@ fn map_err() { struct FromErrTest(u32); impl From for FromErrTest { - fn from(i: u32) -> FromErrTest { - FromErrTest(i) + fn from(i: u32) -> Self { + Self(i) } } @@ -89,40 +83,31 @@ fn filter() { #[test] fn filter_map() { - assert_done(|| list().filter_map(|x| { - ok(if x % 2 == 0 { - Some(x + 10) - } else { - None - }) - }).collect(), Ok(vec![12])); + assert_done( + || list().filter_map(|x| ok(if x % 2 == 0 { Some(x + 10) } else { None })).collect(), + Ok(vec![12]), + ); } #[test] fn and_then() { assert_done(|| list().and_then(|a| Ok(a + 1)).collect(), Ok(vec![2, 3, 4])); - assert_done(|| list().and_then(|a| err::(a as u32)).collect::>(), - Err(1)); + assert_done(|| list().and_then(|a| err::(a as u32)).collect::>(), Err(1)); } #[test] fn then() { assert_done(|| list().then(|a| a.map(|e| e + 1)).collect(), Ok(vec![2, 3, 4])); - } #[test] fn or_else() { - assert_done(|| err_list().or_else(|a| { - ok::(a as i32) - }).collect(), Ok(vec![1, 2, 3])); + assert_done(|| err_list().or_else(|a| ok::(a as i32)).collect(), Ok(vec![1, 2, 3])); } #[test] fn flatten() { - assert_done(|| list().map(|_| list()).flatten().collect(), - Ok(vec![1, 2, 3, 1, 2, 3, 1, 2, 3])); - + assert_done(|| list().map(|_| list()).flatten().collect(), Ok(vec![1, 2, 3, 1, 2, 3, 1, 2, 3])); } #[test] @@ -132,9 +117,7 @@ fn skip() { #[test] fn skip_passes_errors_through() { - let mut s = block_on_stream( - iter(vec![Err(1), Err(2), Ok(3), Ok(4), Ok(5)]).skip(1) - ); + let mut s = block_on_stream(iter(vec![Err(1), Err(2), Ok(3), Ok(4), Ok(5)]).skip(1)); assert_eq!(s.next(), Some(Err(1))); assert_eq!(s.next(), Some(Err(2))); assert_eq!(s.next(), Some(Ok(4))); @@ -144,8 +127,7 @@ fn skip_passes_errors_through() { #[test] fn skip_while() { - assert_done(|| list().skip_while(|e| Ok(*e % 2 == 1)).collect(), - Ok(vec![2, 3])); + assert_done(|| list().skip_while(|e| Ok(*e % 2 == 1)).collect(), Ok(vec![2, 3])); } #[test] fn take() { @@ -154,8 +136,7 @@ fn take() { #[test] fn take_while() { - assert_done(|| list().take_while(|e| Ok(*e < 3)).collect(), - Ok(vec![1, 2])); + assert_done(|| list().take_while(|e| Ok(*e < 3)).collect(), Ok(vec![1, 2])); } #[test] @@ -193,9 +174,9 @@ fn buffered() { let (a, b) = oneshot::channel::(); let (c, d) = oneshot::channel::(); - tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) - .forget(); + tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) + .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) + .forget(); let mut rx = rx.buffered(2); sassert_empty(&mut rx); @@ -211,9 +192,9 @@ fn buffered() { let (a, b) = oneshot::channel::(); let (c, d) = oneshot::channel::(); - tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) - .forget(); + tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) + .and_then(|tx| tx.send(Box::new(d.map_err(|_| panic!())))) + .forget(); let mut rx = rx.buffered(1); sassert_empty(&mut rx); @@ -233,8 +214,8 @@ fn unordered() { let (c, d) = oneshot::channel::(); tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) - .forget(); + .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) + .forget(); let mut rx = rx.buffer_unordered(2); sassert_empty(&mut rx); @@ -250,8 +231,8 @@ fn unordered() { let (c, d) = oneshot::channel::(); tx.send(Box::new(b.recover(|_| panic!())) as Box + Send>) - .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) - .forget(); + .and_then(|tx| tx.send(Box::new(d.recover(|_| panic!())))) + .forget(); // We don't even get to see `c` until `a` completes. let mut rx = rx.buffer_unordered(1); @@ -267,21 +248,17 @@ fn unordered() { #[test] fn zip() { - assert_done(|| list().zip(list()).collect(), - Ok(vec![(1, 1), (2, 2), (3, 3)])); - assert_done(|| list().zip(list().take(2)).collect(), - Ok(vec![(1, 1), (2, 2)])); - assert_done(|| list().take(2).zip(list()).collect(), - Ok(vec![(1, 1), (2, 2)])); + assert_done(|| list().zip(list()).collect(), Ok(vec![(1, 1), (2, 2), (3, 3)])); + assert_done(|| list().zip(list().take(2)).collect(), Ok(vec![(1, 1), (2, 2)])); + assert_done(|| list().take(2).zip(list()).collect(), Ok(vec![(1, 1), (2, 2)])); assert_done(|| err_list().zip(list()).collect::>(), Err(3)); - assert_done(|| list().zip(list().map(|x| x + 1)).collect(), - Ok(vec![(1, 2), (2, 3), (3, 4)])); + assert_done(|| list().zip(list().map(|x| x + 1)).collect(), Ok(vec![(1, 2), (2, 3), (3, 4)])); } #[test] fn peek() { struct Peek { - inner: Peekable + Send>> + inner: Peekable + Send>>, } impl Future for Peek { @@ -299,15 +276,12 @@ fn peek() { } } - block_on(Peek { - inner: list().peekable(), - }).unwrap() + block_on(Peek { inner: list().peekable() }).unwrap() } #[test] fn wait() { - assert_eq!(block_on_stream(list()).collect::, _>>(), - Ok(vec![1, 2, 3])); + assert_eq!(block_on_stream(list()).collect::, _>>(), Ok(vec![1, 2, 3])); } #[test] @@ -337,8 +311,10 @@ fn forward() { let v = block_on(iter_ok::<_, Never>(vec![2, 3]).forward(v)).unwrap().1; assert_eq!(v, vec![0, 1, 2, 3]); - assert_done(move || iter_ok::<_, Never>(vec![4, 5]).forward(v).map(|(_, s)| s), - Ok(vec![0, 1, 2, 3, 4, 5])); + assert_done( + move || iter_ok::<_, Never>(vec![4, 5]).forward(v).map(|(_, s)| s), + Ok(vec![0, 1, 2, 3, 4, 5]), + ); } #[test] diff --git a/no_atomic_cas.rs b/no_atomic_cas.rs new file mode 100644 index 0000000000..9b05d4b9f4 --- /dev/null +++ b/no_atomic_cas.rs @@ -0,0 +1,13 @@ +// This file is @generated by no_atomic_cas.sh. +// It is not intended for manual editing. + +const NO_ATOMIC_CAS: &[&str] = &[ + "avr-unknown-gnu-atmega328", + "bpfeb-unknown-none", + "bpfel-unknown-none", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", + "thumbv4t-none-eabi", + "thumbv6m-none-eabi", +];