diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..708d20212 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Our contribution policy can be found at [async.rs/contribute][policy]. + +[policy]: https://async.rs/contribute/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c622a59bf..a3eb933dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,13 @@ on: pull_request: push: branches: + - main - staging - trying +env: + RUSTFLAGS: -Dwarnings + jobs: build_and_test: name: Build and test @@ -14,10 +18,10 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [nightly] + rust: [nightly, beta, stable, 1.63] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: Install ${{ matrix.rust }} uses: actions-rs/toolchain@v1 @@ -29,35 +33,177 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --benches --bins --examples --tests + args: --all --bins --tests - name: check unstable uses: actions-rs/cargo@v1 with: command: check - args: --features unstable --all --benches --bins --examples --tests + args: --features unstable --all --bins --examples --tests + + - name: check wasm + uses: actions-rs/cargo@v1 + with: + command: check + target: wasm32-unknown-unknown + override: true + args: --features unstable --all --bins --tests + + - name: check bench + uses: actions-rs/cargo@v1 + if: matrix.rust == 'nightly' + with: + command: check + args: --benches + + - name: check std only + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features --features std + + - name: check attributes + uses: actions-rs/cargo@v1 + with: + command: check + args: --features attributes + + - name: build unstable only + uses: actions-rs/cargo@v1 + with: + command: build + args: --no-default-features --features unstable - name: tests uses: actions-rs/cargo@v1 with: command: test - args: --all --features unstable + args: --all --features "unstable attributes" - check_fmt_and_docs: - name: Checking fmt and docs + build__with_no_std: + name: Build with no-std + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: setup + run: | + rustup default nightly + rustup target add thumbv7m-none-eabi + + - name: check no_std + uses: actions-rs/cargo@v1 + with: + command: check + args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps + + check_tokio_02_feature: + name: Check tokio02 feature + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: check tokio02 + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --features tokio02 + + check_io_safety_feature: + name: Check io_safety feature + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v3 + - name: check io_safety + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --features io_safety + + + cross: + name: Cross compile + runs-on: ubuntu-latest + strategy: + matrix: + target: + - i686-unknown-linux-gnu + - powerpc-unknown-linux-gnu + - powerpc64-unknown-linux-gnu +# - mips-unknown-linux-gnu + - arm-linux-androideabi + + steps: + - uses: actions/checkout@v3 + + - name: Install nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Install cross + run: cargo install cross + + - name: check + run: cross check --all --target ${{ matrix.target }} + + - name: check unstable + run: cross check --all --features unstable --target ${{ matrix.target }} + + - name: test + run: cross test --all --features unstable --target ${{ matrix.target }} + + check_wasm: + name: Check wasm targets runs-on: ubuntu-latest + strategy: + matrix: + rust: [nightly, beta, stable] + steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - - id: component - uses: actions-rs/components-nightly@v1 + - name: Install rust with wasm32-unknown-unknown + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: wasm32-unknown-unknown + override: true + + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: ~/.cargo/registry + key: wasm32-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }} + + - name: check + uses: actions-rs/cargo@v1 + with: + command: check + args: --target wasm32-unknown-unknown + + - name: check unstable + uses: actions-rs/cargo@v1 with: - component: rustfmt + command: check + args: --target wasm32-unknown-unknown --tests --all --features unstable + + check_fmt_and_docs: + name: Checking fmt and docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: ${{ steps.component.outputs.toolchain }} + profile: minimal + toolchain: nightly override: true + components: rustfmt - name: setup run: | @@ -73,21 +219,3 @@ jobs: - name: Docs run: cargo doc --features docs - - clippy_check: - name: Clippy check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - id: component - uses: actions-rs/components-nightly@v1 - with: - component: clippy - - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ steps.component.outputs.toolchain }} - override: true - - run: rustup component add clippy - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e735a1b..ebb3ffd80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,673 @@ All notable changes to async-std will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). -## [Unreleased] +# [1.13.1] - 2025-02-21 + +`async-std` has officially been discontinued. We recommend that all users and +libraries migrate to the excellent [`smol`](https://github.com/smol-rs/smol/) +project. + +# [1.13.0] - 2024-09-06 + +## Added +- IO Safety traits implementations + +## Changed +- Various dependencies updates +- Export `BufReadExt` and `SeekExt` from `async_std::io` + +# [1.12.0] - 2022-06-18 + +## Added +- `async_std::task::spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation. +- Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode. + +## Changed +- async-std no longer depends on `num_cpus`; it uses functionality in the standard library instead (via `async-global-executor`). +- Miscellaneous documentation fixes and cleanups. + +# [1.11.0] - 2022-03-22 + +This release improves compile times by up to 55% on initial builds, and up to 75% on recompilation. Additionally we've added a few new APIs and made some tweaks. + +## Added +- `TcpListener::into_incoming` to convert a `TcpListener` into a stream of incoming TCP connections + +## Removed +- The internal `extension_trait` macro had been removed. This drastically improves compile times for `async-std`, but changes the way our documentation is rendered. This is a cosmetic change only, and all existing code should continue to work as it did before. + +## Changed +- Some internal code has been de-macro-ified, making for quicker compile times. +- We now use the default recursion limit. + +## Docs +- Several docs improvements / fixes. + +# [1.10.0] - 2021-08-25 + +This release comes with an assortment of small features and fixes. + +## Added +- `File` now implements `Clone` so that `File`s can be passed into closures for use in `spawn_blocking`. + - `File`'s contents are already wrapped in `Arc`s, so the implementation of `Clone` is straightforward. +- `task::try_current()` which returns a handle to the current task if called within the context of a task created by async-std. +- `async_std::io` now re-exports `WriteExt` directly. + +## Fixed +- `write!` now takes already written bytes into account on `File`. + +## Internal +- `TcpStream` now properly makes use of vectored IO. +- The `net::*::Incoming` implementations now do less allocation. + +## Docs +- Several docs improvements / fixes. + +# [1.9.0] - 2021-01-15 + +This patch stabilizes the `async_std::channel` submodule, removes the +deprecated `sync::channel` types, and introduces the `tokio1` feature. + +## New Channels + +As part of our `1.8.0` release last month we introduced the new +`async_std::channel` submodule and deprecated the unstable +`async_std::sync::channel` types. You can read our full motivation for this +change in the last patch notes. But the short version is that the old +channels had some fundamental problems, and the `sync` submodule is a bit of +a mess. + +This release of `async-std` promotes `async_std::channel` to stable, and +fully removes the `async_std::sync::channel` types. In practice many +libraries have already been upgraded to the new channels in the past month, +and this will enable much of the ecosystem to switch off "unstable" versions +of `async-std`. + +```rust +use async_std::channel; + +let (sender, receiver) = channel::unbounded(); + +assert_eq!(sender.send("Hello").await, Ok(())); +assert_eq!(receiver.recv().await, Ok("Hello")); +``` + +## Tokio 1.0 compat + +The Tokio project recently released version 1.0 of their runtime, and the +async-std team would like to congratulate the Tokio team on achieving this +milestone. + +This release of `async-std` adds the `tokio1` feature flag, enabling Tokio's +TLS constructors to be initialized within the `async-std` runtime. This is in +addition to the `tokio02` and `tokio03` feature flags which we were already +exposing. + +In terms of stability it's worth noting that we will continue to provide +support for the `tokio02`, `tokio03`, and `tokio1` on the current major +release line of `async-std`. These flags are part of our public API, and +removing compat support for older Tokio versions is considered a breaking +change. + +## Added + +- Added the `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924)) +- Stabilized the `async_std::channel` submodule ([#934](https://github.com/async-rs/async-std/pull/934)) + +## Removed + +- Removed deprecated `sync::channel` ([#933](https://github.com/async-rs/async-std/pull/933)) + +## Fixed + +- Fixed a typo for [sic] `FuturesExt` trait ([#930](https://github.com/async-rs/async-std/pull/930)) +- Update the link to `cargo-edit` in the installation section of the docs ([#932](https://github.com/async-rs/async-std/pull/932)) +- Fixed a small typo for stream ([#926](https://github.com/async-rs/async-std/pull/926)) + +## Internal + +- Updated `rand` to 0.8 ([#923](https://github.com/async-rs/async-std/pull/923)) +- Migrated `RwLock` and `Barrier` to use the `async-lock` crate internally ([#925](https://github.com/async-rs/async-std/pull/925)) +- Replaced uses of deprecated the `compare_and_swap` method with `compare_exchange` ([#927](https://github.com/async-rs/async-std/pull/927)) + +# [1.8.0] - 2020-12-04 + +This patch introduces `async_std::channel`, a new submodule for our async channels implementation. `channels` have been one of async-std's most requested features, and have existed as "unstable" for the past year. We've been cautious about stabilizing channels, and this caution turned out to be warranted: we realized our channels could hang indefinitely under certain circumstances, and people ended up expressing a need for unbounded channels. + +So today we're introducing the new `async_std::channel` submodule which exports the `async-channel` crate, and we're marking the older unstable `async_std::sync::channel` API as "deprecated". This release includes both APIs, but we intend to stabilize `async_std::channel` and remove the older API in January. This should give dependent projects a month to upgrade, though we can extend that if it proves to be too short. + +The rationale for adding a new top-level `channel` submodule, rather than extending `sync` is that the `std::sync` and `async_std::sync` submodule are a bit of a mess, and the libs team [has been talking about splitting `std::sync` up]([https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478](https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478)) into separate modules. The stdlib has to guarantee it'll forever be backwards compatible, but `async-std` does not (we fully expect a 2.0 once we have async closures & traits). So we're experimenting with this change before `std` does, with the expectation that this change can serve as a data point when the libs team decides how to proceed in std. + +### Added + +- `async_std::channel` as "unstable" #915 +- `async_std::process` as "unstable" #916 + +### Fixed + +- Fixed mentions of the `tokio03` flags in the docs #909 +- Fixed a double drop issue in `StreamExt::cycle` #903 + +### Internal + +- updated `pin-project` to `v0.2.0` + +# [1.7.0] - 2020-10-30 + +This patch adds a feature to enable compatibility with the new `tokio` 0.3.0 +release, and updates internal dependencies. + +## Added + +- Add `tokio03` feature ([#895](https://github.com/async-rs/async-std/pull/895)) + +## Internal + +- chore: update dependencies ([#897](https://github.com/async-rs/async-std/pull/897)) + +# [1.6.5] - 2020-09-28 + +## Fixed + +- Fix `TcpListener::incoming`. ([#889](https://github.com/async-rs/async-std/pull/889)) +- Fix tokio compatibility flag. ([#882](https://github.com/async-rs/async-std/pull/882)) + +# [1.6.4] - 2020-09-16 + +## Added + +- Added `UdpSocket::peek` and `UdpSocket::peek_from` ([#853](https://github.com/async-rs/async-std/pull/853)) + +## Changed + +- Extracted the executor into [async-global-executor](https://github.com/async-rs/async-global-executor) ([#867](https://github.com/async-rs/async-std/pull/867)) + +- Updated various dependencies + +## Fixed + +- Ensure `UnixStream::into_raw_fd` doesn't close the file descriptor ([#855](https://github.com/async-rs/async-std/issues/855)) +- Fixed wasm builds and ensured better dependency management depending on the compile target ([#863](https://github.com/async-rs/async-std/pull/863)) + + +# [1.6.3] - 2020-07-31 + +## Added + +## Changed + +- Switched from smol to individual executor parts. ([#836](https://github.com/async-rs/async-std/pull/836)) +- Replaced internal `Mutex` implementation with `async-mutex`. ([#822](https://github.com/async-rs/async-std/pull/822)) + +## Fixed + +- Added missing `Send` guards to `Stream::collect`. ([#665](https://github.com/async-rs/async-std/pull/665)) + + +# [1.6.2] - 2020-06-19 + +## Added + +- Add `UdpSocket::peer_addr` ([#816](https://github.com/async-rs/async-std/pull/816)) + +## Changed + +## Fixed + +- Ensure the reactor is running for sockets and timers ([#819](https://github.com/async-rs/async-std/pull/819)). +- Avoid excessive polling in `flatten` and `flat_map` ([#701](https://github.com/async-rs/async-std/pull/701)) + + +# [1.6.1] - 2020-06-11 + +## Added + +- Added `tokio02` feature flag, to allow compatibility usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)). + +## Changed + +- Removed unstable `stdio` lock methods, due to their unsoundness ([#807](https://github.com/async-rs/async-std/pull/807)). + +## Fixed + +- Fixed wrong slice index for file reading ([#802](https://github.com/async-rs/async-std/pull/802)). +- Fixed recursive calls to `block_on` ([#799](https://github.com/async-rs/async-std/pull/799)) and ([#809](https://github.com/async-rs/async-std/pull/809)). +- Remove `default` feature requirement for the `unstable` feature ([#806](https://github.com/async-rs/async-std/pull/806)). + +# [1.6.0] - 2020-05-22 + +See `1.6.0-beta.1` and `1.6.0-beta.2`. + +# [1.6.0-beta.2] - 2020-05-19 + +## Added + +- Added an environment variable to configure the thread pool size of the runtime. ([#774](https://github.com/async-rs/async-std/pull/774)) +- Implement `Clone` for `UnixStream` ([#772](https://github.com/async-rs/async-std/pull/772)) + +## Changed + +- For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776)) + +## Fixed + +- Use `smol::block_on` to handle drop of `File`, avoiding nested executor panic. ([#768](https://github.com/async-rs/async-std/pull/768)) + +# [1.6.0-beta.1] - 2020-05-07 + +## Added + +- Added `task::spawn_local`. ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added out of the box support for `wasm`. ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added `JoinHandle::cancel` ([#757](https://github.com/async-rs/async-std/pull/757)) +- Added `sync::Condvar` ([#369](https://github.com/async-rs/async-std/pull/369)) +- Added `sync::Sender::try_send` and `sync::Receiver::try_recv` ([#585](https://github.com/async-rs/async-std/pull/585)) +- Added `no_std` support for `task`, `future` and `stream` ([#680](https://github.com/async-rs/async-std/pull/680)) + +## Changed + +- Switched underlying runtime to [`smol`](https://github.com/stjepang/smol/). ([#757](https://github.com/async-rs/async-std/pull/757)) +- Switched implementation of `sync::Barrier` to use `sync::Condvar` like `std` does. ([#581](https://github.com/async-rs/async-std/pull/581)) + +## Fixed + +- Allow compilation on 32 bit targets, by using `AtomicUsize` for `TaskId`. ([#756](https://github.com/async-rs/async-std/pull/756)) + +# [1.5.0] - 2020-02-03 + +[API Documentation](https://docs.rs/async-std/1.5.0/async-std) + +This patch includes various quality of life improvements to async-std. +Including improved performance, stability, and the addition of various +`Clone` impls that replace the use of `Arc` in many cases. + +## Added + +- Added links to various ecosystem projects from the README ([#660](https://github.com/async-rs/async-std/pull/660)) +- Added an example on `FromStream` for `Result` ([#643](https://github.com/async-rs/async-std/pull/643)) +- Added `stream::pending` as "unstable" ([#615](https://github.com/async-rs/async-std/pull/615)) +- Added an example of `stream::timeout` to document the error flow ([#675](https://github.com/async-rs/async-std/pull/675)) +- Implement `Clone` for `DirEntry` ([#682](https://github.com/async-rs/async-std/pull/682)) +- Implement `Clone` for `TcpStream` ([#689](https://github.com/async-rs/async-std/pull/689)) + +## Changed + +- Removed internal comment on `stream::Interval` ([#645](https://github.com/async-rs/async-std/pull/645)) +- The "unstable" feature can now be used without requiring the "default" feature ([#647](https://github.com/async-rs/async-std/pull/647)) +- Removed unnecessary trait bound on `stream::FlatMap` ([#651](https://github.com/async-rs/async-std/pull/651)) +- Updated the "broadcaster" dependency used by "unstable" to `1.0.0` ([#681](https://github.com/async-rs/async-std/pull/681)) +- Updated `async-task` to 1.2.1 ([#676](https://github.com/async-rs/async-std/pull/676)) +- `task::block_on` now parks after a single poll, improving performance in many cases ([#684](https://github.com/async-rs/async-std/pull/684)) +- Improved reading flow of the "client" part of the async-std tutorial ([#550](https://github.com/async-rs/async-std/pull/550)) +- Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` ([#667](https://github.com/async-rs/async-std/pull/667)) +- `TcpStream::connect` no longer uses a thread from the threadpool, improving performance ([#687](https://github.com/async-rs/async-std/pull/687)) + +## Fixed + +- Fixed crate documentation typo ([#655](https://github.com/async-rs/async-std/pull/655)) +- Fixed documentation for `UdpSocket::recv` ([#648](https://github.com/async-rs/async-std/pull/648)) +- Fixed documentation for `UdpSocket::send` ([#671](https://github.com/async-rs/async-std/pull/671)) +- Fixed typo in stream documentation ([#650](https://github.com/async-rs/async-std/pull/650)) +- Fixed typo on `sync::JoinHandle` documentation ([#659](https://github.com/async-rs/async-std/pull/659)) +- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/661)) +- Removed the use of rustfmt's unstable `format_code_in_doc_comments` option which failed CI ([#685](https://github.com/async-rs/async-std/pull/685)) +- Fixed a code typo in the `task::sleep` example ([#688](https://github.com/async-rs/async-std/pull/688)) + +# [1.4.0] - 2019-12-20 + +[API Documentation](https://docs.rs/async-std/1.4.0/async-std) + +This patch adds `Future::timeout`, providing a method counterpart to the +`future::timeout` free function. And includes several bug fixes around missing +APIs. Notably we're not shipping our new executor yet, first announced [on our +blog](https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/). + +## Examples + +```rust +use async_std::prelude::*; +use async_std::future; +use std::time::Duration; + +let fut = future::pending::<()>(); // This future will never resolve. +let res = fut.timeout(Duration::from_millis(100)).await; +assert!(res.is_err()); // The future timed out, returning an err. +``` + +## Added + +- Added `Future::timeout` as "unstable" [(#600)](https://github.com/async-rs/async-std/pull/600) + +## Fixes + +- Fixed a doc test and enabled it on CI [(#597)](https://github.com/async-rs/async-std/pull/597) +- Fixed a rendering issue with the `stream` submodule documentation [(#621)](https://github.com/async-rs/async-std/pull/621) +- `Write::write_fmt`'s future is now correctly marked as `#[must_use]` [(#628)](https://github.com/async-rs/async-std/pull/628) +- Fixed the missing `io::Bytes` export [(#633)](https://github.com/async-rs/async-std/pull/633) +- Fixed the missing `io::Chain` export [(#633)](https://github.com/async-rs/async-std/pull/633) +- Fixed the missing `io::Take` export [(#633)](https://github.com/async-rs/async-std/pull/633) + +# [1.3.0] - 2019-12-12 + +[API Documentation](https://docs.rs/async-std/1.3.0/async-std) + +This patch introduces `Stream::delay`, more methods on `DoubleEndedStream`, +and improves compile times. `Stream::delay` is a new API that's similar to +[`task::sleep`](https://docs.rs/async-std/1.2.0/async_std/task/fn.sleep.html), +but can be passed as part of as stream, rather than as a separate block. This is +useful for examples, or when manually debugging race conditions. + +## Examples + +```rust +let start = Instant::now(); +let mut s = stream::from_iter(vec![0u8, 1]).delay(Duration::from_millis(200)); + +// The first time will take more than 200ms due to delay. +s.next().await; +assert!(start.elapsed().as_millis() >= 200); + +// There will be no delay after the first time. +s.next().await; +assert!(start.elapsed().as_millis() <= 210); +``` + +## Added + +- Added `Stream::delay` as "unstable" [(#309)](https://github.com/async-rs/async-std/pull/309) +- Added `DoubleEndedStream::next_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::nth_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::rfind` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- Added `DoubleEndedStream::try_rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562) +- `stream::Once` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562) +- `stream::FromIter` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562) + +## Changed + +- Removed our dependency on `async-macros`, speeding up compilation [(#610)](https://github.com/async-rs/async-std/pull/610) + +## Fixes + +- Fixed a link in the task docs [(#598)](https://github.com/async-rs/async-std/pull/598) +- Fixed the `UdpSocket::recv` example [(#603)](https://github.com/async-rs/async-std/pull/603) +- Fixed a link to `task::block_on` [(#608)](https://github.com/async-rs/async-std/pull/608) +- Fixed an incorrect API mention in `task::Builder` [(#612)](https://github.com/async-rs/async-std/pull/612) +- Fixed leftover mentions of `futures-preview` [(#595)](https://github.com/async-rs/async-std/pull/595) +- Fixed a typo in the tutorial [(#614)](https://github.com/async-rs/async-std/pull/614) +- `::poll_close` now closes the write half of the stream [(#618)](https://github.com/async-rs/async-std/pull/618) + +# [1.2.0] - 2019-11-27 + +[API Documentation](https://docs.rs/async-std/1.2.0/async-std) + +This patch includes some minor quality-of-life improvements, introduces a +new `Stream::unzip` API, and adds verbose errors to our networking types. + +This means if you can't connect to a socket, you'll never have to wonder again +*which* address it was you couldn't connect to, instead of having to go through +the motions to debug what the address was. + +## Example + +Unzip a stream of tuples into two collections: + +```rust +use async_std::prelude::*; +use async_std::stream; + +let s = stream::from_iter(vec![(1,2), (3,4)]); + +let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; + +assert_eq!(left, [1, 3]); +assert_eq!(right, [2, 4]); +``` + +## Added + +- Added `Stream::unzip` as "unstable". +- Added verbose errors to the networking types. + +## Changed + +- Enabled CI. +- `Future::join` and `Future::try_join` can now join futures with different + output types. + +## Fixed + +- Fixed the docs and `Debug` output of `BufWriter`. +- Fixed a bug in `Stream::throttle` that made it consume too much CPU. + +# [1.1.0] - 2019-11-21 + +[API Documentation](https://docs.rs/async-std/1.1.0/async-std) + +This patch introduces a faster scheduler algorithm, `Stream::throttle`, and +stabilizes `task::yield_now`. Additionally we're introducing several more stream +APIs, bringing us to almost complete parity with the standard library. + +Furthermore our `path` submodule now returns more context in errors. So if +opening a file fails, async-std will tell you *which* file was failed to open, +making it easier to write and debug programs. + +## Examples + +```rust +let start = Instant::now(); + +let mut s = stream::interval(Duration::from_millis(5)) + .throttle(Duration::from_millis(10)) + .take(2); + +s.next().await; +assert!(start.elapsed().as_millis() >= 5); + +s.next().await; +assert!(start.elapsed().as_millis() >= 15); + +s.next().await; +assert!(start.elapsed().as_millis() >= 25); +``` + +## Added + +- Added `Stream::throttle` as "unstable". +- Added `Stream::count` as "unstable". +- Added `Stream::max` as "unstable". +- Added `Stream::successors` as "unstable". +- Added `Stream::by_ref` as "unstable". +- Added `Stream::partition` as "unstable". +- Added contextual errors to the `path` submodule. +- Added `os::windows::symlink_dir` as "unstable". +- Added `os::windows::symlink_file` as "unstable". +- Stabilized `task::yield_now`. + +## Fixes + +- We now ignore seek errors when rolling back failed `read` calls on `File`. +- Fixed a bug where `Stream::max_by_key` was returning the wrong result. +- Fixed a bug where `Stream::min_by_key` was returning the wrong result. + +## Changed + +- Applied various fixes to the tutorial. +- Fixed an issue with Clippy. +- Optimized an internal code generation macro, improving compilation speeds. +- Removed an `Unpin` bound from `stream::Once`. +- Removed various extra internal uses of `pin_mut!`. +- Simplified `Stream::any` and `Stream::all`'s internals. +- The `surf` example is now enabled again. +- Tweaked some streams internals. +- Updated `futures-timer` to 2.0.0, improving compilation speed. +- Upgraded `async-macros` to 2.0.0. +- `Stream::merge` now uses randomized ordering to reduce overall latency. +- The scheduler is now more efficient by keeping a slot for the next task to + run. This is similar to Go's scheduler, and Tokio's scheduler. +- Fixed the documentation of the `channel` types to link back to the `channel` + function. + +# [1.0.1] - 2019-11-12 + +[API Documentation](https://docs.rs/async-std/1.0.1/async-std) + +We were seeing a regression in our fs performance, caused by too many +long-running tasks. This patch fixes that regression by being more proactive +about closing down idle threads. + +## Changes + +- Improved thread startup/shutdown algorithm in `task::spawn_blocking`. +- Fixed a typo in the tutorial. + +# [1.0.0] - 2019-11-11 + +[API Documentation](https://docs.rs/async-std/1.0.0/async-std) + +This release marks the `1.0.0` release of async-std; a major milestone for our +development. This release itself mostly includes quality of life improvements +for all of modules, including more consistent API bounds for a lot of our +submodules. + +The biggest change is that we're now using the full semver range, +`major.minor.patch`, and any breaking changes to our "stable" APIs will require +an update of the `major` number. + +We're excited we've hit this milestone together with you all. Thank you! + +## Added + +- Added `Future::join` as "unstable", replacing `future::join!`. +- Added `Future::try_join` as "unstable", replacing `future::try_join!`. +- Enabled `stable` and `beta` channel testing on CI. +- Implemented `FromIterator` and `Extend` for `PathBuf`. +- Implemented `FromStream` for `PathBuf`. +- Loosened the trait bounds of `io::copy` on "unstable". + +## Changed + +- Added a `Sync` bound to `RwLock`, resolving a memory safety issue. +- Fixed a bug in `Stream::take_while` where it could continue after it should've + ended. +- Fixed a bug where our `attributes` Cargo feature wasn't working as intended. +- Improved documentation of `Stream::merge`, documenting ordering guarantees. +- Update doc imports in examples to prefer async-std's types. +- Various quality of life improvements to the `future` submodule. +- Various quality of life improvements to the `path` submodule. +- Various quality of life improvements to the `stream` submodule. + +## Removed + +- Removed `future::join!` in favor of `Future::join`. +- Removed `future::try_join!` in favor of `Future::try_join`. + +# [0.99.12] - 2019-11-07 + +[API Documentation](https://docs.rs/async-std/0.99.12/async-std) + +This patch upgrades us to `futures` 0.3, support for `async/await` on Rust +Stable, performance improvements, and brand new module-level documentation. + +## Added + +- Added `Future::flatten` as "unstable". +- Added `Future::race` as "unstable" (replaces `future::select!`). +- Added `Future::try_race` as "unstable" (replaces `future::try_select!`). +- Added `Stderr::lock` as "unstable". +- Added `Stdin::lock` as "unstable". +- Added `Stdout::lock` as "unstable". +- Added `Stream::copied` as "unstable". +- Added `Stream::eq` as "unstable". +- Added `Stream::max_by_key` as "unstable". +- Added `Stream::min` as "unstable". +- Added `Stream::ne` as "unstable". +- Added `Stream::position` as "unstable". +- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`. +- Added `TcpListener` and `TcpStream` integration tests. +- Added `stream::from_iter`. +- Added `sync::WakerSet` for internal use. +- Added an example to handle both `IP v4` and `IP v6` connections. +- Added the `default` Cargo feature. +- Added the `attributes` Cargo feature. +- Added the `std` Cargo feature. + +## Changed + +- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread. +- Fixed a bug with `Stream::merge` where sometimes it ended too soon. +- Fixed a bug with our GitHub actions setup. +- Fixed an issue where our channels could spuriously deadlock. +- Refactored the `task` module. +- Removed a deprecated GitHub action. +- Replaced `futures-preview` with `futures`. +- Replaced `lazy_static` with `once_cell`. +- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`. +- Simplified `sync::RwLock` using the internal `sync::WakerSet` type. +- Updated the `path` submodule documentation to match std. +- Updated the mod-level documentation to match std. + +## Removed + +- Removed `future::select!` (replaced by `Future::race`). +- Removed `future::try_select!` (replaced by `Future::try_race`). + +# [0.99.11] - 2019-10-29 + +This patch introduces `async_std::sync::channel`, a novel asynchronous port of +the ultra-fast Crossbeam channels. This has been one of the most anticipated +features for async-std, and we're excited to be providing a first version of +this! + +In addition to channels, this patch has the regular list of new methods, types, +and doc fixes. + +## Examples + +__Send and receive items from a channel__ +```rust +// Create a bounded channel with a max-size of 1 +let (s, r) = channel(1); + +// This call returns immediately because there is enough space in the channel. +s.send(1).await; + +task::spawn(async move { + // This call blocks the current task because the channel is full. + // It will be able to complete only after the first message is received. + s.send(2).await; +}); + +// Receive items from the channel +task::sleep(Duration::from_secs(1)).await; +assert_eq!(r.recv().await, Some(1)); +assert_eq!(r.recv().await, Some(2)); +``` + +## Added +- Added `Future::delay` as "unstable" +- Added `Stream::flat_map` as "unstable" +- Added `Stream::flatten` as "unstable" +- Added `Stream::product` as "unstable" +- Added `Stream::sum` as "unstable" +- Added `Stream::min_by_key` +- Added `Stream::max_by` +- Added `Stream::timeout` as "unstable" +- Added `sync::channel` as "unstable". +- Added doc links from instantiated structs to the methods that create them. +- Implemented `Extend` + `FromStream` for `PathBuf`. + +## Changed +- Fixed an issue with `block_on` so it works even when nested. +- Fixed issues with our Clippy check on CI. +- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase. +- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs). +- Updated the module-level documentation for `stream` and `sync`. +- Various typos and grammar fixes. +- Removed redundant file flushes, improving the performance of `File` operations + +## Removed +Nothing was removed in this release. # [0.99.10] - 2019-10-16 @@ -35,9 +701,9 @@ use async_std::prelude::*; use async_std::task; task::spawn(async { - let x = fibonnacci(1000); // Do expensive work + let x = fibonacci(1000); // Do expensive work task::yield_now().await; // Allow other tasks to run - x + fibonnacci(100) // Do more work + x + fibonacci(100) // Do more work }) ``` @@ -281,7 +947,25 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v1.7.0...HEAD +[1.7.0]: https://github.com/async-rs/async-std/compare/v1.6.5...1.7.0 +[1.6.5]: https://github.com/async-rs/async-std/compare/v1.6.4...v1.6.5 +[1.6.4]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4 +[1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3 +[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2 +[1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1 +[1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0 +[1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2 +[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1 +[1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0 +[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0 +[0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12 +[0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11 [0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10 [0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8 diff --git a/Cargo.toml b/Cargo.toml index 2741a19af..2e944394b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,55 +1,115 @@ [package] name = "async-std" -version = "0.99.10" +version = "1.13.2" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", + "Friedel Ziegelmayer ", "Contributors to async-std", ] edition = "2018" -license = "Apache-2.0/MIT" +rust-version = "1.63" +license = "Apache-2.0 OR MIT" repository = "https://github.com/async-rs/async-std" -homepage = "https://github.com/async-rs/async-std" -documentation = "https://docs.rs/async-std" -description = "Async version of the Rust standard library" +homepage = "https://async.rs" +description = "Deprecated in favor of `smol` - Async version of the Rust standard library" keywords = ["async", "await", "future", "std", "task"] categories = ["asynchronous", "concurrency", "network-programming"] -readme = "README.md" [package.metadata.docs.rs] features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -docs = ["unstable"] -unstable = ["broadcaster"] +default = [ + "std", + "async-global-executor", + "async-io", + "futures-lite", + "kv-log-macro", + "log", + "pin-project-lite", + "gloo-timers", +] +docs = ["attributes", "unstable", "default"] +unstable = [ + "std", + "async-io", + "async-process", +] +attributes = ["async-attributes"] +std = [ + "alloc", + "crossbeam-utils", + "futures-core/std", + "futures-io", + "memchr", + "once_cell", + "pin-utils", + "slab", + "wasm-bindgen-futures", + "futures-channel", + "async-channel", + "async-lock", +] +alloc = [ + "futures-core/alloc", + "pin-project-lite", +] +tokio1 = ["async-global-executor/tokio"] +tokio02 = ["async-global-executor/tokio02"] +tokio03 = ["async-global-executor/tokio03"] +io_safety = [] [dependencies] -async-macros = "1.0.0" -async-task = "1.0.0" -crossbeam-channel = "0.3.9" -crossbeam-deque = "0.7.1" -crossbeam-utils = "0.6.6" -futures-core-preview = "=0.3.0-alpha.19" -futures-io-preview = "=0.3.0-alpha.19" -futures-timer = "1.0.2" -lazy_static = "1.4.0" -log = { version = "0.4.8", features = ["kv_unstable"] } -memchr = "2.2.1" -mio = "0.6.19" -mio-uds = "0.6.7" -num_cpus = "1.10.1" -pin-utils = "0.1.0-alpha.4" -slab = "0.4.2" -kv-log-macro = "1.0.4" -broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } +async-attributes = { version = "1.1.2", optional = true } +async-lock = { version = "3.1.0", optional = true } +crossbeam-utils = { version = "0.8.0", optional = true } +futures-core = { version = "0.3.4", optional = true, default-features = false } +futures-io = { version = "0.3.4", optional = true } +kv-log-macro = { version = "1.0.6", optional = true } +log = { version = "0.4.8", features = ["kv_unstable"], optional = true } +memchr = { version = "2.3.3", optional = true } +once_cell = { version = "1.3.1", optional = true } +pin-project-lite = { version = "0.2.0", optional = true } +pin-utils = { version = "0.1.0-alpha.4", optional = true } +slab = { version = "0.4.2", optional = true } +async-channel = { version = "1.8.0", optional = true } + +# dev dependency, but they are not allowed to be optional :/ +surf = { version = "2.0.0", optional = true } + + +[target.'cfg(not(target_os = "unknown"))'.dependencies] +async-global-executor = { version = "2.4.0", optional = true, features = ["async-io"] } +async-io = { version = "2.2.0", optional = true } +futures-lite = { version = "2.0.0", optional = true } +async-process = { version = "2.0.0", optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +gloo-timers = { version = "0.3.0", features = ["futures"], optional = true } +wasm-bindgen-futures = { version = "0.4.10", optional = true } +futures-channel = { version = "0.3.4", optional = true } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3.10" +getrandom = { version = "0.2.0", features = ["js"] } [dev-dependencies] -femme = "1.2.0" -# surf = "1.0.2" -tempdir = "0.3.7" -futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } - -# These are used by the book for examples -futures-channel-preview = "=0.3.0-alpha.19" -futures-util-preview = "=0.3.0-alpha.19" +femme = "2.1.1" +rand = "0.8.0" +tempfile = "3.1.0" +futures = "0.3.4" +rand_xorshift = "0.3.0" + +[[test]] +name = "stream" +required-features = ["unstable"] + +[[example]] +name = "tcp-ipv4-and-6-echo" +required-features = ["unstable"] + +[[example]] +name = "surf-web" +required-features = ["surf"] diff --git a/README.md b/README.md index 7aeaed86a..f432898dc 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,19 @@ -

async-std

-
- - Async version of the Rust standard library - -
+# `async-std` has been discontinued; use `smol` instead -
+We created `async-std` to demonstrate the value of making a library as close to +`std` as possible, but async. We think that demonstration was successful, and +we hope it will influence future design and development directions of async in +`std`. However, in the meantime, the [`smol`](https://github.com/smol-rs/smol/) +project came about and provided a great executor and libraries for asynchronous +use in the Rust ecosystem. We think that resources would be better spent +consolidating around `smol`, rather than continuing to provide occasional +maintenance of `async-std`. As such, we recommend that all users of +`async-std`, and all libraries built on `async-std`, switch to `smol` instead. + +In addition to the `smol` project as a direct replacement, you may find other +parts of the futures ecosystem useful, including `futures-concurrency`, +`async-io`, `futures-lite`, and `async-compat`. -
- - - Crates.io version - - - - Download - - - - docs.rs docs - - - - chat - -

@@ -39,14 +24,6 @@ Book - | - - Releases - - | - - Contributing -

@@ -61,7 +38,7 @@ syntax. ## Features - __Modern:__ Built from the ground up for `std::future` and `async/await` with - blazing fast compilation times. + blazing fast compilation time. - __Fast:__ Our robust allocator and threadpool designs provide ultra-high throughput with predictably low latency. - __Intuitive:__ Complete parity with the stdlib means you only need to learn @@ -77,17 +54,22 @@ syntax. ```rust use async_std::task; +async fn say_hello() { + println!("Hello, world!"); +} + fn main() { - task::block_on(async { - println!("Hello, world!"); - }) + task::block_on(say_hello()) } ``` More examples, including networking and file access, can be found in our -[`examples`] directory. +[`examples`] directory and in our [documentation]. -[`examples`]: https://github.com/async-rs/async-std/tree/master/examples +[`examples`]: https://github.com/async-rs/async-std/tree/HEAD/examples +[documentation]: https://docs.rs/async-std#examples +[`task::block_on`]: https://docs.rs/async-std/*/async_std/task/fn.block_on.html +[`"attributes"` feature]: https://docs.rs/async-std/#features ## Philosophy @@ -101,20 +83,6 @@ creation, with an adaptive lock-free executor, threadpool and network driver to create a smooth system that processes work at a high pace with low latency, using Rust's familiar stdlib API. -## Installation - -With [cargo add][cargo-add] installed run: - -```sh -$ cargo add async-std -``` - -We also provide a set of "unstable" features with async-std. See the [features -documentation] on how to enable them. - -[cargo-add]: https://github.com/killercup/cargo-edit -[features documentation]: https://docs.rs/async-std/#features - ## License diff --git a/benches/mutex.rs b/benches/mutex.rs new file mode 100644 index 000000000..4f1910a6f --- /dev/null +++ b/benches/mutex.rs @@ -0,0 +1,40 @@ +#![feature(test)] + +extern crate test; + +use async_std::sync::{Arc, Mutex}; +use async_std::task; +use test::Bencher; + +#[bench] +fn create(b: &mut Bencher) { + b.iter(|| Mutex::new(())); +} + +#[bench] +fn contention(b: &mut Bencher) { + b.iter(|| task::block_on(run(10, 1000))); +} + +#[bench] +fn no_contention(b: &mut Bencher) { + b.iter(|| task::block_on(run(1, 10000))); +} + +async fn run(task: usize, iter: usize) { + let m = Arc::new(Mutex::new(())); + let mut tasks = Vec::new(); + + for _ in 0..task { + let m = m.clone(); + tasks.push(task::spawn(async move { + for _ in 0..iter { + let _ = m.lock().await; + } + })); + } + + for t in tasks { + t.await; + } +} diff --git a/benches/task.rs b/benches/task.rs new file mode 100644 index 000000000..b31447130 --- /dev/null +++ b/benches/task.rs @@ -0,0 +1,11 @@ +#![feature(test)] + +extern crate test; + +use async_std::task; +use test::Bencher; + +#[bench] +fn block_on(b: &mut Bencher) { + b.iter(|| task::block_on(async {})); +} diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 732f8fcfc..000000000 --- a/bors.toml +++ /dev/null @@ -1,7 +0,0 @@ -status = [ - "Build and test (ubuntu-latest, nightly)", - "Build and test (windows-latest, nightly)", - "Build and test (macOS-latest, nightly)", - "Checking fmt and docs", - "Clippy check", -] diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh index 01c87f883..52422b970 100755 --- a/ci/install-mdbook.sh +++ b/ci/install-mdbook.sh @@ -1,7 +1,7 @@ set -euxo pipefail # Based on the Rust-Embedded WG's book CI -# https://github.com/rust-embedded/book/blob/master/ci/install.sh +# https://github.com/rust-embedded/book/blob/HEAD/ci/install.sh main() { # Note - this will only accept releases tagged with v0.3.x diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d679825e8..9e828f660 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -2,7 +2,6 @@ - [Introduction](./introduction.md) - [Welcome to `async-std`!](./overview/async-std.md) - - [`std::future` and `futures-rs`](./overview/std-and-library-futures.md) - [Stability guarantees](./overview/stability-guarantees.md) - [Async concepts using async-std](./concepts.md) - [Futures](./concepts/futures.md) @@ -19,8 +18,9 @@ - [Clean Shutdown](./tutorial/clean_shutdown.md) - [Handling Disconnection](./tutorial/handling_disconnection.md) - [Implementing a Client](./tutorial/implementing_a_client.md) -- [TODO: Async Patterns](./patterns.md) +- [Async Patterns](./patterns.md) - [TODO: Collected Small Patterns](./patterns/small-patterns.md) + - [Production-Ready Accept Loop](./patterns/accept-loop.md) - [Security practices](./security/index.md) - [Security Disclosures and Policy](./security/policy.md) - [Glossary](./glossary.md) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index 67db720ca..7d9cc6360 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie ## An easy view of computation -While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: - -- computation is a sequence of composable operations -- they can branch based on a decision -- they either run to succession and yield a result, or they can yield an error +While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error ## Deferring computation @@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system ## Conclusion -Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling. +Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling. A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple. -Next, we will introduce you to `tasks`, which we need to actually *run* Futures. +Next, we will introduce you to `tasks`, which we will use to actually *run* Futures. [^1]: Two parties reading while it is guaranteed that no one is writing is always safe. diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index d4037a3bb..c3dbbe202 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -2,7 +2,7 @@ Now that we know what Futures are, we want to run them! -In `async-std`, the [`tasks`][tasks] module is responsible for this. The simplest way is using the `block_on` function: +In `async-std`, the [`task`][tasks] module is responsible for this. The simplest way is using the `block_on` function: ```rust,edition2018 # extern crate async_std; @@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread` ## Blocking -`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: +`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: ```rust,edition2018 # extern crate async_std; @@ -118,7 +118,7 @@ thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` -While panicing a spawned task will abort: +While panicking a spawned task will abort: ```rust,edition2018,should_panic # extern crate async_std; diff --git a/docs/src/overview/async-std.md b/docs/src/overview/async-std.md index 2b59ffb03..0086599f1 100644 --- a/docs/src/overview/async-std.md +++ b/docs/src/overview/async-std.md @@ -4,4 +4,4 @@ `async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`. -[organization]: https://github.com/async-rs/async-std +[organization]: https://github.com/async-rs diff --git a/docs/src/overview/stability-guarantees.md b/docs/src/overview/stability-guarantees.md index 84bb68d7d..8c14e20fd 100644 --- a/docs/src/overview/stability-guarantees.md +++ b/docs/src/overview/stability-guarantees.md @@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte ## Security fixes -Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice. +Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead. ## Credits diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md deleted file mode 100644 index 5c5f96f5f..000000000 --- a/docs/src/overview/std-and-library-futures.md +++ /dev/null @@ -1,27 +0,0 @@ -# `std::future` and `futures-rs` - -Rust has two kinds of types commonly referred to as `Future`: - - -- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html). -- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`. - -The future defined in the [futures-rs](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`. - -It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features. - -In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`. - -## Interfaces and Stability - - `async-std` aims to be a stable and reliable library, at the level of the Rust standard library. This also means that we don't rely on the `futures` library for our interface. Yet, we appreciate that many users have come to like the conveniences that `futures-rs` brings. For that reason, `async-std` implements all `futures` traits for its types. - - Luckily, the approach from above gives you full flexibility. If you care about stability a lot, you can just use `async-std` as is. If you prefer the `futures` library interfaces, you link those in. Both uses are first class. - -## `async_std::future` - -There’s some support functions that we see as important for working with futures of any kind. These can be found in the `async_std::future` module and are covered by our stability guarantees. - -## Streams and Read/Write/Seek/BufRead traits - -Due to limitations of the Rust compiler, those are currently implemented in `async_std`, but cannot be implemented by users themselves. diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md new file mode 100644 index 000000000..4bc43e7d1 --- /dev/null +++ b/docs/src/patterns/accept-loop.md @@ -0,0 +1,266 @@ +# Production-Ready Accept Loop + +A production-ready accept loop needs the following things: +1. Handling errors +2. Limiting the number of simultanteous connections to avoid deny-of-service + (DoS) attacks + + +## Handling errors + +There are two kinds of errors in an accept loop: +1. Per-connection errors. The system uses them to notify that there was a + connection in the queue and it's dropped by the peer. Subsequent connections + can be already queued so next connection must be accepted immediately. +2. Resource shortages. When these are encountered it doesn't make sense to + accept the next socket immediately. But the listener stays active, so you server + should try to accept socket later. + +Here is the example of a per-connection error (printed in normal and debug mode): +``` +Error: Connection reset by peer (os error 104) +Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" } +``` + +And the following is the most common example of a resource shortage error: +``` +Error: Too many open files (os error 24) +Error: Os { code: 24, kind: Other, message: "Too many open files" } +``` + +### Testing Application + +To test your application for these errors try the following (this works +on unixes only). + +Lower limits and start the application: +``` +$ ulimit -n 100 +$ cargo run --example your_app + Compiling your_app v0.1.0 (/work) + Finished dev [unoptimized + debuginfo] target(s) in 5.47s + Running `target/debug/examples/your_app` +Server is listening on: http://127.0.0.1:1234 +``` +Then in another console run the [`wrk`] benchmark tool: +``` +$ wrk -c 1000 http://127.0.0.1:1234 +Running 10s test @ http://localhost:8080/ + 2 threads and 1000 connections +$ telnet localhost 1234 +Trying ::1... +Connected to localhost. +``` + +Important is to check the following things: + +1. The application doesn't crash on error (but may log errors, see below) +2. It's possible to connect to the application again once load is stopped + (few seconds after `wrk`). This is what `telnet` does in example above, + make sure it prints `Connected to `. +3. The `Too many open files` error is logged in the appropriate log. This + requires to set "maximum number of simultaneous connections" parameter (see + below) of your application to a value greater then `100` for this example. +4. Check CPU usage of the app while doing a test. It should not occupy 100% + of a single CPU core (it's unlikely that you can exhaust CPU by 1000 + connections in Rust, so this means error handling is not right). + +#### Testing non-HTTP applications + +If it's possible, use the appropriate benchmark tool and set the appropriate +number of connections. For example `redis-benchmark` has a `-c` parameter for +that, if you implement redis protocol. + +Alternatively, can still use `wrk`, just make sure that connection is not +immediately closed. If it is, put a temporary timeout before handing +the connection to the protocol handler, like this: + +```rust,edition2018 +# extern crate async_std; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +#async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { +# let listener = TcpListener::bind(addr).await?; +# let mut incoming = listener.incoming(); +while let Some(stream) = incoming.next().await { + task::spawn(async { + task::sleep(Duration::from_secs(10)).await; // 1 + connection_loop(stream).await; + }); +} +# Ok(()) +# } +``` + +1. Make sure the sleep coroutine is inside the spawned task, not in the loop. + +[`wrk`]: https://github.com/wg/wrk + + +### Handling Errors Manually + +Here is how basic accept loop could look like: + +```rust,edition2018 +# extern crate async_std; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + let listener = TcpListener::bind(addr).await?; + let mut incoming = listener.incoming(); + while let Some(result) = incoming.next().await { + let stream = match result { + Err(ref e) if is_connection_error(e) => continue, // 1 + Err(e) => { + eprintln!("Error: {}. Pausing for 500ms.", e); // 3 + task::sleep(Duration::from_millis(500)).await; // 2 + continue; + } + Ok(s) => s, + }; + // body + } + Ok(()) +} +``` + +1. Ignore per-connection errors. +2. Sleep and continue on resource shortage. +3. It's important to log the message, because these errors commonly mean the + misconfiguration of the system and are helpful for operations people running + the application. + +Be sure to [test your application](#testing-application). + + +### External Crates + +The crate [`async-listen`] has a helper to achieve this task: +```rust,edition2018 +# extern crate async_std; +# extern crate async_listen; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +use async_listen::{ListenExt, error_hint}; + +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + + let listener = TcpListener::bind(addr).await?; + let mut incoming = listener + .incoming() + .log_warnings(log_accept_error) // 1 + .handle_errors(Duration::from_millis(500)); + while let Some(socket) = incoming.next().await { // 2 + // body + } + Ok(()) +} + +fn log_accept_error(e: &io::Error) { + eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)) // 3 +} +``` + +1. Logs resource shortages (`async-listen` calls them warnings). If you use + `log` crate or any other in your app this should go to the log. +2. Stream yields sockets without `Result` wrapper after `handle_errors` because + all errors are already handled. +3. Together with the error we print a hint, which explains some errors for end + users. For example, it recommends increasing open file limit and gives + a link. + +[`async-listen`]: https://crates.io/crates/async-listen/ + +Be sure to [test your application](#testing-application). + + +## Connections Limit + +Even if you've applied everything described in +[Handling Errors](#handling-errors) section, there is still a problem. + +Let's imagine you have a server that needs to open a file to process +client request. At some point, you might encounter the following situation: + +1. There are as many client connection as max file descriptors allowed for + the application. +2. Listener gets `Too many open files` error so it sleeps. +3. Some client sends a request via the previously open connection. +4. Opening a file to serve request fails, because of the same + `Too many open files` error, until some other client drops a connection. + +There are many more possible situations, this is just a small illustation that +limiting number of connections is very useful. Generally, it's one of the ways +to control resources used by a server and avoiding some kinds of deny of +service (DoS) attacks. + +### `async-listen` crate + +Limiting maximum number of simultaneous connections with [`async-listen`] +looks like the following: + +```rust,edition2018 +# extern crate async_std; +# extern crate async_listen; +# use std::time::Duration; +# use async_std::{ +# net::{TcpListener, TcpStream, ToSocketAddrs}, +# prelude::*, +# }; +# +# type Result = std::result::Result>; +# +use async_listen::{ListenExt, Token, error_hint}; + +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + + let listener = TcpListener::bind(addr).await?; + let mut incoming = listener + .incoming() + .log_warnings(log_accept_error) + .handle_errors(Duration::from_millis(500)) // 1 + .backpressure(100); + while let Some((token, socket)) = incoming.next().await { // 2 + task::spawn(async move { + connection_loop(&token, stream).await; // 3 + }); + } + Ok(()) +} +async fn connection_loop(_token: &Token, stream: TcpStream) { // 4 + // ... +} +# fn log_accept_error(e: &io::Error) { +# eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)); +# } +``` + +1. We need to handle errors first, because [`backpressure`] helper expects + stream of `TcpStream` rather than `Result`. +2. The token yielded by a new stream is what is counted by backpressure helper. + I.e. if you drop a token, new connection can be established. +3. We give the connection loop a reference to token to bind token's lifetime to + the lifetime of the connection. +4. The token itsellf in the function can be ignored, hence `_token` + +[`backpressure`]: https://docs.rs/async-listen/0.1.2/async_listen/trait.ListenExt.html#method.backpressure + +Be sure to [test this behavior](#testing-application). diff --git a/docs/src/security/index.md b/docs/src/security/index.md index 5a94a5401..02fef4c0a 100644 --- a/docs/src/security/index.md +++ b/docs/src/security/index.md @@ -1,6 +1,6 @@ # Security -Writing a highly perfomant async core library is a task involving some instances of unsafe code. +Writing a highly performant async core library is a task involving some instances of unsafe code. We take great care in vetting all unsafe code included in `async-std` and do follow generally accepted practices. diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index dcc06616a..b8174d300 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; use async_std::{ - io::{self, BufReader}, + io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_channel::mpsc; -use futures_util::SinkExt; +use futures::channel::mpsc; +use futures::sink::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, @@ -125,8 +124,8 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { Entry::Occupied(..) => (), Entry::Vacant(entry) => { let (client_sender, client_receiver) = mpsc::unbounded(); - entry.insert(client_sender); // 4 - spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5 + entry.insert(client_sender); + spawn_and_log_error(connection_writer_loop(client_receiver, stream)); } } } diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 234067a3a..0937d7086 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -22,16 +22,15 @@ Let's add waiting to the server: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -156,16 +155,15 @@ And to the broker: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -247,7 +245,7 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { Notice what happens with all of the channels once we exit the accept loop: 1. First, we drop the main broker's sender. - That way when the readers are done, there's no sender for the broker's channel, and the chanel closes. + That way when the readers are done, there's no sender for the broker's channel, and the channel closes. 2. Next, the broker exits `while let Some(event) = events.next().await` loop. 3. It's crucial that, at this stage, we drop the `peers` map. This drops writer's senders. diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index c1ebe9b8a..921cf90ca 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -2,25 +2,24 @@ ## Connecting Readers and Writers So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`? -We should somehow maintain an `peers: HashMap>` map which allows a client to find destination channels. +We should somehow maintain a `peers: HashMap>` map which allows a client to find destination channels. However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message. One trick to make reasoning about state simpler comes from the actor model. -We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels. -By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit. +We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels. +By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit. The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue. ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # net::TcpStream, # prelude::*, # task, # }; -# use futures_channel::mpsc; -# use futures_util::sink::SinkExt; +# use futures::channel::mpsc; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -93,9 +92,9 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { } ``` -1. Broker should handle two types of events: a message or an arrival of a new peer. -2. Internal state of the broker is a `HashMap`. +1. The broker task should handle two types of events: a message or an arrival of a new peer. +2. The internal state of the broker is a `HashMap`. Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers 3. To handle a message, we send it over a channel to each destination -4. To handle new peer, we first register it in the peer's map ... +4. To handle a new peer, we first register it in the peer's map ... 5. ... and then spawn a dedicated task to actually write the messages to the socket. diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index a1f51d134..b6e53641c 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::net::TcpStream; -# use futures_channel::mpsc; -# use futures_util::SinkExt; +# use futures::channel::mpsc; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -61,8 +60,8 @@ async fn connection_loop(mut broker: Sender, stream: Arc) -> R } ``` -1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type. -2. We pass the shutdown channel to the writer task +1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type. +2. We pass the shutdown channel to the writer task. 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped. In the `connection_writer_loop`, we now need to choose between shutdown and message channels. @@ -70,17 +69,14 @@ We use the `select` macro for this purpose: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{net::TcpStream, prelude::*}; -use futures_channel::mpsc; -use futures_util::{select, FutureExt}; +# use futures::channel::mpsc; +use futures::{select, FutureExt}; # use std::sync::Arc; - # type Receiver = mpsc::UnboundedReceiver; # type Result = std::result::Result>; # type Sender = mpsc::UnboundedSender; - # #[derive(Debug)] # enum Void {} // 1 @@ -94,12 +90,12 @@ async fn connection_writer_loop( let mut shutdown = shutdown.fuse(); loop { // 2 select! { - msg = messages.next().fuse() => match msg { + msg = messages.next().fuse() => match msg { // 3 Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, void = shutdown.next().fuse() => match void { - Some(void) => match void {}, // 3 + Some(void) => match void {}, // 4 None => break, } } @@ -110,11 +106,12 @@ async fn connection_writer_loop( 1. We add shutdown channel as an argument. 2. Because of `select`, we can't use a `while let` loop, so we desugar it further into a `loop`. -3. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`. +3. Function fuse() is used to turn any `Stream` into a `FusedStream`. This is used for fusing a stream such that poll_next will never again be called once it has finished. +4. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`. Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. To not lose these messages completely, we'll return the messages channel back to the broker. -This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable. +This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible. ## Final Code @@ -122,16 +119,16 @@ The final code looks like this: ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; use async_std::{ io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_channel::mpsc; -use futures_util::{select, FutureExt, SinkExt}; +use futures::channel::mpsc; +use futures::sink::SinkExt; +use futures::{select, FutureExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 3aac67f35..ba9d6f335 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -1,29 +1,27 @@ ## Implementing a client -Let's now implement the client for the chat. -Because the protocol is line-based, the implementation is pretty straightforward: +Since the protocol is line-based, implementing a client for the chat is straightforward: * Lines read from stdin should be sent over the socket. * Lines read from the socket should be echoed to stdout. -Unlike the server, the client needs only limited concurrency, as it interacts with only a single user. -For this reason, async doesn't bring a lot of performance benefits in this case. +Although async does not significantly affect client performance (as unlike the server, the client interacts solely with one user and only needs limited concurrency), async is still useful for managing concurrency! + +The client has to read from stdin and the socket *simultaneously*. +Programming this with threads is cumbersome, especially when implementing a clean shutdown. +With async, the `select!` macro is all that is needed. -However, async is still useful for managing concurrency! -Specifically, the client should *simultaneously* read from stdin and from the socket. -Programming this with threads is cumbersome, especially when implementing clean shutdown. -With async, we can just use the `select!` macro. ```rust,edition2018 # extern crate async_std; -# extern crate futures_util; +# extern crate futures; use async_std::{ io::{stdin, BufReader}, net::{TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures_util::{select, FutureExt}; +use futures::{select, FutureExt}; type Result = std::result::Result>; diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md index 99ddf8eb3..076ecf418 100644 --- a/docs/src/tutorial/index.md +++ b/docs/src/tutorial/index.md @@ -1,11 +1,14 @@ # Tutorial: Writing a chat -Nothing is as simple as a chat server, right? Not quite, chat servers -already expose you to all the fun of asynchronous programming: how -do you handle clients connecting concurrently. How do you handle them disconnecting? +Nothing is simpler than creating a chat server, right? +Not quite, chat servers expose you to all the fun of asynchronous programming: -How do you distribute the messages? +How will the server handle clients connecting concurrently? -In this tutorial, we will show you how to write one in `async-std`. +How will it handle them disconnecting? -You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat). +How will it distribute the messages? + +This tutorial explains how to write a chat server in `async-std`. + +You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/HEAD/examples/a-chat). diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 213589c07..036bc4597 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,14 +10,18 @@ We need to: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# io::BufReader, -# net::{TcpListener, TcpStream, ToSocketAddrs}, +# net::{TcpListener, ToSocketAddrs}, # prelude::*, # task, # }; # # type Result = std::result::Result>; # +use async_std::{ + io::BufReader, + net::TcpStream, +}; + async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); @@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> { Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), }; let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); - let msg: String = msg.trim().to_string(); + let msg: String = msg.to_string(); } Ok(()) } @@ -107,14 +111,14 @@ We can "fix" it by waiting for the task to be joined, like this: # # async move |stream| { let handle = task::spawn(connection_loop(stream)); -handle.await +handle.await? # }; ``` The `.await` waits until the client finishes, and `?` propagates the result. There are two problems with this solution however! -*First*, because we immediately await the client, we can only handle one client at time, and that completely defeats the purpose of async! +*First*, because we immediately await the client, we can only handle one client at a time, and that completely defeats the purpose of async! *Second*, if a client encounters an IO error, the whole server immediately exits. That is, a flaky internet connection of one peer brings down the whole chat room! @@ -130,7 +134,7 @@ So let's use a helper function for this: # }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { task::spawn(async move { if let Err(e) = fut.await { diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index b381aacbc..3f426d020 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the ```rust,edition2018 # extern crate async_std; -# extern crate futures_channel; -# extern crate futures_util; +# extern crate futures; # use async_std::{ # net::TcpStream, # prelude::*, # }; -use futures_channel::mpsc; // 1 -use futures_util::sink::SinkExt; +use futures::channel::mpsc; // 1 +use futures::sink::SinkExt; use std::sync::Arc; # type Result = std::result::Result>; diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index bda457660..7b1a01670 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy login1, login2, ... loginN: message ``` -Each of the specified clients than receives a `from login: message` message. +Each of the specified clients then receives a `from login: message` message. A possible session might look like this @@ -38,18 +38,10 @@ $ cargo new a-chat $ cd a-chat ``` -At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience: - -```bash -$ rustup override add nightly -$ rustc --version -rustc 1.38.0-nightly (c4715198b 2019-08-05) -``` - Add the following lines to `Cargo.toml`: ```toml [dependencies] -futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await" ] } -async-std = "0.99" +futures = "0.3.0" +async-std = "1" ``` diff --git a/examples/README.md b/examples/README.md index 903ed2cdb..bcaae42c8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -154,18 +154,18 @@ Make requests by running the client example: cargo run --example udp-client ``` -[hello-world]: https://github.com/async-rs/async-std/blob/master/examples/hello-world.rs -[line-count]: https://github.com/async-rs/async-std/blob/master/examples/line-count.rs -[list-dir]: https://github.com/async-rs/async-std/blob/master/examples/list-dir.rs -[logging]: https://github.com/async-rs/async-std/blob/master/examples/logging.rs -[print-file]: https://github.com/async-rs/async-std/blob/master/examples/print-file.rs -[socket-timeouts]: https://github.com/async-rs/async-std/blob/master/examples/socket-timeouts.rs -[stdin-echo]: https://github.com/async-rs/async-std/blob/master/examples/stdin-echo.rs -[stdin-timeout]: https://github.com/async-rs/async-std/blob/master/examples/stdin-timeout.rs -[surf-web]: https://github.com/async-rs/async-std/blob/master/examples/surf-web.rs -[task-local]: https://github.com/async-rs/async-std/blob/master/examples/task-local.rs -[task-name]: https://github.com/async-rs/async-std/blob/master/examples/task-name.rs -[tcp-client]: https://github.com/async-rs/async-std/blob/master/examples/tcp-client.rs -[tcp-echo]: https://github.com/async-rs/async-std/blob/master/examples/tcp-echo.rs -[udp-client]: https://github.com/async-rs/async-std/blob/master/examples/udp-client.rs -[udp-echo]: https://github.com/async-rs/async-std/blob/master/examples/udp-echo.rs +[hello-world]: https://github.com/async-rs/async-std/blob/HEAD/examples/hello-world.rs +[line-count]: https://github.com/async-rs/async-std/blob/HEAD/examples/line-count.rs +[list-dir]: https://github.com/async-rs/async-std/blob/HEAD/examples/list-dir.rs +[logging]: https://github.com/async-rs/async-std/blob/HEAD/examples/logging.rs +[print-file]: https://github.com/async-rs/async-std/blob/HEAD/examples/print-file.rs +[socket-timeouts]: https://github.com/async-rs/async-std/blob/HEAD/examples/socket-timeouts.rs +[stdin-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-echo.rs +[stdin-timeout]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-timeout.rs +[surf-web]: https://github.com/async-rs/async-std/blob/HEAD/examples/surf-web.rs +[task-local]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-local.rs +[task-name]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-name.rs +[tcp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-client.rs +[tcp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-echo.rs +[udp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-client.rs +[udp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-echo.rs diff --git a/examples/a-chat/main.rs b/examples/a-chat/main.rs index ced7cac24..89e5e2b62 100644 --- a/examples/a-chat/main.rs +++ b/examples/a-chat/main.rs @@ -8,6 +8,6 @@ fn main() -> Result<()> { match (args.nth(1).as_ref().map(String::as_str), args.next()) { (Some("client"), None) => client::main(), (Some("server"), None) => server::main(), - _ => Err("Usage: a-chat [client|server]")?, + _ => Err("Usage: a-chat [client|server]".into()), } } diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs index 77ebfd1e3..d3ac74699 100644 --- a/examples/a-chat/server.rs +++ b/examples/a-chat/server.rs @@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result let mut lines = reader.lines(); let name = match lines.next().await { - None => Err("peer disconnected immediately")?, + None => return Err("peer disconnected immediately".into()), Some(line) => line?, }; let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::(); @@ -96,6 +96,7 @@ async fn connection_writer_loop( None => break, }, void = shutdown.next().fuse() => match void { + #[allow(unreachable_patterns)] Some(void) => match void {}, None => break, } diff --git a/examples/logging.rs b/examples/logging.rs index bd55aaa0c..0e4526b38 100644 --- a/examples/logging.rs +++ b/examples/logging.rs @@ -3,7 +3,7 @@ use async_std::task; fn main() { - femme::start(log::LevelFilter::Trace).unwrap(); + femme::with_level(log::LevelFilter::Trace); task::block_on(async { let handle = task::spawn(async { diff --git a/examples/surf-web.rs b/examples/surf-web.rs index b3101d15c..6ff043d99 100644 --- a/examples/surf-web.rs +++ b/examples/surf-web.rs @@ -1,23 +1,18 @@ -/* TODO: Once the next version of surf released, re-enable this example. -//! Sends an HTTP request to the Rust website. - use async_std::task; -fn main() -> Result<(), surf::Exception> { +fn main() -> Result<(), surf::Error> { task::block_on(async { let url = "https://www.rust-lang.org"; - let mut response = surf::get(url).await?; + let mut response = surf::get(url).send().await?; let body = response.body_string().await?; dbg!(url); dbg!(response.status()); dbg!(response.version()); - dbg!(response.headers()); + dbg!(response.header_names()); + dbg!(response.header_values()); dbg!(body.len()); Ok(()) }) } -*/ - -fn main() {} diff --git a/examples/tcp-echo.rs b/examples/tcp-echo.rs index 7c50be016..c04f07765 100644 --- a/examples/tcp-echo.rs +++ b/examples/tcp-echo.rs @@ -14,8 +14,9 @@ use async_std::task; async fn process(stream: TcpStream) -> io::Result<()> { println!("Accepted from: {}", stream.peer_addr()?); - let (reader, writer) = &mut (&stream, &stream); - io::copy(reader, writer).await?; + let mut reader = stream.clone(); + let mut writer = stream; + io::copy(&mut reader, &mut writer).await?; Ok(()) } diff --git a/examples/tcp-ipv4-and-6-echo.rs b/examples/tcp-ipv4-and-6-echo.rs new file mode 100644 index 000000000..a00e1f386 --- /dev/null +++ b/examples/tcp-ipv4-and-6-echo.rs @@ -0,0 +1,45 @@ +//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets. +//! +//! To send messages, do: +//! +//! ```sh +//! $ nc 127.0.0.1 8080 +//! $ nc ::1 8080 +//! ``` + +use async_std::io; +use async_std::net::{TcpListener, TcpStream}; +use async_std::prelude::*; +use async_std::task; + +async fn process(stream: TcpStream) -> io::Result<()> { + println!("Accepted from: {}", stream.peer_addr()?); + + let mut reader = stream.clone(); + let mut writer = stream; + io::copy(&mut reader, &mut writer).await?; + + Ok(()) +} + +fn main() -> io::Result<()> { + task::block_on(async { + let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?; + println!("Listening on {}", ipv4_listener.local_addr()?); + let ipv6_listener = TcpListener::bind("[::1]:8080").await?; + println!("Listening on {}", ipv6_listener.local_addr()?); + + let ipv4_incoming = ipv4_listener.incoming(); + let ipv6_incoming = ipv6_listener.incoming(); + + let mut incoming = ipv4_incoming.merge(ipv6_incoming); + + while let Some(stream) = incoming.next().await { + let stream = stream?; + task::spawn(async { + process(stream).await.unwrap(); + }); + } + Ok(()) + }) +} diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 1082fd888..000000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -version = "Two" diff --git a/src/channel.rs b/src/channel.rs new file mode 100644 index 000000000..8eb30a42d --- /dev/null +++ b/src/channel.rs @@ -0,0 +1,8 @@ +//! Channels +//! +//! Multi-producer, multi-consumer queues, used for message-based +//! communication. Can provide a lightweight inter-task synchronisation +//! mechanism, at the cost of some extra memory. + +#[doc(inline)] +pub use async_channel::*; diff --git a/src/collections/binary_heap/extend.rs b/src/collections/binary_heap/extend.rs index 4503fe393..1a9479e14 100644 --- a/src/collections/binary_heap/extend.rs +++ b/src/collections/binary_heap/extend.rs @@ -2,13 +2,16 @@ use std::collections::BinaryHeap; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for BinaryHeap { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for BinaryHeap { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs index c8e44e93a..614f5f5de 100644 --- a/src/collections/binary_heap/from_stream.rs +++ b/src/collections/binary_heap/from_stream.rs @@ -1,23 +1,22 @@ use std::collections::BinaryHeap; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for BinaryHeap { +impl FromStream for BinaryHeap { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BinaryHeap::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/btree_map/extend.rs b/src/collections/btree_map/extend.rs index ae02c4248..4a1ad0618 100644 --- a/src/collections/btree_map/extend.rs +++ b/src/collections/btree_map/extend.rs @@ -2,13 +2,16 @@ use std::collections::BTreeMap; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend<(K, V)> for BTreeMap { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend<(K, V)> for BTreeMap { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { Box::pin(stream.into_stream().for_each(move |(k, v)| { self.insert(k, v); })) diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs index bd80c0697..ef01b6e0d 100644 --- a/src/collections/btree_map/from_stream.rs +++ b/src/collections/btree_map/from_stream.rs @@ -1,23 +1,22 @@ use std::collections::BTreeMap; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream<(K, V)> for BTreeMap { +impl FromStream<(K, V)> for BTreeMap { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeMap::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/btree_set/extend.rs b/src/collections/btree_set/extend.rs index ccf033783..0f7421023 100644 --- a/src/collections/btree_set/extend.rs +++ b/src/collections/btree_set/extend.rs @@ -2,13 +2,16 @@ use std::collections::BTreeSet; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for BTreeSet { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for BTreeSet { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { Box::pin(stream.into_stream().for_each(move |item| { self.insert(item); })) diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs index bd2a744ba..5fdb33e6c 100644 --- a/src/collections/btree_set/from_stream.rs +++ b/src/collections/btree_set/from_stream.rs @@ -1,23 +1,22 @@ use std::collections::BTreeSet; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for BTreeSet { +impl FromStream for BTreeSet { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = BTreeSet::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/hash_map/extend.rs b/src/collections/hash_map/extend.rs index c34bb9ed3..15b7e7d46 100644 --- a/src/collections/hash_map/extend.rs +++ b/src/collections/hash_map/extend.rs @@ -3,17 +3,21 @@ use std::hash::{BuildHasher, Hash}; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend<(K, V)> for HashMap +impl stream::Extend<(K, V)> for HashMap where - K: Eq + Hash, - H: BuildHasher + Default, + K: Eq + Hash + Send, + V: Send, + H: BuildHasher + Default + Send, { - fn stream_extend<'a, S: IntoStream + 'a>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); // The following is adapted from the hashbrown source code: diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs index 2b7bbc9b7..ae3f11a56 100644 --- a/src/collections/hash_map/from_stream.rs +++ b/src/collections/hash_map/from_stream.rs @@ -2,27 +2,27 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream<(K, V)> for HashMap where - K: Eq + Hash, - H: BuildHasher + Default, + K: Eq + Hash + Send, + H: BuildHasher + Default + Send, + V: Send, { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashMap::with_hasher(Default::default()); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/hash_set/extend.rs b/src/collections/hash_set/extend.rs index 123e844e2..0246a1e88 100644 --- a/src/collections/hash_set/extend.rs +++ b/src/collections/hash_set/extend.rs @@ -3,17 +3,20 @@ use std::hash::{BuildHasher, Hash}; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for HashSet +impl stream::Extend for HashSet where - T: Eq + Hash, - H: BuildHasher + Default, + T: Eq + Hash + Send, + H: BuildHasher + Default + Send, { - fn stream_extend<'a, S: IntoStream + 'a>( + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { // The Extend impl for HashSet in the standard library delegates to the internal HashMap. // Thus, this impl is just a copy of the async Extend impl for HashMap in this crate. diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs index 42447fef0..6f640695a 100644 --- a/src/collections/hash_set/from_stream.rs +++ b/src/collections/hash_set/from_stream.rs @@ -2,27 +2,26 @@ use std::collections::HashSet; use std::hash::{BuildHasher, Hash}; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for HashSet where - T: Eq + Hash, - H: BuildHasher + Default, + T: Eq + Hash + Send, + H: BuildHasher + Default + Send, { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = HashSet::with_hasher(Default::default()); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/linked_list/extend.rs b/src/collections/linked_list/extend.rs index 63a1a97c3..8adc41d73 100644 --- a/src/collections/linked_list/extend.rs +++ b/src/collections/linked_list/extend.rs @@ -2,13 +2,16 @@ use std::collections::LinkedList; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for LinkedList { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for LinkedList { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); Box::pin(stream.for_each(move |item| self.push_back(item))) } diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs index 3ffe149bb..7c3eb738c 100644 --- a/src/collections/linked_list/from_stream.rs +++ b/src/collections/linked_list/from_stream.rs @@ -1,23 +1,22 @@ use std::collections::LinkedList; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for LinkedList { +impl FromStream for LinkedList { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = LinkedList::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/collections/mod.rs b/src/collections/mod.rs index ae9efaa92..745aed01c 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -11,10 +11,17 @@ pub mod hash_set; pub mod linked_list; pub mod vec_deque; +#[allow(unused)] pub use binary_heap::BinaryHeap; +#[allow(unused)] pub use btree_map::BTreeMap; +#[allow(unused)] pub use btree_set::BTreeSet; +#[allow(unused)] pub use hash_map::HashMap; +#[allow(unused)] pub use hash_set::HashSet; +#[allow(unused)] pub use linked_list::LinkedList; +#[allow(unused)] pub use vec_deque::VecDeque; diff --git a/src/collections/vec_deque/extend.rs b/src/collections/vec_deque/extend.rs index 17e396ab8..9dea92231 100644 --- a/src/collections/vec_deque/extend.rs +++ b/src/collections/vec_deque/extend.rs @@ -2,13 +2,16 @@ use std::collections::VecDeque; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for VecDeque { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for VecDeque { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs index 8903de0ea..3e0719ab2 100644 --- a/src/collections/vec_deque/from_stream.rs +++ b/src/collections/vec_deque/from_stream.rs @@ -1,23 +1,22 @@ use std::collections::VecDeque; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for VecDeque { +impl FromStream for VecDeque { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = VecDeque::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 601d477cc..38a5a6b90 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Returns the canonical form of a path. /// @@ -32,5 +33,10 @@ use crate::task::blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await + spawn_blocking(move || { + std::fs::canonicalize(&path) + .map(Into::into) + .context(|| format!("could not canonicalize `{}`", path.display())) + }) + .await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 733fb64b3..8fb447bb0 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Copies the contents and permissions of a file to a new location. /// @@ -41,5 +42,9 @@ use crate::task::blocking; pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::copy(&from, &to)).await + spawn_blocking(move || { + std::fs::copy(&from, &to) + .context(|| format!("could not copy `{}` to `{}`", from.display(), to.display())) + }) + .await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 740d303c6..37923c055 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory. /// @@ -34,5 +35,9 @@ use crate::task::blocking; /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::create_dir(path)).await + spawn_blocking(move || { + std::fs::create_dir(&path) + .context(|| format!("could not create directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 76604de7f..753dfd492 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a new directory and all of its parents if they are missing. /// @@ -29,5 +30,9 @@ use crate::task::blocking; /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::create_dir_all(path)).await + spawn_blocking(move || { + std::fs::create_dir_all(&path) + .context(|| format!("could not create directory path `{}`", path.display())) + }) + .await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index a55a9a922..9ee6b55ac 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -2,7 +2,7 @@ use std::future::Future; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// A builder for creating directories with configurable options. /// @@ -107,7 +107,7 @@ impl DirBuilder { } let path = path.as_ref().to_owned(); - async move { blocking::spawn(move || builder.create(path)).await } + async move { spawn_blocking(move || builder.create(path)).await } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 959e2adaa..e1622eec6 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use crate::fs::{FileType, Metadata}; use crate::io; use crate::path::PathBuf; -use crate::task::blocking; +use crate::task::spawn_blocking; /// An entry in a directory. /// @@ -87,7 +87,7 @@ impl DirEntry { /// ``` pub async fn metadata(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(move || inner.metadata()).await + spawn_blocking(move || inner.metadata()).await } /// Reads the file type for this entry. @@ -125,7 +125,7 @@ impl DirEntry { /// ``` pub async fn file_type(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(move || inner.file_type()).await + spawn_blocking(move || inner.file_type()).await } /// Returns the bare name of this entry without the leading path. @@ -158,6 +158,12 @@ impl fmt::Debug for DirEntry { } } +impl Clone for DirEntry { + fn clone(&self) -> Self { + DirEntry(self.0.clone()) + } +} + cfg_unix! { use crate::os::unix::fs::DirEntryExt; diff --git a/src/fs/file.rs b/src/fs/file.rs index 3129e96a0..7dc12dd0e 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -12,7 +12,10 @@ use crate::future; use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::path::Path; use crate::prelude::*; -use crate::task::{self, blocking, Context, Poll, Waker}; +use crate::task::{spawn_blocking, Context, Poll, Waker}; +use crate::utils::Context as _; + +const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; /// An open file on the filesystem. /// @@ -57,6 +60,7 @@ use crate::task::{self, blocking, Context, Poll, Waker}; /// # /// # Ok(()) }) } /// ``` +#[derive(Clone)] pub struct File { /// A reference to the inner file. file: Arc, @@ -66,6 +70,23 @@ pub struct File { } impl File { + /// Creates an async file handle. + pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File { + let file = Arc::new(file); + + File { + file: file.clone(), + lock: Lock::new(State { + file, + mode: Mode::Idle, + cache: Vec::new(), + is_flushed, + last_read_err: None, + last_write_err: None, + }), + } + } + /// Opens a file in read-only mode. /// /// See the [`OpenOptions::open`] function for more options. @@ -95,8 +116,11 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(move || std::fs::File::open(&path)).await?; - Ok(file.into()) + let file = spawn_blocking(move || { + std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display())) + }) + .await?; + Ok(File::new(file, true)) } /// Opens a file in write-only mode. @@ -130,8 +154,11 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(move || std::fs::File::create(&path)).await?; - Ok(file.into()) + let file = spawn_blocking(move || { + std::fs::File::create(&path) + }) + .await?; + Ok(File::new(file, true)) } /// Synchronizes OS-internal buffered contents and metadata to disk. @@ -163,7 +190,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.sync_all()).await + spawn_blocking(move || state.file.sync_all()).await } /// Synchronizes OS-internal buffered contents to disk. @@ -199,7 +226,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.sync_data()).await + spawn_blocking(move || state.file.sync_data()).await } /// Truncates or extends the file. @@ -232,7 +259,7 @@ impl File { }) .await?; - blocking::spawn(move || state.file.set_len(size)).await + spawn_blocking(move || state.file.set_len(size)).await } /// Reads the file's metadata. @@ -251,7 +278,7 @@ impl File { /// ``` pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); - blocking::spawn(move || file.metadata()).await + spawn_blocking(move || file.metadata()).await } /// Changes the permissions on the file. @@ -280,7 +307,7 @@ impl File { /// ``` pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); - blocking::spawn(move || file.set_permissions(perm)).await + spawn_blocking(move || file.set_permissions(perm)).await } } @@ -290,7 +317,7 @@ impl Drop for File { // non-blocking fashion, but our only other option here is losing data remaining in the // write cache. Good task schedulers should be resilient to occasional blocking hiccups in // file destructors so we don't expect this to be a common problem in practice. - let _ = task::block_on(self.flush()); + let _ = futures_lite::future::block_on(self.flush()); } } @@ -383,25 +410,22 @@ impl Seek for &File { impl From for File { fn from(file: std::fs::File) -> File { - let file = Arc::new(file); - - File { - file: file.clone(), - lock: Lock::new(State { - file, - mode: Mode::Idle, - cache: Vec::new(), - is_flushed: false, - last_read_err: None, - last_write_err: None, - }), - } + File::new(file, false) } } cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + impl File { + fn into_std_file(self) -> std::fs::File { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + } + } + impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() @@ -416,11 +440,29 @@ cfg_unix! { impl IntoRawFd for File { fn into_raw_fd(self) -> RawFd { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") - .into_raw_fd() + self.into_std_file().into_raw_fd() + } + } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.file.as_fd() + } + } + + impl From for File { + fn from(fd: OwnedFd) -> Self { + std::fs::File::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(val: File) -> OwnedFd { + val.into_std_file().into() + } } } } @@ -445,10 +487,36 @@ cfg_windows! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") + .expect(ARC_TRY_UNWRAP_EXPECT) .into_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle}; + + impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.file.as_handle() + } + } + + impl From for File { + fn from(handle: OwnedHandle) -> Self { + std::fs::File::from(handle).into() + } + } + + impl From for OwnedHandle { + fn from(val: File) -> OwnedHandle { + let file = val.file.clone(); + drop(val); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + .into() + } + } + } } /// An async mutex with non-borrowing lock guards. @@ -457,6 +525,13 @@ struct Lock(Arc>); unsafe impl Send for Lock {} unsafe impl Sync for Lock {} +impl Clone for Lock { + #[inline] + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + /// The state of a lock. struct LockState { /// Set to `true` when locked. @@ -497,14 +572,16 @@ impl Lock { } // The lock was successfully acquired. - Poll::Ready(LockGuard(self.0.clone())) + Poll::Ready(LockGuard(Some(self.0.clone()))) } } /// A lock guard. /// /// When dropped, ownership of the inner value is returned back to the lock. -struct LockGuard(Arc>); +/// The inner value is always Some, except when the lock is dropped, where we +/// set it to None. See comment in drop(). +struct LockGuard(Option>>); unsafe impl Send for LockGuard {} unsafe impl Sync for LockGuard {} @@ -514,7 +591,7 @@ impl LockGuard { /// /// When this lock guard gets dropped, all registered tasks will be woken up. fn register(&self, cx: &Context<'_>) { - let mut list = self.0.wakers.lock().unwrap(); + let mut list = self.0.as_ref().unwrap().wakers.lock().unwrap(); if list.iter().all(|w| !w.will_wake(cx.waker())) { list.push(cx.waker().clone()); @@ -524,11 +601,22 @@ impl LockGuard { impl Drop for LockGuard { fn drop(&mut self) { + // Set the Option to None and take its value so we can drop the Arc + // before we wake up the tasks. + let lock = self.0.take().unwrap(); + + // Prepare to wake up all registered tasks interested in acquiring the lock. + let wakers: Vec<_> = lock.wakers.lock().unwrap().drain(..).collect(); + // Release the lock. - self.0.locked.store(false, Ordering::Release); + lock.locked.store(false, Ordering::Release); + + // Drop the Arc _before_ waking up the tasks, to avoid races. See + // reproducer and discussion in https://github.com/async-rs/async-std/issues/1001. + drop(lock); // Wake up all registered tasks interested in acquiring the lock. - for w in self.0.wakers.lock().unwrap().drain(..) { + for w in wakers { w.wake(); } } @@ -538,13 +626,19 @@ impl Deref for LockGuard { type Target = T; fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } + // SAFETY: Safe because the lock is held when this method is called. And + // the inner value is always Some since it is only set to None in + // drop(). + unsafe { &*self.0.as_ref().unwrap().value.get() } } } impl DerefMut for LockGuard { fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } + // SAFETY: Safe because the lock is held when this method is called. And + // the inner value is always Some since it is only set to None in + // drop(). + unsafe { &mut *self.0.as_ref().unwrap().value.get() } } } @@ -650,17 +744,22 @@ impl LockGuard { match self.mode { Mode::Idle => {} + Mode::Reading(0) if self.cache.is_empty() => { + // If the cache is empty in reading mode, the last operation didn't read any bytes, + // which indicates that it reached the end of the file. In this case we need to + // reset the mode to idle so that next time we try to read again, since the file + // may grow after the first EOF. + self.mode = Mode::Idle; + return Poll::Ready(Ok(0)); + } Mode::Reading(start) => { // How many bytes in the cache are available for reading. let available = self.cache.len() - start; - // If there is cached unconsumed data or if the cache is empty, we can read from - // it. Empty cache in reading mode indicates that the last operation didn't read - // any bytes, i.e. it reached the end of the file. - if available > 0 || self.cache.is_empty() { + if available > 0 { // Copy data from the cache into the buffer. let n = cmp::min(available, buf.len()); - buf[..n].copy_from_slice(&self.cache[start..n]); + buf[..n].copy_from_slice(&self.cache[start..(start + n)]); // Move the read cursor forward. self.mode = Mode::Reading(start + n); @@ -687,7 +786,7 @@ impl LockGuard { self.register(cx); // Start a read operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { // Read some data from the file into the cache. let res = { let State { file, cache, .. } = &mut *self; @@ -728,7 +827,10 @@ impl LockGuard { if n > 0 { // Seek `n` bytes backwards. This call should not block because it only changes // the internal offset into the file and doesn't touch the actual file on disk. - (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; + // + // We ignore errors here because special files like `/dev/random` are not + // seekable. + let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64))); } // Switch to idle mode. @@ -796,7 +898,7 @@ impl LockGuard { self.register(cx); // Start a write operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { match (&*self.file).write_all(&self.cache) { Ok(_) => { // Switch to idle mode. @@ -829,7 +931,7 @@ impl LockGuard { self.register(cx); // Start a flush operation asynchronously. - blocking::spawn(move || { + spawn_blocking(move || { match (&*self.file).flush() { Ok(()) => { // Mark the file as flushed. @@ -851,3 +953,79 @@ impl LockGuard { Poll::Ready(Ok(())) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn async_file_drop() { + crate::task::block_on(async move { + File::open(file!()).await.unwrap(); + }); + } + + #[test] + fn async_file_clone() { + crate::task::block_on(async move { + let file = File::open(file!()).await.unwrap(); + let mut clone = file.clone(); + let len = crate::task::spawn_blocking(move || { + let mut buf = Vec::new(); + crate::task::block_on(async move { + clone.read_to_end(&mut buf).await.unwrap(); + drop(clone); + buf.len() + }) + }).await; + assert_eq!(len as u64, file.metadata().await.unwrap().len()); + }); + } + + #[test] + fn async_file_create_error() { + let file_name = Path::new("/tmp/does_not_exist/test"); + let expect = std::fs::File::create(file_name).unwrap_err(); + + crate::task::block_on(async move { + let actual = File::create(file_name).await.unwrap_err(); + assert_eq!(format!("{}", expect), format!("{}", actual)); + }) + } + + #[test] + fn file_eof_is_not_permanent() -> crate::io::Result<()> { + let tempdir = tempfile::Builder::new() + .prefix("async-std-file-eof-test") + .tempdir()?; + let path = tempdir.path().join("testfile"); + + crate::task::block_on(async { + let mut file_w = File::create(&path).await?; + let mut file_r = File::open(&path).await?; + + file_w.write_all(b"data").await?; + file_w.flush().await?; + + let mut buf = [0u8; 4]; + let mut len = file_r.read(&mut buf).await?; + assert_eq!(len, 4); + assert_eq!(&buf, b"data"); + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 0); + + file_w.write_all(b"more").await?; + file_w.flush().await?; + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 4); + assert_eq!(&buf, b"more"); + + len = file_r.read(&mut buf).await?; + assert_eq!(len, 0); + + Ok(()) + }) + } +} diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs index 11f47d1c7..d7ce25704 100644 --- a/src/fs/file_type.rs +++ b/src/fs/file_type.rs @@ -40,7 +40,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_dir(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this file type represents a regular file. @@ -60,7 +60,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_file(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this file type represents a symbolic link. @@ -78,7 +78,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_symlink(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 8b09b5d1d..a6a406989 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Creates a hard link on the filesystem. /// @@ -32,5 +33,14 @@ use crate::task::blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::hard_link(&from, &to)).await + spawn_blocking(move || { + std::fs::hard_link(&from, &to).context(|| { + format!( + "could not create a hard link from `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 4afc55953..2948016e2 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,6 +1,6 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads metadata for a path. /// @@ -34,7 +34,7 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::metadata(path)).await + spawn_blocking(move || std::fs::metadata(path)).await } cfg_not_docs! { @@ -78,7 +78,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn file_type(&self) -> FileType { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this metadata is for a regular directory. @@ -98,7 +98,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_dir(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns `true` if this metadata is for a regular file. @@ -118,7 +118,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn is_file(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the file size in bytes. @@ -136,7 +136,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn len(&self) -> u64 { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the permissions from this metadata. @@ -154,7 +154,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn permissions(&self) -> Permissions { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the last modification time. @@ -177,7 +177,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn modified(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the last access time. @@ -200,7 +200,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn accessed(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Returns the creation time. @@ -223,7 +223,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn created(&self) -> io::Result { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 4598ec849..5cf086def 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -3,11 +3,13 @@ //! This module is an async version of [`std::fs`]. //! //! [`os::unix::fs`]: ../os/unix/fs/index.html +//! [`os::windows::fs`]: ../os/windows/fs/index.html //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html //! //! # Platform-specific extensions //! //! * Unix: use the [`os::unix::fs`] module. +//! * Windows: use the [`os::windows::fs`] module. //! //! # Examples //! diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index a2eb9e76b..18aa89eb3 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -3,7 +3,7 @@ use std::future::Future; use crate::fs::File; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// A builder for opening files with configurable options. /// @@ -136,7 +136,7 @@ impl OpenOptions { /// Configures the option for append mode. /// /// When set to `true`, this option means the file will be writable after opening and the file - /// cursor will be moved to the end of file before every write operaiton. + /// cursor will be moved to the end of file before every write operation. /// /// # Examples /// @@ -284,7 +284,10 @@ impl OpenOptions { pub fn open>(&self, path: P) -> impl Future> { let path = path.as_ref().to_owned(); let options = self.0.clone(); - async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await } + async move { + let file = spawn_blocking(move || options.open(path)).await?; + Ok(File::new(file, true)) + } } } diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs index 1339a7c7d..50aa45cd1 100644 --- a/src/fs/permissions.rs +++ b/src/fs/permissions.rs @@ -29,7 +29,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn readonly(&self) -> bool { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } /// Configures the read-only flag. @@ -50,7 +50,7 @@ cfg_docs! { /// # Ok(()) }) } /// ``` pub fn set_readonly(&mut self, readonly: bool) { - unimplemented!() + unreachable!("this impl only appears in the rendered docs") } } } diff --git a/src/fs/read.rs b/src/fs/read.rs index a0eb130ba..3b568f7a6 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as raw bytes. /// @@ -36,5 +37,8 @@ use crate::task::blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read(path)).await + spawn_blocking(move || { + std::fs::read(&path).context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 6e478019d..d8261a949 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,11 +1,12 @@ +use std::future::Future; use std::pin::Pin; use crate::fs::DirEntry; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::stream::Stream; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; /// Returns a stream of entries in a directory. /// @@ -45,9 +46,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_dir(path)) - .await - .map(ReadDir::new) + spawn_blocking(move || { + std::fs::read_dir(&path) + .context(|| format!("could not read directory `{}`", path.display())) + }) + .await + .map(ReadDir::new) } /// A stream of entries in a directory. @@ -91,7 +95,7 @@ impl Stream for ReadDir { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - self.0 = State::Busy(blocking::spawn(move || { + self.0 = State::Busy(spawn_blocking(move || { let next = inner.next(); (inner, next) })); diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index eaa7b6245..d8cabb725 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::{Path, PathBuf}; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads a symbolic link and returns the path it points to. /// @@ -28,5 +29,10 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_link(path).map(Into::into)).await + spawn_blocking(move || { + std::fs::read_link(&path) + .map(Into::into) + .context(|| format!("could not read link `{}`", path.display())) + }) + .await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index 40c4b6b8b..2378aaedc 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Reads the entire contents of a file as a string. /// @@ -37,5 +38,9 @@ use crate::task::blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::read_to_string(path)).await + spawn_blocking(move || { + std::fs::read_to_string(&path) + .context(|| format!("could not read file `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index d1fa7bf33..8fdba18c1 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes an empty directory. /// @@ -29,5 +30,9 @@ use crate::task::blocking; /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_dir(path)).await + spawn_blocking(move || { + std::fs::remove_dir(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 0a0fceb79..d4bad3a28 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a directory and all of its contents. /// @@ -29,5 +30,9 @@ use crate::task::blocking; /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_dir_all(path)).await + spawn_blocking(move || { + std::fs::remove_dir_all(&path) + .context(|| format!("could not remove directory `{}`", path.display())) + }) + .await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 5bc0608dc..b881f8be5 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Removes a file. /// @@ -29,5 +30,9 @@ use crate::task::blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::remove_file(path)).await + spawn_blocking(move || { + std::fs::remove_file(&path) + .context(|| format!("could not remove file `{}`", path.display())) + }) + .await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index c2aa77b7f..25fc55fa2 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Renames a file or directory to a new location. /// @@ -34,5 +35,14 @@ use crate::task::blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(move || std::fs::rename(&from, &to)).await + spawn_blocking(move || { + std::fs::rename(&from, &to).context(|| { + format!( + "could not rename `{}` to `{}`", + from.display(), + to.display() + ) + }) + }) + .await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index d14ced944..60a6d6f16 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,7 +1,7 @@ use crate::fs::Permissions; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Changes the permissions of a file or directory. /// @@ -32,5 +32,5 @@ use crate::task::blocking; /// ``` pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::set_permissions(path, perm)).await + spawn_blocking(move || std::fs::set_permissions(path, perm)).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index bc5cce863..45be6d99d 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -1,7 +1,7 @@ use crate::fs::Metadata; use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Reads metadata for a path without following symbolic links. /// @@ -34,5 +34,5 @@ use crate::task::blocking; /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(move || std::fs::symlink_metadata(path)).await + spawn_blocking(move || std::fs::symlink_metadata(path)).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 3df56042f..7c14098b0 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,6 +1,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; +use crate::utils::Context as _; /// Writes a slice of bytes as the new contents of a file. /// @@ -33,5 +34,9 @@ use crate::task::blocking; pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - blocking::spawn(move || std::fs::write(path, contents)).await + spawn_blocking(move || { + std::fs::write(&path, contents) + .context(|| format!("could not write to file `{}`", path.display())) + }) + .await } diff --git a/src/future/future.rs b/src/future/future.rs deleted file mode 100644 index 8c9c12a23..000000000 --- a/src/future/future.rs +++ /dev/null @@ -1,139 +0,0 @@ -extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; - - use crate::task::{Context, Poll}; - - #[doc = r#" - A future represents an asynchronous computation. - - A future is a value that may not have finished computing yet. This kind of - "asynchronous value" makes it possible for a thread to continue doing useful - work while it waits for the value to become available. - - # The `poll` method - - The core method of future, `poll`, *attempts* to resolve the future into a - final value. This method does not block if the value is not ready. Instead, - the current task is scheduled to be woken up when it's possible to make - further progress by `poll`ing again. The `context` passed to the `poll` - method can provide a [`Waker`], which is a handle for waking up the current - task. - - When using a future, you generally won't call `poll` directly, but instead - `.await` the value. - - [`Waker`]: ../task/struct.Waker.html - "#] - pub trait Future { - #[doc = r#" - The type of value produced on completion. - "#] - type Output; - - #[doc = r#" - Attempt to resolve the future to a final value, registering - the current task for wakeup if the value is not yet available. - - # Return value - - This function returns: - - - [`Poll::Pending`] if the future is not ready yet - - [`Poll::Ready(val)`] with the result `val` of this future if it - finished successfully. - - Once a future has finished, clients should not `poll` it again. - - When a future is not ready yet, `poll` returns `Poll::Pending` and - stores a clone of the [`Waker`] copied from the current [`Context`]. - This [`Waker`] is then woken once the future can make progress. - For example, a future waiting for a socket to become - readable would call `.clone()` on the [`Waker`] and store it. - When a signal arrives elsewhere indicating that the socket is readable, - [`Waker::wake`] is called and the socket future's task is awoken. - Once a task has been woken up, it should attempt to `poll` the future - again, which may or may not produce a final value. - - Note that on multiple calls to `poll`, only the [`Waker`] from the - [`Context`] passed to the most recent call should be scheduled to - receive a wakeup. - - # Runtime characteristics - - Futures alone are *inert*; they must be *actively* `poll`ed to make - progress, meaning that each time the current task is woken up, it should - actively re-`poll` pending futures that it still has an interest in. - - The `poll` function is not called repeatedly in a tight loop -- instead, - it should only be called when the future indicates that it is ready to - make progress (by calling `wake()`). If you're familiar with the - `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures - typically do *not* suffer the same problems of "all wakeups must poll - all events"; they are more like `epoll(4)`. - - An implementation of `poll` should strive to return quickly, and should - not block. Returning quickly prevents unnecessarily clogging up - threads or event loops. If it is known ahead of time that a call to - `poll` may end up taking awhile, the work should be offloaded to a - thread pool (or something similar) to ensure that `poll` can return - quickly. - - # Panics - - Once a future has completed (returned `Ready` from `poll`), calling its - `poll` method again may panic, block forever, or cause other kinds of - problems; the `Future` trait places no requirements on the effects of - such a call. However, as the `poll` 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 future's state. - - [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending - [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready - [`Context`]: ../task/struct.Context.html - [`Waker`]: ../task/struct.Waker.html - [`Waker::wake`]: ../task/struct.Waker.html#method.wake - "#] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll; - } - - pub trait FutureExt: std::future::Future { - } - - impl Future for Box { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Future for &mut F { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl

Future for Pin

- where - P: DerefMut + Unpin, -

::Target: Future, - { - type Output = <

::Target as Future>::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } - - impl Future for std::panic::AssertUnwindSafe { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unreachable!("this impl only appears in the rendered docs") - } - } -} diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs new file mode 100644 index 000000000..092639d91 --- /dev/null +++ b/src/future/future/delay.rs @@ -0,0 +1,43 @@ +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use crate::utils::{timer_after, Timer}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct DelayFuture { + #[pin] + future: F, + #[pin] + delay: Timer, + } +} + +impl DelayFuture { + pub fn new(future: F, dur: Duration) -> DelayFuture { + let delay = timer_after(dur); + + DelayFuture { future, delay } + } +} + +impl Future for DelayFuture { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + match this.delay.poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => match this.future.poll(cx) { + Poll::Ready(v) => Poll::Ready(v), + Poll::Pending => Poll::Pending, + }, + } + } +} diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs new file mode 100644 index 000000000..0ea77bbfb --- /dev/null +++ b/src/future/future/flatten.rs @@ -0,0 +1,65 @@ +use pin_project_lite::pin_project; +use std::future::Future; +use std::pin::Pin; + +use crate::future::IntoFuture; +use crate::task::{ready, Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FlattenFuture { + #[pin] + state: State, + } +} + +pin_project! { + #[project = StateProj] + #[derive(Debug)] + enum State { + First { + #[pin] + fut1: Fut1, + }, + Second { + #[pin] + fut2: Fut2, + }, + Empty, + } +} + +impl FlattenFuture { + pub(crate) fn new(fut1: Fut1) -> FlattenFuture { + FlattenFuture { + state: State::First { fut1 }, + } + } +} + +impl Future for FlattenFuture::Future> +where + Fut1: Future, + Fut1::Output: IntoFuture, +{ + type Output = ::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut state = self.project().state; + loop { + match state.as_mut().project() { + StateProj::First { fut1 } => { + let fut2 = ready!(fut1.poll(cx)).into_future(); + state.set(State::Second { fut2 }); + } + StateProj::Second { fut2 } => { + let v = ready!(fut2.poll(cx)); + state.set(State::Empty); + return Poll::Ready(v); + } + StateProj::Empty => panic!("polled a completed future"), + } + } + } +} diff --git a/src/future/future/join.rs b/src/future/future/join.rs new file mode 100644 index 000000000..4a508ce8e --- /dev/null +++ b/src/future/future/join.rs @@ -0,0 +1,60 @@ +use std::pin::Pin; + +use crate::future::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct Join + where + L: Future, + R: Future, + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl Join +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for Join +where + L: Future, + R: Future, +{ + type Output = (L::Output, R::Output); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + let mut right = this.right; + + let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready(); + if is_left_ready && right.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); + } + + let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready(); + if is_right_ready && left.as_ref().output().is_some() { + return Poll::Ready((left.take().unwrap(), right.take().unwrap())); + } + + Poll::Pending + } +} diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs new file mode 100644 index 000000000..9a8bfcc1f --- /dev/null +++ b/src/future/future/mod.rs @@ -0,0 +1,290 @@ +cfg_unstable! { + mod delay; + mod flatten; + mod race; + mod try_race; + mod join; + mod try_join; + + use std::time::Duration; + use delay::DelayFuture; + use flatten::FlattenFuture; + use crate::future::IntoFuture; + use race::Race; + use try_race::TryRace; + use join::Join; + use try_join::TryJoin; +} + +cfg_unstable_default! { + use crate::future::timeout::TimeoutFuture; +} + +pub use core::future::Future as Future; + +#[doc = r#" + Extension methods for [`Future`]. + + [`Future`]: ../future/trait.Future.html +"#] +#[cfg(any(feature = "std", feature = "docs"))] +pub trait FutureExt: Future { + /// Returns a Future that delays execution for a specified time. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use async_std::prelude::*; + /// use async_std::future; + /// use std::time::Duration; + /// + /// let a = future::ready(1).delay(Duration::from_millis(2000)); + /// dbg!(a.await); + /// # }) + /// ``` + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn delay(self, dur: Duration) -> DelayFuture + where + Self: Sized, + { + DelayFuture::new(self, dur) + } + + /// Flatten out the execution of this future when the result itself + /// can be converted into another future. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use async_std::prelude::*; + /// + /// let nested_future = async { async { 1 } }; + /// let future = nested_future.flatten(); + /// assert_eq!(future.await, 1); + /// # }) + /// ``` + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flatten( + self, + ) -> FlattenFuture::Future> + where + Self: Sized, + ::Output: IntoFuture, + { + FlattenFuture::new(self) + } + + #[doc = r#" + Waits for one of two similarly-typed futures to complete. + + Awaits multiple futures simultaneously, returning the output of the + first future that completes. + + This function will return a new future which awaits for either one of both + futures to complete. If multiple futures are completed at the same time, + resolution will occur in the order that they have been passed. + + Note that this function consumes all futures passed, and once a future is + completed, all other futures are dropped. + + # Examples + + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::future; + + let a = future::pending(); + let b = future::ready(1u8); + let c = future::ready(2u8); + + let f = a.race(b).race(c); + assert_eq!(f.await, 1u8); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn race( + self, + other: F, + ) -> Race + where + Self: std::future::Future + Sized, + F: std::future::Future::Output>, + { + Race::new(self, other) + } + + #[doc = r#" + Waits for one of two similarly-typed fallible futures to complete. + + Awaits multiple futures simultaneously, returning all results once complete. + + `try_race` is similar to [`race`], but keeps going if a future + resolved to an error until all futures have been resolved. In which case + an error is returned. + + The ordering of which value is yielded when two futures resolve + simultaneously is intentionally left unspecified. + + [`race`]: #method.race + + # Examples + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::future; + use std::io::{Error, ErrorKind}; + + let a = future::pending::>(); + let b = future::ready(Err(Error::from(ErrorKind::Other))); + let c = future::ready(Ok(1u8)); + + let f = a.try_race(b).try_race(c); + assert_eq!(f.await?, 1u8); + # + # Ok(()) }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_race( + self, + other: F + ) -> TryRace + where + Self: std::future::Future> + Sized, + F: std::future::Future::Output>, + { + TryRace::new(self, other) + } + + #[doc = r#" + Waits for two similarly-typed futures to complete. + + Awaits multiple futures simultaneously, returning the output of the + futures once both complete. + + This function returns a new future which polls both futures + concurrently. + + # Examples + + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::future; + + let a = future::ready(1u8); + let b = future::ready(2u16); + + let f = a.join(b); + assert_eq!(f.await, (1u8, 2u16)); + # }); + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn join( + self, + other: F + ) -> Join + where + Self: std::future::Future + Sized, + F: std::future::Future, + { + Join::new(self, other) + } + + #[doc = r#" + Waits for two similarly-typed fallible futures to complete. + + Awaits multiple futures simultaneously, returning all results once + complete. + + `try_join` is similar to [`join`], but returns an error immediately + if a future resolves to an error. + + [`join`]: #method.join + + # Examples + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::future; + + let a = future::ready(Err::("Error")); + let b = future::ready(Ok(1u8)); + + let f = a.try_join(b); + assert_eq!(f.await, Err("Error")); + + let a = future::ready(Ok::(1u8)); + let b = future::ready(Ok::(2u16)); + + let f = a.try_join(b); + assert_eq!(f.await, Ok((1u8, 2u16))); + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn try_join( + self, + other: F + ) -> TryJoin + where + Self: std::future::Future> + Sized, + F: std::future::Future>, + { + TryJoin::new(self, other) + } + + #[doc = r#" + Waits for both the future and a timeout, if the timeout completes before + the future, it returns a TimeoutError. + + # Example + ``` + # async_std::task::block_on(async { + # + use std::time::Duration; + + use async_std::prelude::*; + use async_std::future; + + let fut = future::ready(0); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_ok()); + + let fut = future::pending::<()>(); + let dur = Duration::from_millis(100); + let res = fut.timeout(dur).await; + assert!(res.is_err()) + # + # }); + ``` + "#] + #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> TimeoutFuture + where Self: Sized + { + TimeoutFuture::new(self, dur) + } +} + +#[cfg(any(feature = "std", feature = "docs"))] +impl FutureExt for T {} + diff --git a/src/future/future/race.rs b/src/future/future/race.rs new file mode 100644 index 000000000..82165a0f1 --- /dev/null +++ b/src/future/future/race.rs @@ -0,0 +1,57 @@ +use std::future::Future; +use std::pin::Pin; + +use crate::future::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct Race + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl Race +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for Race +where + L: Future, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + if Future::poll(Pin::new(&mut left), cx).is_ready() { + return Poll::Ready(left.take().unwrap()); + } + + let mut right = this.right; + if Future::poll(Pin::new(&mut right), cx).is_ready() { + return Poll::Ready(right.take().unwrap()); + } + + Poll::Pending + } +} diff --git a/src/future/future/try_join.rs b/src/future/future/try_join.rs new file mode 100644 index 000000000..5277ae317 --- /dev/null +++ b/src/future/future/try_join.rs @@ -0,0 +1,72 @@ +use std::pin::Pin; + +use crate::future::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct TryJoin + where + L: Future, + R: Future, + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl TryJoin +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for TryJoin +where + L: Future>, + R: Future>, +{ + type Output = Result<(A, B), E>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut left = this.left; + let mut right = this.right; + + if Future::poll(Pin::new(&mut left), cx).is_ready() { + if left.as_ref().output().unwrap().is_err() { + return Poll::Ready(Err(left.take().unwrap().err().unwrap())); + } else if right.as_ref().output().is_some() { + return Poll::Ready(Ok(( + left.take().unwrap().ok().unwrap(), + right.take().unwrap().ok().unwrap(), + ))); + } + } + + if Future::poll(Pin::new(&mut right), cx).is_ready() { + if right.as_ref().output().unwrap().is_err() { + return Poll::Ready(Err(right.take().unwrap().err().unwrap())); + } else if left.as_ref().output().is_some() { + return Poll::Ready(Ok(( + left.take().unwrap().ok().unwrap(), + right.take().unwrap().ok().unwrap(), + ))); + } + } + + Poll::Pending + } +} diff --git a/src/future/future/try_race.rs b/src/future/future/try_race.rs new file mode 100644 index 000000000..45199570f --- /dev/null +++ b/src/future/future/try_race.rs @@ -0,0 +1,66 @@ +use std::pin::Pin; + +use crate::future::MaybeDone; +use pin_project_lite::pin_project; + +use crate::task::{Context, Poll}; +use std::future::Future; + +pin_project! { + #[allow(missing_docs)] + #[allow(missing_debug_implementations)] + pub struct TryRace + where + L: Future, + R: Future + { + #[pin] left: MaybeDone, + #[pin] right: MaybeDone, + } +} + +impl TryRace +where + L: Future, + R: Future, +{ + pub(crate) fn new(left: L, right: R) -> Self { + Self { + left: MaybeDone::new(left), + right: MaybeDone::new(right), + } + } +} + +impl Future for TryRace +where + L: Future>, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let mut left_errored = false; + + // Check if the left future is ready & successful. Continue if not. + let mut left = this.left; + if Future::poll(Pin::new(&mut left), cx).is_ready() { + if left.as_ref().output().unwrap().is_ok() { + return Poll::Ready(left.take().unwrap()); + } else { + left_errored = true; + } + } + + // Check if the right future is ready & successful. Return err if left + // future also resolved to err. Continue if not. + let mut right = this.right; + let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready(); + if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) { + return Poll::Ready(right.take().unwrap()); + } + + Poll::Pending + } +} diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 42839a203..8e5e5e046 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -1,4 +1,4 @@ -use crate::future::Future; +use std::future::Future; /// Convert a type into a `Future`. /// @@ -45,7 +45,6 @@ pub trait IntoFuture { impl IntoFuture for T { type Output = T::Output; - type Future = T; fn into_future(self) -> Self::Future { diff --git a/src/future/maybe_done.rs b/src/future/maybe_done.rs new file mode 100644 index 000000000..7a3a7fa33 --- /dev/null +++ b/src/future/maybe_done.rs @@ -0,0 +1,79 @@ +//! A type that wraps a future to keep track of its completion status. +//! +//! This implementation was taken from the original `macro_rules` `join/try_join` +//! macros in the `futures-preview` crate. + +use std::future::Future; +use std::mem; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use futures_core::ready; + +/// A future that may have completed. +#[derive(Debug)] +pub(crate) enum MaybeDone { + /// A not-yet-completed future + Future(Fut), + + /// The output of the completed future + Done(Fut::Output), + + /// The empty variant after the result of a [`MaybeDone`] has been + /// taken using the [`take`](MaybeDone::take) method. + Gone, +} + +impl MaybeDone { + /// Create a new instance of `MaybeDone`. + pub(crate) fn new(future: Fut) -> MaybeDone { + Self::Future(future) + } + + /// Returns an [`Option`] containing a reference to the output of the future. + /// The output of this method will be [`Some`] if and only if the inner + /// future has been completed and [`take`](MaybeDone::take) + /// has not yet been called. + #[inline] + pub(crate) fn output(self: Pin<&Self>) -> Option<&Fut::Output> { + let this = self.get_ref(); + match this { + MaybeDone::Done(res) => Some(res), + _ => None, + } + } + + /// Attempt to take the output of a `MaybeDone` without driving it + /// towards completion. + #[inline] + pub(crate) fn take(self: Pin<&mut Self>) -> Option { + unsafe { + let this = self.get_unchecked_mut(); + match this { + MaybeDone::Done(_) => {} + MaybeDone::Future(_) | MaybeDone::Gone => return None, + }; + if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) { + Some(output) + } else { + unreachable!() + } + } + } +} + +impl Future for MaybeDone { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let res = unsafe { + match Pin::as_mut(&mut self).get_unchecked_mut() { + MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)), + MaybeDone::Done(_) => return Poll::Ready(()), + MaybeDone::Gone => panic!("MaybeDone polled after value taken"), + } + }; + self.set(MaybeDone::Done(res)); + Poll::Ready(()) + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index a45bf96cd..1b3cd5863 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -2,64 +2,73 @@ //! //! ## Base Futures Concurrency //! -//! Often it's desireable to await multiple futures as if it was a single +//! Often it's desirable to await multiple futures as if it was a single //! future. The `join` family of operations converts multiple futures into a -//! single future that returns all of their outputs. The `select` family of +//! single future that returns all of their outputs. The `race` family of //! operations converts multiple future into a single future that returns the //! first output. //! -//! For operating on futures the following macros can be used: +//! For operating on futures the following functions can be used: //! -//! | Name | Return signature | When does it return? | -//! | --- | --- | --- | -//! | `future::join` | `(T1, T2)` | Wait for all to complete -//! | `future::select` | `T` | Return on first value +//! | Name | Return signature | When does it return? | +//! | --- | --- | --- | +//! | [`Future::join`] | `(T1, T2)` | Wait for all to complete +//! | [`Future::race`] | `T` | Return on first value //! //! ## Fallible Futures Concurrency //! //! For operating on futures that return `Result` additional `try_` variants of -//! the macros mentioned before can be used. These macros are aware of `Result`, +//! the functions mentioned before can be used. These functions are aware of `Result`, //! and will behave slightly differently from their base variants. //! //! In the case of `try_join`, if any of the futures returns `Err` all //! futures are dropped and an error is returned. This is referred to as //! "short-circuiting". //! -//! In the case of `try_select`, instead of returning the first future that +//! In the case of `try_race`, instead of returning the first future that //! completes it returns the first future that _successfully_ completes. This -//! means `try_select` will keep going until any one of the futures returns +//! means `try_race` will keep going until any one of the futures returns //! `Ok`, or _all_ futures have returned `Err`. //! -//! However sometimes it can be useful to use the base variants of the macros +//! However sometimes it can be useful to use the base variants of the functions //! even on futures that return `Result`. Here is an overview of operations that //! work on `Result`, and their respective semantics: //! -//! | Name | Return signature | When does it return? | -//! | --- | --- | --- | -//! | `future::join` | `(Result, Result)` | Wait for all to complete -//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete -//! | `future::select` | `Result` | Return on first value -//! | `future::try_select` | `Result` | Return on first `Ok`, reject on last Err +//! | Name | Return signature | When does it return? | +//! | --- | --- | --- | +//! | [`Future::join`] | `(Result, Result)` | Wait for all to complete +//! | [`Future::try_join`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete +//! | [`Future::race`] | `Result` | Return on first value +//! | [`Future::try_race`] | `Result` | Return on first `Ok`, reject on last Err +//! +//! [`Future::join`]: trait.Future.html#method.join +//! [`Future::try_join`]: trait.Future.html#method.try_join +//! [`Future::race`]: trait.Future.html#method.race +//! [`Future::try_race`]: trait.Future.html#method.try_race -#[doc(inline)] -pub use async_macros::{join, try_join}; +cfg_alloc! { + pub use future::Future; + pub(crate) mod future; +} -pub use future::Future; -pub use pending::pending; -pub use poll_fn::poll_fn; -pub use ready::ready; -pub use timeout::{timeout, TimeoutError}; +cfg_std! { + pub use pending::pending; + pub use poll_fn::poll_fn; + pub use ready::ready; -pub(crate) mod future; -mod pending; -mod poll_fn; -mod ready; + mod pending; + mod poll_fn; + mod ready; +} + +#[cfg(any(feature = "unstable", feature = "default"))] +pub use timeout::{timeout, TimeoutError}; +#[cfg(any(feature = "unstable", feature = "default"))] mod timeout; cfg_unstable! { - #[doc(inline)] - pub use async_macros::{select, try_select}; - pub use into_future::IntoFuture; + pub(crate) use maybe_done::MaybeDone; mod into_future; + mod maybe_done; } diff --git a/src/future/pending.rs b/src/future/pending.rs index 2138a3012..968972b51 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -1,7 +1,7 @@ +use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; -use crate::future::Future; use crate::task::{Context, Poll}; /// Never resolves to a value. diff --git a/src/future/poll_fn.rs b/src/future/poll_fn.rs index a808f97f3..194526400 100644 --- a/src/future/poll_fn.rs +++ b/src/future/poll_fn.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::task::{Context, Poll}; /// Creates a new future wrapping around a function returning [`Poll`]. diff --git a/src/future/timeout.rs b/src/future/timeout.rs index a8338fbaa..384662149 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -1,12 +1,13 @@ use std::error::Error; use std::fmt; +use std::future::Future; use std::pin::Pin; use std::time::Duration; -use futures_timer::Delay; +use pin_project_lite::pin_project; -use crate::future::Future; use crate::task::{Context, Poll}; +use crate::utils::{timer_after, Timer}; /// Awaits a future or times out after a duration of time. /// @@ -32,31 +33,37 @@ pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, { - let f = TimeoutFuture { - future: f, - delay: Delay::new(dur), - }; - f.await + TimeoutFuture::new(f, dur).await } -/// A future that times out after a duration of time. -struct TimeoutFuture { - future: F, - delay: Delay, +pin_project! { + /// A future that times out after a duration of time. + pub struct TimeoutFuture { + #[pin] + future: F, + #[pin] + delay: Timer, + } } impl TimeoutFuture { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(delay: Delay); + #[allow(dead_code)] + pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture { + TimeoutFuture { + future, + delay: timer_after(dur), + } + } } impl Future for TimeoutFuture { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Ready(v) => Poll::Ready(Ok(v)), - Poll::Pending => match self.delay().poll(cx) { + Poll::Pending => match this.delay.poll(cx) { Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Pending => Poll::Pending, }, diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 6cb4a0769..c60529cd7 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -2,50 +2,55 @@ use std::mem; use std::pin::Pin; use std::str; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream of lines in a byte stream. -/// -/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Lines`]. -/// -/// [`lines`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html -#[derive(Debug)] -pub struct Lines { - pub(crate) reader: R, - pub(crate) buf: String, - pub(crate) bytes: Vec, - pub(crate) read: usize, +pin_project! { + /// A stream of lines in a byte stream. + /// + /// This stream is created by the [`lines`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Lines`]. + /// + /// [`lines`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html + #[derive(Debug)] + pub struct Lines { + #[pin] + pub(crate) reader: R, + pub(crate) buf: String, + pub(crate) bytes: Vec, + pub(crate) read: usize, + } } 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 = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::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/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index b82971fe7..bcb9d907c 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -15,326 +15,231 @@ use std::pin::Pin; use crate::io; use crate::task::{Context, Poll}; -extension_trait! { - use std::ops::{Deref, DerefMut}; +pub use futures_io::AsyncBufRead as BufRead; +#[doc = r#" + Extension methods for [`BufRead`]. + + [`BufRead`]: ../trait.BufRead.html +"#] +pub trait BufReadExt: BufRead { #[doc = r#" - Allows reading from a buffered byte stream. + Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - This trait is a re-export of [`futures::io::AsyncBufRead`] and is an async version of - [`std::io::BufRead`]. + This function will read bytes from the underlying stream until the delimiter or EOF + is found. Once found, all bytes up to, and including, the delimiter (if found) will + be appended to `buf`. - The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + If successful, this function will return the total number of bytes read. - ``` - # #[allow(unused_imports)] + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; use async_std::prelude::*; + + let mut file = BufReader::new(File::open("a.txt").await?); + + let mut buf = Vec::with_capacity(1024); + let n = file.read_until(b'\n', &mut buf).await?; + # + # Ok(()) }) } + ``` + + Multiple successful calls to `read_until` append all bytes up to and including to + `buf`: ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::BufReader; + use async_std::prelude::*; - [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html - [`futures::io::AsyncBufRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html - [provided methods]: #provided-methods + let from: &[u8] = b"append\nexample\n"; + let mut reader = BufReader::new(from); + let mut buf = vec![]; + + let mut size = reader.read_until(b'\n', &mut buf).await?; + assert_eq!(size, 7); + assert_eq!(buf, b"append\n"); + + size += reader.read_until(b'\n', &mut buf).await?; + assert_eq!(size, from.len()); + + assert_eq!(buf, from); + # + # Ok(()) }) } + ``` "#] - pub trait BufRead { - #[doc = r#" - Returns the contents of the internal buffer, filling it with more data from the - inner reader if it is empty. - - This function is a lower-level call. It needs to be paired with the [`consume`] - method to function properly. When calling this method, none of the contents will be - "read" in the sense that later calling `read` may return the same contents. As - such, [`consume`] must be called with the number of bytes that are consumed from - this buffer to ensure that the bytes are never returned twice. - - [`consume`]: #tymethod.consume - - An empty buffer returned indicates that the stream has reached EOF. - "#] - // TODO: write a proper doctest with `consume` - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - - #[doc = r#" - Tells this buffer that `amt` bytes have been consumed from the buffer, so they - should no longer be returned in calls to `read`. - "#] - fn consume(self: Pin<&mut Self>, amt: usize); + fn read_until<'a>( + &'a mut self, + byte: u8, + buf: &'a mut Vec, + ) -> ReadUntilFuture<'a, Self> + where + Self: Unpin, + { + ReadUntilFuture { + reader: self, + byte, + buf, + read: 0, + } } - pub trait BufReadExt: futures_io::AsyncBufRead { - #[doc = r#" - Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - - This function will read bytes from the underlying stream until the delimiter or EOF - is found. Once found, all bytes up to, and including, the delimiter (if found) will - be appended to `buf`. - - If successful, this function will return the total number of bytes read. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; - - let mut file = BufReader::new(File::open("a.txt").await?); - - let mut buf = Vec::with_capacity(1024); - let n = file.read_until(b'\n', &mut buf).await?; - # - # Ok(()) }) } - ``` - - Multiple successful calls to `read_until` append all bytes up to and including to - `buf`: - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::BufReader; - use async_std::prelude::*; - - let from: &[u8] = b"append\nexample\n"; - let mut reader = BufReader::new(from); - let mut buf = vec![]; - - let mut size = reader.read_until(b'\n', &mut buf).await?; - assert_eq!(size, 7); - assert_eq!(buf, b"append\n"); - - size += reader.read_until(b'\n', &mut buf).await?; - assert_eq!(size, from.len()); - - assert_eq!(buf, from); - # - # Ok(()) }) } - ``` - "#] - fn read_until<'a>( - &'a mut self, - byte: u8, - buf: &'a mut Vec, - ) -> impl Future + 'a [ReadUntilFuture<'a, Self>] - where - Self: Unpin, - { - ReadUntilFuture { - reader: self, - byte, - buf, - read: 0, - } - } + #[doc = r#" + Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is + reached. - #[doc = r#" - Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is - reached. - - This function will read bytes from the underlying stream until the newline - delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and - including, the delimiter (if found) will be appended to `buf`. - - If successful, this function will return the total number of bytes read. - - If this function returns `Ok(0)`, the stream has reached EOF. - - # Errors - - This function has the same error semantics as [`read_until`] and will also return - an error if the read bytes are not valid UTF-8. If an I/O error is encountered then - `buf` may contain some bytes already read in the event that all data read so far - was valid UTF-8. - - [`read_until`]: #method.read_until - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; - - let mut file = BufReader::new(File::open("a.txt").await?); - - let mut buf = String::new(); - file.read_line(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_line<'a>( - &'a mut self, - buf: &'a mut String, - ) -> impl Future> + 'a [ReadLineFuture<'a, Self>] - where - Self: Unpin, - { - ReadLineFuture { - reader: self, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - read: 0, - } - } + This function will read bytes from the underlying stream until the newline + delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and + including, the delimiter (if found) will be appended to `buf`. - #[doc = r#" - Returns a stream over the lines of this byte stream. + If successful, this function will return the total number of bytes read. - The stream returned from this function will yield instances of - [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte - (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. + If this function returns `Ok(0)`, the stream has reached EOF. - [`io::Result`]: type.Result.html - [`String`]: https://doc.rust-lang.org/std/string/struct.String.html + # Errors - # Examples + This function has the same error semantics as [`read_until`] and will also return + an error if the read bytes are not valid UTF-8. If an I/O error is encountered then + `buf` may contain some bytes already read in the event that all data read so far + was valid UTF-8. - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::BufReader; - use async_std::prelude::*; + [`read_until`]: #method.read_until - let file = File::open("a.txt").await?; - let mut lines = BufReader::new(file).lines(); - let mut count = 0; + # Examples - while let Some(line) = lines.next().await { - line?; - count += 1; - } - # - # Ok(()) }) } - ``` - "#] - fn lines(self) -> Lines - where - Self: Unpin + Sized, - { - Lines { - reader: self, - buf: String::new(), - bytes: Vec::new(), - read: 0, - } - } + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; + use async_std::prelude::*; - #[doc = r#" - Returns a stream over the contents of this reader split on the byte `byte`. - - The stream returned from this function will return instances of - [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have - the delimiter byte at the end. - - This function will yield errors whenever [`read_until`] would have - also yielded an error. - - [`io::Result`]: type.Result.html - [`Vec`]: ../vec/struct.Vec.html - [`read_until`]: #method.read_until - - # Examples - - [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - this example, we use [`Cursor`] to iterate over all hyphen delimited - segments in a byte slice - - [`Cursor`]: struct.Cursor.html - - ``` - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::io; - - let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - - let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - assert_eq!(split_iter.next().await, Some(b"lorem".to_vec())); - assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec())); - assert_eq!(split_iter.next().await, Some(b"dolor".to_vec())); - assert_eq!(split_iter.next().await, None); - # - # Ok(()) }) } - ``` - "#] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { - reader: self, - buf: Vec::new(), - delim: byte, - read: 0, - } + let mut file = BufReader::new(File::open("a.txt").await?); + + let mut buf = String::new(); + file.read_line(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_line<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ReadLineFuture<'a, Self> + where + Self: Unpin, + { + ReadLineFuture { + reader: self, + bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + buf, + read: 0, } } - impl BufRead for Box { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + #[doc = r#" + Returns a stream over the lines of this byte stream. - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } + The stream returned from this function will yield instances of + [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte + (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. - impl BufRead for &mut T { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + [`io::Result`]: type.Result.html + [`String`]: https://doc.rust-lang.org/std/string/struct.String.html - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") - } - } + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::BufReader; + use async_std::prelude::*; - impl

BufRead for Pin

+ let file = File::open("a.txt").await?; + let mut lines = BufReader::new(file).lines(); + let mut count = 0; + + while let Some(line) = lines.next().await { + line?; + count += 1; + } + # + # Ok(()) }) } + ``` + "#] + fn lines(self) -> Lines where - P: DerefMut + Unpin, -

::Target: BufRead, + Self: Unpin + Sized, { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") + Lines { + reader: self, + buf: String::new(), + bytes: Vec::new(), + read: 0, } } - impl BufRead for &[u8] { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - unreachable!() - } + #[doc = r#" + Returns a stream over the contents of this reader split on the byte `byte`. - fn consume(self: Pin<&mut Self>, amt: usize) { - unreachable!("this impl only appears in the rendered docs") + The stream returned from this function will return instances of + [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have + the delimiter byte at the end. + + This function will yield errors whenever [`read_until`] would have + also yielded an error. + + [`io::Result`]: type.Result.html + [`Vec`]: ../vec/struct.Vec.html + [`read_until`]: #method.read_until + + # Examples + + [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + this example, we use [`Cursor`] to iterate over all hyphen delimited + segments in a byte slice + + [`Cursor`]: struct.Cursor.html + + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::io; + + let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + + let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + assert_eq!(split_iter.next().await, Some(b"lorem".to_vec())); + assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec())); + assert_eq!(split_iter.next().await, Some(b"dolor".to_vec())); + assert_eq!(split_iter.next().await, None); + # + # Ok(()) }) } + ``` + "#] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { + reader: self, + buf: Vec::new(), + delim: byte, + read: 0, } } } -pub fn read_until_internal( +impl BufReadExt for T {} + +pub(crate) fn read_until_internal( mut reader: Pin<&mut R>, cx: &mut Context<'_>, byte: u8, diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs index 29866be0f..b66079bc8 100644 --- a/src/io/buf_read/read_line.rs +++ b/src/io/buf_read/read_line.rs @@ -1,9 +1,9 @@ use std::mem; use std::pin::Pin; use std::str; +use std::future::Future; use super::read_until_internal; -use crate::future::Future; use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; @@ -37,8 +37,12 @@ impl Future for ReadLineFuture<'_, T> { )) })) } else { - debug_assert!(buf.is_empty()); - debug_assert_eq!(*read, 0); + #[allow(clippy::debug_assert_with_mut_call)] + { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + } + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. mem::swap(unsafe { buf.as_mut_vec() }, bytes); Poll::Ready(ret) diff --git a/src/io/buf_read/read_until.rs b/src/io/buf_read/read_until.rs index 72385abbe..bda1eee90 100644 --- a/src/io/buf_read/read_until.rs +++ b/src/io/buf_read/read_until.rs @@ -1,7 +1,7 @@ use std::pin::Pin; +use std::future::Future; use super::read_until_internal; -use crate::future::Future; use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; diff --git a/src/io/buf_read/split.rs b/src/io/buf_read/split.rs index aa3b6fb6c..229a99b3a 100644 --- a/src/io/buf_read/split.rs +++ b/src/io/buf_read/split.rs @@ -1,46 +1,51 @@ use std::mem; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::read_until_internal; use crate::io::{self, BufRead}; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. -/// -/// This stream is created by the [`split`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Split`]. -/// -/// [`split`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html -#[derive(Debug)] -pub struct Split { - pub(crate) reader: R, - pub(crate) buf: Vec, - pub(crate) read: usize, - pub(crate) delim: u8, +pin_project! { + /// A stream over the contents of an instance of [`BufRead`] split on a particular byte. + /// + /// This stream is created by the [`split`] method on types that implement [`BufRead`]. + /// + /// This type is an async version of [`std::io::Split`]. + /// + /// [`split`]: trait.BufRead.html#method.lines + /// [`BufRead`]: trait.BufRead.html + /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html + #[derive(Debug)] + pub struct Split { + #[pin] + pub(crate) reader: R, + pub(crate) buf: Vec, + pub(crate) read: usize, + pub(crate) delim: u8, + } } impl Stream for Split { type Item = io::Result>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - read, - delim, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; - if n == 0 && buf.is_empty() { + let this = self.project(); + let n = futures_core::ready!(read_until_internal( + this.reader, + cx, + *this.delim, + this.buf, + this.read + ))?; + if n == 0 && this.buf.is_empty() { return Poll::Ready(None); } - if buf[buf.len() - 1] == *delim { - buf.pop(); + if this.buf[this.buf.len() - 1] == *this.delim { + this.buf.pop(); } - Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) + Poll::Ready(Some(Ok(mem::replace(this.buf, vec![])))) } } diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 13cb4cc56..e6d8e669c 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,51 +2,54 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; -use crate::io::{self, BufRead, Read, Seek, SeekFrom}; -use crate::task::{Context, Poll}; +use pin_project_lite::pin_project; -const DEFAULT_CAPACITY: usize = 8 * 1024; +use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; +use crate::task::{Context, Poll}; -/// Adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` -/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer -/// of the incoming byte stream. -/// -/// `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. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`Read`]: trait.Read.html -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// -/// # Examples -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// # -/// use async_std::fs::File; -/// use async_std::io::BufReader; -/// use async_std::prelude::*; -/// -/// let mut file = BufReader::new(File::open("a.txt").await?); -/// -/// let mut line = String::new(); -/// file.read_line(&mut line).await?; -/// # -/// # Ok(()) }) } -/// ``` -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, +pin_project! { + /// Adds buffering to any reader. + /// + /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` + /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer + /// of the incoming byte stream. + /// + /// `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. + /// + /// This type is an async version of [`std::io::BufReader`]. + /// + /// [`Read`]: trait.Read.html + /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// + /// let mut line = String::new(); + /// file.read_line(&mut line).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub struct BufReader { + #[pin] + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, + } } impl BufReader { @@ -67,7 +70,7 @@ impl BufReader { /// # Ok(()) }) } /// ``` pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_CAPACITY, inner) + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new buffered reader with the specified capacity. @@ -95,10 +98,6 @@ impl BufReader { } impl BufReader { - pin_utils::unsafe_pinned!(inner: R); - pin_utils::unsafe_unpinned!(pos: usize); - pin_utils::unsafe_unpinned!(cap: usize); - /// Gets a reference to the underlying reader. /// /// It is inadvisable to directly read from the underlying reader. @@ -141,6 +140,13 @@ impl BufReader { &mut self.inner } + /// Gets a pinned mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { + self.project().inner + } + /// Returns a reference to the internal buffer. /// /// This function will not attempt to fill the buffer if it is empty. @@ -185,9 +191,10 @@ impl BufReader { /// 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; } } @@ -201,7 +208,7 @@ impl Read for BufReader { // (larger than our internal buffer), bypass our internal buffer // entirely. if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); + let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -218,7 +225,8 @@ impl Read for BufReader { ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.pos == self.cap && total_len >= self.buf.len() { - let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + let res = + futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -234,28 +242,23 @@ impl BufRead for BufReader { self: Pin<&'a 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) }; + let mut 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 = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; - *pos = 0; + if *this.pos >= *this.cap { + debug_assert!(*this.pos == *this.cap); + *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?; + *this.pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&this.buf[*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) { + let this = self.project(); + *this.pos = cmp::min(*this.pos + amt, *this.cap); } } @@ -305,24 +308,26 @@ impl Seek for BufReader { if let Some(offset) = n.checked_sub(remainder) { result = futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(offset)) )?; } else { // seek backwards by our remainder, and then by the offset futures_core::ready!( self.as_mut() - .inner() + .get_pin_mut() .poll_seek(cx, SeekFrom::Current(-remainder)) )?; self.as_mut().discard_buffer(); result = futures_core::ready!( - self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) + self.as_mut() + .get_pin_mut() + .poll_seek(cx, SeekFrom::Current(n)) )?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs index f12aacbc7..230954e88 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -1,96 +1,122 @@ use std::fmt; use std::pin::Pin; -use futures_core::ready; +use pin_project_lite::pin_project; use crate::io::write::WriteExt; -use crate::io::{self, Seek, SeekFrom, Write}; -use crate::task::{Context, Poll}; +use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE}; +use crate::task::{Context, Poll, ready}; -const DEFAULT_CAPACITY: usize = 8 * 1024; +pin_project! { + /// Wraps a writer and buffers its output. + /// + /// It can be excessively inefficient to work directly with something that + /// implements [`Write`]. For example, every call to + /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. 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`. + /// + /// Unlike the `BufWriter` type in `std`, this type does not write out the + /// contents of its buffer when it is dropped. Therefore, it is absolutely + /// critical that users explicitly flush the buffer before dropping a + /// `BufWriter`. + /// + /// This type is an async version of [`std::io::BufWriter`]. + /// + /// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html + /// + /// # Examples + /// + /// Let's write the numbers one through ten to a [`TcpStream`]: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; + /// + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// } + /// # + /// # Ok(()) }) } + /// ``` + /// + /// Because we're not buffering, we write each one in turn, incurring the + /// overhead of a system call per byte written. We can fix this with a + /// `BufWriter`: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// use async_std::io::BufWriter; + /// use async_std::net::TcpStream; + /// use async_std::prelude::*; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); + /// + /// for i in 0..10 { + /// let arr = [i+1]; + /// stream.write(&arr).await?; + /// }; + /// + /// stream.flush().await?; + /// # + /// # Ok(()) }) } + /// ``` + /// + /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped + /// together by the buffer, and will all be written out in one system call when + /// `stream.flush()` completes. (As mentioned above, dropping a `BufWriter` + /// does not flush its buffers, so a `flush` call is essential.) + /// + /// [`Write`]: trait.Write.html + /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write + /// [`TcpStream`]: ../net/struct.TcpStream.html + /// [`flush`]: trait.Write.html#tymethod.flush + pub struct BufWriter { + #[pin] + inner: W, + buf: Vec, + written: usize, + } +} -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. 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 written -/// out. However, any errors that happen in the process of flushing the buffer -/// when the writer is dropped will be ignored. Code that wishes to handle such -/// errors must manually call [`flush`] before the writer is dropped. -/// -/// This type is an async version of [`std::io::BufReader`]. -/// -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html +/// An error returned by `into_inner` which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. /// /// # Examples /// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use async_std::net::TcpStream; -/// use async_std::prelude::*; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; -/// -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; -/// } -/// # -/// # Ok(()) }) } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// use async_std::io::BufWriter; /// use async_std::net::TcpStream; -/// use async_std::prelude::*; /// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); -/// for i in 0..10 { -/// let arr = [i+1]; -/// stream.write(&arr).await?; +/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?); +/// +/// // unwrap the TcpStream and flush the buffer +/// let stream = match buf_writer.into_inner().await { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } /// }; /// # /// # Ok(()) }) } -/// ``` -/// -/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped -/// together by the buffer, and will all be written out in one system call when -/// the `stream` is dropped. -/// -/// [`Write`]: trait.Write.html -/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write -/// [`TcpStream`]: ../net/struct.TcpStream.html -/// [`flush`]: trait.Write.html#tymethod.flush -pub struct BufWriter { - inner: W, - buf: Vec, - written: usize, -} - +///``` #[derive(Debug)] -pub struct IntoInnerError(W, std::io::Error); +pub struct IntoInnerError(W, #[allow(dead_code)] crate::io::Error); impl BufWriter { - pin_utils::unsafe_pinned!(inner: W); - pin_utils::unsafe_unpinned!(buf: Vec); - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. /// @@ -107,7 +133,7 @@ impl BufWriter { /// # Ok(()) }) } /// ``` pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_CAPACITY, inner) + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } /// Creates a new `BufWriter` with the specified buffer capacity. @@ -178,6 +204,13 @@ impl BufWriter { &mut self.inner } + /// Gets a pinned mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { + self.project().inner + } + /// Consumes BufWriter, returning the underlying writer /// /// This method will not write leftover data, it will be lost. @@ -234,16 +267,15 @@ impl BufWriter { /// /// [`LineWriter`]: struct.LineWriter.html fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - inner, - buf, - written, - } = unsafe { Pin::get_unchecked_mut(self) }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; - let len = buf.len(); + let mut this = self.project(); + let len = this.buf.len(); let mut ret = Ok(()); - while *written < len { - match inner.as_mut().poll_write(cx, &buf[*written..]) { + while *this.written < len { + match this + .inner + .as_mut() + .poll_write(cx, &this.buf[*this.written..]) + { Poll::Ready(Ok(0)) => { ret = Err(io::Error::new( io::ErrorKind::WriteZero, @@ -251,7 +283,7 @@ impl BufWriter { )); break; } - Poll::Ready(Ok(n)) => *written += n, + Poll::Ready(Ok(n)) => *this.written += n, Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {} Poll::Ready(Err(e)) => { ret = Err(e); @@ -260,10 +292,10 @@ impl BufWriter { Poll::Pending => return Poll::Pending, } } - if *written > 0 { - buf.drain(..*written); + if *this.written > 0 { + this.buf.drain(..*this.written); } - *written = 0; + *this.written = 0; Poll::Ready(ret) } } @@ -278,26 +310,26 @@ impl Write for BufWriter { ready!(self.as_mut().poll_flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.get_pin_mut().poll_write(cx, buf) } else { - Pin::new(&mut *self.buf()).poll_write(cx, buf) + Pin::new(&mut *self.project().buf).poll_write(cx, buf) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_flush(cx) + self.get_pin_mut().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_close(cx) + self.get_pin_mut().poll_close(cx) } } impl fmt::Debug for BufWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BufReader") + f.debug_struct("BufWriter") .field("writer", &self.inner) .field("buf", &self.buf) .finish() @@ -314,6 +346,6 @@ impl Seek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().poll_flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.get_pin_mut().poll_seek(cx, pos) } } diff --git a/src/io/copy.rs b/src/io/copy.rs index 3840d2af9..e8d5d733f 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,8 +1,17 @@ +use std::future::Future; use std::pin::Pin; -use crate::future::Future; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; +use crate::utils::Context as _; + +// Note: There are two otherwise-identical implementations of this +// function because unstable has removed the `?Sized` bound for the +// reader and writer and accepts `R` and `W` instead of `&mut R` and +// `&mut W`. If making a change to either of the implementations, +// ensure that you copy it into the other. /// Copies the entire contents of a reader into a writer. /// @@ -41,52 +50,148 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` +#[cfg(any(feature = "docs", not(feature = "unstable")))] pub async fn copy(reader: &mut R, writer: &mut W) -> io::Result where R: Read + Unpin + ?Sized, W: Write + Unpin + ?Sized, { - pub struct CopyFuture<'a, R, W: ?Sized> { - reader: R, - writer: &'a mut W, - amt: u64, + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, + reader_eof: bool + } } - impl CopyFuture<'_, R, W> { - fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { - unsafe { - let this = self.get_unchecked_mut(); - ( - Pin::new_unchecked(&mut this.reader), - Pin::new(&mut *this.writer), - &mut this.amt, - ) + impl Future for CopyFuture + where + R: BufRead, + W: Write + Unpin, + { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + if *this.reader_eof { + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); + } + + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; + + if buffer.is_empty() { + *this.reader_eof = true; + continue; + } + + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *this.amt += i as u64; + this.reader.as_mut().consume(i); } } } - impl Future for CopyFuture<'_, R, W> + let future = CopyFuture { + reader: BufReader::new(reader), + writer, + reader_eof: false, + amt: 0, + }; + future.await.context(|| String::from("io::copy failed")) +} + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `reader` and then +/// write it into `writer` in a streaming fashion until `reader` +/// returns EOF. +/// +/// On success, the total number of bytes that were copied from +/// `reader` to `writer` is returned. +/// +/// If you’re wanting to copy the contents of one file to another and you’re +/// working with filesystem paths, see the [`fs::copy`] function. +/// +/// This function is an async version of [`std::io::copy`]. +/// +/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html +/// [`fs::copy`]: ../fs/fn.copy.html +/// +/// # Errors +/// +/// This function will return an error immediately if any call to `read` or +/// `write` returns an error. All instances of `ErrorKind::Interrupted` are +/// handled by this function and the underlying operation is retried. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// +/// let mut reader: &[u8] = b"hello"; +/// let mut writer = io::stdout(); +/// +/// io::copy(&mut reader, &mut writer).await?; +/// # +/// # Ok(()) }) } +/// ``` +#[cfg(all(feature = "unstable", not(feature = "docs")))] +pub async fn copy(reader: R, writer: W) -> io::Result +where + R: Read + Unpin, + W: Write + Unpin, +{ + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, + reader_eof: bool + } + } + + impl Future for CopyFuture where R: BufRead, - W: Write + Unpin + ?Sized, + W: Write + Unpin, { 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 = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; + if *this.reader_eof { + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); + } + + let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?; + if buffer.is_empty() { - futures_core::ready!(writer.as_mut().poll_flush(cx))?; - return Poll::Ready(Ok(*amt)); + *this.reader_eof = true; + continue; } - let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?; if i == 0 { 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); } } } @@ -94,7 +199,8 @@ where let future = CopyFuture { reader: BufReader::new(reader), writer, + reader_eof: false, amt: 0, }; - future.await + future.await.context(|| String::from("io::copy failed")) } diff --git a/src/io/empty.rs b/src/io/empty.rs index d8d768e02..904426757 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -28,9 +28,10 @@ pub fn empty() -> Empty { /// A reader that contains no data. /// -/// This reader is constructed by the [`sink`] function. +/// This reader is created by the [`empty`] function. See its +/// documentation for more. /// -/// [`sink`]: fn.sink.html +/// [`empty`]: fn.empty.html pub struct Empty { _private: (), } diff --git a/src/io/mod.rs b/src/io/mod.rs index 9a125b20b..8a415eb71 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -19,8 +19,8 @@ //! [`File`]s: //! //! ```no_run -//! use async_std::prelude::*; //! use async_std::fs::File; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -47,9 +47,9 @@ //! coming from: //! //! ```no_run -//! use async_std::io::prelude::*; -//! use async_std::io::SeekFrom; //! use async_std::fs::File; +//! use async_std::io::SeekFrom; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -82,9 +82,9 @@ //! methods to any reader: //! //! ```no_run -//! use async_std::io::prelude::*; -//! use async_std::io::BufReader; //! use async_std::fs::File; +//! use async_std::io::BufReader; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -104,9 +104,9 @@ //! to [`write`][`Write::write`]: //! //! ```no_run +//! use async_std::fs::File; //! use async_std::io::prelude::*; //! use async_std::io::BufWriter; -//! use async_std::fs::File; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -116,8 +116,8 @@ //! //! // write a byte to the buffer //! writer.write(&[42]).await?; -//! //! } // the buffer is flushed once writer goes out of scope +//! // //! # //! # Ok(()) }) } //! ``` @@ -179,9 +179,9 @@ //! lines: //! //! ```no_run -//! use async_std::prelude::*; -//! use async_std::io::BufReader; //! use async_std::fs::File; +//! use async_std::io::BufReader; +//! use async_std::prelude::*; //! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # @@ -269,45 +269,62 @@ //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap -#[doc(inline)] -pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; +const DEFAULT_BUF_SIZE: usize = 8 * 1024; + +cfg_std! { + #[doc(inline)] + pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; + + pub use buf_read::*; + pub use buf_reader::BufReader; + pub use buf_writer::{BufWriter, IntoInnerError}; + pub use copy::copy; + pub use cursor::Cursor; + pub use empty::{empty, Empty}; + pub use read::*; + pub use repeat::{repeat, Repeat}; + pub use seek::*; + pub use sink::{sink, Sink}; + pub use write::*; + + pub mod prelude; -pub use buf_read::{BufRead, Lines}; -pub use buf_reader::BufReader; -pub use buf_writer::BufWriter; -pub use copy::copy; -pub use cursor::Cursor; -pub use empty::{empty, Empty}; -pub use read::Read; -pub use repeat::{repeat, Repeat}; -pub use seek::Seek; -pub use sink::{sink, Sink}; -pub use stderr::{stderr, Stderr}; -pub use stdin::{stdin, Stdin}; -pub use stdout::{stdout, Stdout}; -pub use timeout::timeout; -pub use write::Write; + pub(crate) mod buf_read; + pub(crate) mod read; + pub(crate) mod seek; + pub(crate) mod write; + pub(crate) mod utils; -// For use in the print macros. -#[doc(hidden)] -pub use stdio::{_eprint, _print}; + mod buf_reader; + mod buf_writer; + mod copy; + mod cursor; + mod empty; + mod repeat; + mod sink; +} -pub mod prelude; +cfg_default! { + // For use in the print macros. + #[doc(hidden)] + #[cfg(not(target_os = "unknown"))] + pub use stdio::{_eprint, _print}; -pub(crate) mod buf_read; -pub(crate) mod read; -pub(crate) mod seek; -pub(crate) mod write; + #[cfg(not(target_os = "unknown"))] + pub use stderr::{stderr, Stderr}; + #[cfg(not(target_os = "unknown"))] + pub use stdin::{stdin, Stdin}; + #[cfg(not(target_os = "unknown"))] + pub use stdout::{stdout, Stdout}; + pub use timeout::timeout; -mod buf_reader; -mod buf_writer; -mod copy; -mod cursor; -mod empty; -mod repeat; -mod sink; -mod stderr; -mod stdin; -mod stdio; -mod stdout; -mod timeout; + mod timeout; + #[cfg(not(target_os = "unknown"))] + mod stderr; + #[cfg(not(target_os = "unknown"))] + mod stdin; + #[cfg(not(target_os = "unknown"))] + mod stdio; + #[cfg(not(target_os = "unknown"))] + mod stdout; +} diff --git a/src/io/prelude.rs b/src/io/prelude.rs index fb1b94562..c90b289af 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -1,4 +1,4 @@ -//! The async I/O Prelude +//! The async I/O prelude. //! //! The purpose of this module is to alleviate imports of many common I/O traits //! by adding a glob import to the top of I/O heavy modules: @@ -17,11 +17,11 @@ pub use crate::io::Seek; #[doc(no_inline)] pub use crate::io::Write; -#[doc(hidden)] -pub use crate::io::buf_read::BufReadExt as _; -#[doc(hidden)] -pub use crate::io::read::ReadExt as _; -#[doc(hidden)] -pub use crate::io::seek::SeekExt as _; -#[doc(hidden)] -pub use crate::io::write::WriteExt as _; +#[doc(inline)] +pub use crate::io::buf_read::BufReadExt; +#[doc(inline)] +pub use crate::io::read::ReadExt; +#[doc(inline)] +pub use crate::io::seek::SeekExt; +#[doc(inline)] +pub use crate::io::write::WriteExt; diff --git a/src/io/read/bytes.rs b/src/io/read/bytes.rs index 422452433..786fdaa57 100644 --- a/src/io/read/bytes.rs +++ b/src/io/read/bytes.rs @@ -32,7 +32,7 @@ impl Stream for Bytes { } } -#[cfg(test)] +#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 09517ccad..b5eac5814 100644 --- a/src/io/read/chain.rs +++ b/src/io/read/chain.rs @@ -1,20 +1,25 @@ -use crate::io::IoSliceMut; use std::fmt; use std::pin::Pin; -use crate::io::{self, BufRead, Read}; +use pin_project_lite::pin_project; + +use crate::io::{self, BufRead, IoSliceMut, Read}; use crate::task::{Context, Poll}; -/// Adaptor to chain together two readers. -/// -/// This struct is generally created by calling [`chain`] on a reader. -/// Please see the documentation of [`chain`] for more details. -/// -/// [`chain`]: trait.Read.html#method.chain -pub struct Chain { - pub(crate) first: T, - pub(crate) second: U, - pub(crate) done_first: bool, +pin_project! { + /// Adaptor to chain together two readers. + /// + /// This struct is generally created by calling [`chain`] on a reader. + /// Please see the documentation of [`chain`] for more details. + /// + /// [`chain`]: trait.Read.html#method.chain + pub struct Chain { + #[pin] + pub(crate) first: T, + #[pin] + pub(crate) second: U, + pub(crate) done_first: bool, + } } impl Chain { @@ -98,81 +103,69 @@ impl fmt::Debug for Chain { } } -impl Read for Chain { +impl Read for Chain { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - let rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read(cx, buf)) { - Ok(0) if !buf.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read(cx, buf)) { + Ok(0) if !buf.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.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 rd = Pin::new(&mut self.first); - - match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) { - Ok(0) if !bufs.is_empty() => self.done_first = true, + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) { + Ok(0) if !bufs.is_empty() => *this.done_first = true, Ok(n) => return Poll::Ready(Ok(n)), Err(err) => return Poll::Ready(Err(err)), } } - let rd = Pin::new(&mut self.second); - rd.poll_read_vectored(cx, bufs) + this.second.poll_read_vectored(cx, bufs) } } -impl BufRead for Chain { +impl BufRead for Chain { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - - if !*done_first { - let first = unsafe { Pin::new_unchecked(first) }; - match futures_core::ready!(first.poll_fill_buf(cx)) { + let this = self.project(); + if !*this.done_first { + match futures_core::ready!(this.first.poll_fill_buf(cx)) { Ok(buf) if buf.is_empty() => { - *done_first = true; + *this.done_first = true; } Ok(buf) => return Poll::Ready(Ok(buf)), Err(err) => return Poll::Ready(Err(err)), } } - let second = unsafe { Pin::new_unchecked(second) }; - second.poll_fill_buf(cx) + this.second.poll_fill_buf(cx) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - let rd = Pin::new(&mut self.first); - rd.consume(amt) + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.project(); + if !*this.done_first { + this.first.consume(amt) } else { - let rd = Pin::new(&mut self.second); - rd.consume(amt) + this.second.consume(amt) } } } -#[cfg(test)] +#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index ed61590be..405422585 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -17,446 +17,364 @@ use std::mem; use crate::io::IoSliceMut; -extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; +pub use bytes::Bytes; +pub use chain::Chain; +pub use take::Take; - use crate::io; - use crate::task::{Context, Poll}; +pub use futures_io::AsyncRead as Read; + +#[doc = r#" + Extension methods for [`Read`]. + [`Read`]: ../trait.Read.html +"#] +pub trait ReadExt: Read { #[doc = r#" - Allows reading from a byte stream. + Reads some bytes from the byte stream. - This trait is a re-export of [`futures::io::AsyncRead`] and is an async version of - [`std::io::Read`]. + Returns the number of bytes read from the start of the buffer. - Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the - trait itself, but they become available when the prelude is imported: + If the return value is `Ok(n)`, then it must be guaranteed that + `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been + filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two + scenarios: - ``` - # #[allow(unused_imports)] + 1. This reader has reached its "end of file" and will likely no longer be able to + produce bytes. Note that this does not mean that the reader will always no + longer be able to produce bytes. + 2. The buffer specified was 0 bytes in length. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; use async_std::prelude::*; - ``` - [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html - [`futures::io::AsyncRead`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html - [`poll_read`]: #tymethod.poll_read - [`poll_read_vectored`]: #method.poll_read_vectored + let mut file = File::open("a.txt").await?; + + let mut buf = vec![0; 1024]; + let n = file.read(&mut buf).await?; + # + # Ok(()) }) } + ``` "#] - pub trait Read { - #[doc = r#" - Attempt to read from the `AsyncRead` into `buf`. - "#] - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll>; - - #[doc = r#" - Attempt to read from the `AsyncRead` into `bufs` using vectored IO operations. - "#] - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + fn read<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> ReadFuture<'a, Self> + where + Self: Unpin + { + ReadFuture { reader: self, buf } } - pub trait ReadExt: futures_io::AsyncRead { - #[doc = r#" - Reads some bytes from the byte stream. - - Returns the number of bytes read from the start of the buffer. - - If the return value is `Ok(n)`, then it must be guaranteed that - `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been - filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two - scenarios: - - 1. This reader has reached its "end of file" and will likely no longer be able to - produce bytes. Note that this does not mean that the reader will always no - longer be able to produce bytes. - 2. The buffer specified was 0 bytes in length. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::open("a.txt").await?; - - let mut buf = vec![0; 1024]; - let n = file.read(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> impl Future> + 'a [ReadFuture<'a, Self>] - where - Self: Unpin - { - ReadFuture { reader: self, buf } - } - - #[doc = r#" - Like [`read`], except that it reads into a slice of buffers. + #[doc = r#" + Like [`read`], except that it reads into a slice of buffers. - Data is copied to fill each buffer in order, with the final buffer written to - possibly being only partially filled. This method must behave as a single call to - [`read`] with the buffers concatenated would. + Data is copied to fill each buffer in order, with the final buffer written to + possibly being only partially filled. This method must behave as a single call to + [`read`] with the buffers concatenated would. - The default implementation calls [`read`] with either the first nonempty buffer - provided, or an empty one if none exists. + The default implementation calls [`read`] with either the first nonempty buffer + provided, or an empty one if none exists. - [`read`]: #tymethod.read - "#] - fn read_vectored<'a>( - &'a mut self, - bufs: &'a mut [IoSliceMut<'a>], - ) -> impl Future> + 'a [ReadVectoredFuture<'a, Self>] - where - Self: Unpin, - { - ReadVectoredFuture { reader: self, bufs } - } + [`read`]: #tymethod.read + "#] + fn read_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSliceMut<'a>], + ) -> ReadVectoredFuture<'a, Self> + where + Self: Unpin, + { + ReadVectoredFuture { reader: self, bufs } + } - #[doc = r#" - Reads all bytes from the byte stream. + #[doc = r#" + Reads all bytes from the byte stream. - All bytes read from this stream will be appended to the specified buffer `buf`. - This function will continuously call [`read`] to append more data to `buf` until - [`read`] returns either `Ok(0)` or an error. + All bytes read from this stream will be appended to the specified buffer `buf`. + This function will continuously call [`read`] to append more data to `buf` until + [`read`] returns either `Ok(0)` or an error. - If successful, this function will return the total number of bytes read. + If successful, this function will return the total number of bytes read. - [`read`]: #tymethod.read + [`read`]: #tymethod.read - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = Vec::new(); - file.read_to_end(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_to_end<'a>( - &'a mut self, - buf: &'a mut Vec, - ) -> impl Future> + 'a [ReadToEndFuture<'a, Self>] - where - Self: Unpin, - { - let start_len = buf.len(); - ReadToEndFuture { - reader: self, - buf, - start_len, - } + let mut buf = Vec::new(); + file.read_to_end(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_to_end<'a>( + &'a mut self, + buf: &'a mut Vec, + ) -> ReadToEndFuture<'a, Self> + where + Self: Unpin, + { + let start_len = buf.len(); + ReadToEndFuture { + reader: self, + buf, + start_len, } + } - #[doc = r#" - Reads all bytes from the byte stream and appends them into a string. + #[doc = r#" + Reads all bytes from the byte stream and appends them into a string. - If successful, this function will return the number of bytes read. + If successful, this function will return the number of bytes read. - If the data in this stream is not valid UTF-8 then an error will be returned and - `buf` will be left unmodified. + If the data in this stream is not valid UTF-8 then an error will be returned and + `buf` will be left unmodified. - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = String::new(); - file.read_to_string(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_to_string<'a>( - &'a mut self, - buf: &'a mut String, - ) -> impl Future> + 'a [ReadToStringFuture<'a, Self>] - where - Self: Unpin, - { - let start_len = buf.len(); - ReadToStringFuture { - reader: self, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, - buf, - start_len, - } + let mut buf = String::new(); + file.read_to_string(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_to_string<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ReadToStringFuture<'a, Self> + where + Self: Unpin, + { + let start_len = buf.len(); + ReadToStringFuture { + reader: self, + bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + buf, + start_len, } + } - #[doc = r#" - Reads the exact number of bytes required to fill `buf`. + #[doc = r#" + Reads the exact number of bytes required to fill `buf`. - This function reads as many bytes as necessary to completely fill the specified - buffer `buf`. + This function reads as many bytes as necessary to completely fill the specified + buffer `buf`. - No guarantees are provided about the contents of `buf` when this function is - called, implementations cannot rely on any property of the contents of `buf` being - true. It is recommended that implementations only write data to `buf` instead of - reading its contents. + No guarantees are provided about the contents of `buf` when this function is + called, implementations cannot rely on any property of the contents of `buf` being + true. It is recommended that implementations only write data to `buf` instead of + reading its contents. - If this function encounters an "end of file" before completely filling the buffer, - it returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of - `buf` are unspecified in this case. + If this function encounters an "end of file" before completely filling the buffer, + it returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of + `buf` are unspecified in this case. - If any other read error is encountered then this function immediately returns. The - contents of `buf` are unspecified in this case. + If any other read error is encountered then this function immediately returns. The + contents of `buf` are unspecified in this case. - If this function returns an error, it is unspecified how many bytes it has read, - but it will never read more than would be necessary to completely fill the buffer. + If this function returns an error, it is unspecified how many bytes it has read, + but it will never read more than would be necessary to completely fill the buffer. - [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof + [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - let mut file = File::open("a.txt").await?; + let mut file = File::open("a.txt").await?; - let mut buf = vec![0; 10]; - file.read_exact(&mut buf).await?; - # - # Ok(()) }) } - ``` - "#] - fn read_exact<'a>( - &'a mut self, - buf: &'a mut [u8], - ) -> impl Future> + 'a [ReadExactFuture<'a, Self>] - where - Self: Unpin, - { - ReadExactFuture { reader: self, buf } - } + let mut buf = vec![0; 10]; + file.read_exact(&mut buf).await?; + # + # Ok(()) }) } + ``` + "#] + fn read_exact<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> ReadExactFuture<'a, Self> + where + Self: Unpin, + { + ReadExactFuture { reader: self, buf } + } - #[doc = r#" - Creates an adaptor which will read at most `limit` bytes from it. + #[doc = r#" + Creates an adaptor which will read at most `limit` bytes from it. - This function returns a new instance of `Read` which will read at most - `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any - read errors will not count towards the number of bytes read and future - calls to [`read()`] may succeed. + This function returns a new instance of `Read` which will read at most + `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + read errors will not count towards the number of bytes read and future + calls to [`read`] may succeed. - # Examples + # Examples - [`File`]s implement `Read`: + [`File`]s implement `Read`: - [`File`]: ../fs/struct.File.html - [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok - [`read()`]: tymethod.read + [`File`]: ../fs/struct.File.html + [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok + [`read`]: tymethod.read - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::prelude::*; - use async_std::fs::File; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::prelude::*; + use async_std::fs::File; - let f = File::open("foo.txt").await?; - let mut buffer = [0; 5]; + let f = File::open("foo.txt").await?; + let mut buffer = [0; 5]; - // read at most five bytes - let mut handle = f.take(5); + // read at most five bytes + let mut handle = f.take(5); - handle.read(&mut buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn take(self, limit: u64) -> take::Take - where - Self: Sized, - { - take::Take { inner: self, limit } - } + handle.read(&mut buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + Take { inner: self, limit } + } - #[doc = r#" - Creates a "by reference" adaptor for this instance of `Read`. + #[doc = r#" + Creates a "by reference" adaptor for this instance of `Read`. - The returned adaptor also implements `Read` and will simply borrow this - current reader. + The returned adaptor also implements `Read` and will simply borrow this + current reader. - # Examples + # Examples - [`File`][file]s implement `Read`: + [`File`][file]s implement `Read`: - [file]: ../fs/struct.File.html + [file]: ../fs/struct.File.html - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; - let mut f = File::open("foo.txt").await?; - let mut buffer = Vec::new(); - let mut other_buffer = Vec::new(); + let mut f = File::open("foo.txt").await?; + let mut buffer = Vec::new(); + let mut other_buffer = Vec::new(); - { - let reference = f.by_ref(); + { + let reference = f.by_ref(); - // read at most 5 bytes - reference.take(5).read_to_end(&mut buffer).await?; + // read at most 5 bytes + reference.take(5).read_to_end(&mut buffer).await?; - } // drop our &mut reference so we can use f again + } // drop our &mut reference so we can use f again - // original file still usable, read the rest - f.read_to_end(&mut other_buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn by_ref(&mut self) -> &mut Self where Self: Sized { self } - - - #[doc = r#" - Transforms this `Read` instance to a `Stream` over its bytes. - - The returned type implements `Stream` where the `Item` is - `Result`. - The yielded item is `Ok` if a byte was successfully read and `Err` - otherwise. EOF is mapped to returning `None` from this iterator. - - # Examples - - [`File`][file]s implement `Read`: - - [file]: ../fs/struct.File.html - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; - - let f = File::open("foo.txt").await?; - let mut s = f.bytes(); - - while let Some(byte) = s.next().await { - println!("{}", byte.unwrap()); - } - # - # Ok(()) }) } - ``` - "#] - fn bytes(self) -> bytes::Bytes where Self: Sized { - bytes::Bytes { inner: self } - } + // original file still usable, read the rest + f.read_to_end(&mut other_buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn by_ref(&mut self) -> &mut Self where Self: Sized { self } - #[doc = r#" - Creates an adaptor which will chain this stream with another. - The returned `Read` instance will first read all bytes from this object - until EOF is encountered. Afterwards the output is equivalent to the - output of `next`. + #[doc = r#" + Transforms this `Read` instance to a `Stream` over its bytes. - # Examples + The returned type implements `Stream` where the `Item` is + `Result`. + The yielded item is `Ok` if a byte was successfully read and `Err` + otherwise. EOF is mapped to returning `None` from this iterator. - [`File`][file]s implement `Read`: + # Examples - [file]: ../fs/struct.File.html + [`File`][file]s implement `Read`: - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::fs::File; + [file]: ../fs/struct.File.html - let f1 = File::open("foo.txt").await?; - let f2 = File::open("bar.txt").await?; + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; - let mut handle = f1.chain(f2); - let mut buffer = String::new(); + let f = File::open("foo.txt").await?; + let mut s = f.bytes(); - // read the value into a String. We could use any Read method here, - // this is just one example. - handle.read_to_string(&mut buffer).await?; - # - # Ok(()) }) } - ``` - "#] - fn chain(self, next: R) -> chain::Chain where Self: Sized { - chain::Chain { first: self, second: next, done_first: false } + while let Some(byte) = s.next().await { + println!("{}", byte.unwrap()); } - + # + # Ok(()) }) } + ``` + "#] + fn bytes(self) -> Bytes where Self: Sized { + Bytes { inner: self } } - impl Read for Box { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + #[doc = r#" + Creates an adaptor which will chain this stream with another. - impl Read for &mut T { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + The returned `Read` instance will first read all bytes from this object + until EOF is encountered. Afterwards the output is equivalent to the + output of `next`. - impl

Read for Pin

- where - P: DerefMut + Unpin, -

::Target: Read, - { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + # Examples - impl Read for &[u8] { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + [`File`][file]s implement `Read`: + + [file]: ../fs/struct.File.html + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::fs::File; + + let f1 = File::open("foo.txt").await?; + let f2 = File::open("bar.txt").await?; + + let mut handle = f1.chain(f2); + let mut buffer = String::new(); + + // read the value into a String. We could use any Read method here, + // this is just one example. + handle.read_to_string(&mut buffer).await?; + # + # Ok(()) }) } + ``` + "#] + fn chain(self, next: R) -> Chain where Self: Sized { + Chain { first: self, second: next, done_first: false } } } +impl ReadExt for T {} + /// Initializes a buffer if necessary. /// /// Currently, a buffer is always initialized because `read_initializer` @@ -466,13 +384,13 @@ unsafe fn initialize(_reader: &R, buf: &mut [u8]) { std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } -#[cfg(test)] +#[cfg(all(test, not(target_os = "unknown")))] mod tests { use crate::io; use crate::prelude::*; #[test] - fn test_read_by_ref() -> io::Result<()> { + fn test_read_by_ref() { crate::task::block_on(async { let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]); let mut buffer = Vec::new(); @@ -482,14 +400,13 @@ mod tests { let reference = f.by_ref(); // read at most 5 bytes - assert_eq!(reference.take(5).read_to_end(&mut buffer).await?, 5); + assert_eq!(reference.take(5).read_to_end(&mut buffer).await.unwrap(), 5); assert_eq!(&buffer, &[0, 1, 2, 3, 4]) } // drop our &mut reference so we can use f again // original file still usable, read the rest - assert_eq!(f.read_to_end(&mut other_buffer).await?, 4); + assert_eq!(f.read_to_end(&mut other_buffer).await.unwrap(), 4); assert_eq!(&other_buffer, &[5, 6, 7, 8]); - Ok(()) - }) + }); } } diff --git a/src/io/read/read.rs b/src/io/read/read.rs index c46aff66a..0ba04e571 100644 --- a/src/io/read/read.rs +++ b/src/io/read/read.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs index c970f431f..71cf004da 100644 --- a/src/io/read/read_exact.rs +++ b/src/io/read/read_exact.rs @@ -1,7 +1,7 @@ use std::mem; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_to_end.rs b/src/io/read/read_to_end.rs index d76ee8c42..c7c47b8f6 100644 --- a/src/io/read/read_to_end.rs +++ b/src/io/read/read_to_end.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs index 60773e07a..5b74389e6 100644 --- a/src/io/read/read_to_string.rs +++ b/src/io/read/read_to_string.rs @@ -1,9 +1,9 @@ use std::mem; use std::pin::Pin; use std::str; +use std::future::Future; use super::read_to_end_internal; -use crate::future::Future; use crate::io::{self, Read}; use crate::task::{Context, Poll}; @@ -37,7 +37,11 @@ impl Future for ReadToStringFuture<'_, T> { )) })) } else { - debug_assert!(buf.is_empty()); + #[allow(clippy::debug_assert_with_mut_call)] + { + debug_assert!(buf.is_empty()); + } + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. mem::swap(unsafe { buf.as_mut_vec() }, bytes); Poll::Ready(ret) diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs index 8e52ba2dc..b4c61b8f6 100644 --- a/src/io/read/read_vectored.rs +++ b/src/io/read/read_vectored.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, IoSliceMut, Read}; use crate::task::{Context, Poll}; diff --git a/src/io/read/take.rs b/src/io/read/take.rs index def4e2405..ba9a9e318 100644 --- a/src/io/read/take.rs +++ b/src/io/read/take.rs @@ -1,19 +1,24 @@ use std::cmp; use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read}; use crate::task::{Context, Poll}; -/// Reader adaptor which limits the bytes read from an underlying reader. -/// -/// This struct is generally created by calling [`take`] on a reader. -/// Please see the documentation of [`take`] for more details. -/// -/// [`take`]: trait.Read.html#method.take -#[derive(Debug)] -pub struct Take { - pub(crate) inner: T, - pub(crate) limit: u64, +pin_project! { + /// Reader adaptor which limits the bytes read from an underlying reader. + /// + /// This struct is generally created by calling [`take`] on a reader. + /// Please see the documentation of [`take`] for more details. + /// + /// [`take`]: trait.Read.html#method.take + #[derive(Debug)] + pub struct Take { + #[pin] + pub(crate) inner: T, + pub(crate) limit: u64, + } } impl Take { @@ -152,15 +157,15 @@ impl Take { } } -impl Read for Take { +impl Read for Take { /// Attempt to read from the `AsyncRead` into `buf`. fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let Self { inner, limit } = &mut *self; - take_read_internal(Pin::new(inner), cx, buf, limit) + let this = self.project(); + take_read_internal(this.inner, cx, buf, this.limit) } } @@ -186,35 +191,34 @@ pub fn take_read_internal( } } -impl BufRead for Take { +impl BufRead 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(); - if *limit == 0 { + if *this.limit == 0 { return Poll::Ready(Ok(&[])); } - match futures_core::ready!(inner.poll_fill_buf(cx)) { + match futures_core::ready!(this.inner.poll_fill_buf(cx)) { Ok(buf) => { - let cap = cmp::min(buf.len() as u64, *limit) as usize; + let cap = cmp::min(buf.len() as u64, *this.limit) as usize; Poll::Ready(Ok(&buf[..cap])) } Err(e) => Poll::Ready(Err(e)), } } - 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.limit -= amt as u64; + let amt = cmp::min(amt as u64, *this.limit) as usize; + *this.limit -= amt as u64; - let rd = Pin::new(&mut self.inner); - rd.consume(amt); + this.inner.consume(amt); } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "unknown")))] mod tests { use crate::io; use crate::prelude::*; diff --git a/src/io/repeat.rs b/src/io/repeat.rs index a82e21be4..563681793 100644 --- a/src/io/repeat.rs +++ b/src/io/repeat.rs @@ -29,7 +29,8 @@ pub fn repeat(byte: u8) -> Repeat { /// A reader which yields one byte over and over and over and over and over and... /// -/// This reader is constructed by the [`repeat`] function. +/// This reader is created by the [`repeat`] function. See its +/// documentation for more. /// /// [`repeat`]: fn.repeat.html pub struct Repeat { diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs index ec2dd8f91..cb0b9e136 100644 --- a/src/io/seek/mod.rs +++ b/src/io/seek/mod.rs @@ -4,110 +4,47 @@ use seek::SeekFuture; use crate::io::SeekFrom; -extension_trait! { - use std::ops::{Deref, DerefMut}; - use std::pin::Pin; +pub use futures_io::AsyncSeek as Seek; - use crate::io; - use crate::task::{Context, Poll}; +#[doc = r#" + Extension methods for [`Seek`]. + [`Seek`]: ../trait.Seek.html +"#] +pub trait SeekExt: Seek { #[doc = r#" - Allows seeking through a byte stream. + Seeks to a new position in a byte stream. - This trait is a re-export of [`futures::io::AsyncSeek`] and is an async version of - [`std::io::Seek`]. + Returns the new position in the byte stream. - The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + A seek beyond the end of stream is allowed, but behavior is defined by the + implementation. - ``` - # #[allow(unused_imports)] - use async_std::prelude::*; - ``` - - [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html - [`futures::io::AsyncSeek`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html - [provided methods]: #provided-methods - "#] - pub trait Seek { - #[doc = r#" - Attempt to seek to an offset, in bytes, in a stream. - "#] - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll>; - } - - pub trait SeekExt: futures_io::AsyncSeek { - #[doc = r#" - Seeks to a new position in a byte stream. - - Returns the new position in the byte stream. - - A seek beyond the end of stream is allowed, but behavior is defined by the - implementation. - - # Examples + # Examples - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::io::SeekFrom; - use async_std::prelude::*; - - let mut file = File::open("a.txt").await?; - - let file_len = file.seek(SeekFrom::End(0)).await?; - # - # Ok(()) }) } - ``` - "#] - fn seek( - &mut self, - pos: SeekFrom, - ) -> impl Future> + '_ [SeekFuture<'_, Self>] - where - Self: Unpin, - { - SeekFuture { seeker: self, pos } - } - } - - impl Seek for Box { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::io::SeekFrom; + use async_std::prelude::*; - impl Seek for &mut T { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - } + let mut file = File::open("a.txt").await?; - impl

Seek for Pin

+ let file_len = file.seek(SeekFrom::End(0)).await?; + # + # Ok(()) }) } + ``` + "#] + fn seek( + &mut self, + pos: SeekFrom, + ) -> SeekFuture<'_, Self> where - P: DerefMut + Unpin, -

::Target: Seek, + Self: Unpin, { - fn poll_seek( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - pos: SeekFrom, - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + SeekFuture { seeker: self, pos } } } + +impl SeekExt for T {} diff --git a/src/io/seek/seek.rs b/src/io/seek/seek.rs index 65743be2d..74aa93e5b 100644 --- a/src/io/seek/seek.rs +++ b/src/io/seek/seek.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Seek, SeekFrom}; use crate::task::{Context, Poll}; diff --git a/src/io/sink.rs b/src/io/sink.rs index faa763c6a..86aeb0aec 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -25,7 +25,8 @@ pub fn sink() -> Sink { /// A writer that consumes and drops all data. /// -/// This writer is constructed by the [`sink`] function. +/// This writer is constructed by the [`sink`] function. See its documentation +/// for more. /// /// [`sink`]: fn.sink.html pub struct Sink { diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 1ec28b299..81cc197b9 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,9 +1,9 @@ use std::pin::Pin; use std::sync::Mutex; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Constructs a new handle to the standard error of the current process. /// @@ -11,6 +11,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html /// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// /// # Examples /// /// ```no_run @@ -34,12 +40,16 @@ pub fn stderr() -> Stderr { /// A handle to the standard error of the current process. /// -/// Created by the [`stderr`] function. +/// This writer is created by the [`stderr`] function. See its documentation for +/// more. /// -/// This type is an async version of [`std::io::Stderr`]. +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. /// /// [`stderr`]: fn.stderr.html -/// [`std::io::Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html #[derive(Debug)] pub struct Stderr(Mutex); @@ -79,11 +89,12 @@ enum Operation { impl Write for Stderr { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -114,7 +125,7 @@ impl Write for Stderr { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { let res = std::io::Write::write(&mut inner.stderr, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -127,8 +138,9 @@ impl Write for Stderr { } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -142,7 +154,7 @@ impl Write for Stderr { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { let res = std::io::Write::flush(&mut inner.stderr); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) @@ -168,6 +180,18 @@ cfg_unix! { std::io::stderr().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stderr { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { + BorrowedFd::borrow_raw(std::io::stderr().as_raw_fd()) + } + } + } + } } cfg_windows! { @@ -178,4 +202,16 @@ cfg_windows! { std::io::stderr().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stderr { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { + BorrowedHandle::borrow_raw(std::io::stderr().as_raw_handle()) + } + } + } + } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index dd3991fdc..d8f96d49e 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,9 +1,11 @@ +use std::future::Future; use std::pin::Pin; use std::sync::Mutex; -use crate::future::{self, Future}; +use crate::future; use crate::io::{self, Read}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as _; /// Constructs a new handle to the standard input of the current process. /// @@ -11,6 +13,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html /// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// /// # Examples /// /// ```no_run @@ -35,12 +43,16 @@ pub fn stdin() -> Stdin { /// A handle to the standard input of the current process. /// -/// Created by the [`stdin`] function. +/// This reader is created by the [`stdin`] function. See its documentation for +/// more. /// -/// This type is an async version of [`std::io::Stdin`]. +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. /// /// [`stdin`]: fn.stdin.html -/// [`std::io::Stdin`]: https://doc.rust-lang.org/std/io/struct.Stdin.html #[derive(Debug)] pub struct Stdin(Mutex); @@ -67,7 +79,7 @@ struct Inner { /// The line buffer. line: String, - /// The write buffer. + /// The read buffer. buf: Vec, /// The result of the last asynchronous operation on the stdin. @@ -117,7 +129,7 @@ impl Stdin { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { inner.line.clear(); let res = inner.stdin.read_line(&mut inner.line); inner.last_op = Some(Operation::ReadLine(res)); @@ -131,16 +143,18 @@ impl Stdin { } }) .await + .context(|| String::from("could not read line on stdin")) } } impl Read for Stdin { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -170,7 +184,7 @@ impl Read for Stdin { } // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); inner.last_op = Some(Operation::Read(res)); State::Idle(Some(inner)) @@ -192,6 +206,18 @@ cfg_unix! { std::io::stdin().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdin { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { + BorrowedFd::borrow_raw(std::io::stdin().as_raw_fd()) + } + } + } + } } cfg_windows! { @@ -202,4 +228,16 @@ cfg_windows! { std::io::stdin().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stdin { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { + BorrowedHandle::borrow_raw(std::io::stdin().as_raw_handle()) + } + } + } + } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 7945bfdd1..3cc570dc8 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,9 +1,9 @@ use std::pin::Pin; use std::sync::Mutex; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; /// Constructs a new handle to the standard output of the current process. /// @@ -11,6 +11,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll}; /// /// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// /// # Examples /// /// ```no_run @@ -34,12 +40,16 @@ pub fn stdout() -> Stdout { /// A handle to the standard output of the current process. /// -/// Created by the [`stdout`] function. +/// This writer is created by the [`stdout`] function. See its documentation +/// for more. /// -/// This type is an async version of [`std::io::Stdout`]. +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. /// /// [`stdout`]: fn.stdout.html -/// [`std::io::Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html #[derive(Debug)] pub struct Stdout(Mutex); @@ -79,11 +89,12 @@ enum Operation { impl Write for Stdout { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -114,7 +125,7 @@ impl Write for Stdout { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { let res = std::io::Write::write(&mut inner.stdout, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -127,8 +138,9 @@ impl Write for Stdout { } } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut state_guard = self.0.lock().unwrap(); + let state = &mut *state_guard; loop { match state { @@ -142,7 +154,7 @@ impl Write for Stdout { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(move || { + *state = State::Busy(spawn_blocking(move || { let res = std::io::Write::flush(&mut inner.stdout); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) @@ -168,6 +180,18 @@ cfg_unix! { std::io::stdout().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdout { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { + BorrowedFd::borrow_raw(std::io::stdout().as_raw_fd()) + } + } + } + } } cfg_windows! { @@ -178,4 +202,16 @@ cfg_windows! { std::io::stdout().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stdout { + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { + BorrowedHandle::borrow_raw(std::io::stdout().as_raw_handle()) + } + } + } + } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 9fcc15efc..073c2f6e9 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -1,12 +1,12 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use futures_timer::Delay; -use pin_utils::unsafe_pinned; +use pin_project_lite::pin_project; -use crate::future::Future; use crate::io; +use crate::utils::{timer_after, Timer}; /// Awaits an I/O future or times out after a duration of time. /// @@ -37,28 +37,24 @@ where F: Future>, { Timeout { - timeout: Delay::new(dur), + timeout: timer_after(dur), future: f, } .await } -/// Future returned by the `FutureExt::timeout` method. -#[derive(Debug)] -pub struct Timeout -where - F: Future>, -{ - future: F, - timeout: Delay, -} - -impl Timeout -where - F: Future>, -{ - unsafe_pinned!(future: F); - unsafe_pinned!(timeout: Delay); +pin_project! { + /// Future returned by the `FutureExt::timeout` method. + #[derive(Debug)] + pub struct Timeout + where + F: Future>, + { + #[pin] + future: F, + #[pin] + timeout: Timer, + } } impl Future for Timeout @@ -67,14 +63,15 @@ where { type Output = io::Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.future.poll(cx) { Poll::Pending => {} other => return other, } - if self.timeout().poll(cx).is_ready() { - let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); + if this.timeout.poll(cx).is_ready() { + let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out")); Poll::Ready(err) } else { Poll::Pending diff --git a/src/io/utils.rs b/src/io/utils.rs new file mode 100644 index 000000000..0ba3ecb2d --- /dev/null +++ b/src/io/utils.rs @@ -0,0 +1,42 @@ +use crate::utils::Context; + +use std::{error::Error as StdError, fmt, io}; + +/// Wrap `std::io::Error` with additional message +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl Context for Result { + fn context(self, message: impl Fn() -> String) -> Self { + self.map_err(|e| VerboseError::wrap(e, message())) + } +} + +#[derive(Debug)] +pub(crate) struct VerboseError { + source: io::Error, + message: String, +} + +impl VerboseError { + pub(crate) fn wrap(source: io::Error, message: impl Into) -> io::Error { + io::Error::new( + source.kind(), + VerboseError { + source, + message: message.into(), + }, + ) + } +} + +impl fmt::Display for VerboseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl StdError for VerboseError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } +} diff --git a/src/io/write/flush.rs b/src/io/write/flush.rs index 08f2b5b4e..590c12e89 100644 --- a/src/io/write/flush.rs +++ b/src/io/write/flush.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index a07c89681..753e7e6aa 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -12,306 +12,176 @@ use write_vectored::WriteVectoredFuture; use crate::io::{self, IoSlice}; -extension_trait! { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; +pub use futures_io::AsyncWrite as Write; - use crate::task::{Context, Poll}; +#[doc = r#" + Extension methods for [`Write`]. + [`Write`]: ../trait.Write.html +"#] +pub trait WriteExt: Write { #[doc = r#" - Allows writing to a byte stream. + Writes some bytes into the byte stream. - This trait is a re-export of [`futures::io::AsyncWrite`] and is an async version of - [`std::io::Write`]. + Returns the number of bytes written from the start of the buffer. - Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and - [`poll_close`] do not really exist in the trait itself, but they become available when - the prelude is imported: + If the return value is `Ok(n)` then it must be guaranteed that + `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying + object is no longer able to accept bytes and will likely not be able to in the + future as well, or that the buffer provided is empty. - ``` - # #[allow(unused_imports)] + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; use async_std::prelude::*; - ``` - [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html - [`futures::io::AsyncWrite`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html - [`poll_write`]: #tymethod.poll_write - [`poll_write_vectored`]: #method.poll_write_vectored - [`poll_flush`]: #tymethod.poll_flush - [`poll_close`]: #tymethod.poll_close + let mut file = File::create("a.txt").await?; + + let n = file.write(b"hello world").await?; + # + # Ok(()) }) } + ``` "#] - pub trait Write { - #[doc = r#" - Attempt to write bytes from `buf` into the object. - "#] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll>; - - #[doc = r#" - Attempt to write bytes from `bufs` into the object using vectored IO operations. - "#] - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>] - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - #[doc = r#" - Attempt to flush the object, ensuring that any buffered data reach - their destination. - "#] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - - #[doc = r#" - Attempt to close the object. - "#] - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + fn write<'a>( + &'a mut self, + buf: &'a [u8], + ) -> WriteFuture<'a, Self> + where + Self: Unpin, + { + WriteFuture { writer: self, buf } } - pub trait WriteExt: futures_io::AsyncWrite { - #[doc = r#" - Writes some bytes into the byte stream. - - Returns the number of bytes written from the start of the buffer. - - If the return value is `Ok(n)` then it must be guaranteed that - `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying - object is no longer able to accept bytes and will likely not be able to in the - future as well, or that the buffer provided is empty. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - let n = file.write(b"hello world").await?; - # - # Ok(()) }) } - ``` - "#] - fn write<'a>( - &'a mut self, - buf: &'a [u8], - ) -> impl Future> + 'a [WriteFuture<'a, Self>] - where - Self: Unpin, - { - WriteFuture { writer: self, buf } - } - - #[doc = r#" - Flushes the stream to ensure that all buffered contents reach their destination. - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - file.write_all(b"hello world").await?; - file.flush().await?; - # - # Ok(()) }) } - ``` - "#] - fn flush(&mut self) -> impl Future> + '_ [FlushFuture<'_, Self>] - where - Self: Unpin, - { - FlushFuture { writer: self } - } - - #[doc = r#" - Like [`write`], except that it writes from a slice of buffers. - - Data is copied from each buffer in order, with the final buffer read from possibly - being only partially consumed. This method must behave as a call to [`write`] with - the buffers concatenated would. - - The default implementation calls [`write`] with either the first nonempty buffer - provided, or an empty one if none exists. - - [`write`]: #tymethod.write - "#] - fn write_vectored<'a>( - &'a mut self, - bufs: &'a [IoSlice<'a>], - ) -> impl Future> + 'a [WriteVectoredFuture<'a, Self>] - where - Self: Unpin, - { - WriteVectoredFuture { writer: self, bufs } - } - - #[doc = r#" - Writes an entire buffer into the byte stream. - - This method will continuously call [`write`] until there is no more data to be - written or an error is returned. This method will not return until the entire - buffer has been successfully written or such an error occurs. - - [`write`]: #tymethod.write - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::fs::File; - use async_std::prelude::*; - - let mut file = File::create("a.txt").await?; - - file.write_all(b"hello world").await?; - # - # Ok(()) }) } - ``` - - [`write`]: #tymethod.write - "#] - fn write_all<'a>( - &'a mut self, - buf: &'a [u8], - ) -> impl Future> + 'a [WriteAllFuture<'a, Self>] - where - Self: Unpin, - { - WriteAllFuture { writer: self, buf } - } - - #[doc = r#" - Writes a formatted string into this writer, returning any error encountered. - - This method will continuously call [`write`] until there is no more data to be - written or an error is returned. This future will not resolve until the entire - buffer has been successfully written or such an error occurs. - - [`write`]: #tymethod.write - - # Examples - - ```no_run - # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - # - use async_std::io::prelude::*; - use async_std::fs::File; - - let mut buffer = File::create("foo.txt").await?; - - // this call - write!(buffer, "{:.*}", 2, 1.234567).await?; - // turns into this: - buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?; - # - # Ok(()) }) } - ``` - "#] - fn write_fmt<'a>( - &'a mut self, - fmt: std::fmt::Arguments<'_>, - ) -> impl Future> + 'a [WriteFmtFuture<'a, Self>] - where - Self: Unpin, - { - // In order to not have to implement an async version of `fmt` including private types - // and all, we convert `Arguments` to a `Result>` and pass that to the Future. - // Doing an owned conversion saves us from juggling references. - let mut string = String::new(); - let res = std::fmt::write(&mut string, fmt) - .map(|_| string.into_bytes()) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error")); - WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } - } - } + #[doc = r#" + Flushes the stream to ensure that all buffered contents reach their destination. + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; - impl Write for Box { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + let mut file = File::create("a.txt").await?; + + file.write_all(b"hello world").await?; + file.flush().await?; + # + # Ok(()) }) } + ``` + "#] + fn flush(&mut self) -> FlushFuture<'_, Self> + where + Self: Unpin, + { + FlushFuture { writer: self } } - impl Write for &mut T { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + #[doc = r#" + Like [`write`], except that it writes from a slice of buffers. + + Data is copied from each buffer in order, with the final buffer read from possibly + being only partially consumed. This method must behave as a call to [`write`] with + the buffers concatenated would. + + The default implementation calls [`write`] with either the first nonempty buffer + provided, or an empty one if none exists. + + [`write`]: #tymethod.write + "#] + fn write_vectored<'a>( + &'a mut self, + bufs: &'a [IoSlice<'a>], + ) -> WriteVectoredFuture<'a, Self> + where + Self: Unpin, + { + WriteVectoredFuture { writer: self, bufs } } - impl

Write for Pin

+ #[doc = r#" + Writes an entire buffer into the byte stream. + + This method will continuously call [`write`] until there is no more data to be + written or an error is returned. This method will not return until the entire + buffer has been successfully written or such an error occurs. + + [`write`]: #tymethod.write + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::fs::File; + use async_std::prelude::*; + + let mut file = File::create("a.txt").await?; + + file.write_all(b"hello world").await?; + # + # Ok(()) }) } + ``` + + [`write`]: #tymethod.write + "#] + fn write_all<'a>( + &'a mut self, + buf: &'a [u8], + ) -> WriteAllFuture<'a, Self> where - P: DerefMut + Unpin, -

::Target: Write, + Self: Unpin, { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + WriteAllFuture { writer: self, buf } } - impl Write for Vec { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + #[doc = r#" + Writes a formatted string into this writer, returning any error encountered. + + This method will continuously call [`write`] until there is no more data to be + written or an error is returned. This future will not resolve until the entire + buffer has been successfully written or such an error occurs. + + [`write`]: #tymethod.write + + # Examples + + ```no_run + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use async_std::io::prelude::*; + use async_std::fs::File; + + let mut buffer = File::create("foo.txt").await?; + + // this call + write!(buffer, "{:.*}", 2, 1.234567).await?; + // turns into this: + buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?; + # + # Ok(()) }) } + ``` + "#] + fn write_fmt<'a>( + &'a mut self, + fmt: std::fmt::Arguments<'_>, + ) -> WriteFmtFuture<'a, Self> + where + Self: Unpin, + { + // In order to not have to implement an async version of `fmt` including private types + // and all, we convert `Arguments` to a `Result>` and pass that to the Future. + // Doing an owned conversion saves us from juggling references. + let mut string = String::new(); + let res = std::fmt::write(&mut string, fmt) + .map(|_| string.into_bytes()) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error")); + WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 } } } + +impl WriteExt for T {} diff --git a/src/io/write/write.rs b/src/io/write/write.rs index da6e5c50d..8f13091dc 100644 --- a/src/io/write/write.rs +++ b/src/io/write/write.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs index 5353f7ab8..f04c55d65 100644 --- a/src/io/write/write_all.rs +++ b/src/io/write/write_all.rs @@ -1,7 +1,7 @@ use std::mem; use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index a1149cde3..a65e06a64 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -1,23 +1,24 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, Write}; use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] +#[must_use] pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> { pub(crate) writer: &'a mut T, pub(crate) res: Option>>, pub(crate) buffer: Option>, - pub(crate) amt: u64, + pub(crate) amt: usize, } impl Future for WriteFmtFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Process the interal Result the first time we run. + // Process the internal Result the first time we run. if self.buffer.is_none() { match self.res.take().unwrap() { Err(err) => return Poll::Ready(Err(err)), @@ -32,19 +33,19 @@ impl Future for WriteFmtFuture<'_, T> { buffer, .. } = &mut *self; - let mut buffer = buffer.as_mut().unwrap(); + let buffer = buffer.as_mut().unwrap(); // Copy the data from the buffer into the writer until it's done. loop { - if *amt == buffer.len() as u64 { + if *amt == buffer.len() { futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; return Poll::Ready(Ok(())); } - let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &mut buffer))?; + let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &buffer[*amt..]))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } - *amt += i as u64; + *amt += i; } } } diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs index 5f8492b77..cdb49d429 100644 --- a/src/io/write/write_vectored.rs +++ b/src/io/write/write_vectored.rs @@ -1,6 +1,6 @@ use std::pin::Pin; +use std::future::Future; -use crate::future::Future; use crate::io::{self, IoSlice, Write}; use crate::task::{Context, Poll}; diff --git a/src/lib.rs b/src/lib.rs index 7f888a14a..0caf23ee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,69 +1,343 @@ -//! Async version of the Rust standard library. +#![allow(rustdoc::invalid_html_tags)] +//! # `async-std` has been discontinued; use `smol` instead //! -//! Modules in this crate are organized in the same way as in the standard library, except blocking +//! We created `async-std` to demonstrate the value of making a library as close to +//! `std` as possible, but async. We think that demonstration was successful, and +//! we hope it will influence future design and development directions of async in +//! `std`. However, in the meantime, the [`smol`](https://github.com/smol-rs/smol/) +//! project came about and provided a great executor and libraries for asynchronous +//! use in the Rust ecosystem. We think that resources would be better spent +//! consolidating around `smol`, rather than continuing to provide occasional +//! maintenance of `async-std`. As such, we recommend that all users of +//! `async-std`, and all libraries built on `async-std`, switch to `smol` instead. +//! +//! In addition to the `smol` project as a direct replacement, you may find other +//! parts of the futures ecosystem useful, including `futures-concurrency`, +//! `async-io`, `futures-lite`, and `async-compat`. +//! +//! # Async version of the Rust standard library +//! +//! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested +//! shared abstractions for the [broader Rust ecosystem][crates.io]. It offers std types, like +//! [`Future`] and [`Stream`], library-defined [operations on language primitives](#primitives), +//! [standard macros](#macros), [I/O] and [multithreading], among [many other things][other]. +//! +//! `async-std` is available from [crates.io]. Once included, `async-std` can be accessed +//! in [`use`] statements through the path `async_std`, as in [`use async_std::future`]. +//! +//! [I/O]: io/index.html +//! [multithreading]: task/index.html +//! [other]: #what-is-in-the-standard-library-documentation +//! [`use`]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html +//! [`use async_std::future`]: future/index.html +//! [crates.io]: https://crates.io +//! [`Future`]: future/trait.Future.html +//! [`Stream`]: stream/trait.Stream.html +//! +//! # How to read this documentation +//! +//! If you already know the name of what you are looking for, the fastest way to +//! find it is to use the search +//! bar at the top of the page. +//! +//! Otherwise, you may want to jump to one of these useful sections: +//! +//! * [`async_std::*` modules](#modules) +//! * [Async macros](#macros) +//! * [The Async Prelude](prelude/index.html) +//! * [Cargo.toml feature flags](#features) +//! * [Examples](#examples) +//! +//! If this is your first time, the documentation for `async-std` is +//! written to be casually perused. Clicking on interesting things should +//! generally lead you to interesting places. Still, there are important bits +//! you don't want to miss, so read on for a tour of the `async-std` and +//! its documentation! +//! +//! Once you are familiar with the contents of `async-std` you may +//! begin to find the verbosity of the prose distracting. At this stage in your +//! development you may want to press the `[-]` button near the top of the +//! page to collapse it into a more skimmable view. +//! +//! While you are looking at that `[-]` button also notice the `[src]` +//! button. Rust's API documentation comes with the source code and you are +//! encouraged to read it. The `async-std` source is generally high +//! quality and a peek behind the curtains is often enlightening. +//! +//! Modules in this crate are organized in the same way as in `std`, except blocking //! functions have been replaced with async functions and threads have been replaced with //! lightweight tasks. //! -//! More information, reading materials, and other resources: +//! You can find more information, reading materials, and other resources here: +//! +//! * [The async-std website](https://async.rs/) +//! * [The async-std book](https://book.async.rs) +//! * [GitHub repository](https://github.com/async-rs/async-std) +//! * [List of code examples](https://github.com/async-rs/async-std/tree/HEAD/examples) +//! * [Discord chat](https://discord.gg/JvZeVNe) +//! +//! # What is in the `async-std` documentation? +//! +//! First, `async-std` is divided into a number of focused +//! modules, [all listed further down this page](#modules). These modules are +//! the bedrock upon which async Rust is forged, and they have mighty names +//! like [`async_std::os`] and [`async_std::task`]. Modules' documentation +//! typically includes an overview of the module along with examples, and are +//! a smart place to start familiarizing yourself with the library. +//! +//! Second, `async-std` defines [The Async Prelude], a small collection +//! of items - mostly traits - that should be imported into every module of +//! every async crate. The traits in the prelude are pervasive, making the +//! prelude documentation a good entry point to learning about the library. +//! +//! [The Async Prelude]: prelude/index.html +//! [`async_std::os`]: os/index.html +//! [`async_std::task`]: task/index.html +//! +//! And finally, `async-std` exports a number of async macros, and +//! [lists them on this page](#macros). +//! +//! # Contributing changes to the documentation +//! +//! Check out `async-std`'s contribution guidelines [here](https://async.rs/contribute). +//! The source for this documentation can be found on [GitHub](https://github.com/async-rs). +//! To contribute changes, make sure you read the guidelines first, then submit +//! pull requests for your suggested changes. +//! +//! Contributions are appreciated! If you see a part of the docs that can be +//! improved, submit a PR, or chat with us first on +//! [Discord](https://discord.gg/JvZeVNe). +//! +//! # A tour of `async-std` +//! +//! The rest of this crate documentation is dedicated to pointing out notable +//! features of `async-std`. //! -//! * [🌐 The async-std website](https://async.rs/) -//! * [📖 The async-std book](https://book.async.rs) -//! * [🐙 GitHub repository](https://github.com/async-rs/async-std) -//! * [📒 List of code examples](https://github.com/async-rs/async-std/tree/master/examples) -//! * [💬 Discord chat](https://discord.gg/JvZeVNe) +//! ## Platform abstractions and I/O +//! +//! Besides basic data types, `async-std` is largely concerned with +//! abstracting over differences in common platforms, most notably Windows and +//! Unix derivatives. +//! +//! Common types of I/O, including [files], [TCP], [UDP], are defined in the +//! [`io`], [`fs`], and [`net`] modules. +//! +//! The [`task`] module contains `async-std`'s task abstractions. [`sync`] +//! contains further primitive shared memory types. [`channel`] contains the channel types for message passing. +//! +//! [files]: fs/struct.File.html +//! [TCP]: net/struct.TcpStream.html +//! [UDP]: net/struct.UdpSocket.html +//! [`io`]: io/index.html +//! [`sync`]: sync/index.html +//! [`channel`]: channel/index.html +//! +//! ## Timeouts, intervals, and delays +//! +//! `async-std` provides several methods to manipulate time: +//! +//! * [`task::sleep`] to wait for a duration to pass without blocking. +//! * [`stream::interval`] for emitting an event at a set interval. +//! * [`future::timeout`] to time-out futures if they don't resolve within a +//! set interval. +//! +//! [`task::sleep`]: task/fn.sleep.html +//! [`stream::interval`]: stream/fn.interval.html +//! [`future::timeout`]: future/fn.timeout.html //! //! # Examples //! -//! Spawn a task and block the current thread on its result: +//! All examples require the [`"attributes"` feature](#features) to be enabled. +//! This feature is not enabled by default because it significantly impacts +//! compile times. See [`task::block_on`] for an alternative way to start +//! executing tasks. +//! +//! Call an async function from the main function: //! +#![cfg_attr(feature = "attributes", doc = "```")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] +//! async fn say_hello() { +//! println!("Hello, world!"); +//! } +//! +//! #[async_std::main] +//! async fn main() { +//! say_hello().await; +//! } //! ``` -//! use async_std::task; //! -//! fn main() { -//! task::block_on(async { -//! println!("Hello, world!"); -//! }) +//! Await two futures concurrently, and return a tuple of their output: +//! +#![cfg_attr(feature = "attributes", doc = "```")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] +//! use async_std::prelude::*; +//! +//! #[async_std::main] +//! async fn main() { +//! let a = async { 1u8 }; +//! let b = async { 2u8 }; +//! assert_eq!(a.join(b).await, (1u8, 2u8)) //! } //! ``` //! +//! Create a UDP server that echoes back each received message to the sender: +//! +#![cfg_attr(feature = "attributes", doc = "```no_run")] +#![cfg_attr(not(feature = "attributes"), doc = "```ignore")] +//! use async_std::net::UdpSocket; +//! +//! #[async_std::main] +//! async fn main() -> std::io::Result<()> { +//! let socket = UdpSocket::bind("127.0.0.1:8080").await?; +//! println!("Listening on {}", socket.local_addr()?); +//! +//! let mut buf = vec![0u8; 1024]; +//! +//! loop { +//! let (recv, peer) = socket.recv_from(&mut buf).await?; +//! let sent = socket.send_to(&buf[..recv], &peer).await?; +//! println!("Sent {} out of {} bytes to {}", sent, recv, peer); +//! } +//! } +//! ``` +//! [`task::block_on`]: task/fn.block_on.html +//! //! # Features //! //! Items marked with //! unstable +//! > unstable //! are available only when the `unstable` Cargo feature is enabled: //! //! ```toml //! [dependencies.async-std] -//! version = "0.99" +//! version = "1.7.0" //! features = ["unstable"] //! ``` +//! +//! Items marked with +//! attributes +//! are available only when the `attributes` Cargo feature is enabled: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["attributes"] +//! ``` +//! +//! Compatibility with the `tokio` 1.0 runtime is also simultaneously possible +//! using the `tokio1` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["tokio1"] +//! ``` +//! +//! Compatibility with the `tokio` 0.2 runtime is possible using the `tokio02` +//! Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["tokio02"] +//! ``` +//! +//! Compatibility with the `tokio` 0.3 runtime is also simultaneously possible +//! using the `tokio03` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! features = ["tokio03"] +//! ``` +//! +//! Additionally it's possible to only use the core traits and combinators by +//! only enabling the `std` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! default-features = false +//! features = ["std"] +//! ``` +//! +//! And to use async-std on `no_std` targets that only support `alloc` only +//! enable the `alloc` Cargo feature: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "1.7.0" +//! default-features = false +//! features = ["alloc"] +//! ``` +//! +//! # Runtime configuration +//! +//! Several environment variables are available to tune the async-std +//! runtime: +//! +//! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the +//! async-std runtime will start. By default, this is one per logical +//! cpu as determined by [async-global-executor](async_global_executor), +//! which may be different than the number of physical cpus. Async-std +//! _will panic_ if this is set to any value other than a positive +//! integer. +//! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime +//! threads report to the operating system. The default value is +//! `"async-std/runtime"`. +//! +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] -#![doc(test(attr(deny(rust_2018_idioms, warnings))))] +#![allow(clippy::mutex_atomic, clippy::module_inception)] +#![doc(test(attr(deny(rust_2018_idioms))))] #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] -#![recursion_limit = "1024"] #[macro_use] mod utils; -pub mod fs; -pub mod future; -pub mod io; -pub mod net; -pub mod os; -pub mod path; -pub mod prelude; -pub mod stream; -pub mod sync; -pub mod task; +#[cfg(feature = "attributes")] +#[cfg_attr(feature = "docs", doc(cfg(attributes)))] +#[doc(inline)] +pub use async_attributes::{main, test}; + +#[cfg(feature = "std")] +mod macros; + +cfg_alloc! { + pub mod task; + pub mod future; + pub mod stream; +} + +cfg_std! { + pub mod io; + pub mod os; + pub mod prelude; + pub mod sync; + pub mod channel; +} + +cfg_default! { + #[cfg(not(target_os = "unknown"))] + pub mod fs; + pub mod path; + pub mod net; + #[cfg(not(target_os = "unknown"))] + pub(crate) mod rt; +} cfg_unstable! { pub mod pin; + #[cfg(all(not(target_os = "unknown"), feature = "std"))] pub mod process; mod unit; @@ -72,9 +346,9 @@ cfg_unstable! { mod option; mod string; mod collections; +} +cfg_unstable_default! { #[doc(inline)] pub use std::{write, writeln}; } - -mod macros; diff --git a/src/macros.rs b/src/macros.rs index f932e47e8..638a2348b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,8 +23,8 @@ /// ``` /// # async_std::task::block_on(async { /// # -/// use async_std::prelude::*; /// use async_std::io; +/// use async_std::prelude::*; /// use async_std::print; /// /// print!("this ").await; @@ -165,3 +165,55 @@ macro_rules! eprintln { } ); } + +/// Declares task-local values. +/// +/// The macro wraps any number of static declarations and makes them task-local. Attributes and +/// visibility modifiers are allowed. +/// +/// Each declared value is of the accessor type [`LocalKey`]. +/// +/// [`LocalKey`]: task/struct.LocalKey.html +/// +/// # Examples +/// +/// ``` +/// # +/// use std::cell::Cell; +/// +/// use async_std::prelude::*; +/// use async_std::task; +/// +/// task_local! { +/// static VAL: Cell = Cell::new(5); +/// } +/// +/// task::block_on(async { +/// let v = VAL.with(|c| c.get()); +/// assert_eq!(v, 5); +/// }); +/// ``` +#[cfg(feature = "default")] +#[macro_export] +macro_rules! task_local { + () => (); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { + #[inline] + fn __init() -> $t { + $init + } + + $crate::task::LocalKey { + __init, + __key: ::std::sync::atomic::AtomicU32::new(0), + } + }; + ); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + $crate::task_local!($(#[$attr])* $vis static $name: $t = $init); + $crate::task_local!($($rest)*); + ); +} diff --git a/src/net/addr.rs b/src/net/addr.rs index 1dada64ce..71988fb37 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -1,11 +1,12 @@ +use std::future::Future; use std::mem; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use crate::future::Future; use crate::io; -use crate::task::{blocking, Context, JoinHandle, Poll}; +use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as ErrorContext; cfg_not_docs! { macro_rules! ret { @@ -67,11 +68,26 @@ pub enum ToSocketAddrsFuture { Done, } +// The field of `Self::Resolving` is `Unpin`, and the field of `Self::Ready` is never pinned. +impl Unpin for ToSocketAddrsFuture {} + +/// Wrap `std::io::Error` with additional message +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl ErrorContext for ToSocketAddrsFuture { + fn context(self, message: impl Fn() -> String) -> Self { + match self { + ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)), + x => x, + } + } +} + impl> Future for ToSocketAddrsFuture { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = unsafe { self.get_unchecked_mut() }; + let this = self.get_mut(); let state = mem::replace(this, ToSocketAddrsFuture::Done); match state { @@ -110,7 +126,9 @@ impl ToSocketAddrs for SocketAddrV4 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V4(*self).to_socket_addrs() + SocketAddr::V4(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -123,7 +141,9 @@ impl ToSocketAddrs for SocketAddrV6 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V6(*self).to_socket_addrs() + SocketAddr::V6(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -194,8 +214,10 @@ impl ToSocketAddrs for (&str, u16) { } let host = host.to_string(); - let task = blocking::spawn(move || { - std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) + let task = spawn_blocking(move || { + let addr = (host.as_str(), port); + std::net::ToSocketAddrs::to_socket_addrs(&addr) + .context(|| format!("could not resolve address `{:?}`", addr)) }); ToSocketAddrsFuture::Resolving(task) } @@ -210,12 +232,15 @@ impl ToSocketAddrs for str { impl Future, ToSocketAddrsFuture ) { - if let Some(addr) = self.parse().ok() { + if let Ok(addr) = self.parse() { return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); } let addr = self.to_string(); - let task = blocking::spawn(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + let task = spawn_blocking(move || { + std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) + .context(|| format!("could not resolve address `{:?}`", addr)) + }); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs deleted file mode 100644 index 806acdbe4..000000000 --- a/src/net/driver/mod.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::fmt; -use std::sync::{Arc, Mutex}; - -use lazy_static::lazy_static; -use mio::{self, Evented}; -use slab::Slab; - -use crate::io; -use crate::task::{Context, Poll, Waker}; -use crate::utils::abort_on_panic; - -/// Data associated with a registered I/O handle. -#[derive(Debug)] -struct Entry { - /// A unique identifier. - token: mio::Token, - - /// Tasks that are blocked on reading from this I/O handle. - readers: Mutex>, - - /// Thasks that are blocked on writing to this I/O handle. - writers: Mutex>, -} - -/// The state of a networking driver. -struct Reactor { - /// A mio instance that polls for new events. - poller: mio::Poll, - - /// A collection of registered I/O handles. - entries: Mutex>>, - - /// Dummy I/O handle that is only used to wake up the polling thread. - notify_reg: (mio::Registration, mio::SetReadiness), - - /// An identifier for the notification handle. - notify_token: mio::Token, -} - -impl Reactor { - /// Creates a new reactor for polling I/O events. - fn new() -> io::Result { - let poller = mio::Poll::new()?; - let notify_reg = mio::Registration::new2(); - - let mut reactor = Reactor { - poller, - entries: Mutex::new(Slab::new()), - notify_reg, - notify_token: mio::Token(0), - }; - - // Register a dummy I/O handle for waking up the polling thread. - let entry = reactor.register(&reactor.notify_reg.0)?; - reactor.notify_token = entry.token; - - Ok(reactor) - } - - /// Registers an I/O event source and returns its associated entry. - fn register(&self, source: &dyn Evented) -> io::Result> { - let mut entries = self.entries.lock().unwrap(); - - // Reserve a vacant spot in the slab and use its key as the token value. - let vacant = entries.vacant_entry(); - let token = mio::Token(vacant.key()); - - // Allocate an entry and insert it into the slab. - let entry = Arc::new(Entry { - token, - readers: Mutex::new(Vec::new()), - writers: Mutex::new(Vec::new()), - }); - vacant.insert(entry.clone()); - - // Register the I/O event source in the poller. - let interest = mio::Ready::all(); - let opts = mio::PollOpt::edge(); - self.poller.register(source, token, interest, opts)?; - - Ok(entry) - } - - /// Deregisters an I/O event source associated with an entry. - fn deregister(&self, source: &dyn Evented, entry: &Entry) -> io::Result<()> { - // Deregister the I/O object from the mio instance. - self.poller.deregister(source)?; - - // Remove the entry associated with the I/O object. - self.entries.lock().unwrap().remove(entry.token.0); - - Ok(()) - } - - // fn notify(&self) { - // self.notify_reg - // .1 - // .set_readiness(mio::Ready::readable()) - // .unwrap(); - // } -} - -lazy_static! { - /// The state of the global networking driver. - static ref REACTOR: Reactor = { - // Spawn a thread that waits on the poller for new events and wakes up tasks blocked on I/O - // handles. - std::thread::Builder::new() - .name("async-net-driver".to_string()) - .spawn(move || { - // If the driver thread panics, there's not much we can do. It is not a - // recoverable error and there is no place to propagate it into so we just abort. - abort_on_panic(|| { - main_loop().expect("async networking thread has panicked"); - }) - }) - .expect("cannot start a thread driving blocking tasks"); - - Reactor::new().expect("cannot initialize reactor") - }; -} - -/// Waits on the poller for new events and wakes up tasks blocked on I/O handles. -fn main_loop() -> io::Result<()> { - let reactor = &REACTOR; - let mut events = mio::Events::with_capacity(1000); - - loop { - // Block on the poller until at least one new event comes in. - reactor.poller.poll(&mut events, None)?; - - // Lock the entire entry table while we're processing new events. - let entries = reactor.entries.lock().unwrap(); - - for event in events.iter() { - let token = event.token(); - - if token == reactor.notify_token { - // If this is the notification token, we just need the notification state. - reactor.notify_reg.1.set_readiness(mio::Ready::empty())?; - } else { - // Otherwise, look for the entry associated with this token. - if let Some(entry) = entries.get(token.0) { - // Set the readiness flags from this I/O event. - let readiness = event.readiness(); - - // Wake up reader tasks blocked on this I/O handle. - if !(readiness & reader_interests()).is_empty() { - for w in entry.readers.lock().unwrap().drain(..) { - w.wake(); - } - } - - // Wake up writer tasks blocked on this I/O handle. - if !(readiness & writer_interests()).is_empty() { - for w in entry.writers.lock().unwrap().drain(..) { - w.wake(); - } - } - } - } - } - } -} - -/// An I/O handle powered by the networking driver. -/// -/// This handle wraps an I/O event source and exposes a "futurized" interface on top of it, -/// implementing traits `AsyncRead` and `AsyncWrite`. -pub struct Watcher { - /// Data associated with the I/O handle. - entry: Arc, - - /// The I/O event source. - source: Option, -} - -impl Watcher { - /// Creates a new I/O handle. - /// - /// The provided I/O event source will be kept registered inside the reactor's poller for the - /// lifetime of the returned I/O handle. - pub fn new(source: T) -> Watcher { - Watcher { - entry: REACTOR - .register(&source) - .expect("cannot register an I/O event source"), - source: Some(source), - } - } - - /// Returns a reference to the inner I/O event source. - pub fn get_ref(&self) -> &T { - self.source.as_ref().unwrap() - } - - /// Polls the inner I/O source for a non-blocking read operation. - /// - /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task - /// will be registered for wakeup when the I/O source becomes readable. - pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll> - where - F: FnMut(&'a T) -> io::Result, - { - // If the operation isn't blocked, return its result. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Lock the waker list. - let mut list = self.entry.readers.lock().unwrap(); - - // Try running the operation again. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Register the task if it isn't registered already. - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); - } - - Poll::Pending - } - - /// Polls the inner I/O source for a non-blocking write operation. - /// - /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task - /// will be registered for wakeup when the I/O source becomes writable. - pub fn poll_write_with<'a, F, R>( - &'a self, - cx: &mut Context<'_>, - mut f: F, - ) -> Poll> - where - F: FnMut(&'a T) -> io::Result, - { - // If the operation isn't blocked, return its result. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Lock the waker list. - let mut list = self.entry.writers.lock().unwrap(); - - // Try running the operation again. - match f(self.source.as_ref().unwrap()) { - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - res => return Poll::Ready(res), - } - - // Register the task if it isn't registered already. - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); - } - - Poll::Pending - } - - /// Deregisters and returns the inner I/O source. - /// - /// This method is typically used to convert `Watcher`s to raw file descriptors/handles. - #[allow(dead_code)] - pub fn into_inner(mut self) -> T { - let source = self.source.take().unwrap(); - REACTOR - .deregister(&source, &self.entry) - .expect("cannot deregister I/O event source"); - source - } -} - -impl Drop for Watcher { - fn drop(&mut self) { - if let Some(ref source) = self.source { - REACTOR - .deregister(source, &self.entry) - .expect("cannot deregister I/O event source"); - } - } -} - -impl fmt::Debug for Watcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Watcher") - .field("entry", &self.entry) - .field("source", &self.source) - .finish() - } -} - -/// Returns a mask containing flags that interest tasks reading from I/O handles. -#[inline] -fn reader_interests() -> mio::Ready { - mio::Ready::all() - mio::Ready::writable() -} - -/// Returns a mask containing flags that interest tasks writing into I/O handles. -#[inline] -fn writer_interests() -> mio::Ready { - mio::Ready::writable() | hup() -} - -/// Returns a flag containing the hangup status. -#[inline] -fn hup() -> mio::Ready { - #[cfg(unix)] - let ready = mio::unix::UnixReady::hup().into(); - - #[cfg(not(unix))] - let ready = mio::Ready::empty(); - - ready -} diff --git a/src/net/mod.rs b/src/net/mod.rs index b3ae287fa..181407357 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,14 +1,42 @@ //! Networking primitives for TCP/UDP communication. //! -//! For OS-specific networking primitives like Unix domain sockets, refer to the [`async_std::os`] -//! module. +//! This module provides networking functionality for the Transmission Control and User +//! Datagram Protocols, as well as types for IP and socket addresses. //! //! This module is an async version of [`std::net`]. //! +//! # Organization +//! +//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP +//! * [`UdpSocket`] provides functionality for communication over UDP +//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and +//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses +//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] +//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses +//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting +//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] +//! * Other types are return or parameter types for various methods in this module +//! +//! [`IpAddr`]: enum.IpAddr.html +//! [`Ipv4Addr`]: struct.Ipv4Addr.html +//! [`Ipv6Addr`]: struct.Ipv6Addr.html +//! [`SocketAddr`]: enum.SocketAddr.html +//! [`SocketAddrV4`]: struct.SocketAddrV4.html +//! [`SocketAddrV6`]: struct.SocketAddrV6.html +//! [`TcpListener`]: struct.TcpListener.html +//! [`TcpStream`]: struct.TcpStream.html +//! [`ToSocketAddrs`]: trait.ToSocketAddrs.html +//! [`UdpSocket`]: struct.UdpSocket.html +//! +//! # Platform-specific extensions +//! +//! APIs such as Unix domain sockets are available on certain platforms only. You can find +//! platform-specific extensions in the [`async_std::os`] module. +//! //! [`async_std::os`]: ../os/index.html //! [`std::net`]: https://doc.rust-lang.org/std/net/index.html //! -//! ## Examples +//! # Examples //! //! A simple UDP echo server: //! @@ -33,11 +61,16 @@ pub use std::net::Shutdown; pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +#[cfg(not(target_os = "unknown"))] pub use addr::ToSocketAddrs; +#[cfg(not(target_os = "unknown"))] pub use tcp::{Incoming, TcpListener, TcpStream}; +#[cfg(not(target_os = "unknown"))] pub use udp::UdpSocket; +#[cfg(not(target_os = "unknown"))] mod addr; -pub(crate) mod driver; +#[cfg(not(target_os = "unknown"))] mod tcp; +#[cfg(not(target_os = "unknown"))] mod udp; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 6fd27f0f1..d014f3387 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,12 +1,15 @@ +use std::fmt; use std::net::SocketAddr; +use std::net::TcpStream as StdTcpStream; use std::pin::Pin; -use crate::future::{self, Future}; +use async_io::Async; + use crate::io; -use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; -use crate::task::{Context, Poll}; +use crate::sync::Arc; +use crate::task::{ready, Context, Poll}; /// A TCP socket server, listening for connections. /// @@ -47,7 +50,7 @@ use crate::task::{Context, Poll}; /// ``` #[derive(Debug)] pub struct TcpListener { - watcher: Watcher, + watcher: Async, } impl TcpListener { @@ -74,13 +77,12 @@ impl TcpListener { /// [`local_addr`]: #method.local_addr pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs.to_socket_addrs().await?; - for addr in addrs.to_socket_addrs().await? { - match mio::net::TcpListener::bind(&addr) { - Ok(mio_listener) => { - return Ok(TcpListener { - watcher: Watcher::new(mio_listener), - }); + for addr in addrs { + match Async::::bind(addr) { + Ok(listener) => { + return Ok(TcpListener { watcher: listener }); } Err(err) => last_err = Some(err), } @@ -111,13 +113,9 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let (io, addr) = - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std())) - .await?; - - let mio_stream = mio::net::TcpStream::from_stream(io)?; + let (stream, addr) = self.watcher.accept().await?; let stream = TcpStream { - watcher: Watcher::new(mio_stream), + watcher: Arc::new(stream), }; Ok((stream, addr)) } @@ -149,7 +147,48 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub fn incoming(&self) -> Incoming<'_> { - Incoming(self) + Incoming { + incoming: Box::pin(self.watcher.incoming()), + } + } + + /// Turn this into a stream over the connections being received on this + /// listener. + /// + /// The returned stream is infinite and will also not yield + /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to + /// calling [`TcpListener::accept`] in a loop. + /// + /// ## Examples + /// + /// Merge the incoming connections of multiple sockets into one [`Stream`]: + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::TcpListener; + /// + /// // Our server listens on multiple ports for some reason + /// let listeners = vec![ + /// TcpListener::bind("[::0]:8080").await?, + /// TcpListener::bind("[::0]:12345").await?, + /// TcpListener::bind("[::0]:5678").await?, + /// ]; + /// // Iterate over all incoming connections + /// let incoming = futures::stream::select_all( + /// listeners.into_iter() + /// .map(TcpListener::into_incoming) + /// .map(Box::pin) + /// ); + /// # + /// # Ok(()) }) } + /// ``` + #[cfg(feature = "unstable")] + pub fn into_incoming(self) -> impl Stream> + Send { + futures_lite::stream::unfold(self, |listener| async move { + let res = listener.accept().await.map(|(stream, _)| stream); + Some((res, listener)) + }) } /// Returns the local address that this listener is bound to. @@ -185,31 +224,44 @@ impl TcpListener { /// [`incoming`]: struct.TcpListener.html#method.incoming /// [`TcpListener`]: struct.TcpListener.html /// [`std::net::Incoming`]: https://doc.rust-lang.org/std/net/struct.Incoming.html -#[derive(Debug)] -pub struct Incoming<'a>(&'a TcpListener); +pub struct Incoming<'a> { + incoming: Pin>> + Send + Sync + 'a>>, +} -impl<'a> Stream for Incoming<'a> { +impl Stream for Incoming<'_> { type Item = io::Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let future = self.0.accept(); - pin_utils::pin_mut!(future); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| TcpStream { watcher: Arc::new(stream) }))) + } +} - let (socket, _) = futures_core::ready!(future.poll(cx))?; - Poll::Ready(Some(Ok(socket))) +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Incoming {{ ... }}") } } impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. fn from(listener: std::net::TcpListener) -> TcpListener { - let mio_listener = mio::net::TcpListener::from_std(listener).unwrap(); TcpListener { - watcher: Watcher::new(mio_listener), + watcher: Async::new(listener).expect("TcpListener is known to be good"), } } } +impl std::convert::TryFrom for std::net::TcpListener { + type Error = io::Error; + /// Converts a `TcpListener` into its synchronous equivalent. + fn try_from(listener: TcpListener) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -227,29 +279,75 @@ cfg_unix! { impl IntoRawFd for TcpListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() + } + } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpListener { + fn from(fd: OwnedFd) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(listener: TcpListener) -> OwnedFd { + listener.watcher.into_inner().unwrap().into() + } } } } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for TcpListener { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpListener { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { - // net::TcpListener::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{ + AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, + }; + + impl AsRawSocket for TcpListener { + fn as_raw_socket(&self) -> RawSocket { + self.watcher.as_raw_socket() + } + } + + impl FromRawSocket for TcpListener { + unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { + std::net::TcpListener::from_raw_socket(handle).into() + } + } + + impl IntoRawSocket for TcpListener { + fn into_raw_socket(self) -> RawSocket { + self.watcher.into_inner().unwrap().into_raw_socket() + } + } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpListener { + fn from(fd: OwnedSocket) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(listener: TcpListener) -> OwnedSocket { + listener.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 5988194f1..3311f904c 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,12 +1,12 @@ -use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; +use std::io::{IoSlice, IoSliceMut}; use std::net::SocketAddr; use std::pin::Pin; -use crate::future; +use async_io::Async; + use crate::io::{self, Read, Write}; -use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; -use crate::task::blocking; +use crate::sync::Arc; use crate::task::{Context, Poll}; /// A TCP stream between a local and a remote socket. @@ -23,9 +23,9 @@ use crate::task::{Context, Poll}; /// [`connect`]: struct.TcpStream.html#method.connect /// [accepting]: struct.TcpListener.html#method.accept /// [listener]: struct.TcpListener.html -/// [`AsyncRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html -/// [`AsyncWrite`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html -/// [`futures::io`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/index.html +/// [`AsyncRead`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html +/// [`AsyncWrite`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html +/// [`futures::io`]: https://docs.rs/futures/0.3/futures/io/index.html /// [`shutdown`]: struct.TcpStream.html#method.shutdown /// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html /// @@ -45,9 +45,9 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TcpStream { - pub(super) watcher: Watcher, + pub(super) watcher: Arc>, } impl TcpStream { @@ -72,20 +72,19 @@ impl TcpStream { /// ``` pub async fn connect(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs.to_socket_addrs().await?; - for addr in addrs.to_socket_addrs().await? { - let res = blocking::spawn(move || { - let std_stream = std::net::TcpStream::connect(addr)?; - let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; - Ok(TcpStream { - watcher: Watcher::new(mio_stream), - }) - }) - .await; - - match res { - Ok(stream) => return Ok(stream), - Err(err) => last_err = Some(err), + for addr in addrs { + match Async::::connect(addr).await { + Ok(stream) => { + return Ok(TcpStream { + watcher: Arc::new(stream), + }); + } + Err(e) => { + last_err = Some(e); + continue; + } } } @@ -203,7 +202,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await + self.watcher.peek(buf).await } /// Gets the value of the `TCP_NODELAY` option on this socket. @@ -306,7 +305,15 @@ impl Read for &TcpStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) + Pin::new(&mut &*self.watcher).poll_read(cx, buf) + } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + Pin::new(&mut &*self.watcher).poll_read_vectored(cx, bufs) } } @@ -342,29 +349,50 @@ impl Write for &TcpStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher - .poll_write_with(cx, |mut inner| inner.write(buf)) + Pin::new(&mut &*self.watcher).poll_write(cx, buf) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut &*self.watcher).poll_write_vectored(cx, bufs) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.flush()) + Pin::new(&mut &*self.watcher).poll_flush(cx) } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut &*self.watcher).poll_close(cx) } } impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. fn from(stream: std::net::TcpStream) -> TcpStream { - let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); TcpStream { - watcher: Watcher::new(mio_stream), + watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")), } } } +impl std::convert::TryFrom for std::net::TcpStream { + type Error = io::Error; + /// Converts a `TcpStream` into its synchronous equivalent. + fn try_from(stream: TcpStream) -> io::Result { + let inner = Arc::try_unwrap(stream.watcher) + .map_err(|_| io::Error::new( + io::ErrorKind::Other, + "Cannot convert TcpStream to synchronous: multiple references", + ))? + .into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -382,29 +410,81 @@ cfg_unix! { impl IntoRawFd for TcpStream { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file + // descriptor because it's possible that there are other clones of this `TcpStream` + // using it at the same time. We should probably document that behavior. + self.as_raw_fd() + } + } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpStream { + fn from(fd: OwnedFd) -> TcpStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: TcpStream) -> OwnedFd { + stream.watcher.get_ref().try_clone().unwrap().into() + } } } } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for TcpStream { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for TcpStream { - // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { - // net::TcpStream::from_raw_socket(handle).try_into().unwrap() - // } - // } - // - // impl IntoRawSocket for TcpListener { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{ + RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket + }; + + impl AsRawSocket for TcpStream { + fn as_raw_socket(&self) -> RawSocket { + self.watcher.get_ref().as_raw_socket() + } + } + + impl FromRawSocket for TcpStream { + unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { + std::net::TcpStream::from_raw_socket(handle).into() + } + } + + impl IntoRawSocket for TcpStream { + fn into_raw_socket(self) -> RawSocket { + // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file + // descriptor because it's possible that there are other clones of this `TcpStream` + // using it at the same time. We should probably document that behavior. + self.as_raw_socket() + } + } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpStream { + fn from(fd: OwnedSocket) -> TcpStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: TcpStream) -> OwnedSocket { + stream.watcher.get_ref().try_clone().unwrap().into() + } + } + } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 37c9d50ce..3bb2c6e9c 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -2,9 +2,10 @@ use std::io; use std::net::SocketAddr; use std::net::{Ipv4Addr, Ipv6Addr}; -use crate::future; -use crate::net::driver::Watcher; +use async_io::Async; + use crate::net::ToSocketAddrs; +use crate::utils::Context as _; /// A UDP socket. /// @@ -44,7 +45,7 @@ use crate::net::ToSocketAddrs; /// ``` #[derive(Debug)] pub struct UdpSocket { - watcher: Watcher, + watcher: Async, } impl UdpSocket { @@ -66,15 +67,14 @@ impl UdpSocket { /// # /// # Ok(()) }) } /// ``` - pub async fn bind(addr: A) -> io::Result { + pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; + let addrs = addrs.to_socket_addrs().await?; - for addr in addr.to_socket_addrs().await? { - match mio::net::UdpSocket::bind(&addr) { - Ok(mio_socket) => { - return Ok(UdpSocket { - watcher: Watcher::new(mio_socket), - }); + for addr in addrs { + match Async::::bind(addr) { + Ok(socket) => { + return Ok(UdpSocket { watcher: socket }); } Err(err) => last_err = Some(err), } @@ -88,6 +88,32 @@ impl UdpSocket { })) } + /// Returns the peer address that this listener is connected to. + /// + /// This can be useful, for example, when connect to port 0 to figure out which port was + /// actually connected. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket1 = UdpSocket::bind("127.0.0.1:0").await?; + /// let socket2 = UdpSocket::bind("127.0.0.1:0").await?; + /// socket1.connect(socket2.local_addr()?).await?; + /// let addr = socket1.peer_addr()?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.watcher + .get_ref() + .peer_addr() + .context(|| String::from("could not get peer address")) + } + /// Returns the local address that this listener is bound to. /// /// This can be useful, for example, when binding to port 0 to figure out which port was @@ -98,7 +124,7 @@ impl UdpSocket { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::net::UdpSocket; + /// use async_std::net::UdpSocket; /// /// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// let addr = socket.local_addr()?; @@ -106,7 +132,10 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.watcher.get_ref().local_addr() + self.watcher + .get_ref() + .local_addr() + .context(|| String::from("could not get local address")) } /// Sends data on the socket to the given address. @@ -146,11 +175,10 @@ impl UdpSocket { } }; - future::poll_fn(|cx| { - self.watcher - .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) - }) - .await + self.watcher + .send_to(buf, addr) + .await + .context(|| format!("could not send packet to {}", addr)) } /// Receives data from the socket. @@ -173,11 +201,30 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - future::poll_fn(|cx| { - self.watcher - .poll_read_with(cx, |inner| inner.recv_from(buf)) - }) - .await + self.watcher.recv_from(buf).await + } + + /// Receives data from socket without removing it from the queue. + /// + /// On success, returns the number of bytes peeked and the origin. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let (n, peer) = socket.peek_from(&mut buf).await?; + /// println!("Peeked {} bytes from {}", n, peer); + /// # + /// # Ok (()) }) } + /// ``` + pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.watcher.peek_from(buf).await } /// Connects the UDP socket to a remote address. @@ -195,7 +242,7 @@ impl UdpSocket { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::net::UdpSocket; + /// use async_std::net::UdpSocket; /// /// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// socket.connect("127.0.0.1:8080").await?; @@ -204,8 +251,12 @@ impl UdpSocket { /// ``` pub async fn connect(&self, addrs: A) -> io::Result<()> { let mut last_err = None; + let addrs = addrs + .to_socket_addrs() + .await + .context(|| String::from("could not resolve addresses"))?; - for addr in addrs.to_socket_addrs().await? { + for addr in addrs { // TODO(stjepang): connect on the blocking pool match self.watcher.get_ref().connect(addr) { Ok(()) => return Ok(()), @@ -221,9 +272,12 @@ impl UdpSocket { })) } - /// Sends data on the socket to the given address. + /// Sends data on the socket to the remote address to which it is connected. /// - /// On success, returns the number of bytes written. + /// The [`connect`] method will connect this socket to a remote address. + /// This method will fail if the socket is not connected. + /// + /// [`connect`]: #method.connect /// /// # Examples /// @@ -232,28 +286,21 @@ impl UdpSocket { /// # /// use async_std::net::UdpSocket; /// - /// const THE_MERCHANT_OF_VENICE: &[u8] = b" - /// If you prick us, do we not bleed? - /// If you tickle us, do we not laugh? - /// If you poison us, do we not die? - /// And if you wrong us, shall we not revenge? - /// "; - /// - /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// let socket = UdpSocket::bind("127.0.0.1:34254").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// let bytes = socket.send(b"Hi there!").await?; /// - /// let addr = "127.0.0.1:7878"; - /// let sent = socket.send_to(THE_MERCHANT_OF_VENICE, &addr).await?; - /// println!("Sent {} bytes to {}", sent, addr); + /// println!("Sent {} bytes", bytes); /// # /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await + self.watcher.send(buf).await } /// Receives data from the socket. /// - /// On success, returns the number of bytes read and the origin. + /// On success, returns the number of bytes read. /// /// # Examples /// @@ -263,15 +310,40 @@ impl UdpSocket { /// use async_std::net::UdpSocket; /// /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// socket.connect("127.0.0.1:8080").await?; /// /// let mut buf = vec![0; 1024]; - /// let (n, peer) = socket.recv_from(&mut buf).await?; - /// println!("Received {} bytes from {}", n, peer); + /// let n = socket.recv(&mut buf).await?; + /// println!("Received {} bytes", n); /// # /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await + self.watcher.recv(buf).await + } + + /// Receives data from the socket without removing it from the queue. + /// + /// On success, returns the number of bytes peeked. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:0").await?; + /// socket.connect("127.0.0.1:8080").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = socket.peek(&mut buf).await?; + /// println!("Peeked {} bytes", n); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn peek(&self, buf: &mut [u8]) -> io::Result { + self.watcher.peek(buf).await } /// Gets the value of the `SO_BROADCAST` option for this socket. @@ -415,7 +487,7 @@ impl UdpSocket { /// use async_std::net::UdpSocket; /// /// let socket_addr = SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(), 0); - /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123) ; + /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123); /// let socket = UdpSocket::bind(&socket_addr).await?; /// /// socket.join_multicast_v6(&mdns_addr, 0)?; @@ -454,13 +526,22 @@ impl UdpSocket { impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. fn from(socket: std::net::UdpSocket) -> UdpSocket { - let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap(); UdpSocket { - watcher: Watcher::new(mio_socket), + watcher: Async::new(socket).expect("UdpSocket is known to be good"), } } } +impl std::convert::TryFrom for std::net::UdpSocket { + type Error = io::Error; + /// Converts a `UdpSocket` into its synchronous equivalent. + fn try_from(listener: UdpSocket) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -478,29 +559,75 @@ cfg_unix! { impl IntoRawFd for UdpSocket { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() + } + } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UdpSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedFd) -> UdpSocket { + std::net::UdpSocket::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UdpSocket) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } } } } cfg_windows! { - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - // - // impl AsRawSocket for UdpSocket { - // fn as_raw_socket(&self) -> RawSocket { - // self.raw_socket - // } - // } - // - // impl FromRawSocket for UdpSocket { - // unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { - // net::UdpSocket::from_raw_socket(handle).into() - // } - // } - // - // impl IntoRawSocket for UdpSocket { - // fn into_raw_socket(self) -> RawSocket { - // self.raw_socket - // } - // } + use crate::os::windows::io::{ + RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket + }; + + impl AsRawSocket for UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + self.watcher.get_ref().as_raw_socket() + } + } + + impl FromRawSocket for UdpSocket { + unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { + std::net::UdpSocket::from_raw_socket(handle).into() + } + } + + impl IntoRawSocket for UdpSocket { + fn into_raw_socket(self) -> RawSocket { + self.watcher.into_inner().unwrap().into_raw_socket() + } + } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for UdpSocket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedSocket) -> UdpSocket { + std::net::UdpSocket::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: UdpSocket) -> OwnedSocket { + stream.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs index e4da809eb..7931d97a6 100644 --- a/src/option/from_stream.rs +++ b/src/option/from_stream.rs @@ -2,8 +2,9 @@ use std::pin::Pin; use crate::prelude::*; use crate::stream::{FromStream, IntoStream}; +use std::convert::identity; -impl FromStream> for Option +impl FromStream> for Option where V: FromStream, { @@ -11,35 +12,31 @@ where /// elements are taken, and `None` is returned. Should no `None` /// occur, a container with the values of each `Option` is returned. #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs - let mut found_error = false; + let mut found_none = false; let out: V = stream - .scan((), |_, elem| { - match elem { - Some(elem) => Some(elem), - None => { - found_error = true; - // Stop processing the stream on error - None - } + .take_while(|elem| { + elem.is_some() || { + found_none = true; + // Stop processing the stream on `None` + false } }) + .filter_map(identity) .collect() .await; - if found_error { None } else { Some(out) } + if found_none { None } else { Some(out) } }) } } diff --git a/src/option/mod.rs b/src/option/mod.rs index afb29adc9..f0d67b77b 100644 --- a/src/option/mod.rs +++ b/src/option/mod.rs @@ -5,5 +5,11 @@ mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::option::Option; + +cfg_unstable! { + mod product; + mod sum; +} diff --git a/src/option/product.rs b/src/option/product.rs new file mode 100644 index 000000000..b446c1ffe --- /dev/null +++ b/src/option/product.rs @@ -0,0 +1,63 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Product, Stream}; +use std::convert::identity; + +impl Product> for Option +where + T: Product, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is a `None`, no further + elements are taken, and the `None` is returned. Should no `None` occur, + the product of all elements is returned. + + # Examples + + This multiplies every integer in a vector, rejecting the product if a negative element is + encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let v = stream::from_iter(vec![1, 2, 4]); + let prod: Option = v.map(|x| + if x < 0 { + None + } else { + Some(x) + }).product().await; + assert_eq!(prod, Some(8)); + # + # }) } + ``` + "#] + fn product<'a, S>(stream: S) -> Pin> + 'a>> + where + S: Stream> + 'a, + { + Box::pin(async move { + // Using `take_while` here because it is able to stop the stream early + // if a failure occurs + let mut found_none = false; + let out = >::product( + stream + .take_while(|elem| { + elem.is_some() || { + found_none = true; + // Stop processing the stream on `None` + false + } + }) + .filter_map(identity), + ) + .await; + + if found_none { None } else { Some(out) } + }) + } +} diff --git a/src/option/sum.rs b/src/option/sum.rs new file mode 100644 index 000000000..de404f42d --- /dev/null +++ b/src/option/sum.rs @@ -0,0 +1,58 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Sum}; +use std::convert::identity; + +impl Sum> for Option +where + T: Sum, +{ + #[doc = r#" + Takes each element in the `Iterator`: if it is a `None`, no further + elements are taken, and the `None` is returned. Should no `None` occur, + the sum of all elements is returned. + + # Examples + + This sums up the position of the character 'a' in a vector of strings, + if a word did not have the character 'a' the operation returns `None`: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let words = stream::from_iter(vec!["have", "a", "great", "day"]); + let total: Option = words.map(|w| w.find('a')).sum().await; + assert_eq!(total, Some(5)); + # + # }) } + ``` + "#] + fn sum<'a, S>(stream: S) -> Pin> + 'a>> + where + S: Stream> + 'a, + { + Box::pin(async move { + // Using `take_while` here because it is able to stop the stream early + // if a failure occurs + let mut found_none = false; + let out = >::sum( + stream + .take_while(|elem| { + elem.is_some() || { + found_none = true; + // Stop processing the stream on `None` + false + } + }) + .filter_map(identity), + ) + .await; + + if found_none { None } else { Some(out) } + }) + } +} diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index d3e85234d..498b3a97f 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -2,7 +2,7 @@ use crate::io; use crate::path::Path; -use crate::task::blocking; +use crate::task::spawn_blocking; /// Creates a new symbolic link on the filesystem. /// @@ -26,7 +26,7 @@ use crate::task::blocking; pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref().to_owned(); let dst = dst.as_ref().to_owned(); - blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await + spawn_blocking(move || std::os::unix::fs::symlink(&src, &dst)).await } cfg_not_docs! { diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 0b9846074..9d1fbaede 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -2,6 +2,10 @@ cfg_not_docs! { pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + cfg_io_safety! { + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } cfg_docs! { @@ -51,4 +55,9 @@ cfg_docs! { /// and must close the descriptor once it's no longer needed. fn into_raw_fd(self) -> RawFd; } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 722cfe6b8..36a97967c 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -1,5 +1,15 @@ //! Platform-specific extensions for Unix platforms. -pub mod fs; -pub mod io; -pub mod net; +cfg_std! { + pub mod io; +} + +cfg_default! { + pub mod fs; + pub mod net; +} + +#[cfg(all(feature = "unstable", feature = "std"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::unix as process; diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index c96afd50e..792860223 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -2,16 +2,14 @@ use std::fmt; use std::net::Shutdown; +use std::os::unix::net::UnixDatagram as StdUnixDatagram; -use mio_uds; +use async_io::Async; use super::SocketAddr; -use crate::future; use crate::io; -use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::blocking; /// A Unix datagram socket. /// @@ -42,13 +40,13 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` pub struct UnixDatagram { - watcher: Watcher, + watcher: Async, } impl UnixDatagram { - fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { + fn new(socket: StdUnixDatagram) -> UnixDatagram { UnixDatagram { - watcher: Watcher::new(socket), + watcher: Async::new(socket).expect("UnixDatagram is known to be good"), } } @@ -67,8 +65,8 @@ impl UnixDatagram { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let socket = blocking::spawn(move || mio_uds::UnixDatagram::bind(path)).await?; - Ok(UnixDatagram::new(socket)) + let socket = Async::::bind(path)?; + Ok(UnixDatagram { watcher: socket }) } /// Creates a Unix datagram which is not bound to any address. @@ -85,7 +83,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn unbound() -> io::Result { - let socket = mio_uds::UnixDatagram::unbound()?; + let socket = StdUnixDatagram::unbound()?; Ok(UnixDatagram::new(socket)) } @@ -105,7 +103,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { - let (a, b) = mio_uds::UnixDatagram::pair()?; + let (a, b) = StdUnixDatagram::pair()?; let a = UnixDatagram::new(a); let b = UnixDatagram::new(b); Ok((a, b)) @@ -197,11 +195,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - future::poll_fn(|cx| { - self.watcher - .poll_read_with(cx, |inner| inner.recv_from(buf)) - }) - .await + self.watcher.recv_from(buf).await } /// Receives data from the socket. @@ -222,7 +216,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await + self.watcher.recv(buf).await } /// Sends data on the socket to the specified address. @@ -242,11 +236,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { - future::poll_fn(|cx| { - self.watcher - .poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref())) - }) - .await + self.watcher.send_to(buf, path.as_ref()).await } /// Sends data on the socket to the socket's peer. @@ -267,7 +257,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await + self.watcher.send(buf).await } /// Shut down the read, write, or both halves of this connection. @@ -312,31 +302,63 @@ impl fmt::Debug for UnixDatagram { } } -impl From for UnixDatagram { +impl From for UnixDatagram { /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. - fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { - let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap(); + fn from(datagram: StdUnixDatagram) -> UnixDatagram { UnixDatagram { - watcher: Watcher::new(mio_datagram), + watcher: Async::new(datagram).expect("UnixDatagram is known to be good"), } } } +impl std::convert::TryFrom for StdUnixDatagram { + type Error = io::Error; + /// Converts a `UnixDatagram` into its synchronous equivalent. + fn try_from(listener: UnixDatagram) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - let datagram = std::os::unix::net::UnixDatagram::from_raw_fd(fd); - datagram.into() + let raw = StdUnixDatagram::from_raw_fd(fd); + let datagram = Async::::new(raw).expect("invalid file descriptor"); + UnixDatagram { watcher: datagram } } } impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() + } +} + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixDatagram { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixDatagram { + fn from(fd: OwnedFd) -> UnixDatagram { + StdUnixDatagram::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixDatagram) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index b6e6a2982..b87b781f2 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -1,19 +1,20 @@ //! Unix-specific networking extensions. use std::fmt; +use std::os::unix::net::UnixListener as StdUnixListener; +use std::os::unix::net::UnixStream as StdUnixStream; use std::pin::Pin; -use mio_uds; +use async_io::Async; use super::SocketAddr; use super::UnixStream; -use crate::future::{self, Future}; use crate::io; -use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; use crate::stream::Stream; -use crate::task::{blocking, Context, Poll}; +use crate::sync::Arc; +use crate::task::{ready, Context, Poll}; /// A Unix domain socket server, listening for connections. /// @@ -49,7 +50,7 @@ use crate::task::{blocking, Context, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixListener { - watcher: Watcher, + watcher: Async, } impl UnixListener { @@ -68,11 +69,9 @@ impl UnixListener { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let listener = blocking::spawn(move || mio_uds::UnixListener::bind(path)).await?; + let listener = Async::::bind(path)?; - Ok(UnixListener { - watcher: Watcher::new(listener), - }) + Ok(UnixListener { watcher: listener }) } /// Accepts a new incoming connection to this listener. @@ -92,29 +91,14 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { - future::poll_fn(|cx| { - let res = futures_core::ready!(self.watcher.poll_read_with(cx, |inner| { - match inner.accept_std() { - // Converting to `WouldBlock` so that the watcher will - // add the waker of this task to a list of readers. - Ok(None) => Err(io::ErrorKind::WouldBlock.into()), - res => res, - } - })); - - match res? { - Some((io, addr)) => { - let mio_stream = mio_uds::UnixStream::from_stream(io)?; - let stream = UnixStream { - watcher: Watcher::new(mio_stream), - }; - Poll::Ready(Ok((stream, addr))) - } - // This should never happen since `None` is converted to `WouldBlock` - None => unreachable!(), - } - }) - .await + let (stream, addr) = self.watcher.accept().await?; + + Ok(( + UnixStream { + watcher: Arc::new(stream), + }, + addr, + )) } /// Returns a stream of incoming connections. @@ -144,7 +128,9 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub fn incoming(&self) -> Incoming<'_> { - Incoming(self) + Incoming { + incoming: Box::pin(self.watcher.incoming()), + } } /// Returns the local socket address of this listener. @@ -190,34 +176,48 @@ impl fmt::Debug for UnixListener { /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`incoming`]: struct.UnixListener.html#method.incoming /// [`UnixListener`]: struct.UnixListener.html -#[derive(Debug)] -pub struct Incoming<'a>(&'a UnixListener); +pub struct Incoming<'a> { + incoming: Pin>> + Send + Sync + 'a>>, +} impl Stream for Incoming<'_> { type Item = io::Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let future = self.0.accept(); - pin_utils::pin_mut!(future); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let res = ready!(Pin::new(&mut self.incoming).poll_next(cx)); + Poll::Ready(res.map(|res| res.map(|stream| UnixStream { watcher: Arc::new(stream) }))) + } +} - let (socket, _) = futures_core::ready!(future.poll(cx))?; - Poll::Ready(Some(Ok(socket))) +impl fmt::Debug for Incoming<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Incoming") + .finish() } } -impl From for UnixListener { +impl From for UnixListener { /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. - fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { - let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap(); + fn from(listener: StdUnixListener) -> UnixListener { UnixListener { - watcher: Watcher::new(mio_listener), + watcher: Async::new(listener).expect("UnixListener is known to be good"), } } } +impl std::convert::TryFrom for StdUnixListener { + type Error = io::Error; + /// Converts a `UnixListener` into its synchronous equivalent. + fn try_from(listener: UnixListener) -> io::Result { + let inner = listener.watcher.into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } @@ -230,6 +230,28 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + self.watcher.into_inner().unwrap().into_raw_fd() + } +} + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixListener { + fn from(fd: OwnedFd) -> UnixListener { + std::os::unix::net::UnixListener::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixListener) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index b16f2a3ce..4a4f45ad6 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -1,18 +1,18 @@ //! Unix-specific networking extensions. use std::fmt; -use std::io::{Read as _, Write as _}; use std::net::Shutdown; +use std::os::unix::net::UnixStream as StdUnixStream; use std::pin::Pin; -use mio_uds; +use async_io::Async; use super::SocketAddr; use crate::io::{self, Read, Write}; -use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::Path; -use crate::task::{blocking, Context, Poll}; +use crate::sync::Arc; +use crate::task::{Context, Poll}; /// A Unix stream socket. /// @@ -37,8 +37,9 @@ use crate::task::{blocking, Context, Poll}; /// # /// # Ok(()) }) } /// ``` +#[derive(Clone)] pub struct UnixStream { - pub(super) watcher: Watcher, + pub(super) watcher: Arc>, } impl UnixStream { @@ -57,15 +58,9 @@ impl UnixStream { /// ``` pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); + let stream = Arc::new(Async::::connect(path).await?); - blocking::spawn(move || { - let std_stream = std::os::unix::net::UnixStream::connect(path)?; - let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?; - Ok(UnixStream { - watcher: Watcher::new(mio_stream), - }) - }) - .await + Ok(UnixStream { watcher: stream }) } /// Creates an unnamed pair of connected sockets. @@ -84,12 +79,12 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - let (a, b) = mio_uds::UnixStream::pair()?; + let (a, b) = Async::::pair()?; let a = UnixStream { - watcher: Watcher::new(a), + watcher: Arc::new(a), }; let b = UnixStream { - watcher: Watcher::new(b), + watcher: Arc::new(b), }; Ok((a, b)) } @@ -169,7 +164,7 @@ impl Read for &UnixStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) + Pin::new(&mut &*self.watcher).poll_read(cx, buf) } } @@ -197,16 +192,15 @@ impl Write for &UnixStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher - .poll_write_with(cx, |mut inner| inner.write(buf)) + Pin::new(&mut &*self.watcher).poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.flush()) + Pin::new(&mut &*self.watcher).poll_flush(cx) } - fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut &*self.watcher).poll_close(cx) } } @@ -227,19 +221,34 @@ impl fmt::Debug for UnixStream { } } -impl From for UnixStream { +impl From for UnixStream { /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. - fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { - let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); + fn from(stream: StdUnixStream) -> UnixStream { + let stream = Async::new(stream).expect("UnixStream is known to be good"); UnixStream { - watcher: Watcher::new(mio_stream), + watcher: Arc::new(stream), } } } +impl std::convert::TryFrom for StdUnixStream { + type Error = io::Error; + /// Converts a `UnixStream` into its synchronous equivalent. + fn try_from(stream: UnixStream) -> io::Result { + let inner = Arc::try_unwrap(stream.watcher) + .map_err(|_| io::Error::new( + io::ErrorKind::Other, + "Cannot convert UnixStream to synchronous: multiple references", + ))? + .into_inner()?; + inner.set_nonblocking(false)?; + Ok(inner) + } +} + impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() + self.watcher.as_raw_fd() } } @@ -252,6 +261,28 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() + (*self.watcher).get_ref().try_clone().unwrap().into_raw_fd() + } +} + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixStream { + fn from(fd: OwnedFd) -> UnixStream { + std::os::unix::net::UnixStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixStream) -> OwnedFd { + stream.watcher.get_ref().try_clone().unwrap().into() + } } } diff --git a/src/os/windows/fs.rs b/src/os/windows/fs.rs new file mode 100644 index 000000000..243f3819d --- /dev/null +++ b/src/os/windows/fs.rs @@ -0,0 +1,55 @@ +//! Windows-specific filesystem extensions. + +use crate::io; +use crate::path::Path; +use crate::task::spawn_blocking; + +/// Creates a new directory symbolic link on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_dir`]. +/// +/// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_dir; +/// +/// symlink_dir("a", "b").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_dir(&src, &dst)).await +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` path. +/// +/// This function is an async version of [`std::os::windows::fs::symlink_file`]. +/// +/// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html +/// +/// # Examples +/// +/// ```no_run +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::os::windows::fs::symlink_file; +/// +/// symlink_file("a.txt", "b.txt").await?; +/// # +/// # Ok(()) }) } +/// ``` +pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + let src = src.as_ref().to_owned(); + let dst = dst.as_ref().to_owned(); + spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await +} diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index e83d55711..caffc6fc6 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -2,8 +2,16 @@ cfg_not_docs! { pub use std::os::windows::io::{ - AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, + AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, + AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, }; + + cfg_io_safety! { + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } cfg_docs! { @@ -45,4 +53,41 @@ cfg_docs! { /// it once it's no longer needed. fn into_raw_handle(self) -> RawHandle; } + + /// Creates I/O objects from raw sockets. + pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will consume ownership of the socket provided and it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the + /// file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause + /// memory unsafety in code that relies on it being true. + unsafe fn from_raw_socket(sock: RawSocket) -> Self; + } + + /// Extracts raw sockets. + pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + fn as_raw_socket(&self) -> RawSocket; + } + + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw `SOCKET`. + pub trait IntoRawSocket { + /// Consumes this object, returning the raw underlying socket. + /// + /// This function **transfers ownership** of the underlying socket to the + /// caller. Callers are then the unique owners of the socket and must close + /// it once it's no longer needed. + fn into_raw_socket(self) -> RawSocket; + } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 30218f0ef..0d45a52e4 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -1,3 +1,13 @@ //! Platform-specific extensions for Windows. -pub mod io; +cfg_std! { + pub mod io; +} + +cfg_unstable! { + #[cfg(feature = "default")] + pub mod fs; + #[cfg(feature = "std")] + #[cfg(windows)] + pub use async_process::windows as process; +} diff --git a/src/path/components.rs b/src/path/components.rs new file mode 100644 index 000000000..51649c55c --- /dev/null +++ b/src/path/components.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::iter::FusedIterator; + +use crate::path::{Component, Path}; + +/// An iterator over the [`Component`]s of a [`Path`]. +/// +/// This `struct` is created by the [`components`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// +/// for component in path.components() { +/// println!("{:?}", component); +/// } +/// ``` +/// +/// [`Component`]: enum.Component.html +/// [`components`]: struct.Path.html#method.components +/// [`Path`]: struct.Path.html +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Components<'a> { + pub(crate) inner: std::path::Components<'a>, +} + +impl<'a> Components<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut components = Path::new("/tmp/foo/bar.txt").components(); + /// components.next(); + /// components.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path().into() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + self.inner.next() + } +} + +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + self.inner.next_back() + } +} + +impl FusedIterator for Components<'_> {} + +impl AsRef for Component<'_> { + fn as_ref(&self) -> &Path { + self.as_os_str().as_ref() + } +} diff --git a/src/path/iter.rs b/src/path/iter.rs new file mode 100644 index 000000000..b4061003b --- /dev/null +++ b/src/path/iter.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::fmt; +use std::iter::FusedIterator; + +use crate::path::{Component, Components, Path}; + +/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. +/// +/// This `struct` is created by the [`iter`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`Component`]: enum.Component.html +/// [`iter`]: struct.Path.html#method.iter +/// [`OsStr`]: ../../std/ffi/struct.OsStr.html +/// [`Path`]: struct.Path.html +#[derive(Clone)] +pub struct Iter<'a> { + pub(crate) inner: Components<'a>, +} + +impl<'a> Iter<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); + /// iter.next(); + /// iter.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +impl fmt::Debug for Iter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_tuple("Iter") + .field(&DebugHelper(self.as_path())) + .finish() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +impl FusedIterator for Iter<'_> {} diff --git a/src/path/mod.rs b/src/path/mod.rs index 36b9f2f08..7ce9b62d6 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -2,28 +2,86 @@ //! //! This module is an async version of [`std::path`]. //! +//! This module provides two types, [`PathBuf`] and [`Path`][`Path`] (akin to [`String`] +//! and [`str`]), for working with paths abstractly. These types are thin wrappers +//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly +//! on strings according to the local platform's path syntax. +//! +//! Paths can be parsed into [`Component`]s by iterating over the structure +//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly +//! correspond to the substrings between path separators (`/` or `\`). You can +//! reconstruct an equivalent path from components with the [`push`] method on +//! [`PathBuf`]; note that the paths may differ syntactically by the +//! normalization described in the documentation for the [`components`] method. +//! //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html +//! +//! ## Simple usage +//! +//! Path manipulation includes both parsing components from slices and building +//! new owned paths. +//! +//! To parse a path, you can create a [`Path`] slice from a [`str`] +//! slice and start asking questions: +//! +//! ``` +//! use async_std::path::Path; +//! use std::ffi::OsStr; +//! +//! let path = Path::new("/tmp/foo/bar.txt"); +//! +//! let parent = path.parent(); +//! assert_eq!(parent, Some(Path::new("/tmp/foo"))); +//! +//! let file_stem = path.file_stem(); +//! assert_eq!(file_stem, Some(OsStr::new("bar"))); +//! +//! let extension = path.extension(); +//! assert_eq!(extension, Some(OsStr::new("txt"))); +//! ``` +//! +//! To build or modify paths, use [`PathBuf`]: +//! +//! ``` +//! use async_std::path::PathBuf; +//! +//! // This way works... +//! let mut path = PathBuf::from("c:\\"); +//! +//! path.push("windows"); +//! path.push("system32"); +//! +//! path.set_extension("dll"); +//! +//! // ... but push is best used if you don't know everything up +//! // front. If you do, this way is better: +//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect(); +//! ``` +//! +//! [`Component`]: enum.Component.html +//! [`components`]: struct.Path.html#method.components +//! [`PathBuf`]: struct.PathBuf.html +//! [`Path`]: struct.Path.html +//! [`push`]: struct.PathBuf.html#method.push +//! [`String`]: https://doc.rust-lang.org/std/string/struct.String.html +//! +//! [`str`]: https://doc.rust-lang.org/std/primitive.str.html +//! [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html +//! [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html mod ancestors; +mod components; +mod iter; mod path; mod pathbuf; -// Structs re-export -#[doc(inline)] -pub use std::path::{Components, Display, Iter, PrefixComponent, StripPrefixError}; - -// Enums re-export -#[doc(inline)] -pub use std::path::{Component, Prefix}; - -// Constants re-export -#[doc(inline)] -pub use std::path::MAIN_SEPARATOR; - -// Functions re-export #[doc(inline)] -pub use std::path::is_separator; +pub use std::path::{ + is_separator, Component, Display, Prefix, PrefixComponent, StripPrefixError, MAIN_SEPARATOR, +}; -use ancestors::Ancestors; +pub use ancestors::Ancestors; +pub use components::Components; +pub use iter::Iter; pub use path::Path; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index aa15ab225..185bfaff0 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,12 +1,51 @@ -use std::ffi::OsStr; +use std::borrow::{Cow, ToOwned}; +use std::cmp::Ordering; +use std::ffi::{OsStr, OsString}; +use std::rc::Rc; +use std::sync::Arc; use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; +#[cfg(not(target_os = "unknown"))] use crate::{fs, io}; +/// A slice of a path. +/// /// This struct is an async version of [`std::path::Path`]. /// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` on Unix and by either +/// `/` or `\` on Windows), extracting the file name, determining whether the path +/// is absolute, and so on. +/// +/// This is an *unsized* type, meaning that it must always be used behind a +/// pointer like `&` or `Box`. For an owned version of this type, +/// see [`PathBuf`]. +/// +/// [`PathBuf`]: struct.PathBuf.html /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html -#[derive(Debug, PartialEq)] +/// +/// More details about the overall approach can be found in +/// the [module documentation](index.html). +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use std::ffi::OsStr; +/// +/// // Note: this example does work on Windows +/// let path = Path::new("./foo/bar.txt"); +/// +/// let parent = path.parent(); +/// assert_eq!(parent, Some(Path::new("./foo"))); +/// +/// let file_stem = path.file_stem(); +/// assert_eq!(file_stem, Some(OsStr::new("bar"))); +/// +/// let extension = path.extension(); +/// assert_eq!(extension, Some(OsStr::new("txt"))); +/// ``` +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Path { inner: std::path::Path, } @@ -38,14 +77,25 @@ impl Path { unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } - /// Yields the underlying [`OsStr`] slice. + /// Returns the underlying [`OsStr`] slice. /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use async_std::path::Path; + /// + /// let os_str = Path::new("foo.txt").as_os_str(); + /// assert_eq!(os_str, OsStr::new("foo.txt")); + /// ``` pub fn as_os_str(&self) -> &OsStr { self.inner.as_os_str() } - /// Yields a [`&str`] slice if the `Path` is valid unicode. + /// Returns a [`&str`] slice if the `Path` is valid unicode. /// /// This conversion may entail doing a check for UTF-8 validity. /// Note that validation is performed because non-UTF-8 strings are @@ -86,7 +136,7 @@ impl Path { /// /// Had `path` contained invalid unicode, the `to_string_lossy` call might /// have returned `"fo�.txt"`. - pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -106,14 +156,16 @@ impl Path { PathBuf::from(self.inner.to_path_buf()) } - /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// Returns `true` if the `Path` is absolute, i.e. if it is independent of /// the current directory. /// /// * On Unix, a path is absolute if it starts with the root, so - /// `is_absolute` and [`has_root`] are equivalent. + /// `is_absolute` and [`has_root`] are equivalent. /// /// * On Windows, a path is absolute if it has a prefix and starts with the - /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// + /// [`has_root`]: #method.has_root /// /// # Examples /// @@ -122,16 +174,16 @@ impl Path { /// /// assert!(!Path::new("foo.txt").is_absolute()); /// ``` - /// - /// [`has_root`]: #method.has_root pub fn is_absolute(&self) -> bool { self.inner.is_absolute() } - /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// Returns `true` if the `Path` is relative, i.e. not absolute. /// /// See [`is_absolute`]'s documentation for more details. /// + /// [`is_absolute`]: #method.is_absolute + /// /// # Examples /// /// ``` @@ -139,8 +191,6 @@ impl Path { /// /// assert!(Path::new("foo.txt").is_relative()); /// ``` - /// - /// [`is_absolute`]: #method.is_absolute pub fn is_relative(&self) -> bool { self.inner.is_relative() } @@ -150,9 +200,9 @@ impl Path { /// * On Unix, a path has a root if it begins with `/`. /// /// * On Windows, a path has a root if it: - /// * has no prefix and begins with a separator, e.g., `\windows` - /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` - /// * has any non-disk prefix, e.g., `\\server\share` + /// * has no prefix and begins with a separator, e.g. `\windows` + /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g. `\\server\share` /// /// # Examples /// @@ -196,6 +246,9 @@ impl Path { /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, /// namely `&self`. /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html + /// [`parent`]: struct.Path.html#method.parent + /// /// # Examples /// /// ``` @@ -207,9 +260,6 @@ impl Path { /// assert_eq!(ancestors.next(), Some(Path::new("/").into())); /// assert_eq!(ancestors.next(), None); /// ``` - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html - /// [`parent`]: struct.Path.html#method.parent pub fn ancestors(&self) -> Ancestors<'_> { Ancestors { next: Some(&self) } } @@ -226,9 +276,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::Path; /// use std::ffi::OsStr; /// + /// use async_std::path::Path; + /// /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); @@ -240,7 +291,7 @@ impl Path { self.inner.file_name() } - /// Returns a path that, when joined onto `base`, yields `self`. + /// Returns a path that becomes `self` when joined onto `base`. /// /// # Errors /// @@ -314,15 +365,15 @@ impl Path { self.inner.ends_with(child.as_ref()) } - /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// Extracts the stem (non-extension) portion of [`file_name`]. /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// /// The stem is: /// - /// * [`None`], if there is no file name; - /// * The entire file name if there is no embedded `.`; - /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * The entire file name if there is no embedded `.` + /// * The entire file name if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name before the final `.` /// /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None @@ -340,16 +391,16 @@ impl Path { self.inner.file_stem() } - /// Extracts the extension of [`self.file_name`], if possible. + /// Extracts the extension of [`file_name`], if possible. /// /// The extension is: /// - /// * [`None`], if there is no file name; - /// * [`None`], if there is no embedded `.`; - /// * [`None`], if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * [`None`], if there is no embedded `.` + /// * [`None`], if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name after the final `.` /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples @@ -442,24 +493,27 @@ impl Path { /// and `a/b/../c` are distinct, to account for the possibility that `b` /// is a symbolic link (so its parent isn't `a`). /// + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + /// /// # Examples /// /// ``` - /// use async_std::path::{Path, Component}; /// use std::ffi::OsStr; /// + /// use async_std::path::{Path, Component}; + /// /// let mut components = Path::new("/tmp/foo.txt").components(); /// /// assert_eq!(components.next(), Some(Component::RootDir)); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); - /// assert_eq!(components.next(), None) + /// assert_eq!(components.next(), None); /// ``` - /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir pub fn components(&self) -> Components<'_> { - self.inner.components() + Components { + inner: self.inner.components(), + } } /// Produces an iterator over the path's components viewed as [`OsStr`] @@ -474,9 +528,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::{self, Path}; /// use std::ffi::OsStr; /// + /// use async_std::path::{self, Path}; + /// /// let mut it = Path::new("/tmp/foo.txt").iter(); /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); @@ -484,7 +539,9 @@ impl Path { /// assert_eq!(it.next(), None) /// ``` pub fn iter(&self) -> Iter<'_> { - self.inner.iter() + Iter { + inner: self.components(), + } } /// Returns an object that implements [`Display`] for safely printing paths @@ -505,7 +562,7 @@ impl Path { self.inner.display() } - /// Queries the file system to get information about a file, directory, etc. + /// Reads the metadata of a file or directory. /// /// This function will traverse symbolic links to query information about the /// destination file. @@ -522,16 +579,17 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.metadata().await.expect("metadata call failed"); + /// let metadata = path.metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn metadata(&self) -> io::Result { fs::metadata(self).await } - /// Queries the metadata about a file without following symlinks. + /// Reads the metadata of a file or directory without following symbolic links. /// /// This is an alias to [`fs::symlink_metadata`]. /// @@ -545,17 +603,20 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// let metadata = path.symlink_metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn symlink_metadata(&self) -> io::Result { fs::symlink_metadata(self).await } - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. + /// Returns the canonical form of a path. + /// + /// The returned path is in absolute form with all intermediate components normalized and + /// symbolic links resolved. /// /// This is an alias to [`fs::canonicalize`]. /// @@ -569,10 +630,11 @@ impl Path { /// use async_std::path::{Path, PathBuf}; /// /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().await.unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// assert_eq!(path.canonicalize().await?, PathBuf::from("/foo/test/bar.rs")); /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn canonicalize(&self) -> io::Result { fs::canonicalize(self).await } @@ -591,17 +653,18 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/laputa/sky_castle.rs"); - /// let path_link = path.read_link().await.expect("read_link call failed"); + /// let path_link = path.read_link().await?; /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn read_link(&self) -> io::Result { fs::read_link(self).await } - /// Returns an iterator over the entries within a directory. + /// Returns a stream over the entries within a directory. /// - /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New + /// The stream will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New /// errors may be encountered after an iterator is initially constructed. /// /// This is an alias to [`fs::read_dir`]. @@ -615,12 +678,13 @@ impl Path { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use async_std::path::Path; /// use async_std::fs; - /// use futures_util::stream::StreamExt; + /// use async_std::path::Path; + /// use async_std::prelude::*; /// /// let path = Path::new("/laputa"); - /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); + /// let mut dir = fs::read_dir(&path).await?; + /// /// while let Some(res) = dir.next().await { /// let entry = res?; /// println!("{}", entry.file_name().to_string_lossy()); @@ -628,6 +692,7 @@ impl Path { /// # /// # Ok(()) }) } /// ``` + #[cfg(not(target_os = "unknown"))] pub async fn read_dir(&self) -> io::Result { fs::read_dir(self).await } @@ -657,6 +722,7 @@ impl Path { /// check errors, call [fs::metadata]. /// /// [fs::metadata]: ../fs/fn.metadata.html + #[cfg(not(target_os = "unknown"))] pub async fn exists(&self) -> bool { fs::metadata(self).await.is_ok() } @@ -689,6 +755,7 @@ impl Path { /// /// [fs::metadata]: ../fs/fn.metadata.html /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file + #[cfg(not(target_os = "unknown"))] pub async fn is_file(&self) -> bool { fs::metadata(self) .await @@ -710,6 +777,7 @@ impl Path { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::path::Path; + /// /// assert_eq!(Path::new("./is_a_directory/").is_dir().await, true); /// assert_eq!(Path::new("a_file.txt").is_dir().await, false); /// # @@ -724,6 +792,7 @@ impl Path { /// /// [fs::metadata]: ../fs/fn.metadata.html /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir + #[cfg(not(target_os = "unknown"))] pub async fn is_dir(&self) -> bool { fs::metadata(self) .await @@ -736,6 +805,15 @@ impl Path { /// /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html /// [`PathBuf`]: struct.PathBuf.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path: Box = Path::new("foo.txt").into(); + /// let path_buf = path.into_path_buf(); + /// ``` pub fn into_path_buf(self: Box) -> PathBuf { let rw = Box::into_raw(self) as *mut std::path::Path; let inner = unsafe { Box::from_raw(rw) }; @@ -743,27 +821,42 @@ impl Path { } } -impl<'a> From<&'a std::path::Path> for &'a Path { - fn from(path: &'a std::path::Path) -> &'a Path { - &Path::new(path.as_os_str()) +impl From<&Path> for Box { + fn from(path: &Path) -> Box { + let boxed: Box = path.inner.into(); + let rw = Box::into_raw(boxed) as *mut Path; + unsafe { Box::from_raw(rw) } } } -impl<'a> Into<&'a std::path::Path> for &'a Path { - fn into(self) -> &'a std::path::Path { - std::path::Path::new(&self.inner) +impl From<&Path> for Arc { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } } } -impl AsRef for Path { - fn as_ref(&self) -> &std::path::Path { - self.into() +impl From<&Path> for Rc { + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } } } -impl AsRef for std::path::Path { - fn as_ref(&self) -> &Path { - self.into() +impl ToOwned for Path { + type Owned = PathBuf; + + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &OsStr { + self.inner.as_ref() } } @@ -773,13 +866,26 @@ impl AsRef for Path { } } -impl AsRef for Path { - fn as_ref(&self) -> &OsStr { - self.inner.as_ref() +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) } } -impl AsRef for OsStr { +impl<'a> From<&'a Path> for Cow<'a, Path> { + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +impl AsRef for Cow<'_, OsStr> { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +impl AsRef for OsString { fn as_ref(&self) -> &Path { Path::new(self) } @@ -797,16 +903,139 @@ impl AsRef for String { } } -impl AsRef for std::path::PathBuf { +impl AsRef for PathBuf { fn as_ref(&self) -> &Path { - Path::new(self.into()) + self } } -impl std::borrow::ToOwned for Path { - type Owned = PathBuf; +impl<'a> IntoIterator for &'a PathBuf { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; - fn to_owned(&self) -> PathBuf { - self.to_path_buf() + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +impl<'a> IntoIterator for &'a Path { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(PathBuf, Path); +impl_cmp!(PathBuf, &'a Path); +impl_cmp!(Cow<'a, Path>, Path); +impl_cmp!(Cow<'a, Path>, &'b Path); +impl_cmp!(Cow<'a, Path>, PathBuf); + +macro_rules! impl_cmp_os_str { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self.as_ref(), other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self.as_ref(), other) + } + } + }; +} + +impl_cmp_os_str!(PathBuf, OsStr); +impl_cmp_os_str!(PathBuf, &'a OsStr); +impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>); +impl_cmp_os_str!(PathBuf, OsString); +impl_cmp_os_str!(Path, OsStr); +impl_cmp_os_str!(Path, &'a OsStr); +impl_cmp_os_str!(Path, Cow<'a, OsStr>); +impl_cmp_os_str!(Path, OsString); +impl_cmp_os_str!(&'a Path, OsStr); +impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>); +impl_cmp_os_str!(&'a Path, OsString); + +impl<'a> From<&'a std::path::Path> for &'a Path { + fn from(path: &'a std::path::Path) -> &'a Path { + &Path::new(path.as_os_str()) + } +} + +impl<'a> Into<&'a std::path::Path> for &'a Path { + fn into(self) -> &'a std::path::Path { + std::path::Path::new(&self.inner) + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &std::path::Path { + self.into() + } +} + +impl AsRef for std::path::Path { + fn as_ref(&self) -> &Path { + self.into() + } +} + +impl AsRef for std::path::PathBuf { + fn as_ref(&self) -> &Path { + let p: &std::path::Path = self.as_ref(); + p.into() } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 64744e140..f9370cbab 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,11 +1,23 @@ +use std::borrow::{Borrow, Cow}; use std::ffi::{OsStr, OsString}; +use std::iter::{self, FromIterator}; +use std::ops::Deref; +#[cfg(feature = "unstable")] +use std::pin::Pin; +use std::rc::Rc; +use std::str::FromStr; +use std::sync::Arc; use crate::path::Path; +#[cfg(feature = "unstable")] +use crate::prelude::*; +#[cfg(feature = "unstable")] +use crate::stream::{self, FromStream, IntoStream}; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -91,9 +103,9 @@ impl PathBuf { /// let mut p = PathBuf::from("/test/test.rs"); /// /// p.pop(); - /// assert_eq!(Path::new("/test"), p.as_ref()); + /// assert_eq!(Path::new("/test"), p); /// p.pop(); - /// assert_eq!(Path::new("/"), p.as_ref()); + /// assert_eq!(Path::new("/"), p); /// ``` pub fn pop(&mut self) -> bool { self.inner.pop() @@ -158,7 +170,7 @@ impl PathBuf { self.inner.set_extension(extension) } - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// Consumes the `PathBuf`, returning its internal [`OsString`] storage. /// /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html /// @@ -184,47 +196,177 @@ impl PathBuf { } } -impl std::ops::Deref for PathBuf { +impl From> for PathBuf { + fn from(boxed: Box) -> PathBuf { + boxed.into_path_buf() + } +} + +impl From for Box { + fn from(p: PathBuf) -> Box { + p.into_boxed_path() + } +} + +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_path_buf().into_boxed_path() + } +} + +impl> From<&T> for PathBuf { + fn from(s: &T) -> PathBuf { + PathBuf::from(s.as_ref().to_os_string()) + } +} + +impl From for PathBuf { + fn from(s: OsString) -> PathBuf { + PathBuf { inner: s.into() } + } +} + +impl From for OsString { + fn from(path_buf: PathBuf) -> OsString { + path_buf.inner.into() + } +} + +impl From for PathBuf { + fn from(s: String) -> PathBuf { + PathBuf::from(OsString::from(s)) + } +} + +impl FromStr for PathBuf { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(PathBuf::from(s)) + } +} + +impl> FromIterator

for PathBuf { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +impl> iter::Extend

for PathBuf { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } +} + +impl Deref for PathBuf { type Target = Path; fn deref(&self) -> &Path { - self.as_ref() + Path::new(&self.inner) } } -impl std::borrow::Borrow for PathBuf { +impl Borrow for PathBuf { fn borrow(&self) -> &Path { - &**self + self.deref() } } -impl From for PathBuf { - fn from(path: std::path::PathBuf) -> PathBuf { - PathBuf { inner: path } +impl<'a> From for Cow<'a, Path> { + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) } } -impl Into for PathBuf { - fn into(self) -> std::path::PathBuf { - self.inner.into() +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) } } -impl From for PathBuf { - fn from(path: OsString) -> PathBuf { - std::path::PathBuf::from(path).into() +impl<'a> From> for PathBuf { + #[inline] + fn from(p: Cow<'a, Path>) -> Self { + p.into_owned() } } -impl From<&str> for PathBuf { - fn from(path: &str) -> PathBuf { - std::path::PathBuf::from(path).into() +impl From for Arc { + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &Path { - Path::new(&self.inner) +impl From for Rc { + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &OsStr { + self.inner.as_ref() + } +} + +#[cfg(feature = "unstable")] +impl> stream::Extend

for PathBuf { + fn extend<'a, S: IntoStream + 'a>( + &'a mut self, + stream: S, + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push(item.as_ref()); + } + }) + } +} + +#[cfg(feature = "unstable")] +impl<'b, P: AsRef + 'b + Send> FromStream

for PathBuf { + #[inline] + fn from_stream<'a, S: IntoStream + 'a>( + stream: S, + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { + let stream = stream.into_stream(); + + Box::pin(async move { + let mut out = Self::new(); + stream::extend(&mut out, stream).await; + out + }) + } +} + +impl From for PathBuf { + fn from(path: std::path::PathBuf) -> PathBuf { + PathBuf { inner: path } + } +} + +impl Into for PathBuf { + fn into(self) -> std::path::PathBuf { + self.inner } } diff --git a/src/prelude.rs b/src/prelude.rs index 91432e0bb..a2a14a182 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,33 +11,39 @@ //! use async_std::prelude::*; //! ``` -#[doc(no_inline)] -pub use crate::future::Future; -#[doc(no_inline)] -pub use crate::io::BufRead as _; -#[doc(no_inline)] -pub use crate::io::Read as _; -#[doc(no_inline)] -pub use crate::io::Seek as _; -#[doc(no_inline)] -pub use crate::io::Write as _; -#[doc(no_inline)] -pub use crate::stream::Stream; -#[doc(no_inline)] -pub use crate::task_local; +cfg_std! { + #[doc(no_inline)] + pub use std::future::Future; + #[doc(no_inline)] + pub use crate::stream::Stream; + + #[doc(inline)] + pub use crate::future::future::FutureExt; + #[doc(inline)] + pub use crate::stream::stream::StreamExt; + #[doc(no_inline)] + pub use crate::io::BufRead as _; + #[doc(no_inline)] + pub use crate::io::Read as _; + #[doc(no_inline)] + pub use crate::io::Seek as _; + #[doc(no_inline)] + pub use crate::io::Write as _; -#[doc(hidden)] -pub use crate::future::future::FutureExt as _; -#[doc(hidden)] -pub use crate::io::buf_read::BufReadExt as _; -#[doc(hidden)] -pub use crate::io::read::ReadExt as _; -#[doc(hidden)] -pub use crate::io::seek::SeekExt as _; -#[doc(hidden)] -pub use crate::io::write::WriteExt as _; -#[doc(hidden)] -pub use crate::stream::stream::StreamExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::BufReadExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::ReadExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::SeekExt as _; + #[doc(no_inline)] + pub use crate::io::prelude::WriteExt as _; +} + +cfg_default! { + #[doc(no_inline)] + pub use crate::task_local; +} cfg_unstable! { #[doc(no_inline)] diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 000000000..8ba803da5 --- /dev/null +++ b/src/process.rs @@ -0,0 +1,43 @@ +//! A module for working with processes. +//! +//! This module is mostly concerned with spawning and interacting with child processes, but it also +//! provides abort and exit for terminating the current process. +//! +//! This is an async version of [`std::process`]. +//! +//! [`std::process`]: https://doc.rust-lang.org/std/process/index.html + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ExitStatus; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Output; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Stdio; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Child; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStderr; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStdin; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::ChildStdout; + +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub use async_process::Command; + +// Re-export functions. +pub use std::process::{abort, exit, id}; diff --git a/src/process/mod.rs b/src/process/mod.rs deleted file mode 100644 index 630c5b9b9..000000000 --- a/src/process/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! A module for working with processes. -//! -//! This module is mostly concerned with spawning and interacting with child processes, but it also -//! provides abort and exit for terminating the current process. -//! -//! This is an async version of [`std::process`]. -//! -//! [`std::process`]: https://doc.rust-lang.org/std/process/index.html - -// Re-export structs. -pub use std::process::{ExitStatus, Output}; - -// Re-export functions. -pub use std::process::{abort, exit, id}; diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 6033eb973..68fa535fa 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -5,43 +5,68 @@ use crate::stream::{FromStream, IntoStream}; impl FromStream> for Result where + T: Send, + E: Send, V: FromStream, { /// Takes each element in the stream: if it is an `Err`, no further /// elements are taken, and the `Err` is returned. Should no `Err` /// occur, a container with the values of each `Result` is returned. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let v = stream::from_iter(vec![1, 2]); + /// let res: Result, &'static str> = v.map(|x: u32| + /// x.checked_add(1).ok_or("Overflow!") + /// ).collect().await; + /// assert_eq!(res, Ok(vec![2, 3])); + /// # + /// # }) } + /// ``` #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - - // Using `scan` here because it is able to stop the stream early + // Using `take_while` here because it is able to stop the stream early // if a failure occurs + let mut is_error = false; let mut found_error = None; let out: V = stream - .scan((), |_, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - found_error = Some(err); - // Stop processing the stream on error - None - } + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None } }) .collect() .await; - match found_error { - Some(err) => Err(err), - None => Ok(out), + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) } }) } diff --git a/src/result/mod.rs b/src/result/mod.rs index 908f9c4dc..fc263318b 100644 --- a/src/result/mod.rs +++ b/src/result/mod.rs @@ -5,5 +5,11 @@ mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::result::Result; + +cfg_unstable! { + mod product; + mod sum; +} diff --git a/src/result/product.rs b/src/result/product.rs new file mode 100644 index 000000000..45782ff70 --- /dev/null +++ b/src/result/product.rs @@ -0,0 +1,75 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Product, Stream}; + +impl Product> for Result +where + T: Product, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is an `Err`, no further + elements are taken, and the `Err` is returned. Should no `Err` occur, + the product of all elements is returned. + + # Examples + + This multiplies every integer in a vector, rejecting the product if a negative element is + encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let v = stream::from_iter(vec![1, 2, 4]); + let res: Result = v.map(|x| + if x < 0 { + Err("Negative element found") + } else { + Ok(x) + }).product().await; + assert_eq!(res, Ok(8)); + # + # }) } + ``` + "#] + fn product<'a, S>(stream: S) -> Pin> + 'a>> + where + S: Stream> + 'a, + { + Box::pin(async move { + // Using `take_while` here because it is able to stop the stream early + // if a failure occurs + let mut is_error = false; + let mut found_error = None; + let out = >::product( + stream + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None + } + }), + ) + .await; + + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) + } + }) + } +} diff --git a/src/result/sum.rs b/src/result/sum.rs new file mode 100644 index 000000000..b6d84a0c4 --- /dev/null +++ b/src/result/sum.rs @@ -0,0 +1,75 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{Stream, Sum}; + +impl Sum> for Result +where + T: Sum, +{ + #[doc = r#" + Takes each element in the `Stream`: if it is an `Err`, no further + elements are taken, and the `Err` is returned. Should no `Err` occur, + the sum of all elements is returned. + + # Examples + + This sums up every integer in a vector, rejecting the sum if a negative + element is encountered: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let v = stream::from_iter(vec![1, 2]); + let res: Result = v.map(|x| + if x < 0 { + Err("Negative element found") + } else { + Ok(x) + }).sum().await; + assert_eq!(res, Ok(3)); + # + # }) } + ``` + "#] + fn sum<'a, S>(stream: S) -> Pin> + 'a>> + where + S: Stream> + 'a, + { + Box::pin(async move { + // Using `take_while` here because it is able to stop the stream early + // if a failure occurs + let mut is_error = false; + let mut found_error = None; + let out = >::sum( + stream + .take_while(|elem| { + // Stop processing the stream on `Err` + !is_error + && (elem.is_ok() || { + is_error = true; + // Capture first `Err` + true + }) + }) + .filter_map(|elem| match elem { + Ok(value) => Some(value), + Err(err) => { + found_error = Some(err); + None + } + }), + ) + .await; + + if is_error { + Err(found_error.unwrap()) + } else { + Ok(out) + } + }) + } +} diff --git a/src/rt/mod.rs b/src/rt/mod.rs new file mode 100644 index 000000000..80f1c4e6b --- /dev/null +++ b/src/rt/mod.rs @@ -0,0 +1,18 @@ +//! The runtime. + +use std::env; + +use once_cell::sync::Lazy; + +/// Dummy runtime struct. +pub struct Runtime {} + +/// The global runtime. +pub static RUNTIME: Lazy = Lazy::new(|| { + // Create an executor thread pool. + + let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string()); + async_global_executor::init_with_config(async_global_executor::GlobalExecutorConfig::default().with_env_var("ASYNC_STD_THREAD_COUNT").with_thread_name_fn(move || thread_name.clone())); + + Runtime {} +}); diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs deleted file mode 100644 index 129bb1cdf..000000000 --- a/src/stream/double_ended_stream.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::stream::Stream; - -use std::pin::Pin; -use std::task::{Context, Poll}; - -/// A stream able to yield elements from both ends. -/// -/// Something that implements `DoubleEndedStream` has one extra capability -/// over something that implements [`Stream`]: the ability to also take -/// `Item`s from the back, as well as the front. -/// -/// [`Stream`]: trait.Stream.html -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub trait DoubleEndedStream: Stream { - /// Removes and returns an element from the end of the stream. - /// - /// Returns `None` when there are no more elements. - /// - /// The [trait-level] docs contain more details. - /// - /// [trait-level]: trait.DoubleEndedStream.html - fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; -} diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs new file mode 100644 index 000000000..0cd705418 --- /dev/null +++ b/src/stream/double_ended_stream/mod.rs @@ -0,0 +1,246 @@ +use crate::stream::Stream; + +use std::pin::Pin; +use std::task::{Context, Poll}; + +mod next_back; +mod nth_back; +mod rfind; +mod rfold; +mod try_rfold; + +use next_back::NextBackFuture; +use nth_back::NthBackFuture; +use rfind::RFindFuture; +use rfold::RFoldFuture; +use try_rfold::TryRFoldFuture; + +/// A stream able to yield elements from both ends. +/// +/// Something that implements `DoubleEndedStream` has one extra capability +/// over something that implements [`Stream`]: the ability to also take +/// `Item`s from the back, as well as the front. +/// +/// [`Stream`]: trait.Stream.html +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub trait DoubleEndedStream: Stream { + #[doc = r#" + Attempts to receive the next item from the back of the stream. + + There are several possible return values: + + * `Poll::Pending` means this stream's next_back value is not ready yet. + * `Poll::Ready(None)` means this stream has been exhausted. + * `Poll::Ready(Some(item))` means `item` was received out of the stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::pin::Pin; + + use async_std::prelude::*; + use async_std::stream; + use async_std::task::{Context, Poll}; + + fn increment( + s: impl DoubleEndedStream + Unpin, + ) -> impl DoubleEndedStream + Unpin { + struct Increment(S); + + impl + Unpin> Stream for Increment { + type Item = S::Item; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.0).poll_next(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), + } + } + } + + impl + Unpin> DoubleEndedStream for Increment { + fn poll_next_back( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.0).poll_next_back(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), + } + } + } + + Increment(s) + } + + let mut s = increment(stream::once(7)); + + assert_eq!(s.next_back().await, Some(8)); + assert_eq!(s.next_back().await, None); + # + # }) } + ``` + "#] + fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + #[doc = r#" + Advances the stream and returns the next value. + + Returns [`None`] when iteration is finished. Individual stream implementations may + choose to resume iteration, and so calling `next()` again may or may not eventually + start returning more values. + + [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![7u8]); + + assert_eq!(s.next_back().await, Some(7)); + assert_eq!(s.next_back().await, None); + # + # }) } + ``` + "#] + fn next_back(&mut self) -> NextBackFuture<'_, Self> + where + Self: Unpin, + { + NextBackFuture { stream: self } + } + + #[doc = r#" + Returns the nth element from the back of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.nth_back(1).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] + fn nth_back(&mut self, n: usize) -> NthBackFuture<'_, Self> + where + Self: Unpin + Sized, + { + NthBackFuture::new(self, n) + } + + #[doc = r#" + Returns the first element from the right that matches the predicate. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.rfind(|v| v % 2 == 0).await; + assert_eq!(second, Some(4)); + # + # }) } + ``` + "#] + fn rfind

(&mut self, p: P) -> RFindFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(&Self::Item) -> bool, + { + RFindFuture::new(self, p) + } + + #[doc = r#" + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + + let second = s.rfold(0, |acc, v| v + acc).await; + + assert_eq!(second, 15); + # + # }) } + ``` + "#] + fn rfold(self, accum: B, f: F) -> RFoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + RFoldFuture::new(self, accum, f) + } + + #[doc = r#" + A combinator that applies a function as long as it returns successfully, producing a single, final value. + Immediately returns the error when the function returns unsuccessfully. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]); + let sum = s.try_rfold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") + } + }).await; + + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_rfold(self, accum: B, f: F) -> TryRFoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryRFoldFuture::new(self, accum, f) + } +} diff --git a/src/stream/double_ended_stream/next_back.rs b/src/stream/double_ended_stream/next_back.rs new file mode 100644 index 000000000..9fb52b7b6 --- /dev/null +++ b/src/stream/double_ended_stream/next_back.rs @@ -0,0 +1,19 @@ +use core::pin::Pin; +use core::future::Future; + +use crate::stream::DoubleEndedStream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct NextBackFuture<'a, T: Unpin + ?Sized> { + pub(crate) stream: &'a mut T, +} + +impl Future for NextBackFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.stream).poll_next_back(cx) + } +} diff --git a/src/stream/double_ended_stream/nth_back.rs b/src/stream/double_ended_stream/nth_back.rs new file mode 100644 index 000000000..2701e9b69 --- /dev/null +++ b/src/stream/double_ended_stream/nth_back.rs @@ -0,0 +1,41 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use crate::stream::DoubleEndedStream; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct NthBackFuture<'a, S> { + stream: &'a mut S, + n: usize, +} + +impl<'a, S> NthBackFuture<'a, S> { + pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { + NthBackFuture { stream, n } + } +} + +impl<'a, S> Future for NthBackFuture<'a, S> +where + S: DoubleEndedStream + Sized + Unpin, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx)); + match next { + Some(v) => match self.n { + 0 => Poll::Ready(Some(v)), + _ => { + self.n -= 1; + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} + diff --git a/src/stream/double_ended_stream/rfind.rs b/src/stream/double_ended_stream/rfind.rs new file mode 100644 index 000000000..366655169 --- /dev/null +++ b/src/stream/double_ended_stream/rfind.rs @@ -0,0 +1,41 @@ +use core::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; + +use crate::stream::DoubleEndedStream; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct RFindFuture<'a, S, P> { + stream: &'a mut S, + p: P, +} + +impl<'a, S, P> RFindFuture<'a, S, P> { + pub(super) fn new(stream: &'a mut S, p: P) -> Self { + RFindFuture { stream, p } + } +} + +impl Unpin for RFindFuture<'_, S, P> {} + +impl<'a, S, P> Future for RFindFuture<'a, S, P> +where + S: DoubleEndedStream + Unpin + Sized, + P: FnMut(&S::Item) -> bool, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx)); + + match item { + Some(v) if (&mut self.p)(&v) => Poll::Ready(Some(v)), + Some(_) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/double_ended_stream/rfold.rs b/src/stream/double_ended_stream/rfold.rs new file mode 100644 index 000000000..9bc18783f --- /dev/null +++ b/src/stream/double_ended_stream/rfold.rs @@ -0,0 +1,52 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use crate::stream::DoubleEndedStream; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct RFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + } +} + +impl RFoldFuture { + pub(super) fn new(stream: S, init: B, f: F) -> Self { + RFoldFuture { + stream, + f, + acc: Some(init), + } + } +} + +impl Future for RFoldFuture +where + S: DoubleEndedStream + Sized, + F: FnMut(B, S::Item) -> B, +{ + type Output = B; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx)); + + match next { + Some(v) => { + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + *this.acc = Some(new); + } + None => return Poll::Ready(this.acc.take().unwrap()), + } + } + } +} diff --git a/src/stream/double_ended_stream/try_rfold.rs b/src/stream/double_ended_stream/try_rfold.rs new file mode 100644 index 000000000..d67b6ecf8 --- /dev/null +++ b/src/stream/double_ended_stream/try_rfold.rs @@ -0,0 +1,56 @@ +use crate::future::Future; +use core::pin::Pin; +use crate::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use crate::stream::DoubleEndedStream; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryRFoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + } +} + +impl TryRFoldFuture { + pub(super) fn new(stream: S, init: T, f: F) -> Self { + TryRFoldFuture { + stream, + f, + acc: Some(init), + } + } +} + +impl Future for TryRFoldFuture +where + S: DoubleEndedStream + Unpin, + F: FnMut(T, S::Item) -> Result, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx)); + + match next { + Some(v) => { + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + + match new { + Ok(o) => *this.acc = Some(o), + Err(e) => return Poll::Ready(Err(e)), + } + } + None => return Poll::Ready(Ok(this.acc.take().unwrap())), + } + } + } +} diff --git a/src/stream/empty.rs b/src/stream/empty.rs index ceb91fead..a11337af3 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -1,11 +1,16 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::marker::PhantomData; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; /// Creates a stream that doesn't yield any items. /// +/// This `struct` is created by the [`empty`] function. See its +/// documentation for more. +/// +/// [`empty`]: fn.empty.html +/// /// # Examples /// /// ``` diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 7d2e7cb96..8b6ba97da 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -59,7 +59,7 @@ pub use crate::stream::Stream; /// # } /// # } /// # } -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// impl ExactSizeStream for Counter { /// // We can easily calculate the remaining number of iterations. @@ -74,8 +74,8 @@ pub use crate::stream::Stream; /// /// assert_eq!(5, counter.len()); /// # }); -/// # } /// ``` +#[allow(clippy::len_without_is_empty)] // ExactSizeIterator::is_empty is unstable #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait ExactSizeStream: Stream { diff --git a/src/stream/extend.rs b/src/stream/extend.rs index d9e148164..0c7d41049 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -1,9 +1,9 @@ -use std::pin::Pin; +use core::pin::Pin; +use core::future::Future; -use crate::prelude::*; use crate::stream::IntoStream; -/// Extend a collection with the contents of a stream. +/// Extends a collection with the contents of a stream. /// /// Streams produce a series of values asynchronously, and collections can also be thought of as a /// series of values. The `Extend` trait bridges this gap, allowing you to extend a collection @@ -14,40 +14,64 @@ use crate::stream::IntoStream; /// ## Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; -/// use async_std::stream::{self, Extend}; +/// use async_std::stream; /// /// let mut v: Vec = vec![1, 2]; /// let s = stream::repeat(3usize).take(3); -/// v.stream_extend(s).await; +/// stream::Extend::extend(&mut v, s).await; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. - fn stream_extend<'a, T: IntoStream + 'a>( + fn extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - A: 'a; + ::IntoStream: Send; } -impl Extend<()> for () { - fn stream_extend<'a, T: IntoStream + 'a>( - &'a mut self, - stream: T, - ) -> Pin + 'a>> { - let stream = stream.into_stream(); - Box::pin(async move { - pin_utils::pin_mut!(stream); - while let Some(_) = stream.next().await {} - }) - } +/// Extends a collection with the contents of a stream. +/// +/// Streams produce a series of values asynchronously, and collections can also be thought of as a +/// series of values. The [`Extend`] trait bridges this gap, allowing you to extend a collection +/// asynchronously by including the contents of that stream. When extending a collection with an +/// already existing key, that entry is updated or, in the case of collections that permit multiple +/// entries with equal keys, that entry is inserted. +/// +/// [`Extend`]: trait.Extend.html +/// +/// ## Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut v: Vec = vec![1, 2]; +/// let s = stream::repeat(3usize).take(3); +/// stream::extend(&mut v, s).await; +/// +/// assert_eq!(v, vec![1, 2, 3, 3, 3]); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub async fn extend<'a, C, T, S>(collection: &mut C, stream: S) +where + C: Extend, + S: IntoStream + 'a, + ::IntoStream: Send, +{ + Extend::extend(collection, stream).await } diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index c1cb97af4..d3736454a 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,22 +1,21 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; /// A stream that yields elements by calling a closure. /// -/// This stream is constructed by [`from_fn`] function. +/// This stream is created by the [`from_fn`] function. See its +/// documentation for more. /// /// [`from_fn`]: fn.from_fn.html -#[derive(Debug)] -pub struct FromFn { +#[derive(Clone, Debug)] +pub struct FromFn { f: F, - future: Option, - __t: PhantomData, } +impl Unpin for FromFn {} + /// Creates a new stream where to produce each new element a provided closure is called. /// /// This allows creating a custom stream with any behaviour without using the more verbose @@ -25,76 +24,45 @@ pub struct FromFn { /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; -/// use async_std::sync::Mutex; -/// use std::sync::Arc; /// use async_std::stream; /// -/// let count = Arc::new(Mutex::new(0u8)); +/// let mut count = 0u8; /// let s = stream::from_fn(|| { -/// let count = Arc::clone(&count); -/// -/// async move { -/// *count.lock().await += 1; -/// -/// if *count.lock().await > 3 { -/// None -/// } else { -/// Some(*count.lock().await) -/// } +/// count += 1; +/// if count > 3 { +/// None +/// } else { +/// Some(count) /// } /// }); /// /// pin_utils::pin_mut!(s); +/// /// 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, None); /// # -/// # }) } -/// +/// # }) /// ``` -pub fn from_fn(f: F) -> FromFn +pub fn from_fn(f: F) -> FromFn where - F: FnMut() -> Fut, - Fut: Future>, + F: FnMut() -> Option, { - FromFn { - f, - future: None, - __t: PhantomData, - } -} - -impl FromFn { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); + FromFn { f } } -impl Stream for FromFn +impl Stream for FromFn where - F: FnMut() -> Fut, - Fut: Future>, + F: FnMut() -> Option, { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match &self.future { - Some(_) => { - let next = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - return Poll::Ready(next); - } - None => { - let fut = (self.as_mut().f())(); - self.as_mut().future().set(Some(fut)); - } - } - } + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + let item = (&mut self.f)(); + Poll::Ready(item) } } diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs new file mode 100644 index 000000000..ea2ed8efb --- /dev/null +++ b/src/stream/from_iter.rs @@ -0,0 +1,62 @@ +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +#[cfg(feature = "unstable")] +use crate::stream::double_ended_stream::DoubleEndedStream; +use crate::task::{Context, Poll}; + +pin_project! { + /// A stream that was created from iterator. + /// + /// This stream is created by the [`from_iter`] function. + /// See it documentation for more. + /// + /// [`from_iter`]: fn.from_iter.html + #[derive(Clone, Debug)] + pub struct FromIter { + iter: I, + } +} + +/// Converts an iterator into a stream. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut s = stream::from_iter(vec![0, 1, 2, 3]); +/// +/// assert_eq!(s.next().await, Some(0)); +/// 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, None); +/// # +/// # }) +/// ``` +pub fn from_iter(iter: I) -> FromIter { + FromIter { + iter: iter.into_iter(), + } +} + +impl Stream for FromIter { + type Item = I::Item; + + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.iter.next()) + } +} + +#[cfg(feature = "unstable")] +impl DoubleEndedStream for FromIter { + fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.iter.next_back()) + } +} diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 54a222910..c4001917c 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -1,6 +1,7 @@ -use super::IntoStream; +use core::future::Future; +use core::pin::Pin; -use std::pin::Pin; +use crate::stream::IntoStream; /// Conversion from a `Stream`. /// @@ -15,22 +16,24 @@ use std::pin::Pin; /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -/// use crate::async_std::stream::FromStream; -/// use async_std::prelude::*; -/// use async_std::stream; +/// # +/// use async_std::prelude::*; +/// use async_std::stream::{self, FromStream}; /// -/// let five_fives = stream::repeat(5).take(5); +/// let five_fives = stream::repeat(5).take(5); /// -/// let v = Vec::from_stream(five_fives).await; +/// let v = Vec::from_stream(five_fives).await; /// -/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// # /// # Ok(()) }) } /// ``` /// /// Using `collect` to implicitly use `FromStream` /// -///``` +/// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # /// use async_std::prelude::*; /// use async_std::stream; /// let five_fives = stream::repeat(5).take(5); @@ -40,14 +43,13 @@ use std::pin::Pin; /// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// # /// # Ok(()) }) } -///``` +/// ``` /// /// Implementing `FromStream` for your type: /// /// ``` /// use async_std::prelude::*; -/// use async_std::stream::{Extend, FromStream, IntoStream}; -/// use async_std::stream; +/// use async_std::stream::{self, FromStream, IntoStream}; /// use std::pin::Pin; /// /// // A sample collection, that's just a wrapper over Vec @@ -70,14 +72,17 @@ use std::pin::Pin; /// impl FromStream for MyCollection { /// fn from_stream<'a, S: IntoStream + 'a>( /// stream: S, -/// ) -> Pin + 'a>> { +/// ) -> Pin + 'a + Send>> +/// where +/// ::IntoStream: Send, +/// { /// let stream = stream.into_stream(); /// /// Box::pin(async move { /// let mut c = MyCollection::new(); /// /// let mut v = vec![]; -/// v.stream_extend(stream).await; +/// stream::extend(&mut v, stream).await; /// /// for i in v { /// c.add(i); @@ -88,6 +93,7 @@ use std::pin::Pin; /// } /// /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # /// // Now we can make a new stream... /// let stream = stream::repeat(5).take(5); /// @@ -102,13 +108,14 @@ use std::pin::Pin; /// let c: MyCollection = stream.collect().await; /// /// assert_eq!(c.0, vec![5, 5, 5, 5, 5]); +/// # /// # Ok(()) }) } -///``` +/// ``` /// /// [`IntoStream`]: trait.IntoStream.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub trait FromStream { +pub trait FromStream { /// Creates a value from a stream. /// /// # Examples @@ -117,18 +124,21 @@ pub trait FromStream { /// /// ``` /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// use crate::async_std::stream::FromStream; - /// use async_std::prelude::*; - /// use async_std::stream; + /// # + /// use async_std::prelude::*; + /// use async_std::stream::{self, FromStream}; /// - /// let five_fives = stream::repeat(5).take(5); + /// let five_fives = stream::repeat(5).take(5); /// - /// let v = Vec::from_stream(five_fives).await; + /// let v = Vec::from_stream(five_fives).await; /// - /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// # /// # Ok(()) }) } /// ``` fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>>; + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send; } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 043d30745..0a7eb4807 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -1,12 +1,10 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; +use std::time::Duration; -use futures_core::future::Future; -use futures_core::stream::Stream; -use pin_utils::unsafe_pinned; - -use futures_timer::Delay; +use crate::stream::Stream; +use crate::utils::{timer_after, Timer}; /// Creates a new stream that yields at a set interval. /// @@ -47,152 +45,34 @@ use futures_timer::Delay; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn interval(dur: Duration) -> Interval { Interval { - delay: Delay::new(dur), + delay: timer_after(dur), interval: dur, } } /// A stream representing notifications at fixed interval /// +/// This stream is created by the [`interval`] function. See its +/// documentation for more. +/// +/// [`interval`]: fn.interval.html #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Interval { - delay: Delay, + delay: Timer, interval: Duration, } -impl Interval { - unsafe_pinned!(delay: Delay); -} - impl Stream for Interval { type Item = (); fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if Pin::new(&mut *self).delay().poll(cx).is_pending() { + if Pin::new(&mut self.delay).poll(cx).is_pending() { return Poll::Pending; } - let when = Instant::now(); - let next = next_interval(when, Instant::now(), self.interval); - self.delay.reset(next); + let interval = self.interval; + let _ = std::mem::replace(&mut self.delay, timer_after(interval)); Poll::Ready(Some(())) } } - -/// Converts Duration object to raw nanoseconds if possible -/// -/// This is useful to divide intervals. -/// -/// While technically for large duration it's impossible to represent any -/// duration as nanoseconds, the largest duration we can represent is about -/// 427_000 years. Large enough for any interval we would use or calculate in -/// tokio. -fn duration_to_nanos(dur: Duration) -> Option { - dur.as_secs() - .checked_mul(1_000_000_000) - .and_then(|v| v.checked_add(u64::from(dur.subsec_nanos()))) -} - -fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant { - let new = prev + interval; - if new > now { - return new; - } - - let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired"); - let interval_ns = - duration_to_nanos(interval).expect("interval is less that 427 thousand years"); - let mult = spent_ns / interval_ns + 1; - assert!( - mult < (1 << 32), - "can't skip more than 4 billion intervals of {:?} \ - (trying to skip {})", - interval, - mult - ); - prev + interval * (mult as u32) -} - -#[cfg(test)] -mod test { - use super::next_interval; - use std::time::{Duration, Instant}; - - struct Timeline(Instant); - - impl Timeline { - fn new() -> Timeline { - Timeline(Instant::now()) - } - fn at(&self, millis: u64) -> Instant { - self.0 + Duration::from_millis(millis) - } - fn at_ns(&self, sec: u64, nanos: u32) -> Instant { - self.0 + Duration::new(sec, nanos) - } - } - - fn dur(millis: u64) -> Duration { - Duration::from_millis(millis) - } - - // The math around Instant/Duration isn't 100% precise due to rounding - // errors, see #249 for more info - fn almost_eq(a: Instant, b: Instant) -> bool { - if a == b { - true - } else if a > b { - a - b < Duration::from_millis(1) - } else { - b - a < Duration::from_millis(1) - } - } - - #[test] - fn norm_next() { - let tm = Timeline::new(); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(2), dur(10)), - tm.at(11) - )); - assert!(almost_eq( - next_interval(tm.at(7777), tm.at(7788), dur(100)), - tm.at(7877) - )); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(1000), dur(2100)), - tm.at(2101) - )); - } - - #[test] - fn fast_forward() { - let tm = Timeline::new(); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(1000), dur(10)), - tm.at(1001) - )); - assert!(almost_eq( - next_interval(tm.at(7777), tm.at(8888), dur(100)), - tm.at(8977) - )); - assert!(almost_eq( - next_interval(tm.at(1), tm.at(10000), dur(2100)), - tm.at(10501) - )); - } - - /// TODO: this test actually should be successful, but since we can't - /// multiply Duration on anything larger than u32 easily we decided - /// to allow it to fail for now - #[test] - #[should_panic(expected = "can't skip more than 4 billion intervals")] - fn large_skip() { - let tm = Timeline::new(); - assert_eq!( - next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)), - tm.at_ns(25, 1) - ); - } -} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index e796510dc..8dd7d6339 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -2,38 +2,323 @@ //! //! This module is an async version of [`std::iter`]. //! -//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html +//! If you've found yourself with an asynchronous collection of some kind, +//! and needed to perform an operation on the elements of said collection, +//! you'll quickly run into 'streams'. Streams are heavily used in idiomatic +//! asynchronous Rust code, so it's worth becoming familiar with them. +//! +//! Before explaining more, let's talk about how this module is structured: +//! +//! # Organization +//! +//! This module is largely organized by type: +//! +//! * [Traits] are the core portion: these traits define what kind of streams +//! exist and what you can do with them. The methods of these traits are worth +//! putting some extra study time into. +//! * [Functions] provide some helpful ways to create some basic streams. +//! * [Structs] are often the return types of the various methods on this +//! module's traits. You'll usually want to look at the method that creates +//! the `struct`, rather than the `struct` itself. For more detail about why, +//! see '[Implementing Stream](#implementing-stream)'. +//! +//! [Traits]: #traits +//! [Functions]: #functions +//! [Structs]: #structs +//! +//! That's it! Let's dig into streams. +//! +//! # Stream +//! +//! The heart and soul of this module is the [`Stream`] trait. The core of +//! [`Stream`] looks like this: +//! +//! ``` +//! #![allow(dead_code)] +//! # use async_std::task::{Context, Poll}; +//! # use std::pin::Pin; +//! pub trait Stream { +//! type Item; +//! fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; +//! } +//! # impl Stream for () { +//! # type Item = (); +//! # fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Pending } +//! # } +//! ``` +//! +//! A stream has a method, [`next`], which when called, returns an +//! [`Poll`]<[`Option`]`>`. [`next`] will return `Ready(Some(Item))` +//! as long as there are elements, and once they've all been exhausted, will +//! return `None` to indicate that iteration is finished. If we're waiting on +//! something asynchronous to resolve `Pending` is returned. +//! +//! Individual streams may choose to resume iteration, and so calling +//! [`next`] again may or may not eventually start returning `Ready(Some(Item))` +//! again at some point. +//! +//! [`Stream`]'s full definition includes a number of other methods as well, +//! but they are default methods, built on top of [`next`], and so you get +//! them for free. +//! +//! Streams are also composable, and it's common to chain them together to do +//! more complex forms of processing. See the [Adapters](#adapters) section +//! below for more details. +//! +//! [`Poll`]: ../task/enum.Poll.html +//! [`Stream`]: trait.Stream.html +//! [`next`]: trait.Stream.html#tymethod.next +//! [`Option`]: ../../std/option/enum.Option.html +//! +//! # The three forms of streaming +//! +//! There are three common methods which can create streams from a collection: +//! +//! * `stream()`, which iterates over `&T`. +//! * `stream_mut()`, which iterates over `&mut T`. +//! * `into_stream()`, which iterates over `T`. +//! +//! Various things in async-std may implement one or more of the +//! three, where appropriate. +//! +//! # Implementing Stream +//! +//! Creating a stream of your own involves two steps: creating a `struct` to +//! hold the stream's state, and then `impl`ementing [`Stream`] for that +//! `struct`. This is why there are so many `struct`s in this module: there is +//! one for each stream and iterator adapter. //! -//! # Examples +//! Let's make a stream named `Counter` which counts from `1` to `5`: //! //! ``` -//! # async_std::task::block_on(async { +//! # use async_std::prelude::*; +//! # use async_std::task::{Context, Poll}; +//! # use std::pin::Pin; +//! // First, the struct: +//! +//! /// A stream which counts from one to five +//! struct Counter { +//! count: usize, +//! } +//! +//! // we want our count to start at one, so let's add a new() method to help. +//! // This isn't strictly necessary, but is convenient. Note that we start +//! // `count` at zero, we'll see why in `next()`'s implementation below. +//! impl Counter { +//! fn new() -> Counter { +//! Counter { count: 0 } +//! } +//! } +//! +//! // Then, we implement `Stream` for our `Counter`: +//! +//! impl Stream for Counter { +//! // we will be counting with usize +//! type Item = usize; +//! +//! // poll_next() is the only required method +//! fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { +//! // Increment our count. This is why we started at zero. +//! self.count += 1; +//! +//! // Check to see if we've finished counting or not. +//! if self.count < 6 { +//! Poll::Ready(Some(self.count)) +//! } else { +//! Poll::Ready(None) +//! } +//! } +//! } +//! +//! // And now we can use it! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut counter = Counter::new(); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); +//! +//! let x = counter.next().await.unwrap(); +//! println!("{}", x); //! # -//! use async_std::prelude::*; -//! use async_std::stream; +//! # Ok(()) }) } +//! ``` +//! +//! This will print `1` through `5`, each on their own line. //! -//! let mut s = stream::repeat(9).take(3); +//! Calling `next().await` this way gets repetitive. Rust has a construct which +//! can call `next()` on your stream, until it reaches `None`. Let's go over +//! that next. //! -//! while let Some(v) = s.next().await { -//! assert_eq!(v, 9); +//! # while let Loops and IntoStream +//! +//! Rust's `while let` loop syntax is an idiomatic way to iterate over streams. Here's a basic +//! example of `while let`: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let mut values = stream::from_iter(1u8..6); +//! +//! while let Some(x) = values.next().await { +//! println!("{}", x); //! } //! # -//! # }) +//! # Ok(()) }) } //! ``` +//! +//! This will print the numbers one through five, each on their own line. But +//! you'll notice something here: we never called anything on our vector to +//! produce a stream. What gives? +//! +//! There's a trait in the standard library for converting something into an +//! stream: [`IntoStream`]. This trait has one method, [`into_stream`], +//! which converts the thing implementing [`IntoStream`] into a stream. +//! +//! Unlike `std::iter::IntoIterator`, `IntoStream` does not have compiler +//! support yet. This means that automatic conversions like with `for` loops +//! doesn't occur yet, and `into_stream` or `from_iter` as above will always +//! have to be called manually. +//! +//! [`IntoStream`]: trait.IntoStream.html +//! [`into_stream`]: trait.IntoStream.html#tymethod.into_stream +//! +//! # Adapters +//! +//! Functions which take an [`Stream`] and return another [`Stream`] are +//! often called 'stream adapters', as they are a form of the 'adapter +//! pattern'. +//! +//! Common stream adapters include [`map`], [`take`], and [`filter`]. +//! For more, see their documentation. +//! +//! [`map`]: trait.Stream.html#method.map +//! [`take`]: trait.Stream.html#method.take +//! [`filter`]: trait.Stream.html#method.filter +//! +//! # Laziness +//! +//! Streams (and stream [adapters](#adapters)) are *lazy*. This means that +//! just creating a stream doesn't _do_ a whole lot. Nothing really happens +//! until you call [`next`]. This is sometimes a source of confusion when +//! creating a stream solely for its side effects. For example, the [`map`] +//! method calls a closure on each element it iterates over: +//! +//! ``` +//! # #![allow(unused_must_use)] +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let v = stream::repeat(1u8).take(5); +//! v.map(|x| println!("{}", x)); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! This will not print any values, as we only created a stream, rather than +//! using it. The compiler will warn us about this kind of behavior: +//! +//! ```text +//! warning: unused result that must be used: streams are lazy and +//! do nothing unless consumed +//! ``` +//! +//! The idiomatic way to write a [`map`] for its side effects is to use a +//! `while let` loop instead: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let mut v = stream::repeat(1u8).take(5); +//! +//! while let Some(x) = &v.next().await { +//! println!("{}", x); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`map`]: trait.Stream.html#method.map +//! +//! The two most common ways to evaluate a stream are to use a `while let` loop +//! like this, or using the [`collect`] method to produce a new collection. +//! +//! [`collect`]: trait.Stream.html#method.collect +//! +//! # Infinity +//! +//! Streams do not have to be finite. As an example, a repeat stream is +//! an infinite stream: +//! +//! ``` +//! # use async_std::stream; +//! let numbers = stream::repeat(1u8); +//! ``` +//! +//! It is common to use the [`take`] stream adapter to turn an infinite +//! stream into a finite one: +//! +//! ``` +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! # use async_std::prelude::*; +//! # use async_std::stream; +//! let numbers = stream::from_iter(0u8..); +//! let mut five_numbers = numbers.take(5); +//! +//! while let Some(number) = five_numbers.next().await { +//! println!("{}", number); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! This will print the numbers `0` through `4`, each on their own line. +//! +//! Bear in mind that methods on infinite streams, even those for which a +//! result can be determined mathematically in finite time, may not terminate. +//! Specifically, methods such as [`min`], which in the general case require +//! traversing every element in the stream, are likely not to return +//! successfully for any infinite streams. +//! +//! ```ignore +//! let ones = async_std::stream::repeat(1); +//! let least = ones.min().await.unwrap(); // Oh no! An infinite loop! +//! // `ones.min()` causes an infinite loop, so we won't reach this point! +//! println!("The smallest number one is {}.", least); +//! ``` +//! +//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html +//! [`take`]: trait.Stream.html#method.take +//! [`min`]: trait.Stream.html#method.min pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; +pub use from_iter::{from_iter, FromIter}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use repeat_with::{repeat_with, RepeatWith}; -pub use stream::{ - Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, TakeWhile, Zip, -}; +pub use stream::*; pub(crate) mod stream; mod empty; mod from_fn; +mod from_iter; mod once; mod repeat; mod repeat_with; @@ -46,17 +331,21 @@ cfg_unstable! { mod fused_stream; mod interval; mod into_stream; + mod pending; mod product; + mod successors; mod sum; pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; - pub use extend::Extend; + pub use extend::{extend, Extend}; pub use from_stream::FromStream; pub use fused_stream::FusedStream; pub use interval::{interval, Interval}; pub use into_stream::IntoStream; + pub use pending::{pending, Pending}; pub use product::Product; pub use stream::Merge; + pub use successors::{successors, Successors}; pub use sum::Sum; } diff --git a/src/stream/once.rs b/src/stream/once.rs index be875e41a..b86f181d9 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,8 +1,13 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; +#[cfg(feature = "unstable")] +use crate::stream::DoubleEndedStream; + /// Creates a stream that yields a single item. /// /// # Examples @@ -24,20 +29,30 @@ pub fn once(t: T) -> Once { Once { value: Some(t) } } -/// A stream that yields a single item. -/// -/// This stream is constructed by the [`once`] function. -/// -/// [`once`]: fn.once.html -#[derive(Debug)] -pub struct Once { - value: Option, +pin_project! { + /// A stream that yields a single item. + /// + /// This stream is created by the [`once`] function. See its + /// documentation for more. + /// + /// [`once`]: fn.once.html + #[derive(Clone, Debug)] + pub struct Once { + value: Option, + } } -impl Stream for Once { +impl Stream for Once { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(self.value.take()) + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(self.project().value.take()) + } +} + +#[cfg(feature = "unstable")] +impl DoubleEndedStream for Once { + fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.project().value.take()) } } diff --git a/src/stream/pending.rs b/src/stream/pending.rs new file mode 100644 index 000000000..edb6be4b1 --- /dev/null +++ b/src/stream/pending.rs @@ -0,0 +1,68 @@ +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use crate::stream::{DoubleEndedStream, ExactSizeStream, FusedStream, Stream}; + +/// A stream that never returns any items. +/// +/// This stream is created by the [`pending`] function. See its +/// documentation for more. +/// +/// [`pending`]: fn.pending.html +#[derive(Debug)] +pub struct Pending { + _marker: PhantomData, +} + +/// Creates a stream that never returns any items. +/// +/// The returned stream will always return `Pending` when polled. +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let dur = Duration::from_millis(100); +/// let mut s = stream::pending::<()>().timeout(dur); +/// +/// let item = s.next().await; +/// +/// assert!(item.is_some()); +/// assert!(item.unwrap().is_err()); +/// +/// # +/// # }) +/// ``` +pub fn pending() -> Pending { + Pending { + _marker: PhantomData, + } +} + +impl Stream for Pending { + type Item = T; + + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} + +impl DoubleEndedStream for Pending { + fn poll_next_back(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} + +impl FusedStream for Pending {} + +impl ExactSizeStream for Pending { + fn len(&self) -> usize { + 0 + } +} diff --git a/src/stream/product.rs b/src/stream/product.rs index 5799990d7..991727d29 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -1,7 +1,9 @@ -use crate::future::Future; +use core::pin::Pin; +use core::future::Future; + use crate::stream::Stream; -/// Trait to represent types that can be created by productming up a stream. +/// Trait to represent types that can be created by multiplying the elements of a stream. /// /// This trait is used to implement the [`product`] method on streams. Types which /// implement the trait can be generated by the [`product`] method. Like @@ -16,8 +18,48 @@ use crate::stream::Stream; pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. - fn product(stream: S) -> F + fn product<'a, S>(stream: S) -> Pin + 'a>> where - S: Stream, - F: Future; + S: Stream + 'a; } + +use core::ops::Mul; +use core::num::Wrapping; +use crate::stream::stream::StreamExt; + +macro_rules! num_product { + ($one:expr, $($a:ty)*) => ($( + impl Product for $a { + fn product<'a, S>(stream: S) -> Pin+ 'a>> + where + S: Stream + 'a, + { + Box::pin(async move { stream.fold($one, Mul::mul).await } ) + } + } + impl<'a> Product<&'a $a> for $a { + fn product<'b, S>(stream: S) -> Pin + 'b>> + where + S: Stream + 'b, + { + Box::pin(async move { stream.fold($one, Mul::mul).await } ) + } + } + )*); +} + +macro_rules! integer_product { + ($($a:ty)*) => ( + num_product!(1, $($a)*); + num_product!(Wrapping(1), $(Wrapping<$a>)*); + ); +} + +macro_rules! float_product { + ($($a:ty)*) => ( + num_product!(1.0, $($a)*); + ); +} + +integer_product!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_product!{ f32 f64 } diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index 75fd69735..e73a2f829 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -1,4 +1,4 @@ -use std::pin::Pin; +use core::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -29,10 +29,11 @@ where /// A stream that yields the same item repeatedly. /// -/// This stream is constructed by the [`repeat`] function. +/// This stream is created by the [`repeat`] function. See its +/// documentation for more. /// /// [`repeat`]: fn.repeat.html -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Repeat { item: T, } diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index f38b323d1..39984a3a2 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,22 +1,21 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; /// A stream that repeats elements of type `T` endlessly by applying a provided closure. /// -/// This stream is constructed by the [`repeat_with`] function. +/// This stream is created by the [`repeat_with`] function. See its +/// documentation for more. /// /// [`repeat_with`]: fn.repeat_with.html -#[derive(Debug)] -pub struct RepeatWith { +#[derive(Clone, Debug)] +pub struct RepeatWith { f: F, - future: Option, - __a: PhantomData, } +impl Unpin for RepeatWith {} + /// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. /// /// # Examples @@ -24,12 +23,12 @@ pub struct RepeatWith { /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::repeat_with(|| async { 1 }); +/// let s = stream::repeat_with(|| 1); /// /// pin_utils::pin_mut!(s); /// @@ -37,67 +36,49 @@ pub struct RepeatWith { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); -/// # }) } +/// # }) /// ``` /// /// Going finite: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; /// -/// let s = stream::repeat_with(|| async { 1u8 }).take(2); +/// let mut n = 1; +/// let s = stream::repeat_with(|| { +/// let item = n; +/// n *= 2; +/// item +/// }) +/// .take(4); /// /// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); -/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(2)); +/// assert_eq!(s.next().await, Some(4)); +/// assert_eq!(s.next().await, Some(8)); /// assert_eq!(s.next().await, None); -/// # }) } +/// # }) /// ``` -pub fn repeat_with(repeater: F) -> RepeatWith +pub fn repeat_with(repeater: F) -> RepeatWith where - F: FnMut() -> Fut, - Fut: Future, + F: FnMut() -> T, { - RepeatWith { - f: repeater, - future: None, - __a: PhantomData, - } -} - -impl RepeatWith { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); + RepeatWith { f: repeater } } -impl Stream for RepeatWith +impl Stream for RepeatWith where - F: FnMut() -> Fut, - Fut: Future, + F: FnMut() -> T, { - type Item = A; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match &self.future { - Some(_) => { - let res = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - - self.as_mut().future().set(None); - - return Poll::Ready(Some(res)); - } - None => { - let fut = (self.as_mut().f())(); + type Item = T; - self.as_mut().future().set(Some(fut)); - } - } - } + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + let item = (&mut self.f)(); + Poll::Ready(Some(item)) } } diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 3b65fc764..06f4d7f80 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -1,7 +1,7 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -10,10 +10,19 @@ use crate::task::{Context, Poll}; pub struct AllFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AllFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + _marker: PhantomData, + } + } +} + impl Unpin for AllFuture<'_, S, F, T> {} impl Future for AllFuture<'_, S, F, S::Item> @@ -29,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { // don't forget to wake this task again to pull the next item from stream @@ -39,7 +47,7 @@ where Poll::Ready(false) } } - None => Poll::Ready(self.result), + None => Poll::Ready(true), } } } diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index a23adf4bf..15154c506 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -1,7 +1,7 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::marker::PhantomData; +use core::pin::Pin; +use core::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -10,10 +10,19 @@ use crate::task::{Context, Poll}; pub struct AnyFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, - pub(crate) result: bool, pub(crate) _marker: PhantomData, } +impl<'a, S, F, T> AnyFuture<'a, S, F, T> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { + stream, + f, + _marker: PhantomData, + } + } +} + impl Unpin for AnyFuture<'_, S, F, T> {} impl Future for AnyFuture<'_, S, F, S::Item> @@ -29,7 +38,6 @@ where match next { Some(v) => { let result = (&mut self.f)(v); - self.result = result; if result { Poll::Ready(true) @@ -39,7 +47,7 @@ where Poll::Pending } } - None => Poll::Ready(self.result), + None => Poll::Ready(false), } } } diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 2693382ee..c034a53e4 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,22 +1,32 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::prelude::*; +use crate::stream::stream::StreamExt; +use crate::stream::Stream; use crate::task::{Context, Poll}; -/// Chains two streams one after another. -#[derive(Debug)] -pub struct Chain { - first: Fuse, - second: Fuse, +pin_project! { + /// A stream that chains two streams one after another. + /// + /// This `struct` is created by the [`chain`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`chain`]: trait.Stream.html#method.chain + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct Chain { + #[pin] + first: Fuse, + #[pin] + second: Fuse, + } } impl Chain { - pin_utils::unsafe_pinned!(first: Fuse); - pin_utils::unsafe_pinned!(second: Fuse); - pub(super) fn new(first: S, second: U) -> Self { - Chain { + Self { first: first.fuse(), second: second.fuse(), } @@ -26,22 +36,23 @@ impl Chain { impl> Stream for Chain { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if !self.first.done { - let next = futures_core::ready!(self.as_mut().first().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if !this.first.done { + let next = futures_core::ready!(this.first.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if !self.second.done { - let next = futures_core::ready!(self.as_mut().second().poll_next(cx)); + if !this.second.done { + let next = futures_core::ready!(this.second.as_mut().poll_next(cx)); if let Some(next) = next { return Poll::Ready(Some(next)); } } - if self.first.done && self.second.done { + if this.first.done && this.second.done { return Poll::Ready(None); } diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs new file mode 100644 index 000000000..eac992dd0 --- /dev/null +++ b/src/stream/stream/cloned.rs @@ -0,0 +1,33 @@ +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use pin_project_lite::pin_project; +use core::pin::Pin; + +pin_project! { + /// A stream that clones the elements of an underlying stream. + #[derive(Debug)] + pub struct Cloned { + #[pin] + stream: S, + } +} + +impl Cloned { + pub(super) fn new(stream: S) -> Self { + Self { stream } + } +} + +impl<'a, S, T: 'a> Stream for Cloned +where + S: Stream, + T: Clone, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.cloned()) + } +} diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs index fc7161ad8..bf93408a6 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,31 +1,32 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another using `Ord`. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct CmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another using `Ord`. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct CmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl CmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { - CmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, @@ -42,11 +43,12 @@ where { type Output = Ordering; - 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 { // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Ordering::Equal); @@ -56,31 +58,31 @@ where return Poll::Ready(Ordering::Greater); } - // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + // Get next value if possible and necessary + if !this.l.done && this.l_cache.is_none() { + let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.take().unwrap(); + let r_value = this.r_cache.take().unwrap(); let result = l_value.cmp(&r_value); if let Ordering::Equal = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs new file mode 100644 index 000000000..19296f5b9 --- /dev/null +++ b/src/stream/stream/copied.rs @@ -0,0 +1,33 @@ +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use pin_project_lite::pin_project; +use core::pin::Pin; + +pin_project! { + /// A stream that copies the elements of an underlying stream. + #[derive(Debug)] + pub struct Copied { + #[pin] + stream: S, + } +} + +impl Copied { + pub(super) fn new(stream: S) -> Self { + Self { stream } + } +} + +impl<'a, S, T: 'a> Stream for Copied +where + S: Stream, + T: Copy, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.copied()) + } +} diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs new file mode 100644 index 000000000..63e044977 --- /dev/null +++ b/src/stream/stream/count.rs @@ -0,0 +1,46 @@ +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub struct CountFuture { + #[pin] + stream: S, + count: usize, + } +} + +impl CountFuture { + pub(crate) fn new(stream: S) -> Self { + Self { stream, count: 0 } + } +} + +impl Future for CountFuture +where + S: Stream, +{ + type Output = usize; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(_) => { + cx.waker().wake_by_ref(); + *this.count += 1; + Poll::Pending + } + None => Poll::Ready(*this.count), + } + } +} diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs new file mode 100644 index 000000000..dc4c3a177 --- /dev/null +++ b/src/stream/stream/cycle.rs @@ -0,0 +1,48 @@ +use core::pin::Pin; + +use futures_core::ready; +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + /// A stream that will repeatedly yield the same list of elements. + #[derive(Debug)] + pub struct Cycle { + orig: S, + #[pin] + source: S, + } +} + +impl Cycle +where + S: Stream + Clone, +{ + pub(crate) fn new(source: S) -> Self { + Self { + orig: source.clone(), + source, + } + } +} + +impl Stream for Cycle +where + S: Stream + Clone, +{ + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + match ready!(this.source.as_mut().poll_next(cx)) { + None => { + this.source.set(this.orig.clone()); + this.source.poll_next(cx) + } + item => Poll::Ready(item), + } + } +} diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs new file mode 100644 index 000000000..9a7f947c6 --- /dev/null +++ b/src/stream/stream/delay.rs @@ -0,0 +1,49 @@ +use core::future::Future; +use core::pin::Pin; +use core::time::Duration; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use crate::utils::{timer_after, Timer}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Delay { + #[pin] + stream: S, + #[pin] + delay: Timer, + delay_done: bool, + } +} + +impl Delay { + pub(super) fn new(stream: S, dur: Duration) -> Self { + Delay { + stream, + delay: timer_after(dur), + delay_done: false, + } + } +} + +impl Stream for Delay +where + S: Stream, +{ + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + if !*this.delay_done { + futures_core::ready!(this.delay.poll(cx)); + *this.delay_done = true; + } + + this.stream.poll_next(cx) + } +} diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index 7d5a3d68e..093fefbda 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,21 +1,22 @@ -use crate::task::{Context, Poll}; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; +use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Enumerate { - stream: S, - i: usize, +pin_project! { + #[derive(Debug)] + pub struct Enumerate { + #[pin] + stream: S, + i: usize, + } } impl Enumerate { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(i: usize); - pub(super) fn new(stream: S) -> Self { - Enumerate { stream, i: 0 } + Self { stream, i: 0 } } } @@ -25,13 +26,14 @@ where { type Item = (usize, S::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(v) => { - let ret = (self.i, v); - *self.as_mut().i() += 1; + let ret = (*this.i, v); + *this.i += 1; Poll::Ready(Some(ret)) } None => Poll::Ready(None), diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs new file mode 100644 index 000000000..3d8307b0e --- /dev/null +++ b/src/stream/stream/eq.rs @@ -0,0 +1,63 @@ +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use super::fuse::Fuse; +use crate::stream::stream::StreamExt; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct EqFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + } +} + +impl EqFuture +where + L::Item: PartialEq, +{ + pub(super) fn new(l: L, r: R) -> Self { + Self { + l: l.fuse(), + r: r.fuse(), + } + } +} + +impl Future for EqFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx)); + let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx)); + + if this.l.done && this.r.done { + return Poll::Ready(true); + } + + match (l_val, r_val) { + (Some(l), Some(r)) if l != r => { + return Poll::Ready(false); + } + _ => {} + } + } + } +} diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 8ed282cec..2dc7dd486 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,42 +1,48 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to filter elements of another stream with a predicate. -#[derive(Debug)] -pub struct Filter { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream to filter elements of another stream with a predicate. + /// + /// This `struct` is created by the [`filter`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`filter`]: trait.Stream.html#method.filter + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct Filter { + #[pin] + stream: S, + predicate: P, + } } -impl Filter { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - +impl Filter { pub(super) fn new(stream: S, predicate: P) -> Self { - Filter { + Self { stream, predicate, - __t: PhantomData, } } } -impl Stream for Filter +impl Stream for Filter where S: Stream, P: FnMut(&S::Item) -> bool, { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), + Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)), Some(_) => { cx.waker().wake_by_ref(); Poll::Pending diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 756efff18..e43e8f09f 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -1,43 +1,37 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use pin_project_lite::pin_project; use crate::stream::Stream; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FilterMap { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[derive(Debug)] + pub struct FilterMap { + #[pin] + stream: S, + f: F, + } } -impl FilterMap { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - +impl FilterMap { pub(crate) fn new(stream: S, f: F) -> Self { - FilterMap { - stream, - f, - __from: PhantomData, - __to: PhantomData, - } + Self { stream, f } } } -impl Stream for FilterMap +impl Stream for FilterMap where S: Stream, F: FnMut(S::Item) -> Option, { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) => match (self.as_mut().f())(v) { + Some(v) => match (this.f)(v) { Some(b) => Poll::Ready(Some(b)), None => { cx.waker().wake_by_ref(); diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index 93624c03e..8652ac095 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -1,31 +1,25 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct FindFuture<'a, S, P, T> { +pub struct FindFuture<'a, S, P> { stream: &'a mut S, p: P, - __t: PhantomData, } -impl<'a, S, P, T> FindFuture<'a, S, P, T> { +impl<'a, S, P> FindFuture<'a, S, P> { pub(super) fn new(stream: &'a mut S, p: P) -> Self { - FindFuture { - stream, - p, - __t: PhantomData, - } + Self { stream, p } } } -impl Unpin for FindFuture<'_, S, P, T> {} +impl Unpin for FindFuture<'_, S, P> {} -impl<'a, S, P> Future for FindFuture<'a, S, P, S::Item> +impl<'a, S, P> Future for FindFuture<'a, S, P> where S: Stream + Unpin + Sized, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index dfcf92d66..f7e3c1e04 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -1,33 +1,25 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; -use crate::future::Future; use crate::stream::Stream; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct FindMapFuture<'a, S, F, T, B> { +pub struct FindMapFuture<'a, S, F> { stream: &'a mut S, f: F, - __b: PhantomData, - __t: PhantomData, } -impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> { +impl<'a, S, F> FindMapFuture<'a, S, F> { pub(super) fn new(stream: &'a mut S, f: F) -> Self { - FindMapFuture { - stream, - f, - __b: PhantomData, - __t: PhantomData, - } + Self { stream, f } } } -impl Unpin for FindMapFuture<'_, S, F, T, B> {} +impl Unpin for FindMapFuture<'_, S, F> {} -impl<'a, S, B, F> Future for FindMapFuture<'a, S, F, S::Item, B> +impl<'a, S, B, F> Future for FindMapFuture<'a, S, F> where S: Stream + Unpin + Sized, F: FnMut(S::Item) -> Option, diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs new file mode 100644 index 000000000..d0cc73d32 --- /dev/null +++ b/src/stream/stream/flat_map.rs @@ -0,0 +1,65 @@ +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::stream::map::Map; +use crate::stream::stream::StreamExt; +use crate::stream::{IntoStream, Stream}; +use crate::task::{Context, Poll}; + +pin_project! { + /// A stream that maps each element to a stream, and yields the elements of the produced + /// streams. + /// + /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`flat_map`]: trait.Stream.html#method.flat_map + /// [`Stream`]: trait.Stream.html + pub struct FlatMap { + #[pin] + stream: Map, + #[pin] + inner_stream: Option, + } +} + +impl FlatMap +where + S: Stream, + U: IntoStream, + F: FnMut(S::Item) -> U, +{ + pub(super) fn new(stream: S, f: F) -> Self { + Self { + stream: stream.map(f), + inner_stream: None, + } + } +} + +impl Stream for FlatMap +where + S: Stream, + U: Stream, + F: FnMut(S::Item) -> U, +{ + type Item = U::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + loop { + if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { + match futures_core::ready!(inner.poll_next(cx)) { + item @ Some(_) => return Poll::Ready(item), + None => this.inner_stream.set(None), + } + } + + match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { + inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)), + None => return Poll::Ready(None), + } + } + } +} diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs new file mode 100644 index 000000000..e7a498dc2 --- /dev/null +++ b/src/stream/stream/flatten.rs @@ -0,0 +1,80 @@ +use core::fmt; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::{IntoStream, Stream}; +use crate::task::{Context, Poll}; + +pin_project! { + /// A stream that flattens one level of nesting in an stream of things that can be turned into + /// streams. + /// + /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`flatten`]: trait.Stream.html#method.flatten + /// [`Stream`]: trait.Stream.html + pub struct Flatten + where + S: Stream, + S::Item: IntoStream, + { + #[pin] + stream: S, + #[pin] + inner_stream: Option<::IntoStream>, + } +} + +impl Flatten +where + S: Stream, + S::Item: IntoStream, +{ + pub(super) fn new(stream: S) -> Self { + Self { + stream, + inner_stream: None, + } + } +} + +impl Stream for Flatten +where + S: Stream, + S::Item: IntoStream, + U: Stream, +{ + type Item = U::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + loop { + if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() { + match futures_core::ready!(inner.poll_next(cx)) { + item @ Some(_) => return Poll::Ready(item), + None => this.inner_stream.set(None), + } + } + + match futures_core::ready!(this.stream.as_mut().poll_next(cx)) { + inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)), + None => return Poll::Ready(None), + } + } + } +} + +impl fmt::Debug for Flatten +where + S: fmt::Debug + Stream, + S::Item: IntoStream, + U: fmt::Debug + Stream, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Flatten") + .field("inner", &self.stream) + .finish() + } +} diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 18ddcd815..3938a3739 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,52 +1,50 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[derive(Debug)] + pub struct FoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + } } -impl FoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - +impl FoldFuture { pub(super) fn new(stream: S, init: B, f: F) -> Self { - FoldFuture { + Self { stream, f, acc: Some(init), - __t: PhantomData, } } } -impl Future for FoldFuture +impl Future for FoldFuture where S: Stream + Sized, F: FnMut(B, S::Item) -> B, { type Output = B; - 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 next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); - *self.as_mut().acc() = Some(new); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); + *this.acc = Some(new); } - None => return Poll::Ready(self.as_mut().acc().take().unwrap()), + None => return Poll::Ready(this.acc.take().unwrap()), } } } diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs index 0406a5075..dbada101c 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,44 +1,44 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; +use core::future::Future; + +use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ForEachFuture { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct ForEachFuture { + #[pin] + stream: S, + f: F, + } } -impl ForEachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - +impl ForEachFuture { pub(super) fn new(stream: S, f: F) -> Self { - ForEachFuture { + Self { stream, f, - __t: PhantomData, } } } -impl Future for ForEachFuture +impl Future for ForEachFuture where S: Stream + Sized, F: FnMut(S::Item), { 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 next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => (self.as_mut().f())(v), + Some(v) => (this.f)(v), None => return Poll::Ready(()), } } diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index ff5bdab1f..f3a059626 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,33 +1,46 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A `Stream` that is permanently closed once a single call to `poll` results in -/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. -#[derive(Clone, Debug)] -pub struct Fuse { - pub(crate) stream: S, - pub(crate) done: bool, +pin_project! { + /// A stream that yields `None` forever after the underlying stream yields `None` once. + /// + /// This `struct` is created by the [`fuse`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`fuse`]: trait.Stream.html#method.fuse + /// [`Stream`]: trait.Stream.html + #[derive(Clone, Debug)] + pub struct Fuse { + #[pin] + pub(crate) stream: S, + pub(crate) done: bool, + } } -impl Unpin for Fuse {} - -impl Fuse { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(done: bool); +impl Fuse { + pub(super) fn new(stream: S) -> Self { + Self { + stream, + done: false, + } + } } 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 { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); if next.is_none() { - *self.as_mut().done() = true; + *this.done = true; } Poll::Ready(next) } diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs index eb9786b5f..1e1b70df5 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,28 +1,31 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { - GeFuture { + Self { partial_cmp: l.partial_cmp(r), } } @@ -30,14 +33,14 @@ where impl Future for GeFuture where - L: Stream + Sized, - R: Stream + Sized, + L: Stream, + R: Stream, L::Item: PartialOrd, { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs index 6c480a25a..d58e7e9e6 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,28 +1,31 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// greater than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct GtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // greater than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct GtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl GtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { - GtFuture { + Self { partial_cmp: l.partial_cmp(r), } } @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Greater) => Poll::Ready(true), diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs index e63b58492..d2f6cf395 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,43 +1,49 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that does something with each element of another stream. -#[derive(Debug)] -pub struct Inspect { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + /// A stream that does something with each element of another stream. + /// + /// This `struct` is created by the [`inspect`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`inspect`]: trait.Stream.html#method.inspect + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct Inspect { + #[pin] + stream: S, + f: F, + } } -impl Inspect { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - +impl Inspect { pub(super) fn new(stream: S, f: F) -> Self { - Inspect { + Self { stream, f, - __t: PhantomData, } } } -impl Stream for Inspect +impl Stream for Inspect where S: Stream, F: FnMut(&S::Item), { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); - Poll::Ready(next.and_then(|x| { - (self.as_mut().f())(&x); - Some(x) + Poll::Ready(next.map(|x| { + (this.f)(&x); + x })) } } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index c58dd66a9..ebf1a484a 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,22 +1,24 @@ -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LastFuture { - stream: S, - last: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LastFuture { + #[pin] + stream: S, + last: Option, + } } impl LastFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(last: Option); - pub(crate) fn new(stream: S) -> Self { - LastFuture { stream, last: None } + Self { stream, last: None } } } @@ -27,16 +29,17 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - *self.as_mut().last() = Some(new); + *this.last = Some(new); Poll::Pending } - None => Poll::Ready(self.last), + None => Poll::Ready(*this.last), } } } diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs index 37b62d831..169f9eced 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,28 +1,31 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// Determines if the elements of this `Stream` are lexicographically -/// less or equal to those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LeFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + /// Determines if the elements of this `Stream` are lexicographically + /// less or equal to those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LeFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LeFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { - LeFuture { + Self { partial_cmp: l.partial_cmp(r), } } @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true), diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs index b774d7b43..1851b8e4c 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,28 +1,31 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::partial_cmp::PartialCmpFuture; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Determines if the elements of this `Stream` are lexicographically -// less than those of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct LtFuture { - partial_cmp: PartialCmpFuture, +pin_project! { + // Determines if the elements of this `Stream` are lexicographically + // less than those of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct LtFuture { + #[pin] + partial_cmp: PartialCmpFuture, + } } impl LtFuture where L::Item: PartialOrd, { - pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); - pub(super) fn new(l: L, r: R) -> Self { - LtFuture { + Self { partial_cmp: l.partial_cmp(r), } } @@ -36,8 +39,8 @@ where { type Output = bool; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result = futures_core::ready!(self.as_mut().partial_cmp().poll(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let result = futures_core::ready!(self.project().partial_cmp.poll(cx)); match result { Some(Ordering::Less) => Poll::Ready(true), diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs index 4bc2e366a..0eab3ce2b 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,41 +1,39 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct Map { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + /// A stream that maps value of another stream with a function. + #[derive(Debug)] + pub struct Map { + #[pin] + stream: S, + f: F, + } } -impl Map { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - +impl Map { pub(crate) fn new(stream: S, f: F) -> Self { - Map { + Self { stream, f, - __from: PhantomData, - __to: PhantomData, } } } -impl Stream for Map +impl Stream for Map where S: Stream, F: FnMut(S::Item) -> B, { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - Poll::Ready(next.map(self.as_mut().f())) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + Poll::Ready(next.map(this.f)) } } diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs new file mode 100644 index 000000000..03fe63595 --- /dev/null +++ b/src/stream/stream/max.rs @@ -0,0 +1,53 @@ +use core::cmp::{Ord, Ordering}; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxFuture { + #[pin] + stream: S, + max: Option, + } +} + +impl MaxFuture { + pub(super) fn new(stream: S) -> Self { + Self { stream, max: None } + } +} + +impl Future for MaxFuture +where + S: Stream, + S::Item: Ord, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.max.take() { + None => *this.max = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take()), + } + } +} diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs new file mode 100644 index 000000000..8f986452d --- /dev/null +++ b/src/stream/stream/max_by.rs @@ -0,0 +1,57 @@ +use core::cmp::Ordering; +use core::pin::Pin; +use core::future::Future; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxByFuture { + #[pin] + stream: S, + compare: F, + max: Option, + } +} + +impl MaxByFuture { + pub(super) fn new(stream: S, compare: F) -> Self { + Self { + stream, + compare, + max: None, + } + } +} + +impl Future for MaxByFuture +where + S: Stream, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.max.take() { + None => *this.max = Some(new), + Some(old) => match (this.compare)(&new, &old) { + Ordering::Greater => *this.max = Some(new), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take()), + } + } +} diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs new file mode 100644 index 000000000..8fa91ab98 --- /dev/null +++ b/src/stream/stream/max_by_key.rs @@ -0,0 +1,65 @@ +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MaxByKeyFuture { + #[pin] + stream: S, + max: Option<(T, T)>, + key_by: K, + } +} + +impl MaxByKeyFuture { + pub(super) fn new(stream: S, key_by: K) -> Self { + Self { + stream, + max: None, + key_by, + } + } +} + +impl Future for MaxByKeyFuture +where + S: Stream, + K: FnMut(&S::Item) -> S::Item, + S::Item: Ord, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + let (key, value) = key(this.key_by)(new); + cx.waker().wake_by_ref(); + + match this.max.take() { + None => *this.max = Some((key, value)), + + Some(old) => match key.cmp(&old.0) { + Ordering::Greater => *this.max = Some((key, value)), + _ => *this.max = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.max.take().map(|max| max.1)), + } + } +} diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 9889dc70a..232097292 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -1,44 +1,73 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; -use futures_core::Stream; +use pin_project_lite::pin_project; -/// A stream that merges two other streams into a single stream. -/// -/// This stream is returned by [`Stream::merge`]. -/// -/// [`Stream::merge`]: trait.Stream.html#method.merge -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct Merge { - left: L, - right: R, -} +use crate::stream::stream::StreamExt; +use crate::stream::Fuse; +use crate::stream::Stream; +use crate::utils; -impl Unpin for Merge {} +pin_project! { + /// A stream that merges two other streams into a single stream. + /// + /// This `struct` is created by the [`merge`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`merge`]: trait.Stream.html#method.merge + /// [`Stream`]: trait.Stream.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[derive(Debug)] + pub struct Merge { + #[pin] + left: Fuse, + #[pin] + right: Fuse, + } +} -impl Merge { +impl Merge { pub(crate) fn new(left: L, right: R) -> Self { - Self { left, right } + Self { + left: left.fuse(), + right: right.fuse(), + } } } impl Stream for Merge where - L: Stream + Unpin, - R: Stream + Unpin, + L: Stream, + R: Stream, { type Item = T; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Poll::Ready(Some(item)) = Pin::new(&mut self.left).poll_next(cx) { - // The first stream made progress. The Merge needs to be polled - // again to check the progress of the second stream. - cx.waker().wake_by_ref(); - Poll::Ready(Some(item)) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if utils::random(2) == 0 { + poll_next_in_order(this.left, this.right, cx) } else { - Pin::new(&mut self.right).poll_next(cx) + poll_next_in_order(this.right, this.left, cx) } } } + +fn poll_next_in_order( + first: Pin<&mut F>, + second: Pin<&mut S>, + cx: &mut Context<'_>, +) -> Poll> +where + F: Stream, + S: Stream, +{ + match first.poll_next(cx) { + Poll::Ready(None) => second.poll_next(cx), + Poll::Ready(item) => Poll::Ready(item), + Poll::Pending => match second.poll_next(cx) { + Poll::Ready(None) | Poll::Pending => Poll::Pending, + Poll::Ready(item) => Poll::Ready(item), + }, + } +} diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs new file mode 100644 index 000000000..8430b943a --- /dev/null +++ b/src/stream/stream/min.rs @@ -0,0 +1,53 @@ +use core::cmp::{Ord, Ordering}; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinFuture { + #[pin] + stream: S, + min: Option, + } +} + +impl MinFuture { + pub(super) fn new(stream: S) -> Self { + Self { stream, min: None } + } +} + +impl Future for MinFuture +where + S: Stream, + S::Item: Ord, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match this.min.take() { + None => *this.min = Some(new), + + Some(old) => match new.cmp(&old) { + Ordering::Less => *this.min = Some(new), + _ => *this.min = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.min.take()), + } + } +} diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index a68cf3130..fe1d40ea8 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,25 +1,26 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::pin::Pin; +use core::future::Future; + +use pin_project_lite::pin_project; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct MinByFuture { - stream: S, - compare: F, - min: Option, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinByFuture { + #[pin] + stream: S, + compare: F, + min: Option, + } } impl MinByFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(compare: F); - pin_utils::unsafe_unpinned!(min: Option); - pub(super) fn new(stream: S, compare: F) -> Self { - MinByFuture { + Self { stream, compare, min: None, @@ -35,22 +36,23 @@ where { type Output = Option; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - match self.as_mut().min().take() { - None => *self.as_mut().min() = Some(new), - Some(old) => match (&mut self.as_mut().compare())(&new, &old) { - Ordering::Less => *self.as_mut().min() = Some(new), - _ => *self.as_mut().min() = Some(old), + match this.min.take() { + None => *this.min = Some(new), + Some(old) => match (this.compare)(&new, &old) { + Ordering::Less => *this.min = Some(new), + _ => *this.min = Some(old), }, } Poll::Pending } - None => Poll::Ready(self.min), + None => Poll::Ready(*this.min), } } } diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs new file mode 100644 index 000000000..549b7983b --- /dev/null +++ b/src/stream/stream/min_by_key.rs @@ -0,0 +1,65 @@ +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct MinByKeyFuture { + #[pin] + stream: S, + min: Option<(T, T)>, + key_by: K, + } +} + +impl MinByKeyFuture { + pub(super) fn new(stream: S, key_by: K) -> Self { + Self { + stream, + min: None, + key_by, + } + } +} + +impl Future for MinByKeyFuture +where + S: Stream, + K: FnMut(&S::Item) -> S::Item, + S::Item: Ord, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { + move |x| (f(&x), x) + } + + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); + + match next { + Some(new) => { + let (key, value) = key(this.key_by)(new); + cx.waker().wake_by_ref(); + + match this.min.take() { + None => *this.min = Some((key, value)), + + Some(old) => match key.cmp(&old.0) { + Ordering::Less => *this.min = Some((key, value)), + _ => *this.min = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(this.min.take().map(|min| min.1)), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 501ece1b2..144194d24 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,8 +24,12 @@ mod all; mod any; mod chain; +mod cloned; mod cmp; +mod copied; +mod cycle; mod enumerate; +mod eq; mod filter; mod filter_map; mod find; @@ -40,10 +44,17 @@ mod last; mod le; mod lt; mod map; +mod max; +mod max_by; +mod max_by_key; +mod min; mod min_by; +mod min_by_key; +mod ne; mod next; mod nth; mod partial_cmp; +mod position; mod scan; mod skip; mod skip_while; @@ -57,7 +68,9 @@ mod zip; use all::AllFuture; use any::AnyFuture; use cmp::CmpFuture; +use cycle::Cycle; use enumerate::Enumerate; +use eq::EqFuture; use filter_map::FilterMap; use find::FindFuture; use find_map::FindMapFuture; @@ -68,14 +81,23 @@ use gt::GtFuture; use last::LastFuture; use le::LeFuture; use lt::LtFuture; +use max::MaxFuture; +use max_by::MaxByFuture; +use max_by_key::MaxByKeyFuture; +use min::MinFuture; use min_by::MinByFuture; +use min_by_key::MinByKeyFuture; +use ne::NeFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use position::PositionFuture; use try_fold::TryFoldFuture; -use try_for_each::TryForEeachFuture; +use try_for_each::TryForEachFuture; pub use chain::Chain; +pub use cloned::Cloned; +pub use copied::Copied; pub use filter::Filter; pub use fuse::Fuse; pub use inspect::Inspect; @@ -88,1497 +110,2201 @@ pub use take::Take; pub use take_while::TakeWhile; pub use zip::Zip; -use std::cmp::Ordering; -use std::marker::PhantomData; +use core::cmp::Ordering; cfg_unstable! { - use std::pin::Pin; + use core::future::Future; + use core::pin::Pin; + use core::time::Duration; - use crate::future::Future; - use crate::stream::FromStream; + use crate::stream::into_stream::IntoStream; + use crate::stream::{FromStream, Product, Sum}; + use crate::stream::Extend; + + use count::CountFuture; + use partition::PartitionFuture; + use unzip::UnzipFuture; pub use merge::Merge; + pub use flatten::Flatten; + pub use flat_map::FlatMap; + pub use timeout::{TimeoutError, Timeout}; + pub use throttle::Throttle; + pub use delay::Delay; + mod count; mod merge; + mod flatten; + mod flat_map; + mod partition; + mod timeout; + mod throttle; + mod delay; + mod unzip; } -extension_trait! { - use std::ops::{Deref, DerefMut}; +pub use futures_core::stream::Stream as Stream; - use crate::task::{Context, Poll}; +#[doc = r#" + Extension methods for [`Stream`]. + [`Stream`]: ../stream/trait.Stream.html +"#] +pub trait StreamExt: Stream { #[doc = r#" - An asynchronous stream of values. + Advances the stream and returns the next value. + + Returns [`None`] when iteration is finished. Individual stream implementations may + choose to resume iteration, and so calling `next()` again may or may not eventually + start returning more values. - This trait is a re-export of [`futures::stream::Stream`] and is an async version of - [`std::iter::Iterator`]. + [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - The [provided methods] do not really exist in the trait itself, but they become - available when the prelude is imported: + # Examples ``` - # #[allow(unused_imports)] + # fn main() { async_std::task::block_on(async { + # use async_std::prelude::*; - ``` + use async_std::stream; - [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html - [`futures::stream::Stream`]: - https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html - [provided methods]: #provided-methods - "#] - pub trait Stream { - #[doc = r#" - The type of items yielded by this stream. - "#] - type Item; + let mut s = stream::once(7); - #[doc = r#" - Attempts to receive the next item from the stream. + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn next(&mut self) -> NextFuture<'_, Self> + where + Self: Unpin, + { + NextFuture { stream: self } + } - There are several possible return values: + #[doc = r#" + Creates a stream that yields its first `n` elements. - * `Poll::Pending` means this stream's next value is not ready yet. - * `Poll::Ready(None)` means this stream has been exhausted. - * `Poll::Ready(Some(item))` means `item` was received out of the stream. + # Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use std::pin::Pin; + let mut s = stream::repeat(9).take(3); - use async_std::prelude::*; - use async_std::stream; - use async_std::task::{Context, Poll}; - - fn increment( - s: impl Stream + Unpin, - ) -> impl Stream + Unpin { - struct Increment(S); - - impl + Unpin> Stream for Increment { - type Item = S::Item; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - match Pin::new(&mut self.0).poll_next(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Ready(None), - Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), - } - } - } - - Increment(s) - } + while let Some(v) = s.next().await { + assert_eq!(v, 9); + } + # + # }) } + ``` + "#] + fn take(self, n: usize) -> Take + where + Self: Sized, + { + Take::new(self, n) + } - let mut s = increment(stream::once(7)); + #[doc = r#" + Creates a stream that yields elements based on a predicate. - assert_eq!(s.next().await, Some(8)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - } + # Examples - pub trait StreamExt: futures_core::stream::Stream { - #[doc = r#" - Advances the stream and returns the next value. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Returns [`None`] when iteration is finished. Individual stream implementations may - choose to resume iteration, and so calling `next()` again may or may not eventually - start returning more values. + let s = stream::from_iter(vec![1, 2, 3, 4]); + let mut s = s.take_while(|x| x < &3 ); - [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn take_while

(self, predicate: P) -> TakeWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + TakeWhile::new(self, predicate) + } - # Examples + #[doc = r#" + Limit the amount of items yielded per timeslice in a stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + This stream does not drop any items, but will only limit the rate at which items pass through. + # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::time::{Duration, Instant}; - let mut s = stream::once(7); + let start = Instant::now(); - assert_eq!(s.next().await, Some(7)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn next(&mut self) -> impl Future> + '_ [NextFuture<'_, Self>] - where - Self: Unpin, - { - NextFuture { stream: self } - } + // emit value every 5 milliseconds + let s = stream::interval(Duration::from_millis(5)).take(2); - #[doc = r#" - Creates a stream that yields its first `n` elements. + // throttle for 10 milliseconds + let mut s = s.throttle(Duration::from_millis(10)); - # Examples + s.next().await; + assert!(start.elapsed().as_millis() >= 5); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + s.next().await; + assert!(start.elapsed().as_millis() >= 15); - let mut s = stream::repeat(9).take(3); + s.next().await; + assert!(start.elapsed().as_millis() >= 25); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn throttle(self, d: Duration) -> Throttle + where + Self: Sized, + { + Throttle::new(self, d) + } - while let Some(v) = s.next().await { - assert_eq!(v, 9); - } - # - # }) } - ``` - "#] - fn take(self, n: usize) -> Take - where - Self: Sized, - { - Take { - stream: self, - remaining: n, - } - } + #[doc = r#" + Creates a stream that yields each `step`th element. - #[doc = r#" - Creates a stream that yields elements based on a predicate. + # Panics - # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + This method will panic if the given step is `0`. - use async_std::prelude::*; + # Examples - let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); - let mut s = s.take_while(|x| x < &3 ); + Basic usage: - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, None); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # - # }) } - "#] - fn take_while

(self, predicate: P) -> TakeWhile - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - TakeWhile::new(self, predicate) - } + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); + let mut stepped = s.step_by(2); - #[doc = r#" - Creates a stream that yields each `step`th element. + assert_eq!(stepped.next().await, Some(0)); + assert_eq!(stepped.next().await, Some(2)); + assert_eq!(stepped.next().await, Some(4)); + assert_eq!(stepped.next().await, None); - # Panics + # + # }) } + ``` + "#] + fn step_by(self, step: usize) -> StepBy + where + Self: Sized, + { + StepBy::new(self, step) + } - This method will panic if the given step is `0`. + #[doc = r#" + Takes two streams and creates a new stream over both in sequence. - # Examples + # Examples - Basic usage: + Basic usage: - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); - let mut stepped = s.step_by(2); + let first = stream::from_iter(vec![0u8, 1]); + let second = stream::from_iter(vec![2, 3]); + let mut c = first.chain(second); - assert_eq!(stepped.next().await, Some(0)); - assert_eq!(stepped.next().await, Some(2)); - assert_eq!(stepped.next().await, Some(4)); - assert_eq!(stepped.next().await, None); + assert_eq!(c.next().await, Some(0)); + assert_eq!(c.next().await, Some(1)); + assert_eq!(c.next().await, Some(2)); + assert_eq!(c.next().await, Some(3)); + assert_eq!(c.next().await, None); - # - # }) } - ``` - "#] - fn step_by(self, step: usize) -> StepBy - where - Self: Sized, - { - StepBy::new(self, step) - } + # + # }) } + ``` + "#] + fn chain(self, other: U) -> Chain + where + Self: Sized, + U: Stream + Sized, + { + Chain::new(self, other) + } #[doc = r#" - Takes two streams and creates a new stream over both in sequence. + Creates an stream which copies all of its elements. - # Examples + # Examples - Basic usage: + Basic usage: - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let first: VecDeque<_> = vec![0u8, 1].into_iter().collect(); - let second: VecDeque<_> = vec![2, 3].into_iter().collect(); - let mut c = first.chain(second); - - assert_eq!(c.next().await, Some(0)); - assert_eq!(c.next().await, Some(1)); - assert_eq!(c.next().await, Some(2)); - assert_eq!(c.next().await, Some(3)); - assert_eq!(c.next().await, None); - - # - # }) } - ``` - "#] - fn chain(self, other: U) -> Chain - where - Self: Sized, - U: Stream + Sized, - { - Chain::new(self, other) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Creates a stream that gives the current element's count as well as the next value. + let v = stream::from_iter(vec![&1, &2, &3]); - # Overflow behaviour. + let mut v_cloned = v.cloned(); - This combinator does no guarding against overflows. + assert_eq!(v_cloned.next().await, Some(1)); + assert_eq!(v_cloned.next().await, Some(2)); + assert_eq!(v_cloned.next().await, Some(3)); + assert_eq!(v_cloned.next().await, None); - # Examples + # + # }) } + ``` + "#] + fn cloned<'a, T>(self) -> Cloned + where + Self: Sized + Stream, + T: Clone + 'a, + { + Cloned::new(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect(); - let mut s = s.enumerate(); + #[doc = r#" + Creates an stream which copies all of its elements. - assert_eq!(s.next().await, Some((0, 'a'))); - assert_eq!(s.next().await, Some((1, 'b'))); - assert_eq!(s.next().await, Some((2, 'c'))); - assert_eq!(s.next().await, None); + # Examples - # - # }) } - ``` - "#] - fn enumerate(self) -> Enumerate - where - Self: Sized, - { - Enumerate::new(self) - } + Basic usage: - #[doc = r#" - Takes a closure and creates a stream that calls that closure on every element of this stream. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s = stream::from_iter(vec![&1, &2, &3]); + let mut s_copied = s.copied(); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; + assert_eq!(s_copied.next().await, Some(1)); + assert_eq!(s_copied.next().await, Some(2)); + assert_eq!(s_copied.next().await, Some(3)); + assert_eq!(s_copied.next().await, None); + # + # }) } + ``` + "#] + fn copied<'a, T>(self) -> Copied + where + Self: Sized + Stream, + T: Copy + 'a, + { + Copied::new(self) + } - let s: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - let mut s = s.map(|x| 2 * x); + #[doc = r#" + Creates a stream that yields the provided values infinitely and in order. - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, Some(4)); - assert_eq!(s.next().await, Some(6)); - assert_eq!(s.next().await, None); + # Examples - # - # }) } - ``` - "#] - fn map(self, f: F) -> Map - where - Self: Sized, - F: FnMut(Self::Item) -> B, - { - Map::new(self, f) - } + Basic usage: - #[doc = r#" - A combinator that does something with each element in the stream, passing the value - on. + ``` + # async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let mut s = stream::once(7).cycle(); - Basic usage: + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + assert_eq!(s.next().await, Some(7)); + # + # }) + ``` + "#] + fn cycle(self) -> Cycle + where + Self: Clone + Sized, + { + Cycle::new(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect(); - let sum = a - .inspect(|x| println!("about to filter {}", x)) - .filter(|x| x % 2 == 0) - .inspect(|x| println!("made it through filter: {}", x)) - .fold(0, |sum, i| sum + i).await; - - assert_eq!(sum, 6); - # - # }) } - ``` - "#] - fn inspect(self, f: F) -> Inspect - where - Self: Sized, - F: FnMut(&Self::Item), - { - Inspect::new(self, f) - } + #[doc = r#" + Creates a stream that gives the current element's count as well as the next value. - #[doc = r#" - Returns the last element of the stream. + # Overflow behaviour. - # Examples + This combinator does no guarding against overflows. - Basic usage: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - use async_std::prelude::*; + let s = stream::from_iter(vec!['a', 'b', 'c']); + let mut s = s.enumerate(); - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + assert_eq!(s.next().await, Some((0, 'a'))); + assert_eq!(s.next().await, Some((1, 'b'))); + assert_eq!(s.next().await, Some((2, 'c'))); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate::new(self) + } - let last = s.last().await; - assert_eq!(last, Some(3)); - # - # }) } - ``` + #[doc = r#" + Creates a stream that is delayed before it starts yielding items. - An empty stream will return `None: - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + # Examples - use async_std::prelude::*; + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::time::{Duration, Instant}; - let s: VecDeque = vec![].into_iter().collect(); + let start = Instant::now(); + let mut s = stream::from_iter(vec![0u8, 1, 2]).delay(Duration::from_millis(200)); - let last = s.last().await; - assert_eq!(last, None); - # - # }) } - ``` + assert_eq!(s.next().await, Some(0)); + // The first time will take more than 200ms due to delay. + assert!(start.elapsed().as_millis() >= 200); - "#] - fn last( - self, - ) -> impl Future> [LastFuture] - where - Self: Sized, - { - LastFuture::new(self) - } + assert_eq!(s.next().await, Some(1)); + // There will be no delay after the first time. + assert!(start.elapsed().as_millis() < 400); - #[doc = r#" - Transforms this `Stream` into a "fused" `Stream` such that after the first time - `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return - `Poll::Ready(None)`. + assert_eq!(s.next().await, Some(2)); + assert!(start.elapsed().as_millis() < 400); - # Examples + assert_eq!(s.next().await, None); + assert!(start.elapsed().as_millis() < 400); + # + # }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn delay(self, dur: std::time::Duration) -> Delay + where + Self: Sized, + { + Delay::new(self, dur) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + #[doc = r#" + Takes a closure and creates a stream that calls that closure on every element of this stream. - let mut s = stream::once(1).fuse(); - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, None); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn fuse(self) -> Fuse - where - Self: Sized, - { - Fuse { - stream: self, - done: false, - } - } + # Examples - #[doc = r#" - Creates a stream that uses a predicate to determine if an element should be yielded. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s = stream::from_iter(vec![1, 2, 3]); + let mut s = s.map(|x| 2 * x); - Basic usage: + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, Some(4)); + assert_eq!(s.next().await, Some(6)); + assert_eq!(s.next().await, None); - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + # + # }) } + ``` + "#] + fn map(self, f: F) -> Map + where + Self: Sized, + F: FnMut(Self::Item) -> B, + { + Map::new(self, f) + } - use async_std::prelude::*; + #[doc = r#" + A combinator that does something with each element in the stream, passing the value + on. - let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); - let mut s = s.filter(|i| i % 2 == 0); - - assert_eq!(s.next().await, Some(2)); - assert_eq!(s.next().await, Some(4)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn filter

(self, predicate: P) -> Filter - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - Filter::new(self, predicate) - } + # Examples - #[doc = r#" - Both filters and maps a stream. + Basic usage: - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let s = stream::from_iter(vec![1, 2, 3, 4, 5]); - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + let sum = s + .inspect(|x| println!("about to filter {}", x)) + .filter(|x| x % 2 == 0) + .inspect(|x| println!("made it through filter: {}", x)) + .fold(0, |sum, i| sum + i) + .await; - use async_std::prelude::*; + assert_eq!(sum, 6); + # + # }) } + ``` + "#] + fn inspect(self, f: F) -> Inspect + where + Self: Sized, + F: FnMut(&Self::Item), + { + Inspect::new(self, f) + } - let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect(); + #[doc = r#" + Returns the last element of the stream. - let mut parsed = s.filter_map(|a| a.parse::().ok()); + # Examples - let one = parsed.next().await; - assert_eq!(one, Some(1)); + Basic usage: - let three = parsed.next().await; - assert_eq!(three, Some(3)); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let five = parsed.next().await; - assert_eq!(five, Some(5)); + let s = stream::from_iter(vec![1, 2, 3]); - let end = parsed.next().await; - assert_eq!(end, None); - # - # }) } - ``` - "#] - fn filter_map(self, f: F) -> FilterMap - where - Self: Sized, - F: FnMut(Self::Item) -> Option, - { - FilterMap::new(self, f) - } + let last = s.last().await; + assert_eq!(last, Some(3)); + # + # }) } + ``` - #[doc = r#" - Returns the element that gives the minimum value with respect to the - specified comparison function. If several elements are equally minimum, - the first element is returned. If the stream is empty, `None` is returned. + An empty stream will return `None`: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use crate::async_std::prelude::*; - # Examples + let s = stream::empty::<()>(); - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + let last = s.last().await; + assert_eq!(last, None); + # + # }) } + ``` + "#] + fn last( + self, + ) -> LastFuture + where + Self: Sized, + { + LastFuture::new(self) + } - use async_std::prelude::*; + #[doc = r#" + Creates a stream which ends after the first `None`. - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + After a stream returns `None`, future calls may or may not yield `Some(T)` again. + `fuse()` adapts an iterator, ensuring that after a `None` is given, it will always + return `None` forever. - let min = s.clone().min_by(|x, y| x.cmp(y)).await; - assert_eq!(min, Some(1)); + # Examples - let min = s.min_by(|x, y| y.cmp(x)).await; - assert_eq!(min, Some(3)); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::once(1).fuse(); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, None); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn fuse(self) -> Fuse + where + Self: Sized, + { + Fuse::new(self) + } - let min = VecDeque::::new().min_by(|x, y| x.cmp(y)).await; - assert_eq!(min, None); - # - # }) } - ``` - "#] - fn min_by( - self, - compare: F, - ) -> impl Future> [MinByFuture] - where - Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Ordering, - { - MinByFuture::new(self, compare) - } + #[doc = r#" + Creates a stream that uses a predicate to determine if an element should be yielded. - #[doc = r#" - Returns the nth element of the stream. + # Examples - # Examples + Basic usage: - Basic usage: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + let s = stream::from_iter(vec![1, 2, 3, 4]); + let mut s = s.filter(|i| i % 2 == 0); - use async_std::prelude::*; + assert_eq!(s.next().await, Some(2)); + assert_eq!(s.next().await, Some(4)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn filter

(self, predicate: P) -> Filter + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + Filter::new(self, predicate) + } - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + #[doc= r#" + Creates an stream that works like map, but flattens nested structure. - let second = s.nth(1).await; - assert_eq!(second, Some(2)); - # - # }) } - ``` - Calling `nth()` multiple times: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + Basic usage: - use async_std::prelude::*; + ``` + # async_std::task::block_on(async { - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + use async_std::prelude::*; + use async_std::stream; - let second = s.nth(0).await; - assert_eq!(second, Some(1)); + let words = stream::from_iter(&["alpha", "beta", "gamma"]); - let second = s.nth(0).await; - assert_eq!(second, Some(2)); - # - # }) } - ``` - Returning `None` if the stream finished before returning `n` elements: - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + let merged: String = words + .flat_map(|s| stream::from_iter(s.chars())) + .collect().await; + assert_eq!(merged, "alphabetagamma"); - use async_std::prelude::*; + let d3 = stream::from_iter(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); + let d1: Vec<_> = d3 + .flat_map(|item| stream::from_iter(item)) + .flat_map(|item| stream::from_iter(item)) + .collect().await; - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - - let fourth = s.nth(4).await; - assert_eq!(fourth, None); - # - # }) } - ``` - "#] - fn nth( - &mut self, - n: usize, - ) -> impl Future> + '_ [NthFuture<'_, Self>] + assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flat_map(self, f: F) -> FlatMap where Self: Sized, - { - NthFuture::new(self, n) - } + U: IntoStream, + F: FnMut(Self::Item) -> U, + { + FlatMap::new(self, f) + } - #[doc = r#" - Tests if every element of the stream matches a predicate. + #[doc = r#" + Creates an stream that flattens nested structure. - `all()` takes a closure that returns `true` or `false`. It applies - this closure to each element of the stream, and if they all return - `true`, then so does `all()`. If any of them return `false`, it - returns `false`. + # Examples - `all()` is short-circuiting; in other words, it will stop processing - as soon as it finds a `false`, given that no matter what else happens, - the result will also be `false`. + Basic usage: - An empty stream returns `true`. + ``` + # async_std::task::block_on(async { - # Examples + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let inner1 = stream::from_iter(vec![1u8,2,3]); + let inner2 = stream::from_iter(vec![4u8,5,6]); + let s = stream::from_iter(vec![inner1, inner2]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + let v: Vec<_> = s.flatten().collect().await; - let mut s = stream::repeat::(42).take(3); - assert!(s.all(|x| x == 42).await); + assert_eq!(v, vec![1,2,3,4,5,6]); - # - # }) } - ``` + # }); + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn flatten(self) -> Flatten + where + Self: Sized, + Self::Item: IntoStream, + { + Flatten::new(self) + } - Empty stream: + #[doc = r#" + Both filters and maps a stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + # Examples - let mut s = stream::empty::(); - assert!(s.all(|_| false).await); - # - # }) } - ``` - "#] - #[inline] - fn all( - &mut self, - f: F, - ) -> impl Future + '_ [AllFuture<'_, Self, F, Self::Item>] - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> bool, - { - AllFuture { - stream: self, - result: true, // the default if the empty stream - _marker: PhantomData, - f, - } - } + Basic usage: - #[doc = r#" - Searches for an element in a stream that satisfies a predicate. + ``` + # fn main() { async_std::task::block_on(async { + # - # Examples + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let s = stream::from_iter(vec!["1", "lol", "3", "NaN", "5"]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; + let mut parsed = s.filter_map(|a| a.parse::().ok()); - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let res = s.find(|x| *x == 2).await; - assert_eq!(res, Some(2)); - # - # }) } - ``` + let one = parsed.next().await; + assert_eq!(one, Some(1)); - Resuming after a first find: + let three = parsed.next().await; + assert_eq!(three, Some(3)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let res = s.find(|x| *x == 2).await; - assert_eq!(res, Some(2)); - - let next = s.next().await; - assert_eq!(next, Some(3)); - # - # }) } - ``` - "#] - fn find

( - &mut self, - p: P, - ) -> impl Future> + '_ [FindFuture<'_, Self, P, Self::Item>] - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - FindFuture::new(self, p) - } + let five = parsed.next().await; + assert_eq!(five, Some(5)); - #[doc = r#" - Applies function to the elements of stream and returns the first non-none result. + let end = parsed.next().await; + assert_eq!(end, None); + # + # }) } + ``` + "#] + fn filter_map(self, f: F) -> FilterMap + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + FilterMap::new(self, f) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect(); - let first_number = s.find_map(|s| s.parse().ok()).await; - - assert_eq!(first_number, Some(2)); - # - # }) } - ``` - "#] - fn find_map( - &mut self, - f: F, - ) -> impl Future> + '_ [FindMapFuture<'_, Self, F, Self::Item, B>] - where - Self: Sized, - F: FnMut(Self::Item) -> Option, - { - FindMapFuture::new(self, f) - } + #[doc = r#" + Returns the element that gives the minimum value with respect to the + specified key function. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. - #[doc = r#" - A combinator that applies a function to every element in a stream - producing a single, final value. + # Examples - # Examples + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - Basic usage: + let s = stream::from_iter(vec![-1isize, 2, -3]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let sum = s.fold(0, |acc, x| acc + x).await; - - assert_eq!(sum, 6); - # - # }) } - ``` - "#] - fn fold( - self, - init: B, - f: F, - ) -> impl Future [FoldFuture] - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - FoldFuture::new(self, init, f) - } + let min = s.clone().min_by_key(|x| x.abs()).await; + assert_eq!(min, Some(-1)); - #[doc = r#" - Call a closure on each element of the stream. + let min = stream::empty::().min_by_key(|x| x.abs()).await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by_key( + self, + key_by: F, + ) -> MinByKeyFuture + where + Self: Sized, + B: Ord, + F: FnMut(&Self::Item) -> B, + { + MinByKeyFuture::new(self, key_by) + } + + #[doc = r#" + Returns the element that gives the maximum value with respect to the + specified key function. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![-3_i32, 0, 1, 5, -10]); + + let max = s.clone().max_by_key(|x| x.abs()).await; + assert_eq!(max, Some(-10)); + + let max = stream::empty::().max_by_key(|x| x.abs()).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by_key( + self, + key_by: F, + ) -> MaxByKeyFuture + where + Self: Sized, + B: Ord, + F: FnMut(&Self::Item) -> B, + { + MaxByKeyFuture::new(self, key_by) + } + + #[doc = r#" + Returns the element that gives the minimum value with respect to the + specified comparison function. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1u8, 2, 3]); + + let min = s.clone().min_by(|x, y| x.cmp(y)).await; + assert_eq!(min, Some(1)); + + let min = s.min_by(|x, y| y.cmp(x)).await; + assert_eq!(min, Some(3)); + + let min = stream::empty::().min_by(|x, y| x.cmp(y)).await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min_by( + self, + compare: F, + ) -> MinByFuture + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MinByFuture::new(self, compare) + } + + #[doc = r#" + Returns the element that gives the maximum value. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1usize, 2, 3]); + + let max = s.clone().max().await; + assert_eq!(max, Some(3)); + + let max = stream::empty::().max().await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max( + self, + ) -> MaxFuture + where + Self: Sized, + Self::Item: Ord, + { + MaxFuture::new(self) + } + + #[doc = r#" + Returns the element that gives the minimum value. If several elements are equally minimum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1usize, 2, 3]); + + let min = s.clone().min().await; + assert_eq!(min, Some(1)); + + let min = stream::empty::().min().await; + assert_eq!(min, None); + # + # }) } + ``` + "#] + fn min( + self, + ) -> MinFuture + where + Self: Sized, + Self::Item: Ord, + { + MinFuture::new(self) + } + + #[doc = r#" + Returns the element that gives the maximum value with respect to the + specified comparison function. If several elements are equally maximum, + the first element is returned. If the stream is empty, `None` is returned. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1u8, 2, 3]); + + let max = s.clone().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, Some(3)); + + let max = s.max_by(|x, y| y.cmp(x)).await; + assert_eq!(max, Some(1)); + + let max = stream::empty::().max_by(|x, y| x.cmp(y)).await; + assert_eq!(max, None); + # + # }) } + ``` + "#] + fn max_by( + self, + compare: F, + ) -> MaxByFuture + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MaxByFuture::new(self, compare) + } + + #[doc = r#" + Returns the nth element of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let second = s.nth(1).await; + assert_eq!(second, Some(2)); + # + # }) } + ``` + Calling `nth()` multiple times: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::stream; + use async_std::prelude::*; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let second = s.nth(0).await; + assert_eq!(second, Some(1)); + + let second = s.nth(0).await; + assert_eq!(second, Some(2)); + # + # }) } + ``` + Returning `None` if the stream finished before returning `n` elements: + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + + let fourth = s.nth(4).await; + assert_eq!(fourth, None); + # + # }) } + ``` + "#] + fn nth( + &mut self, + n: usize, + ) -> NthFuture<'_, Self> + where + Self: Unpin + Sized, + { + NthFuture::new(self, n) + } + + #[doc = r#" + Tests if every element of the stream matches a predicate. + + `all()` takes a closure that returns `true` or `false`. It applies + this closure to each element of the stream, and if they all return + `true`, then so does `all()`. If any of them return `false`, it + returns `false`. + + `all()` is short-circuiting; in other words, it will stop processing + as soon as it finds a `false`, given that no matter what else happens, + the result will also be `false`. + + An empty stream returns `true`. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::repeat::(42).take(3); + assert!(s.all(|x| x == 42).await); + + # + # }) } + ``` + + Empty stream: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::empty::(); + assert!(s.all(|_| false).await); + # + # }) } + ``` + "#] + #[inline] + fn all( + &mut self, + f: F, + ) -> AllFuture<'_, Self, F, Self::Item> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + AllFuture::new(self, f) + } + + #[doc = r#" + Searches for an element in a stream that satisfies a predicate. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1u8, 2, 3]); + let res = s.find(|x| *x == 2).await; + assert_eq!(res, Some(2)); + # + # }) } + ``` + + Resuming after a first find: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s= stream::from_iter(vec![1, 2, 3]); + let res = s.find(|x| *x == 2).await; + assert_eq!(res, Some(2)); + + let next = s.next().await; + assert_eq!(next, Some(3)); + # + # }) } + ``` + "#] + fn find

( + &mut self, + p: P, + ) -> FindFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(&Self::Item) -> bool, + { + FindFuture::new(self, p) + } + + #[doc = r#" + Applies function to the elements of stream and returns the first non-none result. + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec!["lol", "NaN", "2", "5"]); + let first_number = s.find_map(|s| s.parse().ok()).await; + + assert_eq!(first_number, Some(2)); + # + # }) } + ``` + "#] + fn find_map( + &mut self, + f: F, + ) -> FindMapFuture<'_, Self, F> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> Option, + { + FindMapFuture::new(self, f) + } + + #[doc = r#" + A combinator that applies a function to every element in a stream + producing a single, final value. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1u8, 2, 3]); + let sum = s.fold(0, |acc, x| acc + x).await; + + assert_eq!(sum, 6); + # + # }) } + ``` + "#] + fn fold( + self, + init: B, + f: F, + ) -> FoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + FoldFuture::new(self, init, f) + } + + #[doc = r#" + A combinator that applies a function to every element in a stream + creating two collections from it. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let (even, odd): (Vec, Vec) = stream::from_iter(vec![1, 2, 3]) + .partition(|&n| n % 2 == 0).await; + + assert_eq!(even, vec![2]); + assert_eq!(odd, vec![1, 3]); + + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn partition( + self, + f: F, + ) -> PartitionFuture + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + B: Default + Extend, + { + PartitionFuture::new(self, f) + } + + #[doc = r#" + Call a closure on each element of the stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::sync::mpsc::channel; + + let (tx, rx) = channel(); + + let s = stream::from_iter(vec![1usize, 2, 3]); + let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await; + + let v: Vec<_> = rx.iter().collect(); + + assert_eq!(v, vec![1, 2, 3]); + # + # }) } + ``` + "#] + fn for_each( + self, + f: F, + ) -> ForEachFuture + where + Self: Sized, + F: FnMut(Self::Item), + { + ForEachFuture::new(self, f) + } + + #[doc = r#" + Tests if any element of the stream matches a predicate. + + `any()` takes a closure that returns `true` or `false`. It applies + this closure to each element of the stream, and if any of them return + `true`, then so does `any()`. If they all return `false`, it + returns `false`. + + `any()` is short-circuiting; in other words, it will stop processing + as soon as it finds a `true`, given that no matter what else happens, + the result will also be `true`. + + An empty stream returns `false`. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::repeat::(42).take(3); + assert!(s.any(|x| x == 42).await); + # + # }) } + ``` + + Empty stream: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::empty::(); + assert!(!s.any(|_| false).await); + # + # }) } + ``` + "#] + #[inline] + fn any( + &mut self, + f: F, + ) -> AnyFuture<'_, Self, F, Self::Item> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + AnyFuture::new(self, f) + } + + #[doc = r#" + Borrows an stream, rather than consuming it. + + This is useful to allow applying stream adaptors while still retaining ownership of the original stream. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let a = vec![1isize, 2, 3]; + + let stream = stream::from_iter(a); + + let sum: isize = stream.take(5).sum().await; + + assert_eq!(sum, 6); + + // if we try to use stream again, it won't work. The following line + // gives error: use of moved value: `stream` + // assert_eq!(stream.next(), None); + + // let's try that again + let a = vec![1isize, 2, 3]; + + let mut stream = stream::from_iter(a); + + // instead, we add in a .by_ref() + let sum: isize = stream.by_ref().take(2).sum().await; + + assert_eq!(sum, 3); + + // now this is just fine: + assert_eq!(stream.next().await, Some(3)); + assert_eq!(stream.next().await, None); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn by_ref(&mut self) -> &mut Self { + self + } + + #[doc = r#" + A stream adaptor similar to [`fold`] that holds internal state and produces a new + stream. + + [`fold`]: #method.fold + + `scan()` takes two arguments: an initial value which seeds the internal state, and + a closure with two arguments, the first being a mutable reference to the internal + state and the second a stream element. The closure can assign to the internal state + to share state between iterations. + + On iteration, the closure will be applied to each element of the stream and the + return value from the closure, an `Option`, is yielded by the stream. + + ## Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![1isize, 2, 3]); + let mut s = s.scan(1, |state, x| { + *state = *state * x; + Some(-*state) + }); + + assert_eq!(s.next().await, Some(-1)); + assert_eq!(s.next().await, Some(-2)); + assert_eq!(s.next().await, Some(-6)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + #[inline] + fn scan(self, initial_state: St, f: F) -> Scan + where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option, + { + Scan::new(self, initial_state, f) + } + + #[doc = r#" + Combinator that `skip`s elements based on a predicate. + + Takes a closure argument. It will call this closure on every element in + the stream and ignore elements until it returns `false`. + + After `false` is returned, `SkipWhile`'s job is over and all further + elements in the stream are yielded. + + ## Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let a = stream::from_iter(vec![-1i32, 0, 1]); + let mut s = a.skip_while(|x| x.is_negative()); + + assert_eq!(s.next().await, Some(0)); + assert_eq!(s.next().await, Some(1)); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + fn skip_while

(self, predicate: P) -> SkipWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + SkipWhile::new(self, predicate) + } - # Examples + #[doc = r#" + Creates a combinator that skips the first `n` elements. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - use std::sync::mpsc::channel; + ## Examples - let (tx, rx) = channel(); + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await; + let s = stream::from_iter(vec![1u8, 2, 3]); + let mut skipped = s.skip(2); - let v: Vec<_> = rx.iter().collect(); + assert_eq!(skipped.next().await, Some(3)); + assert_eq!(skipped.next().await, None); + # + # }) } + ``` + "#] + fn skip(self, n: usize) -> Skip + where + Self: Sized, + { + Skip::new(self, n) + } - assert_eq!(v, vec![1, 2, 3]); - # - # }) } - ``` - "#] - fn for_each( - self, - f: F, - ) -> impl Future [ForEachFuture] - where - Self: Sized, - F: FnMut(Self::Item), - { - ForEachFuture::new(self, f) - } + #[doc=r#" + Await a stream or times out after a duration of time. - #[doc = r#" - Tests if any element of the stream matches a predicate. + If you want to await an I/O future consider using + [`io::timeout`](../io/fn.timeout.html) instead. - `any()` takes a closure that returns `true` or `false`. It applies - this closure to each element of the stream, and if any of them return - `true`, then so does `any()`. If they all return `false`, it - returns `false`. + # Examples - `any()` is short-circuiting; in other words, it will stop processing - as soon as it finds a `true`, given that no matter what else happens, - the result will also be `true`. + ``` + # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + # + use std::time::Duration; - An empty stream returns `false`. + use async_std::stream; + use async_std::prelude::*; - # Examples + let mut s = stream::repeat(1).take(3).timeout(Duration::from_secs(1)); - Basic usage: + while let Some(v) = s.next().await { + assert_eq!(v, Ok(1)); + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + // when timeout + let mut s = stream::pending::<()>().timeout(Duration::from_millis(10)); + match s.next().await { + Some(item) => assert!(item.is_err()), + None => panic!() + }; + # + # Ok(()) }) } + ``` + "#] + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn timeout(self, dur: Duration) -> Timeout + where + Self: Stream + Sized, + { + Timeout::new(self, dur) + } - let mut s = stream::repeat::(42).take(3); - assert!(s.any(|x| x == 42).await); - # - # }) } - ``` + #[doc = r#" + A combinator that applies a function as long as it returns successfully, producing a single, final value. + Immediately returns the error when the function returns unsuccessfully. - Empty stream: + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + Basic usage: - let mut s = stream::empty::(); - assert!(!s.any(|_| false).await); - # - # }) } - ``` - "#] - #[inline] - fn any( - &mut self, - f: F, - ) -> impl Future + '_ [AnyFuture<'_, Self, F, Self::Item>] - where - Self: Unpin + Sized, - F: FnMut(Self::Item) -> bool, - { - AnyFuture { - stream: self, - result: false, // the default if the empty stream - _marker: PhantomData, - f, + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let mut s = stream::from_iter(vec![1usize, 2, 3]); + let sum = s.try_fold(0, |acc, v| { + if (acc+v) % 2 == 1 { + Ok(v+3) + } else { + Err("fail") } - } + }).await; - #[doc = r#" - A stream adaptor similar to [`fold`] that holds internal state and produces a new - stream. + assert_eq!(sum, Err("fail")); + # + # }) } + ``` + "#] + fn try_fold( + &mut self, + init: T, + f: F, + ) -> TryFoldFuture<'_, Self, F, T> + where + Self: Unpin + Sized, + F: FnMut(B, Self::Item) -> Result, + { + TryFoldFuture::new(self, init, f) + } - [`fold`]: #method.fold + #[doc = r#" + Applies a falliable function to each element in a stream, stopping at first error and returning it. - `scan()` takes two arguments: an initial value which seeds the internal state, and - a closure with two arguments, the first being a mutable reference to the internal - state and the second a stream element. The closure can assign to the internal state - to share state between iterations. + # Examples - On iteration, the closure will be applied to each element of the stream and the - return value from the closure, an `Option`, is yielded by the stream. + ``` + # fn main() { async_std::task::block_on(async { + # + use std::sync::mpsc::channel; + use async_std::prelude::*; + use async_std::stream; - ## Examples + let (tx, rx) = channel(); - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + let mut s = stream::from_iter(vec![1u8, 2, 3]); + let s = s.try_for_each(|v| { + if v % 2 == 1 { + tx.clone().send(v).unwrap(); + Ok(()) + } else { + Err("even") + } + }); - use async_std::prelude::*; + let res = s.await; + drop(tx); + let values: Vec<_> = rx.iter().collect(); - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let mut s = s.scan(1, |state, x| { - *state = *state * x; - Some(-*state) - }); - - assert_eq!(s.next().await, Some(-1)); - assert_eq!(s.next().await, Some(-2)); - assert_eq!(s.next().await, Some(-6)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - #[inline] - fn scan(self, initial_state: St, f: F) -> Scan - where - Self: Sized, - F: FnMut(&mut St, Self::Item) -> Option, - { - Scan::new(self, initial_state, f) - } + assert_eq!(values, vec![1]); + assert_eq!(res, Err("even")); + # + # }) } + ``` + "#] + fn try_for_each( + &mut self, + f: F, + ) -> TryForEachFuture<'_, Self, F> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> Result<(), E>, + { + TryForEachFuture::new(self, f) + } - #[doc = r#" - Combinator that `skip`s elements based on a predicate. + #[doc = r#" + 'Zips up' two streams into a single stream of pairs. - Takes a closure argument. It will call this closure on every element in - the stream and ignore elements until it returns `false`. + `zip()` returns a new stream that will iterate over two other streams, returning a + tuple where the first element comes from the first stream, and the second element + comes from the second stream. - After `false` is returned, `SkipWhile`'s job is over and all further - elements in the strem are yielded. + In other words, it zips two streams together, into a single one. - ## Examples + If either stream returns [`None`], [`poll_next`] from the zipped stream will return + [`None`]. If the first stream returns [`None`], `zip` will short-circuit and + `poll_next` will not be called on the second stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + [`poll_next`]: #tymethod.poll_next - use async_std::prelude::*; + ## Examples - let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect(); - let mut s = a.skip_while(|x| x.is_negative()); - - assert_eq!(s.next().await, Some(0)); - assert_eq!(s.next().await, Some(1)); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - fn skip_while

(self, predicate: P) -> SkipWhile - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - SkipWhile::new(self, predicate) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let l = stream::from_iter(vec![1u8, 2, 3]); + let r = stream::from_iter(vec![4u8, 5, 6, 7]); + let mut s = l.zip(r); + + assert_eq!(s.next().await, Some((1, 4))); + assert_eq!(s.next().await, Some((2, 5))); + assert_eq!(s.next().await, Some((3, 6))); + assert_eq!(s.next().await, None); + # + # }) } + ``` + "#] + #[inline] + fn zip(self, other: U) -> Zip + where + Self: Sized, + U: Stream, + { + Zip::new(self, other) + } - #[doc = r#" - Creates a combinator that skips the first `n` elements. + #[doc = r#" + Converts an stream of pairs into a pair of containers. - ## Examples + `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements. - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + This function is, in some sense, the opposite of [`zip`]. - use async_std::prelude::*; + [`zip`]: trait.Stream.html#method.zip - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let mut skipped = s.skip(2); + # Example - assert_eq!(skipped.next().await, Some(3)); - assert_eq!(skipped.next().await, None); - # - # }) } - ``` - "#] - fn skip(self, n: usize) -> Skip - where - Self: Sized, - { - Skip::new(self, n) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - A combinator that applies a function as long as it returns successfully, producing a single, final value. - Immediately returns the error when the function returns unsuccessfully. + let s = stream::from_iter(vec![(1,2), (3,4)]); - # Examples + let (left, right): (Vec<_>, Vec<_>) = s.unzip().await; - Basic usage: + assert_eq!(left, [1, 3]); + assert_eq!(right, [2, 4]); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn unzip(self) -> UnzipFuture + where + FromA: Default + Extend, + FromB: Default + Extend, + Self: Stream + Sized, + { + UnzipFuture::new(self) + } - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let sum = s.try_fold(0, |acc, v| { - if (acc+v) % 2 == 1 { - Ok(v+3) - } else { - Err("fail") - } - }).await; - - assert_eq!(sum, Err("fail")); - # - # }) } - ``` - "#] - fn try_fold( - self, - init: T, - f: F, - ) -> impl Future> [TryFoldFuture] - where - Self: Sized, - F: FnMut(B, Self::Item) -> Result, - { - TryFoldFuture::new(self, init, f) - } + #[doc = r#" + Transforms a stream into a collection. - #[doc = r#" - Applies a falliable function to each element in a stream, stopping at first error and returning it. + `collect()` can take anything streamable, and turn it into a relevant + collection. This is one of the more powerful methods in the async + standard library, used in a variety of contexts. - # Examples + The most basic pattern in which `collect()` is used is to turn one + collection into another. You take a collection, call [`into_stream`] on it, + do a bunch of transformations, and then `collect()` at the end. - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; - use std::sync::mpsc::channel; - use async_std::prelude::*; + Because `collect()` is so general, it can cause problems with type + inference. As such, `collect()` is one of the few times you'll see + the syntax affectionately known as the 'turbofish': `::<>`. This + helps the inference algorithm understand specifically which collection + you're trying to collect into. - let (tx, rx) = channel(); - - let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - let s = s.try_for_each(|v| { - if v % 2 == 1 { - tx.clone().send(v).unwrap(); - Ok(()) - } else { - Err("even") - } - }); - - let res = s.await; - drop(tx); - let values: Vec<_> = rx.iter().collect(); - - assert_eq!(values, vec![1]); - assert_eq!(res, Err("even")); - # - # }) } - ``` - "#] - fn try_for_each( - self, - f: F, - ) -> impl Future [TryForEeachFuture] - where - Self: Sized, - F: FnMut(Self::Item) -> Result<(), E>, - { - TryForEeachFuture::new(self, f) - } + # Examples - #[doc = r#" - 'Zips up' two streams into a single stream of pairs. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - `zip()` returns a new stream that will iterate over two other streams, returning a - tuple where the first element comes from the first stream, and the second element - comes from the second stream. + let s = stream::repeat(9u8).take(3); + let buf: Vec = s.collect().await; - In other words, it zips two streams together, into a single one. + assert_eq!(buf, vec![9; 3]); - If either stream returns [`None`], [`poll_next`] from the zipped stream will return - [`None`]. If the first stream returns [`None`], `zip` will short-circuit and - `poll_next` will not be called on the second stream. + // You can also collect streams of Result values + // into any collection that implements FromStream + let s = stream::repeat(Ok(9)).take(3); + // We are using Vec here, but other collections + // are supported as well + let buf: Result, ()> = s.collect().await; - [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - [`poll_next`]: #tymethod.poll_next + assert_eq!(buf, Ok(vec![9; 3])); - ## Examples + // The stream will stop on the first Err and + // return that instead + let s = stream::repeat(Err(5)).take(3); + let buf: Result, u8> = s.collect().await; - ``` - # fn main() { async_std::task::block_on(async { - # - use std::collections::VecDeque; + assert_eq!(buf, Err(5)); + # + # }) } + ``` - use async_std::prelude::*; + [`into_stream`]: trait.IntoStream.html#tymethod.into_stream + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn collect<'a, B>( + self, + ) -> Pin + 'a + Send>> + where + Self: Sized + 'a + Send, + B: FromStream, + Self::Item: Send, + { + FromStream::from_stream(self) + } - let l: VecDeque = vec![1, 2, 3].into_iter().collect(); - let r: VecDeque = vec![4, 5, 6, 7].into_iter().collect(); - let mut s = l.zip(r); - - assert_eq!(s.next().await, Some((1, 4))); - assert_eq!(s.next().await, Some((2, 5))); - assert_eq!(s.next().await, Some((3, 6))); - assert_eq!(s.next().await, None); - # - # }) } - ``` - "#] - #[inline] - fn zip(self, other: U) -> Zip - where - Self: Sized + Stream, - U: Stream, - { - Zip::new(self, other) - } + #[doc = r#" + Combines multiple streams into a single stream of all their outputs. - #[doc = r#" - Transforms a stream into a collection. + Items are yielded as soon as they're received, and the stream continues yield until + both streams have been exhausted. The output ordering between streams is not guaranteed. - `collect()` can take anything streamable, and turn it into a relevant - collection. This is one of the more powerful methods in the async - standard library, used in a variety of contexts. + # Examples - The most basic pattern in which `collect()` is used is to turn one - collection into another. You take a collection, call [`stream`] on it, - do a bunch of transformations, and then `collect()` at the end. + ``` + # async_std::task::block_on(async { + use async_std::prelude::*; + use async_std::stream::{self, FromStream}; - Because `collect()` is so general, it can cause problems with type - inference. As such, `collect()` is one of the few times you'll see - the syntax affectionately known as the 'turbofish': `::<>`. This - helps the inference algorithm understand specifically which collection - you're trying to collect into. + let a = stream::once(1u8); + let b = stream::once(2u8); + let c = stream::once(3u8); - # Examples + let s = a.merge(b).merge(c); + let mut lst = Vec::from_stream(s).await; - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use async_std::stream; + lst.sort_unstable(); + assert_eq!(&lst, &[1u8, 2u8, 3u8]); + # }); + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn merge(self, other: U) -> Merge + where + Self: Sized, + U: Stream + Sized, + { + Merge::new(self, other) + } - let s = stream::repeat(9u8).take(3); - let buf: Vec = s.collect().await; - - assert_eq!(buf, vec![9; 3]); - - // You can also collect streams of Result values - // into any collection that implements FromStream - let s = stream::repeat(Ok(9)).take(3); - // We are using Vec here, but other collections - // are supported as well - let buf: Result, ()> = s.collect().await; - - assert_eq!(buf, Ok(vec![9; 3])); - - // The stream will stop on the first Err and - // return that instead - let s = stream::repeat(Err(5)).take(3); - let buf: Result, u8> = s.collect().await; - - assert_eq!(buf, Err(5)); - # - # }) } - ``` - - [`stream`]: trait.Stream.html#tymethod.next - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] - fn collect<'a, B>( - self, - ) -> impl Future + 'a [Pin + 'a>>] - where - Self: Sized + 'a, - B: FromStream, - { - FromStream::from_stream(self) - } + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another. - #[doc = r#" - Combines multiple streams into a single stream of all their outputs. + # Examples - Items are yielded as soon as they're received, and the stream continues yield until both - streams have been exhausted. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + use std::cmp::Ordering; + + let s1 = stream::from_iter(vec![1]); + let s2 = stream::from_iter(vec![1, 2]); + let s3 = stream::from_iter(vec![1, 2, 3]); + let s4 = stream::from_iter(vec![1, 2, 4]); + assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); + assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); + assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); + assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); + assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); + # + # }) } + ``` + "#] + fn partial_cmp( + self, + other: S + ) -> PartialCmpFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + PartialCmpFuture::new(self, other) + } - # Examples + #[doc = r#" + Searches for an element in a Stream that satisfies a predicate, returning + its index. - ``` - # async_std::task::block_on(async { - use async_std::prelude::*; - use async_std::stream; + # Examples - let a = stream::once(1u8); - let b = stream::once(2u8); - let c = stream::once(3u8); - - let mut s = a.merge(b).merge(c); - - assert_eq!(s.next().await, Some(1u8)); - assert_eq!(s.next().await, Some(2u8)); - assert_eq!(s.next().await, Some(3u8)); - assert_eq!(s.next().await, None); - # }); - ``` - "#] - #[cfg(feature = "unstable")] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - fn merge(self, other: U) -> Merge - where - Self: Sized, - U: Stream + Sized, - { - Merge::new(self, other) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - #[doc = r#" - Lexicographically compares the elements of this `Stream` with those - of another. + let s = stream::from_iter(vec![1usize, 2, 3]); + let res = s.clone().position(|x| x == 1).await; + assert_eq!(res, Some(0)); - # Examples + let res = s.clone().position(|x| x == 2).await; + assert_eq!(res, Some(1)); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - use std::cmp::Ordering; - - let s1 = VecDeque::from(vec![1]); - let s2 = VecDeque::from(vec![1, 2]); - let s3 = VecDeque::from(vec![1, 2, 3]); - let s4 = VecDeque::from(vec![1, 2, 4]); - assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); - assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); - assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); - assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); - assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); - # - # }) } - ``` - "#] - fn partial_cmp( - self, - other: S - ) -> impl Future> [PartialCmpFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - PartialCmpFuture::new(self, other) - } + let res = s.clone().position(|x| x == 3).await; + assert_eq!(res, Some(2)); - #[doc = r#" - Lexicographically compares the elements of this `Stream` with those - of another using 'Ord'. + let res = s.clone().position(|x| x == 4).await; + assert_eq!(res, None); + # + # }) } + ``` + "#] + fn position

( + &mut self, + predicate: P, + ) -> PositionFuture<'_, Self, P> + where + Self: Unpin + Sized, + P: FnMut(Self::Item) -> bool, + { + PositionFuture::new(self, predicate) + } - # Examples + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another using 'Ord'. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - use std::cmp::Ordering; - let s1 = VecDeque::from(vec![1]); - let s2 = VecDeque::from(vec![1, 2]); - let s3 = VecDeque::from(vec![1, 2, 3]); - let s4 = VecDeque::from(vec![1, 2, 4]); - assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); - assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); - assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); - assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); - assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); - # - # }) } - ``` - "#] - fn cmp( - self, - other: S - ) -> impl Future [CmpFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: Ord - { - CmpFuture::new(self, other) - } + # Examples - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - greater than or equal to those of another. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + use std::cmp::Ordering; + + let s1 = stream::from_iter(vec![1]); + let s2 = stream::from_iter(vec![1, 2]); + let s3 = stream::from_iter(vec![1, 2, 3]); + let s4 = stream::from_iter(vec![1, 2, 4]); + + assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); + assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); + assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); + assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); + assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); + # + # }) } + ``` + "#] + fn cmp( + self, + other: S + ) -> CmpFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: Ord + { + CmpFuture::new(self, other) + } - # Examples + #[doc = r#" + Counts the number of elements in the stream. - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let single: VecDeque = vec![1].into_iter().collect(); - let single_gt: VecDeque = vec![10].into_iter().collect(); - let multi: VecDeque = vec![1,2].into_iter().collect(); - let multi_gt: VecDeque = vec![1,5].into_iter().collect(); - assert_eq!(single.clone().ge(single.clone()).await, true); - assert_eq!(single_gt.clone().ge(single.clone()).await, true); - assert_eq!(multi.clone().ge(single_gt.clone()).await, false); - assert_eq!(multi_gt.clone().ge(multi.clone()).await, true); - # - # }) } - ``` - "#] - fn ge( - self, - other: S - ) -> impl Future [GeFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - GeFuture::new(self, other) - } + # Examples - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - greater than those of another. + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; - # Examples + let s1 = stream::from_iter(vec![0]); + let s2 = stream::from_iter(vec![1, 2, 3]); - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(vec![1,5]); - assert_eq!(single.clone().gt(single.clone()).await, false); - assert_eq!(single_gt.clone().gt(single.clone()).await, true); - assert_eq!(multi.clone().gt(single_gt.clone()).await, false); - assert_eq!(multi_gt.clone().gt(multi.clone()).await, true); - # - # }) } - ``` - "#] - fn gt( - self, - other: S - ) -> impl Future [GtFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - GtFuture::new(self, other) - } + assert_eq!(s1.count().await, 1); + assert_eq!(s2.count().await, 3); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn count(self) -> CountFuture + where + Self: Sized, + { + CountFuture::new(self) + } - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - less or equal to those of another. + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + not equal to those of another. - # Examples + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(vec![1,5]); - assert_eq!(single.clone().le(single.clone()).await, true); - assert_eq!(single.clone().le(single_gt.clone()).await, true); - assert_eq!(multi.clone().le(single_gt.clone()).await, true); - assert_eq!(multi_gt.clone().le(multi.clone()).await, false); - # - # }) } - ``` - "#] - fn le( - self, - other: S - ) -> impl Future [LeFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - LeFuture::new(self, other) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1usize]); + let single_ne = stream::from_iter(vec![10usize]); + let multi = stream::from_iter(vec![1usize,2]); + let multi_ne = stream::from_iter(vec![1usize,5]); + + assert_eq!(single.clone().ne(single.clone()).await, false); + assert_eq!(single_ne.clone().ne(single.clone()).await, true); + assert_eq!(multi.clone().ne(single_ne.clone()).await, true); + assert_eq!(multi_ne.clone().ne(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn ne( + self, + other: S + ) -> NeFuture + where + Self: Sized, + S: Sized + Stream, + ::Item: PartialEq, + { + NeFuture::new(self, other) + } - #[doc = r#" - Determines if the elements of this `Stream` are lexicographically - less than those of another. + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than or equal to those of another. - # Examples + # Examples - ``` - # fn main() { async_std::task::block_on(async { - # - use async_std::prelude::*; - use std::collections::VecDeque; - - let single = VecDeque::from(vec![1]); - let single_gt = VecDeque::from(vec![10]); - let multi = VecDeque::from(vec![1,2]); - let multi_gt = VecDeque::from(vec![1,5]); - - assert_eq!(single.clone().lt(single.clone()).await, false); - assert_eq!(single.clone().lt(single_gt.clone()).await, true); - assert_eq!(multi.clone().lt(single_gt.clone()).await, true); - assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); - # - # }) } - ``` - "#] - fn lt( - self, - other: S - ) -> impl Future [LtFuture] - where - Self: Sized + Stream, - S: Stream, - ::Item: PartialOrd, - { - LtFuture::new(self, other) - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().ge(single.clone()).await, true); + assert_eq!(single_gt.clone().ge(single.clone()).await, true); + assert_eq!(multi.clone().ge(single_gt.clone()).await, false); + assert_eq!(multi_gt.clone().ge(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn ge( + self, + other: S + ) -> GeFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GeFuture::new(self, other) } - impl Stream for Box { - type Item = S::Item; + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + equal to those of another. + + # Examples - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_eq = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_eq = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().eq(single.clone()).await, true); + assert_eq!(single_eq.clone().eq(single.clone()).await, false); + assert_eq!(multi.clone().eq(single_eq.clone()).await, false); + assert_eq!(multi_eq.clone().eq(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn eq( + self, + other: S + ) -> EqFuture + where + Self: Sized + Stream, + S: Sized + Stream, + ::Item: PartialEq, + { + EqFuture::new(self, other) } - impl Stream for &mut S { - type Item = S::Item; + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than those of another. - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().gt(single.clone()).await, false); + assert_eq!(single_gt.clone().gt(single.clone()).await, true); + assert_eq!(multi.clone().gt(single_gt.clone()).await, false); + assert_eq!(multi_gt.clone().gt(multi.clone()).await, true); + # + # }) } + ``` + "#] + fn gt( + self, + other: S + ) -> GtFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + GtFuture::new(self, other) } - impl

Stream for Pin

+ #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less or equal to those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().le(single.clone()).await, true); + assert_eq!(single.clone().le(single_gt.clone()).await, true); + assert_eq!(multi.clone().le(single_gt.clone()).await, true); + assert_eq!(multi_gt.clone().le(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn le( + self, + other: S + ) -> LeFuture where - P: DerefMut + Unpin, -

::Target: Stream, + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, { - type Item = <

::Target as Stream>::Item; + LeFuture::new(self, other) + } - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + less than those of another. + + # Examples + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let single = stream::from_iter(vec![1]); + let single_gt = stream::from_iter(vec![10]); + let multi = stream::from_iter(vec![1,2]); + let multi_gt = stream::from_iter(vec![1,5]); + + assert_eq!(single.clone().lt(single.clone()).await, false); + assert_eq!(single.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi.clone().lt(single_gt.clone()).await, true); + assert_eq!(multi_gt.clone().lt(multi.clone()).await, false); + # + # }) } + ``` + "#] + fn lt( + self, + other: S + ) -> LtFuture + where + Self: Sized + Stream, + S: Stream, + ::Item: PartialOrd, + { + LtFuture::new(self, other) } - impl Stream for std::collections::VecDeque { - type Item = T; + #[doc = r#" + Sums the elements of a stream. + + Takes each element, adds them together, and returns the result. - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") - } + An empty streams returns the zero value of the type. + + # Panics + + When calling `sum()` and a primitive integer type is being returned, this + method will panic if the computation overflows and debug assertions are + enabled. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use async_std::prelude::*; + use async_std::stream; + + let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]); + let sum: u8 = s.sum().await; + + assert_eq!(sum, 10); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn sum<'a, S>( + self, + ) -> Pin + 'a>> + where + Self: Sized + Stream + 'a, + S: Sum, + { + Sum::sum(self) } - impl Stream for std::panic::AssertUnwindSafe { - type Item = S::Item; + #[doc = r#" + Multiplies all elements of the stream. + + An empty stream returns the one value of the type. + + # Panics + + When calling `product()` and a primitive integer type is being returned, + method will panic if the computation overflows and debug assertions are + enabled. + + # Examples + + This example calculates the factorial of n (i.e. the product of the numbers from 1 to + n, inclusive): + + ``` + # fn main() { async_std::task::block_on(async { + # + async fn factorial(n: u32) -> u32 { + use async_std::prelude::*; + use async_std::stream; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unreachable!("this impl only appears in the rendered docs") + let s = stream::from_iter(1..=n); + s.product().await } + + assert_eq!(factorial(0).await, 1); + assert_eq!(factorial(1).await, 1); + assert_eq!(factorial(5).await, 120); + # + # }) } + ``` + "#] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + fn product<'a, P>( + self, + ) -> Pin + 'a>> + where + Self: Sized + Stream + 'a, + P: Product, + { + Product::product(self) } } + +impl StreamExt for T {} + diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs new file mode 100644 index 000000000..c51ab31ef --- /dev/null +++ b/src/stream/stream/ne.rs @@ -0,0 +1,65 @@ +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use super::fuse::Fuse; +use crate::stream::stream::StreamExt; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct NeFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + } +} + +impl NeFuture +where + L::Item: PartialEq, +{ + pub(super) fn new(l: L, r: R) -> Self { + Self { + l: l.fuse(), + r: r.fuse(), + } + } +} + +impl Future for NeFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: PartialEq, +{ + type Output = bool; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx)); + let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx)); + + if this.l.done || this.r.done { + return Poll::Ready(false); + } + + match (l_val, r_val) { + (Some(l), Some(r)) if l == r => { + continue; + } + _ => { + return Poll::Ready(true); + } + } + } + } +} diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs index de75f5e93..7bd208320 100644 --- a/src/stream/stream/next.rs +++ b/src/stream/stream/next.rs @@ -1,6 +1,6 @@ -use std::pin::Pin; +use core::pin::Pin; +use core::future::Future; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index e7e042a95..8cdabb6c4 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -1,7 +1,7 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::future::Future; -use crate::future::Future; use crate::stream::Stream; #[doc(hidden)] @@ -15,7 +15,7 @@ impl Unpin for NthFuture<'_, S> {} impl<'a, S> NthFuture<'a, S> { pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { - NthFuture { stream, n } + Self { stream, n } } } diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index fac2705dc..c328a9119 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,31 +1,32 @@ -use std::cmp::Ordering; -use std::pin::Pin; +use core::cmp::Ordering; +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; use super::fuse::Fuse; -use crate::future::Future; -use crate::prelude::*; +use crate::stream::stream::StreamExt; use crate::stream::Stream; use crate::task::{Context, Poll}; -// Lexicographically compares the elements of this `Stream` with those -// of another. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct PartialCmpFuture { - l: Fuse, - r: Fuse, - l_cache: Option, - r_cache: Option, +pin_project! { + // Lexicographically compares the elements of this `Stream` with those + // of another. + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct PartialCmpFuture { + #[pin] + l: Fuse, + #[pin] + r: Fuse, + l_cache: Option, + r_cache: Option, + } } impl PartialCmpFuture { - pin_utils::unsafe_pinned!(l: Fuse); - pin_utils::unsafe_pinned!(r: Fuse); - pin_utils::unsafe_unpinned!(l_cache: Option); - pin_utils::unsafe_unpinned!(r_cache: Option); - pub(super) fn new(l: L, r: R) -> Self { - PartialCmpFuture { + Self { l: l.fuse(), r: r.fuse(), l_cache: None, @@ -42,12 +43,13 @@ where { type Output = Option; - 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 { // Short circuit logic // Stream that completes earliest can be considered Less, etc - let l_complete = self.l.done && self.as_mut().l_cache.is_none(); - let r_complete = self.r.done && self.as_mut().r_cache.is_none(); + let l_complete = this.l.done && this.l_cache.is_none(); + let r_complete = this.r.done && this.r_cache.is_none(); if l_complete && r_complete { return Poll::Ready(Some(Ordering::Equal)); @@ -57,31 +59,31 @@ where return Poll::Ready(Some(Ordering::Greater)); } - // Get next value if possible and necesary - if !self.l.done && self.as_mut().l_cache.is_none() { - let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); + // Get next value if possible and necessary + if !this.l.done && this.l_cache.is_none() { + let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx)); if let Some(item) = l_next { - *self.as_mut().l_cache() = Some(item); + *this.l_cache = Some(item); } } - if !self.r.done && self.as_mut().r_cache.is_none() { - let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); + if !this.r.done && this.r_cache.is_none() { + let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx)); if let Some(item) = r_next { - *self.as_mut().r_cache() = Some(item); + *this.r_cache = Some(item); } } // Compare if both values are available. - if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { - let l_value = self.as_mut().l_cache().take().unwrap(); - let r_value = self.as_mut().r_cache().take().unwrap(); + if this.l_cache.is_some() && this.r_cache.is_some() { + let l_value = this.l_cache.as_mut().take().unwrap(); + let r_value = this.r_cache.as_mut().take().unwrap(); let result = l_value.partial_cmp(&r_value); if let Some(Ordering::Equal) = result { // Reset cache to prepare for next comparison - *self.as_mut().l_cache() = None; - *self.as_mut().r_cache() = None; + *this.l_cache = None; + *this.r_cache = None; } else { // Return non equal value return Poll::Ready(result); diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs new file mode 100644 index 000000000..69268ecb8 --- /dev/null +++ b/src/stream/stream/partition.rs @@ -0,0 +1,59 @@ +use pin_project_lite::pin_project; +use core::default::Default; +use core::future::Future; +use core::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[derive(Debug)] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub struct PartitionFuture { + #[pin] + stream: S, + f: F, + res: Option<(B, B)>, + } +} + +impl PartitionFuture { + pub(super) fn new(stream: S, f: F) -> Self { + Self { + stream, + f, + res: Some((B::default(), B::default())), + } + } +} + +impl Future for PartitionFuture +where + S: Stream + Sized, + F: FnMut(&S::Item) -> bool, + B: Default + Extend, +{ + type Output = (B, B); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some(v) => { + let res = this.res.as_mut().unwrap(); + + if (this.f)(&v) { + res.0.extend(Some(v)) + } else { + res.1.extend(Some(v)) + } + } + None => return Poll::Ready(this.res.take().unwrap()), + } + } + } +} diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs new file mode 100644 index 000000000..2811b6d86 --- /dev/null +++ b/src/stream/stream/position.rs @@ -0,0 +1,50 @@ +use core::future::Future; +use core::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct PositionFuture<'a, S, P> { + stream: &'a mut S, + predicate: P, + index: usize, +} + +impl<'a, S, P> Unpin for PositionFuture<'a, S, P> {} + +impl<'a, S, P> PositionFuture<'a, S, P> { + pub(super) fn new(stream: &'a mut S, predicate: P) -> Self { + Self { + stream, + predicate, + index: 0, + } + } +} + +impl<'a, S, P> Future for PositionFuture<'a, S, P> +where + S: Stream + Unpin, + P: FnMut(S::Item) -> bool, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); + + match next { + Some(v) => { + if (&mut self.predicate)(v) { + Poll::Ready(Some(self.index)) + } else { + cx.waker().wake_by_ref(); + self.index += 1; + Poll::Pending + } + } + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 78975161b..d72b0dc23 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,13 +1,24 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to maintain state while polling another stream. -#[derive(Debug)] -pub struct Scan { - stream: S, - state_f: (St, F), +pin_project! { + /// A stream to maintain state while polling another stream. + /// + /// This `struct` is created by the [`scan`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`scan`]: trait.Stream.html#method.scan + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct Scan { + #[pin] + stream: S, + state_f: (St, F), + } } impl Scan { @@ -17,13 +28,8 @@ impl Scan { state_f: (initial_state, f), } } - - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(state_f: (St, F)); } -impl Unpin for Scan {} - impl Stream for Scan where S: Stream, @@ -31,11 +37,12 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let poll_result = self.as_mut().stream().poll_next(cx); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + let poll_result = this.stream.as_mut().poll_next(cx); poll_result.map(|item| { item.and_then(|item| { - let (state, f) = self.as_mut().state_f(); + let (state, f) = this.state_f; f(state, item) }) }) diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index 8a2d966d8..52b137d06 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -1,21 +1,29 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use pin_project_lite::pin_project; use crate::stream::Stream; -/// A stream to skip first n elements of another stream. -#[derive(Debug)] -pub struct Skip { - stream: S, - n: usize, +pin_project! { + /// A stream to skip first n elements of another stream. + /// + /// This `struct` is created by the [`skip`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`skip`]: trait.Stream.html#method.skip + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct Skip { + #[pin] + stream: S, + n: usize, + } } impl Skip { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(n: usize); - pub(crate) fn new(stream: S, n: usize) -> Self { - Skip { stream, n } + Self { stream, n } } } @@ -25,14 +33,15 @@ where { type Item = S::Item; - 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(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.n { + Some(v) => match *this.n { 0 => return Poll::Ready(Some(v)), - _ => *self.as_mut().n() -= 1, + _ => *this.n -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index b1a8d9eaa..d139de4d0 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,46 +1,52 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream to skip elements of another stream based on a predicate. -#[derive(Debug)] -pub struct SkipWhile { - stream: S, - predicate: Option

, - __t: PhantomData, +pin_project! { + /// A stream to skip elements of another stream based on a predicate. + /// + /// This `struct` is created by the [`skip_while`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`skip_while`]: trait.Stream.html#method.skip_while + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct SkipWhile { + #[pin] + stream: S, + predicate: Option

, + } } -impl SkipWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: Option

); - +impl SkipWhile { pub(crate) fn new(stream: S, predicate: P) -> Self { - SkipWhile { + Self { stream, predicate: Some(predicate), - __t: PhantomData, } } } -impl Stream for SkipWhile +impl Stream for SkipWhile where S: Stream, P: FnMut(&S::Item) -> bool, { type Item = S::Item; - 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(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.as_mut().predicate() { + Some(v) => match this.predicate { Some(p) => { if !p(&v) { - *self.as_mut().predicate() = None; + *this.predicate = None; return Poll::Ready(Some(v)); } } diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs index f84feecd3..3dd3d6259 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -1,23 +1,30 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that steps a given amount of elements of another stream. -#[derive(Debug)] -pub struct StepBy { - stream: S, - step: usize, - i: usize, +pin_project! { + /// A stream that steps a given amount of elements of another stream. + /// + /// This `struct` is created by the [`step_by`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`step_by`]: trait.Stream.html#method.step_by + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct StepBy { + #[pin] + stream: S, + step: usize, + i: usize, + } } impl StepBy { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(step: usize); - pin_utils::unsafe_unpinned!(i: usize); - pub(crate) fn new(stream: S, step: usize) -> Self { - StepBy { + Self { stream, step: step.checked_sub(1).unwrap(), i: 0, @@ -31,17 +38,18 @@ where { type Item = S::Item; - 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(); loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match next { - Some(v) => match self.i { + Some(v) => match this.i { 0 => { - *self.as_mut().i() = self.step; + *this.i = *this.step; return Poll::Ready(Some(v)); } - _ => *self.as_mut().i() -= 1, + _ => *this.i -= 1, }, None => return Poll::Ready(None), } diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 81d48d23e..ffc3e9935 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,33 +1,47 @@ -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields the first `n` items of another stream. -#[derive(Clone, Debug)] -pub struct Take { - pub(crate) stream: S, - pub(crate) remaining: usize, +pin_project! { + /// A stream that yields the first `n` items of another stream. + /// + /// This `struct` is created by the [`take`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`take`]: trait.Stream.html#method.take + /// [`Stream`]: trait.Stream.html + #[derive(Clone, Debug)] + pub struct Take { + #[pin] + pub(crate) stream: S, + pub(crate) remaining: usize, + } } -impl Unpin for Take {} - -impl Take { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(remaining: usize); +impl Take { + pub(super) fn new(stream: S, remaining: usize) -> Self { + Self { + stream, + remaining, + } + } } impl Stream for Take { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.remaining == 0 { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if *this.remaining == 0 { Poll::Ready(None) } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + Some(_) => *this.remaining -= 1, + None => *this.remaining = 0, } Poll::Ready(next) } diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs index 6f3cc8f2d..60eb8c517 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,45 +1,53 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A stream that yields elements based on a predicate. -#[derive(Debug)] -pub struct TakeWhile { - stream: S, - predicate: P, - __t: PhantomData, +pin_project! { + /// A stream that yields elements based on a predicate. + /// + /// This `struct` is created by the [`take_while`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`take_while`]: trait.Stream.html#method.take_while + /// [`Stream`]: trait.Stream.html + #[derive(Debug)] + pub struct TakeWhile { + #[pin] + stream: S, + predicate: P, + } } -impl TakeWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - +impl TakeWhile { pub(super) fn new(stream: S, predicate: P) -> Self { - TakeWhile { + Self { stream, predicate, - __t: PhantomData, } } } -impl Stream for TakeWhile +impl Stream for TakeWhile where S: Stream, P: FnMut(&S::Item) -> bool, { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + let next = futures_core::ready!(this.stream.poll_next(cx)); match next { - Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), - Some(_) => { - cx.waker().wake_by_ref(); - Poll::Pending + Some(v) => { + if (this.predicate)(&v) { + Poll::Ready(Some(v)) + } else { + Poll::Ready(None) + } } None => Poll::Ready(None), } diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs new file mode 100644 index 000000000..d0e2cdd14 --- /dev/null +++ b/src/stream/stream/throttle.rs @@ -0,0 +1,67 @@ +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use crate::utils::{timer_after, Timer}; + +pin_project! { + /// A stream that only yields one element once every `duration`. + /// + /// This `struct` is created by the [`throttle`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`throttle`]: trait.Stream.html#method.throttle + /// [`Stream`]: trait.Stream.html + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Throttle { + #[pin] + stream: S, + duration: Duration, + #[pin] + blocked: bool, + #[pin] + delay: Timer, + } +} + +impl Throttle { + pub(super) fn new(stream: S, duration: Duration) -> Self { + Self { + stream, + duration, + blocked: false, + delay: timer_after(Duration::default()), + } + } +} + +impl Stream for Throttle { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if *this.blocked { + let d = this.delay.as_mut(); + if d.poll(cx).is_ready() { + *this.blocked = false; + } else { + return Poll::Pending; + } + } + + match this.stream.poll_next(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), + Poll::Ready(Some(v)) => { + *this.blocked = true; + let _ = std::mem::replace(&mut *this.delay, timer_after(*this.duration)); + Poll::Ready(Some(v)) + } + } + } +} diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs new file mode 100644 index 000000000..ec15728b8 --- /dev/null +++ b/src/stream/stream/timeout.rs @@ -0,0 +1,68 @@ +use std::error::Error; +use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::time::Duration; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; +use crate::utils::{timer_after, Timer}; + +pin_project! { + /// A stream with timeout time set + #[derive(Debug)] + pub struct Timeout { + #[pin] + stream: S, + #[pin] + delay: Timer, + duration: Duration, + } +} + +impl Timeout { + pub(crate) fn new(stream: S, dur: Duration) -> Self { + let delay = timer_after(dur); + + Self { stream, delay, duration: dur } + } +} + +impl Stream for Timeout { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + let r = match this.stream.poll_next(cx) { + Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => match this.delay.as_mut().poll(cx) { + Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError { _private: () }))), + Poll::Pending => return Poll::Pending, + }, + }; + + *this.delay.as_mut() = timer_after(*this.duration); + + r + } +} + +/// An error returned when a stream times out. +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct TimeoutError { + _private: (), +} + +impl Error for TimeoutError {} + +impl fmt::Display for TimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "stream has timed out".fmt(f) + } +} diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs index 212b05891..73312ff78 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,5 +1,4 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::pin::Pin; use crate::future::Future; use crate::stream::Stream; @@ -7,52 +6,46 @@ use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct TryFoldFuture { - stream: S, +pub struct TryFoldFuture<'a, S, F, T> { + stream: &'a mut S, f: F, acc: Option, - __t: PhantomData, } -impl TryFoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); +impl<'a, S, F, T> Unpin for TryFoldFuture<'a, S, F, T> {} - pub(super) fn new(stream: S, init: T, f: F) -> Self { - TryFoldFuture { +impl<'a, S, F, T> TryFoldFuture<'a, S, F, T> { + pub(super) fn new(stream: &'a mut S, init: T, f: F) -> Self { + Self { stream, f, acc: Some(init), - __t: PhantomData, } } } -impl Future for TryFoldFuture +impl<'a, S, F, T, E> Future for TryFoldFuture<'a, S, F, T> where - S: Stream + Sized, + S: Stream + Unpin, F: FnMut(T, S::Item) -> Result, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); match next { Some(v) => { - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); + let old = self.acc.take().unwrap(); + let new = (&mut self.f)(old, v); match new { - Ok(o) => { - *self.as_mut().acc() = Some(o); - } + Ok(o) => self.acc = Some(o), Err(e) => return Poll::Ready(Err(e)), } } - None => return Poll::Ready(Ok(self.as_mut().acc().take().unwrap())), + None => return Poll::Ready(Ok(self.acc.take().unwrap())), } } } diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index ae3d5ea5b..917bfd16e 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,49 +1,39 @@ -use std::marker::PhantomData; -use std::pin::Pin; +use core::future::Future; +use core::pin::Pin; -use crate::future::Future; use crate::stream::Stream; use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] -pub struct TryForEeachFuture { - stream: S, +pub struct TryForEachFuture<'a, S, F> { + stream: &'a mut S, f: F, - __from: PhantomData, - __to: PhantomData, } -impl TryForEeachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); +impl<'a, S, F> Unpin for TryForEachFuture<'a, S, F> {} - pub(crate) fn new(stream: S, f: F) -> Self { - TryForEeachFuture { - stream, - f, - __from: PhantomData, - __to: PhantomData, - } +impl<'a, S, F> TryForEachFuture<'a, S, F> { + pub(crate) fn new(stream: &'a mut S, f: F) -> Self { + Self { stream, f } } } -impl Future for TryForEeachFuture +impl<'a, S, F, E> Future for TryForEachFuture<'a, S, F> where - S: Stream, - S::Item: std::fmt::Debug, + S: Stream + Unpin, F: FnMut(S::Item) -> Result<(), E>, { type Output = Result<(), E>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); match item { None => return Poll::Ready(Ok(())), Some(v) => { - let res = (self.as_mut().f())(v); + let res = (&mut self.f)(v); if let Err(e) = res { return Poll::Ready(Err(e)); } diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs new file mode 100644 index 000000000..7771509a5 --- /dev/null +++ b/src/stream/stream/unzip.rs @@ -0,0 +1,57 @@ +use core::future::Future; +use core::pin::Pin; + +use pin_project_lite::pin_project; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +pin_project! { + #[derive(Clone, Debug)] + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + pub struct UnzipFuture { + #[pin] + stream: S, + res: Option<(FromA, FromB)>, + } +} + +impl UnzipFuture +where + FromA: Default, + FromB: Default, +{ + pub(super) fn new(stream: S) -> Self { + UnzipFuture { + stream, + res: Some((FromA::default(), FromB::default())), + } + } +} + +impl Future for UnzipFuture +where + S: Stream, + FromA: Default + Extend, + FromB: Default + Extend, +{ + type Output = (FromA, FromB); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + let next = futures_core::ready!(this.stream.as_mut().poll_next(cx)); + + match next { + Some((a, b)) => { + let res = this.res.as_mut().unwrap(); + res.0.extend(Some(a)); + res.1.extend(Some(b)); + } + None => return Poll::Ready(this.res.take().unwrap()), + } + } + } +} diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 4c66aefd0..83d613c8c 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -1,14 +1,26 @@ -use std::fmt; -use std::pin::Pin; +use core::fmt; +use core::pin::Pin; + +use pin_project_lite::pin_project; use crate::stream::Stream; use crate::task::{Context, Poll}; -/// An iterator that iterates two other iterators simultaneously. -pub struct Zip { - item_slot: Option, - first: A, - second: B, +pin_project! { + /// A stream that takes items from two other streams simultaneously. + /// + /// This `struct` is created by the [`zip`] method on [`Stream`]. See its + /// documentation for more. + /// + /// [`zip`]: trait.Stream.html#method.zip + /// [`Stream`]: trait.Stream.html + pub struct Zip { + item_slot: Option, + #[pin] + first: A, + #[pin] + second: B, + } } impl fmt::Debug for Zip { @@ -20,35 +32,30 @@ impl fmt::Debug for Zip { } } -impl Unpin for Zip {} - impl Zip { pub(crate) fn new(first: A, second: B) -> Self { - Zip { + Self { item_slot: None, first, second, } } - - pin_utils::unsafe_unpinned!(item_slot: Option); - pin_utils::unsafe_pinned!(first: A); - pin_utils::unsafe_pinned!(second: B); } impl Stream for Zip { type Item = (A::Item, B::Item); - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.as_mut().item_slot().is_none() { - match self.as_mut().first().poll_next(cx) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if this.item_slot.is_none() { + match this.first.poll_next(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(None) => return Poll::Ready(None), - Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item), + Poll::Ready(Some(item)) => *this.item_slot = Some(item), } } - let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx)); - let first_item = self.as_mut().item_slot().take().unwrap(); + let second_item = futures_core::ready!(this.second.poll_next(cx)); + let first_item = this.item_slot.take().unwrap(); Poll::Ready(second_item.map(|second_item| (first_item, second_item))) } } diff --git a/src/stream/successors.rs b/src/stream/successors.rs new file mode 100644 index 000000000..eb0e4bf48 --- /dev/null +++ b/src/stream/successors.rs @@ -0,0 +1,77 @@ +use core::mem; +use core::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +/// Creates a new stream where to produce each new element a closure is called with the previous +/// value. +/// +/// # Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let mut s = stream::successors(Some(22), |&val| Some(val + 1)); +/// +/// assert_eq!(s.next().await, Some(22)); +/// assert_eq!(s.next().await, Some(23)); +/// assert_eq!(s.next().await, Some(24)); +/// assert_eq!(s.next().await, Some(25)); +/// +/// # +/// # }) } +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub fn successors(first: Option, succ: F) -> Successors +where + F: FnMut(&T) -> Option, +{ + Successors { succ, slot: first } +} + +pin_project! { + /// A stream that yields elements by calling an async closure with the previous value as an + /// argument + /// + /// This stream is constructed by [`successors`] function + /// + /// [`successors`]: fn.successors.html + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[derive(Debug)] + pub struct Successors + where + F: FnMut(&T) -> Option + { + succ: F, + slot: Option, + } +} + +impl Stream for Successors +where + F: FnMut(&T) -> Option, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + if this.slot.is_none() { + return Poll::Ready(None); + } + + let mut next = (this.succ)(&this.slot.as_ref().unwrap()); + + // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration + mem::swap(this.slot, &mut next); + Poll::Ready(next) + } +} diff --git a/src/stream/sum.rs b/src/stream/sum.rs index a87ade1a4..65f6e8881 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -1,4 +1,6 @@ -use crate::future::Future; +use core::future::Future; +use core::pin::Pin; + use crate::stream::Stream; /// Trait to represent types that can be created by summing up a stream. @@ -16,8 +18,48 @@ use crate::stream::Stream; pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. - fn sum(stream: S) -> F + fn sum<'a, S>(stream: S) -> Pin + 'a>> where - S: Stream, - F: Future; + S: Stream + 'a; } + +use crate::stream::stream::StreamExt; +use core::num::Wrapping; +use core::ops::Add; + +macro_rules! num_sum { + ($zero:expr, $($a:ty)*) => ($( + impl Sum for $a { + fn sum<'a, S>(stream: S) -> Pin+ 'a>> + where + S: Stream + 'a, + { + Box::pin(async move { stream.fold($zero, Add::add).await } ) + } + } + impl<'a> Sum<&'a $a> for $a { + fn sum<'b, S>(stream: S) -> Pin + 'b>> + where + S: Stream + 'b, + { + Box::pin(async move { stream.fold($zero, Add::add).await } ) + } + } + )*); +} + +macro_rules! integer_sum { + ($($a:ty)*) => ( + num_sum!(0, $($a)*); + num_sum!(Wrapping(0), $(Wrapping<$a>)*); + ); +} + +macro_rules! float_sum { + ($($a:ty)*) => ( + num_sum!(0.0, $($a)*); + ); +} + +integer_sum! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +float_sum! { f32 f64 } diff --git a/src/string/extend.rs b/src/string/extend.rs index 8572cc3ce..eae824cbd 100644 --- a/src/string/extend.rs +++ b/src/string/extend.rs @@ -2,65 +2,105 @@ use std::borrow::Cow; use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); - self.reserve(stream.size_hint().0); - Box::pin(stream.for_each(move |c| self.push(c))) + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push(item); + } + }) } } -impl<'b> Extend<&'b char> for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl<'b> stream::Extend<&'b char> for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, - //TODO: Remove the underscore when uncommenting the body of this impl - _stream: S, - ) -> Pin + 'a>> + stream: S, + ) -> Pin + 'a + Send>> where - 'b: 'a, + ::IntoStream: Send, { - //TODO: This can be uncommented when `copied` is added to Stream/StreamExt - //Box::pin(stream.into_stream().copied()) - unimplemented!() + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push(*item); + } + }) } } -impl<'b> Extend<&'b str> for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl<'b> stream::Extend<&'b str> for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - 'b: 'a, + ::IntoStream: Send, { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(s))) + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(item); + } + }) } } -impl Extend for String { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for String { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s))) + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(&item); + } + }) } } -impl<'b> Extend> for String { - fn stream_extend<'a, S: IntoStream> + 'a>( +impl<'b> stream::Extend> for String { + fn extend<'a, S: IntoStream> + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - 'b: 'a, + ::IntoStream: Send, { - Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s))) + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(item) = stream.next().await { + self.push_str(&item); + } + }) } } diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs index 276d13a73..d9e824681 100644 --- a/src/string/from_stream.rs +++ b/src/string/from_stream.rs @@ -1,23 +1,22 @@ use std::borrow::Cow; use std::pin::Pin; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; impl FromStream for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -25,19 +24,17 @@ impl FromStream for String { impl<'b> FromStream<&'b char> for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -45,19 +42,17 @@ impl<'b> FromStream<&'b char> for String { impl<'b> FromStream<&'b str> for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -65,19 +60,17 @@ impl<'b> FromStream<&'b str> for String { impl FromStream for String { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } @@ -85,19 +78,17 @@ impl FromStream for String { impl<'b> FromStream> for String { #[inline] - fn from_stream<'a, S: IntoStream>>( + fn from_stream<'a, S: IntoStream> + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = String::new(); - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } diff --git a/src/string/mod.rs b/src/string/mod.rs index e382fcf2c..3f6141fa3 100644 --- a/src/string/mod.rs +++ b/src/string/mod.rs @@ -5,5 +5,6 @@ mod extend; mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::string::String; diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs deleted file mode 100644 index 080eff8c4..000000000 --- a/src/sync/barrier.rs +++ /dev/null @@ -1,261 +0,0 @@ -use broadcaster::BroadcastChannel; - -use crate::sync::Mutex; - -/// A barrier enables multiple tasks to synchronize the beginning -/// of some computation. -/// -/// # Examples -/// -/// ``` -/// # fn main() { async_std::task::block_on(async { -/// # -/// use async_std::sync::{Arc, Barrier}; -/// use async_std::task; -/// -/// let mut handles = Vec::with_capacity(10); -/// let barrier = Arc::new(Barrier::new(10)); -/// for _ in 0..10 { -/// let c = barrier.clone(); -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// handles.push(task::spawn(async move { -/// println!("before wait"); -/// c.wait().await; -/// println!("after wait"); -/// })); -/// } -/// // Wait for the other futures to finish. -/// for handle in handles { -/// handle.await; -/// } -/// # }); -/// # } -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug)] -pub struct Barrier { - state: Mutex, - wait: BroadcastChannel<(usize, usize)>, - n: usize, -} - -// The inner state of a double barrier -#[derive(Debug)] -struct BarrierState { - waker: BroadcastChannel<(usize, usize)>, - count: usize, - generation_id: usize, -} - -/// A `BarrierWaitResult` is returned by `wait` when all threads in the `Barrier` have rendezvoused. -/// -/// [`wait`]: struct.Barrier.html#method.wait -/// [`Barrier`]: struct.Barrier.html -/// -/// # Examples -/// -/// ``` -/// use async_std::sync::Barrier; -/// -/// let barrier = Barrier::new(1); -/// let barrier_wait_result = barrier.wait(); -/// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[derive(Debug, Clone)] -pub struct BarrierWaitResult(bool); - -impl Barrier { - /// Creates a new barrier that can block a given number of tasks. - /// - /// A barrier will block `n`-1 tasks which call [`wait`] and then wake up - /// all tasks at once when the `n`th task calls [`wait`]. - /// - /// [`wait`]: #method.wait - /// - /// # Examples - /// - /// ``` - /// use std::sync::Barrier; - /// - /// let barrier = Barrier::new(10); - /// ``` - pub fn new(mut n: usize) -> Barrier { - let waker = BroadcastChannel::new(); - let wait = waker.clone(); - - if n == 0 { - // if n is 0, it's not clear what behavior the user wants. - // in std::sync::Barrier, an n of 0 exhibits the same behavior as n == 1, where every - // .wait() immediately unblocks, so we adopt that here as well. - n = 1; - } - - Barrier { - state: Mutex::new(BarrierState { - waker, - count: 0, - generation_id: 1, - }), - n, - wait, - } - } - - /// Blocks the current task until all tasks have rendezvoused here. - /// - /// Barriers are re-usable after all tasks have rendezvoused once, and can - /// be used continuously. - /// - /// A single (arbitrary) task will receive a [`BarrierWaitResult`] that - /// returns `true` from [`is_leader`] when returning from this function, and - /// all other tasks will receive a result that will return `false` from - /// [`is_leader`]. - /// - /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html - /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::sync::{Arc, Barrier}; - /// use async_std::task; - /// - /// let mut handles = Vec::with_capacity(10); - /// let barrier = Arc::new(Barrier::new(10)); - /// for _ in 0..10 { - /// let c = barrier.clone(); - /// // The same messages will be printed together. - /// // You will NOT see any interleaving. - /// handles.push(task::spawn(async move { - /// println!("before wait"); - /// c.wait().await; - /// println!("after wait"); - /// })); - /// } - /// // Wait for the other futures to finish. - /// for handle in handles { - /// handle.await; - /// } - /// # }); - /// # } - /// ``` - pub async fn wait(&self) -> BarrierWaitResult { - let mut lock = self.state.lock().await; - let local_gen = lock.generation_id; - - lock.count += 1; - - if lock.count < self.n { - let mut wait = self.wait.clone(); - - let mut generation_id = lock.generation_id; - let mut count = lock.count; - - drop(lock); - - while local_gen == generation_id && count < self.n { - let (g, c) = wait.recv().await.expect("sender has not been closed"); - generation_id = g; - count = c; - } - - BarrierWaitResult(false) - } else { - lock.count = 0; - lock.generation_id = lock.generation_id.wrapping_add(1); - - lock.waker - .send(&(lock.generation_id, lock.count)) - .await - .expect("there should be at least one receiver"); - - BarrierWaitResult(true) - } - } -} - -impl BarrierWaitResult { - /// Returns `true` if this task from [`wait`] is the "leader task". - /// - /// Only one task will have `true` returned from their result, all other - /// tasks will have `false` returned. - /// - /// [`wait`]: struct.Barrier.html#method.wait - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::sync::Barrier; - /// - /// let barrier = Barrier::new(1); - /// let barrier_wait_result = barrier.wait().await; - /// println!("{:?}", barrier_wait_result.is_leader()); - /// # }); - /// # } - /// ``` - pub fn is_leader(&self) -> bool { - self.0 - } -} - -#[cfg(test)] -mod test { - use futures_channel::mpsc::unbounded; - use futures_util::sink::SinkExt; - use futures_util::stream::StreamExt; - - use crate::sync::{Arc, Barrier}; - use crate::task; - - #[test] - fn test_barrier() { - // NOTE(dignifiedquire): Based on the test in std, I was seeing some - // race conditions, so running it in a loop to make sure things are - // solid. - - for _ in 0..1_000 { - task::block_on(async move { - const N: usize = 10; - - let barrier = Arc::new(Barrier::new(N)); - let (tx, mut rx) = unbounded(); - - for _ in 0..N - 1 { - let c = barrier.clone(); - let mut tx = tx.clone(); - task::spawn(async move { - let res = c.wait().await; - - tx.send(res.is_leader()).await.unwrap(); - }); - } - - // At this point, all spawned threads should be blocked, - // so we shouldn't get anything from the port - let res = rx.try_next(); - assert!(match res { - Err(_err) => true, - _ => false, - }); - - let mut leader_found = barrier.wait().await.is_leader(); - - // Now, the barrier is cleared and we should get data. - for _ in 0..N - 1 { - if rx.next().await.unwrap() { - assert!(!leader_found); - leader_found = true; - } - } - assert!(leader_found); - }); - } - } -} diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs new file mode 100644 index 000000000..b7e81ed29 --- /dev/null +++ b/src/sync/condvar.rs @@ -0,0 +1,417 @@ +use std::fmt; +use std::pin::Pin; +use std::time::Duration; + +use super::MutexGuard; +use crate::future::{timeout, Future}; +use crate::sync::WakerSet; +use crate::task::{Context, Poll}; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct WaitTimeoutResult(bool); + +/// A type indicating whether a timed wait on a condition variable returned due to a time out or +/// not +impl WaitTimeoutResult { + /// Returns `true` if the wait was known to have timed out. + pub fn timed_out(self) -> bool { + self.0 + } +} + +/// A Condition Variable +/// +/// This type is an async version of [`std::sync::Condvar`]. +/// +/// [`std::sync::Condvar`]: https://doc.rust-lang.org/std/sync/struct.Condvar.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::sync::Arc; +/// +/// use async_std::sync::{Mutex, Condvar}; +/// use async_std::task; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = pair.clone(); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start. +/// task::spawn(async move { +/// let (lock, cvar) = &*pair2; +/// let mut started = lock.lock().await; +/// *started = true; +/// // We notify the condvar that the value has changed. +/// cvar.notify_one(); +/// }); +/// +/// // Wait for the thread to start up. +/// let (lock, cvar) = &*pair; +/// let mut started = lock.lock().await; +/// while !*started { +/// started = cvar.wait(started).await; +/// } +/// +/// # }) +/// ``` +pub struct Condvar { + wakers: WakerSet, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Default for Condvar { + fn default() -> Self { + Condvar::new() + } +} + +impl Condvar { + /// Creates a new condition variable + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::Condvar; + /// + /// let cvar = Condvar::new(); + /// ``` + pub fn new() -> Self { + Condvar { + wakers: WakerSet::new(), + } + } + + /// Blocks the current task until this condition variable receives a notification. + /// + /// Unlike the std equivalent, this does not check that a single mutex is used at runtime. + /// However, as a best practice avoid using with multiple mutexes. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + let mutex = MutexGuard::source(&guard); + + self.await_notify(guard).await; + + mutex.lock().await + } + + fn await_notify<'a, T>(&self, guard: MutexGuard<'a, T>) -> AwaitNotify<'_, 'a, T> { + AwaitNotify { + cond: self, + guard: Some(guard), + key: None, + } + } + + /// Blocks the current task until this condition variable receives a notification and the + /// required condition is met. Spurious wakeups are ignored and this function will only + /// return once the condition has been met. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// let _guard = cvar.wait_until(lock.lock().await, |started| { *started }).await; + /// # + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_until<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + mut condition: F, + ) -> MutexGuard<'a, T> + where + F: FnMut(&mut T) -> bool, + { + while !condition(&mut *guard) { + guard = self.wait(guard).await; + } + guard + } + + /// Waits on this condition variable for a notification, timing out after a specified duration. + /// + /// For these reasons `Condvar::wait_timeout_until` is recommended in most cases. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// loop { + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).await; + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// # + /// # }) + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_timeout<'a, T>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { + let mutex = MutexGuard::source(&guard); + match timeout(dur, self.wait(guard)).await { + Ok(guard) => (guard, WaitTimeoutResult(false)), + Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), + } + } + + /// Waits on this condition variable for a notification, timing out after a specified duration. + /// Spurious wakes will not cause this function to return. + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use std::sync::Arc; + /// use std::time::Duration; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let result = cvar.wait_timeout_until( + /// lock.lock().await, + /// Duration::from_millis(100), + /// |&mut started| started, + /// ).await; + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to true. + /// } + /// // access the locked mutex via result.0 + /// # }); + /// ``` + #[allow(clippy::needless_lifetimes)] + pub async fn wait_timeout_until<'a, T, F>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + condition: F, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) + where + F: FnMut(&mut T) -> bool, + { + let mutex = MutexGuard::source(&guard); + match timeout(dur, self.wait_until(guard, condition)).await { + Ok(guard) => (guard, WaitTimeoutResult(false)), + Err(_) => (mutex.lock().await, WaitTimeoutResult(true)), + } + } + + /// Wakes up one blocked task on this condvar. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # }) } + /// ``` + pub fn notify_one(&self) { + self.wakers.notify_one(); + } + + /// Wakes up all blocked tasks on this condvar. + /// + /// # Examples + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::sync::Arc; + /// + /// use async_std::sync::{Mutex, Condvar}; + /// use async_std::task; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = pair.clone(); + /// + /// task::spawn(async move { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().await; + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_all(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().await; + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started).await; + /// } + /// # + /// # }) } + /// ``` + pub fn notify_all(&self) { + self.wakers.notify_all(); + } +} + +impl fmt::Debug for Condvar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Condvar { .. }") + } +} + +/// A future that waits for another task to notify the condition variable. +/// +/// This is an internal future that `wait` and `wait_until` await on. +struct AwaitNotify<'a, 'b, T> { + /// The condition variable that we are waiting on + cond: &'a Condvar, + /// The lock used with `cond`. + /// This will be released the first time the future is polled, + /// after registering the context to be notified. + guard: Option>, + /// A key into the conditions variable's `WakerSet`. + /// This is set to the index of the `Waker` for the context each time + /// the future is polled and not completed. + key: Option, +} + +impl<'a, 'b, T> Future for AwaitNotify<'a, 'b, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.guard.take() { + Some(_) => { + self.key = Some(self.cond.wakers.insert(cx)); + // the guard is dropped when we return, which frees the lock + Poll::Pending + } + None => { + if let Some(key) = self.key { + if self.cond.wakers.remove_if_notified(key, cx) { + self.key = None; + Poll::Ready(()) + } else { + Poll::Pending + } + } else { + // This should only happen if it is polled twice after receiving a notification + Poll::Ready(()) + } + } + } + } +} + +impl<'a, 'b, T> Drop for AwaitNotify<'a, 'b, T> { + fn drop(&mut self) { + if let Some(key) = self.key { + self.cond.wakers.cancel(key); + } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index be74d8f7b..1d8579614 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -4,6 +4,148 @@ //! //! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html //! +//! ## The need for synchronization +//! +//! async-std's sync primitives are scheduler-aware, making it possible to +//! `.await` their operations - for example the locking of a [`Mutex`]. +//! +//! Conceptually, a Rust program is a series of operations which will +//! be executed on a computer. The timeline of events happening in the +//! program is consistent with the order of the operations in the code. +//! +//! Consider the following code, operating on some global static variables: +//! +//! ``` +//! static mut A: u32 = 0; +//! static mut B: u32 = 0; +//! static mut C: u32 = 0; +//! +//! fn main() { +//! unsafe { +//! A = 3; +//! B = 4; +//! A = A + B; +//! C = B; +//! println!("{} {} {}", A, B, C); +//! C = A; +//! } +//! } +//! ``` +//! +//! It appears as if some variables stored in memory are changed, an addition +//! is performed, result is stored in `A` and the variable `C` is +//! modified twice. +//! +//! When only a single thread is involved, the results are as expected: +//! the line `7 4 4` gets printed. +//! +//! As for what happens behind the scenes, when optimizations are enabled the +//! final generated machine code might look very different from the code: +//! +//! - The first store to `C` might be moved before the store to `A` or `B`, +//! _as if_ we had written `C = 4; A = 3; B = 4`. +//! +//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored +//! in a temporary location until it gets printed, with the global variable +//! never getting updated. +//! +//! - The final result could be determined just by looking at the code +//! at compile time, so [constant folding] might turn the whole +//! block into a simple `println!("7 4 4")`. +//! +//! The compiler is allowed to perform any combination of these +//! optimizations, as long as the final optimized code, when executed, +//! produces the same results as the one without optimizations. +//! +//! Due to the [concurrency] involved in modern computers, assumptions +//! about the program's execution order are often wrong. Access to +//! global variables can lead to nondeterministic results, **even if** +//! compiler optimizations are disabled, and it is **still possible** +//! to introduce synchronization bugs. +//! +//! Note that thanks to Rust's safety guarantees, accessing global (static) +//! variables requires `unsafe` code, assuming we don't use any of the +//! synchronization primitives in this module. +//! +//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding +//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science) +//! +//! ## Out-of-order execution +//! +//! Instructions can execute in a different order from the one we define, due to +//! various reasons: +//! +//! - The **compiler** reordering instructions: If the compiler can issue an +//! instruction at an earlier point, it will try to do so. For example, it +//! might hoist memory loads at the top of a code block, so that the CPU can +//! start [prefetching] the values from memory. +//! +//! In single-threaded scenarios, this can cause issues when writing +//! signal handlers or certain kinds of low-level code. +//! Use [compiler fences] to prevent this reordering. +//! +//! - A **single processor** executing instructions [out-of-order]: +//! Modern CPUs are capable of [superscalar] execution, +//! i.e., multiple instructions might be executing at the same time, +//! even though the machine code describes a sequential process. +//! +//! This kind of reordering is handled transparently by the CPU. +//! +//! - A **multiprocessor** system executing multiple hardware threads +//! at the same time: In multi-threaded scenarios, you can use two +//! kinds of primitives to deal with synchronization: +//! - [memory fences] to ensure memory accesses are made visible to +//! other CPUs in the right order. +//! - [atomic operations] to ensure simultaneous access to the same +//! memory location doesn't lead to undefined behavior. +//! +//! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching +//! [compiler fences]: https://doc.rust-lang.org/std/sync/atomic/fn.compiler_fence.html +//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution +//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor +//! [memory fences]: https://doc.rust-lang.org/std/sync/atomic/fn.fence.html +//! [atomic operations]: https://doc.rust-lang.org/std/sync/atomic/index.html +//! +//! ## Higher-level synchronization objects +//! +//! Most of the low-level synchronization primitives are quite error-prone and +//! inconvenient to use, which is why async-std also exposes some +//! higher-level synchronization objects. +//! +//! These abstractions can be built out of lower-level primitives. +//! For efficiency, the sync objects in async-std are usually +//! implemented with help from the scheduler, which is +//! able to reschedule the tasks while they are blocked on acquiring +//! a lock. +//! +//! The following is an overview of the available synchronization +//! objects: +//! +//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used +//! in multithreaded environments to prolong the lifetime of some +//! data until all the threads have finished using it. +//! +//! - [`Barrier`]: Ensures multiple threads will wait for each other +//! to reach a point in the program, before continuing execution all +//! together. +//! +//! - [`Mutex`]: Mutual exclusion mechanism, which ensures that at +//! most one task at a time is able to access some data. +//! +//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows +//! multiple readers at the same time, while allowing only one +//! writer at a time. In some cases, this can be more efficient than +//! a mutex. +//! +//! If you're looking for channels, check out +//! [`async_std::channel`][crate::channel]. +//! +//! [`Arc`]: struct.Arc.html +//! [`Barrier`]: struct.Barrier.html +//! [`channel`]: fn.channel.html +//! [`Mutex`]: struct.Mutex.html +//! [`RwLock`]: struct.RwLock.html +//! //! # Examples //! //! Spawn a task that updates an integer protected by a mutex: @@ -11,9 +153,7 @@ //! ``` //! # async_std::task::block_on(async { //! # -//! use std::sync::Arc; -//! -//! use async_std::sync::Mutex; +//! use async_std::sync::{Arc, Mutex}; //! use async_std::task; //! //! let m1 = Arc::new(Mutex::new(0)); @@ -29,16 +169,23 @@ //! # }) //! ``` +#![allow(clippy::needless_doctest_main)] + #[doc(inline)] pub use std::sync::{Arc, Weak}; -pub use mutex::{Mutex, MutexGuard}; -pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +#[doc(inline)] +pub use async_lock::{Mutex, MutexGuard, MutexGuardArc}; -mod mutex; -mod rwlock; +#[doc(inline)] +pub use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard}; cfg_unstable! { - pub use barrier::{Barrier, BarrierWaitResult}; - mod barrier; + pub use async_lock::{Barrier, BarrierWaitResult}; + pub use condvar::Condvar; + pub(crate) use waker_set::WakerSet; + + mod condvar; + + pub(crate) mod waker_set; } diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs deleted file mode 100644 index cd7a3577f..000000000 --- a/src/sync/mutex.rs +++ /dev/null @@ -1,346 +0,0 @@ -use std::cell::UnsafeCell; -use std::fmt; -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use slab::Slab; - -use crate::future::Future; -use crate::task::{Context, Poll, Waker}; - -/// Set if the mutex is locked. -const LOCK: usize = 1; - -/// Set if there are tasks blocked on the mutex. -const BLOCKED: usize = 1 << 1; - -/// A mutual exclusion primitive for protecting shared data. -/// -/// This type is an async version of [`std::sync::Mutex`]. -/// -/// [`std::sync::Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use std::sync::Arc; -/// -/// use async_std::sync::Mutex; -/// use async_std::task; -/// -/// let m = Arc::new(Mutex::new(0)); -/// let mut tasks = vec![]; -/// -/// for _ in 0..10 { -/// let m = m.clone(); -/// tasks.push(task::spawn(async move { -/// *m.lock().await += 1; -/// })); -/// } -/// -/// for t in tasks { -/// t.await; -/// } -/// assert_eq!(*m.lock().await, 10); -/// # -/// # }) -/// ``` -pub struct Mutex { - state: AtomicUsize, - blocked: std::sync::Mutex>>, - value: UnsafeCell, -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Mutex { - /// Creates a new mutex. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// ``` - pub fn new(t: T) -> Mutex { - Mutex { - state: AtomicUsize::new(0), - blocked: std::sync::Mutex::new(Slab::new()), - value: UnsafeCell::new(t), - } - } - - /// Acquires the lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use std::sync::Arc; - /// - /// use async_std::sync::Mutex; - /// use async_std::task; - /// - /// let m1 = Arc::new(Mutex::new(10)); - /// let m2 = m1.clone(); - /// - /// task::spawn(async move { - /// *m1.lock().await = 20; - /// }) - /// .await; - /// - /// assert_eq!(*m2.lock().await, 20); - /// # - /// # }) - /// ``` - pub async fn lock(&self) -> MutexGuard<'_, T> { - pub struct LockFuture<'a, T> { - mutex: &'a Mutex, - opt_key: Option, - acquired: bool, - } - - impl<'a, T> Future for LockFuture<'a, T> { - type Output = MutexGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.mutex.try_lock() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => { - let mut blocked = self.mutex.blocked.lock().unwrap(); - - // Register the current task. - match self.opt_key { - None => { - // Insert a new entry into the list of blocked tasks. - let w = cx.waker().clone(); - let key = blocked.insert(Some(w)); - self.opt_key = Some(key); - - if blocked.len() == 1 { - self.mutex.state.fetch_or(BLOCKED, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked tasks. Just - // reset the waker if it was removed. - if blocked[key].is_none() { - let w = cx.waker().clone(); - blocked[key] = Some(w); - } - } - } - - // Try locking again because it's possible the mutex got unlocked just - // before the current task was registered as a blocked task. - match self.mutex.try_lock() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => Poll::Pending, - } - } - } - } - } - - impl Drop for LockFuture<'_, T> { - fn drop(&mut self) { - if let Some(key) = self.opt_key { - let mut blocked = self.mutex.blocked.lock().unwrap(); - let opt_waker = blocked.remove(key); - - if opt_waker.is_none() && !self.acquired { - // We were awoken but didn't acquire the lock. Wake up another task. - if let Some((_, opt_waker)) = blocked.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } - - if blocked.is_empty() { - self.mutex.state.fetch_and(!BLOCKED, Ordering::Relaxed); - } - } - } - } - - LockFuture { - mutex: self, - opt_key: None, - acquired: false, - } - .await - } - - /// Attempts to acquire the lock. - /// - /// If the lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use std::sync::Arc; - /// - /// use async_std::sync::Mutex; - /// use async_std::task; - /// - /// let m1 = Arc::new(Mutex::new(10)); - /// let m2 = m1.clone(); - /// - /// task::spawn(async move { - /// if let Some(mut guard) = m1.try_lock() { - /// *guard = 20; - /// } else { - /// println!("try_lock failed"); - /// } - /// }) - /// .await; - /// - /// assert_eq!(*m2.lock().await, 20); - /// # - /// # }) - /// ``` - pub fn try_lock(&self) -> Option> { - if self.state.fetch_or(LOCK, Ordering::Acquire) & LOCK == 0 { - Some(MutexGuard(self)) - } else { - None - } - } - - /// Consumes the mutex, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::Mutex; - /// - /// let mutex = Mutex::new(10); - /// assert_eq!(mutex.into_inner(), 10); - /// ``` - pub fn into_inner(self) -> T { - self.value.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the mutex mutably, no actual locking takes place -- the mutable - /// borrow statically guarantees no locks exist. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(0); - /// *mutex.get_mut() = 10; - /// assert_eq!(*mutex.lock().await, 10); - /// # - /// # }) - /// ``` - pub fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } - } -} - -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - f.debug_struct("Mutex") - .field("data", &LockedPlaceholder) - .finish() - } - Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), - } - } -} - -impl From for Mutex { - fn from(val: T) -> Mutex { - Mutex::new(val) - } -} - -impl Default for Mutex { - fn default() -> Mutex { - Mutex::new(Default::default()) - } -} - -/// A guard that releases the lock when dropped. -pub struct MutexGuard<'a, T>(&'a Mutex); - -unsafe impl Send for MutexGuard<'_, T> {} -unsafe impl Sync for MutexGuard<'_, T> {} - -impl Drop for MutexGuard<'_, T> { - fn drop(&mut self) { - let state = self.0.state.fetch_and(!LOCK, Ordering::AcqRel); - - // If there are any blocked tasks, wake one of them up. - if state & BLOCKED != 0 { - let mut blocked = self.0.blocked.lock().unwrap(); - - if let Some((_, opt_waker)) = blocked.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } - } -} - -impl fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for MutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -impl DerefMut for MutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } - } -} diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs deleted file mode 100644 index ed1d2185b..000000000 --- a/src/sync/rwlock.rs +++ /dev/null @@ -1,579 +0,0 @@ -use std::cell::UnsafeCell; -use std::fmt; -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use slab::Slab; - -use crate::future::Future; -use crate::task::{Context, Poll, Waker}; - -/// Set if a write lock is held. -const WRITE_LOCK: usize = 1; - -/// Set if there are read operations blocked on the lock. -const BLOCKED_READS: usize = 1 << 1; - -/// Set if there are write operations blocked on the lock. -const BLOCKED_WRITES: usize = 1 << 2; - -/// The value of a single blocked read contributing to the read count. -const ONE_READ: usize = 1 << 3; - -/// The bits in which the read count is stored. -const READ_COUNT_MASK: usize = !(ONE_READ - 1); - -/// A reader-writer lock for protecting shared data. -/// -/// This type is an async version of [`std::sync::RwLock`]. -/// -/// [`std::sync::RwLock`]: https://doc.rust-lang.org/std/sync/struct.RwLock.html -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::sync::RwLock; -/// -/// let lock = RwLock::new(5); -/// -/// // Multiple read locks can be held at a time. -/// let r1 = lock.read().await; -/// let r2 = lock.read().await; -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// drop((r1, r2)); -/// -/// // Only one write locks can be held at a time. -/// let mut w = lock.write().await; -/// *w += 1; -/// assert_eq!(*w, 6); -/// # -/// # }) -/// ``` -pub struct RwLock { - state: AtomicUsize, - reads: std::sync::Mutex>>, - writes: std::sync::Mutex>>, - value: UnsafeCell, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - /// Creates a new reader-writer lock. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(0); - /// ``` - pub fn new(t: T) -> RwLock { - RwLock { - state: AtomicUsize::new(0), - reads: std::sync::Mutex::new(Slab::new()), - writes: std::sync::Mutex::new(Slab::new()), - value: UnsafeCell::new(t), - } - } - - /// Acquires a read lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_read().is_some()); - /// # - /// # }) - /// ``` - pub async fn read(&self) -> RwLockReadGuard<'_, T> { - pub struct LockFuture<'a, T> { - lock: &'a RwLock, - opt_key: Option, - acquired: bool, - } - - impl<'a, T> Future for LockFuture<'a, T> { - type Output = RwLockReadGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.lock.try_read() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => { - let mut reads = self.lock.reads.lock().unwrap(); - - // Register the current task. - match self.opt_key { - None => { - // Insert a new entry into the list of blocked reads. - let w = cx.waker().clone(); - let key = reads.insert(Some(w)); - self.opt_key = Some(key); - - if reads.len() == 1 { - self.lock.state.fetch_or(BLOCKED_READS, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked reads. Just - // reset the waker if it was removed. - if reads[key].is_none() { - let w = cx.waker().clone(); - reads[key] = Some(w); - } - } - } - - // Try locking again because it's possible the lock got unlocked just - // before the current task was registered as a blocked task. - match self.lock.try_read() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => Poll::Pending, - } - } - } - } - } - - impl Drop for LockFuture<'_, T> { - fn drop(&mut self) { - if let Some(key) = self.opt_key { - let mut reads = self.lock.reads.lock().unwrap(); - let opt_waker = reads.remove(key); - - if reads.is_empty() { - self.lock.state.fetch_and(!BLOCKED_READS, Ordering::Relaxed); - } - - if opt_waker.is_none() { - // We were awoken. Wake up another blocked read. - if let Some((_, opt_waker)) = reads.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - drop(reads); - - if !self.acquired { - // We didn't acquire the lock and didn't wake another blocked read. - // Wake a blocked write instead. - let mut writes = self.lock.writes.lock().unwrap(); - if let Some((_, opt_waker)) = writes.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - } - } - } - } - } - - LockFuture { - lock: self, - opt_key: None, - acquired: false, - } - .await - } - - /// Attempts to acquire a read lock. - /// - /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_read().is_some()); - /// # - /// # }) - /// ``` - pub fn try_read(&self) -> Option> { - let mut state = self.state.load(Ordering::Acquire); - - loop { - // If a write lock is currently held, then a read lock cannot be acquired. - if state & WRITE_LOCK != 0 { - return None; - } - - // Increment the number of active reads. - match self.state.compare_exchange_weak( - state, - state + ONE_READ, - Ordering::AcqRel, - Ordering::Acquire, - ) { - Ok(_) => return Some(RwLockReadGuard(self)), - Err(s) => state = s, - } - } - } - - /// Acquires a write lock. - /// - /// Returns a guard that releases the lock when dropped. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let mut n = lock.write().await; - /// *n = 2; - /// - /// assert!(lock.try_read().is_none()); - /// # - /// # }) - /// ``` - pub async fn write(&self) -> RwLockWriteGuard<'_, T> { - pub struct LockFuture<'a, T> { - lock: &'a RwLock, - opt_key: Option, - acquired: bool, - } - - impl<'a, T> Future for LockFuture<'a, T> { - type Output = RwLockWriteGuard<'a, T>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.lock.try_write() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => { - let mut writes = self.lock.writes.lock().unwrap(); - - // Register the current task. - match self.opt_key { - None => { - // Insert a new entry into the list of blocked writes. - let w = cx.waker().clone(); - let key = writes.insert(Some(w)); - self.opt_key = Some(key); - - if writes.len() == 1 { - self.lock.state.fetch_or(BLOCKED_WRITES, Ordering::Relaxed); - } - } - Some(key) => { - // There is already an entry in the list of blocked writes. Just - // reset the waker if it was removed. - if writes[key].is_none() { - let w = cx.waker().clone(); - writes[key] = Some(w); - } - } - } - - // Try locking again because it's possible the lock got unlocked just - // before the current task was registered as a blocked task. - match self.lock.try_write() { - Some(guard) => { - self.acquired = true; - Poll::Ready(guard) - } - None => Poll::Pending, - } - } - } - } - } - - impl Drop for LockFuture<'_, T> { - fn drop(&mut self) { - if let Some(key) = self.opt_key { - let mut writes = self.lock.writes.lock().unwrap(); - let opt_waker = writes.remove(key); - - if writes.is_empty() { - self.lock - .state - .fetch_and(!BLOCKED_WRITES, Ordering::Relaxed); - } - - if opt_waker.is_none() && !self.acquired { - // We were awoken but didn't acquire the lock. Wake up another write. - if let Some((_, opt_waker)) = writes.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - drop(writes); - - // There are no blocked writes. Wake a blocked read instead. - let mut reads = self.lock.reads.lock().unwrap(); - if let Some((_, opt_waker)) = reads.iter_mut().next() { - if let Some(w) = opt_waker.take() { - w.wake(); - return; - } - } - } - } - } - } - - LockFuture { - lock: self, - opt_key: None, - acquired: false, - } - .await - } - - /// Attempts to acquire a write lock. - /// - /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, a - /// guard is returned that releases the lock when dropped. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().await; - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_write().is_none()); - /// # - /// # }) - /// ``` - pub fn try_write(&self) -> Option> { - let mut state = self.state.load(Ordering::Acquire); - - loop { - // If any kind of lock is currently held, then a write lock cannot be acquired. - if state & (WRITE_LOCK | READ_COUNT_MASK) != 0 { - return None; - } - - // Set the write lock. - match self.state.compare_exchange_weak( - state, - state | WRITE_LOCK, - Ordering::AcqRel, - Ordering::Acquire, - ) { - Ok(_) => return Some(RwLockWriteGuard(self)), - Err(s) => state = s, - } - } - } - - /// Consumes the lock, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// use async_std::sync::RwLock; - /// - /// let lock = RwLock::new(10); - /// assert_eq!(lock.into_inner(), 10); - /// ``` - pub fn into_inner(self) -> T { - self.value.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the lock mutably, no actual locking takes place -- the mutable - /// borrow statically guarantees no locks exist. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::sync::RwLock; - /// - /// let mut lock = RwLock::new(0); - /// *lock.get_mut() = 10; - /// assert_eq!(*lock.write().await, 10); - /// # - /// # }) - /// ``` - pub fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } - } -} - -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_read() { - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - f.debug_struct("RwLock") - .field("data", &LockedPlaceholder) - .finish() - } - Some(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), - } - } -} - -impl From for RwLock { - fn from(val: T) -> RwLock { - RwLock::new(val) - } -} - -impl Default for RwLock { - fn default() -> RwLock { - RwLock::new(Default::default()) - } -} - -/// A guard that releases the read lock when dropped. -pub struct RwLockReadGuard<'a, T>(&'a RwLock); - -unsafe impl Send for RwLockReadGuard<'_, T> {} -unsafe impl Sync for RwLockReadGuard<'_, T> {} - -impl Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - let state = self.0.state.fetch_sub(ONE_READ, Ordering::AcqRel); - - // If this was the last read and there are blocked writes, wake one of them up. - if (state & READ_COUNT_MASK) == ONE_READ && state & BLOCKED_WRITES != 0 { - let mut writes = self.0.writes.lock().unwrap(); - - if let Some((_, opt_waker)) = writes.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } - } -} - -impl fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -/// A guard that releases the write lock when dropped. -pub struct RwLockWriteGuard<'a, T>(&'a RwLock); - -unsafe impl Send for RwLockWriteGuard<'_, T> {} -unsafe impl Sync for RwLockWriteGuard<'_, T> {} - -impl Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - let state = self.0.state.fetch_and(!WRITE_LOCK, Ordering::AcqRel); - - let mut guard = None; - - // Check if there are any blocked reads or writes. - if state & BLOCKED_READS != 0 { - guard = Some(self.0.reads.lock().unwrap()); - } else if state & BLOCKED_WRITES != 0 { - guard = Some(self.0.writes.lock().unwrap()); - } - - // Wake up a single blocked task. - if let Some(mut guard) = guard { - if let Some((_, opt_waker)) = guard.iter_mut().next() { - // If there is no waker in this entry, that means it was already woken. - if let Some(w) = opt_waker.take() { - w.wake(); - } - } - } - } -} - -impl fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.0.value.get() } - } -} - -impl DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.value.get() } - } -} diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs new file mode 100644 index 000000000..05590f488 --- /dev/null +++ b/src/sync/waker_set.rs @@ -0,0 +1,235 @@ +//! A common utility for building synchronization primitives. +//! +//! When an async operation is blocked, it needs to register itself somewhere so that it can be +//! notified later on. The `WakerSet` type helps with keeping track of such async operations and +//! notifying them when they may make progress. + +use std::cell::UnsafeCell; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Waker}; + +use crossbeam_utils::Backoff; +use slab::Slab; + +/// Set when the entry list is locked. +#[allow(clippy::identity_op)] +const LOCKED: usize = 1 << 0; + +/// Set when there is at least one entry that has already been notified. +const NOTIFIED: usize = 1 << 1; + +/// Set when there is at least one notifiable entry. +const NOTIFIABLE: usize = 1 << 2; + +/// Inner representation of `WakerSet`. +struct Inner { + /// A list of entries in the set. + /// + /// Each entry has an optional waker associated with the task that is executing the operation. + /// If the waker is set to `None`, that means the task has been woken up but hasn't removed + /// itself from the `WakerSet` yet. + /// + /// The key of each entry is its index in the `Slab`. + entries: Slab>, + + /// The number of notifiable entries. + notifiable: usize, +} + +/// A set holding wakers. +pub struct WakerSet { + /// Holds three bits: `LOCKED`, `NOTIFY_ONE`, and `NOTIFY_ALL`. + flag: AtomicUsize, + + /// A set holding wakers. + inner: UnsafeCell, +} + +impl WakerSet { + /// Creates a new `WakerSet`. + #[inline] + pub fn new() -> WakerSet { + WakerSet { + flag: AtomicUsize::new(0), + inner: UnsafeCell::new(Inner { + entries: Slab::new(), + notifiable: 0, + }), + } + } + + /// Inserts a waker for a blocked operation and returns a key associated with it. + #[cold] + pub fn insert(&self, cx: &Context<'_>) -> usize { + let w = cx.waker().clone(); + let mut inner = self.lock(); + + let key = inner.entries.insert(Some(w)); + inner.notifiable += 1; + key + } + + /// If the waker for this key is still waiting for a notification, then update + /// the waker for the entry, and return false. If the waker has been notified, + /// treat the entry as completed and return true. + #[cfg(feature = "unstable")] + pub fn remove_if_notified(&self, key: usize, cx: &Context<'_>) -> bool { + let mut inner = self.lock(); + + match &mut inner.entries[key] { + None => { + inner.entries.remove(key); + true + } + Some(w) => { + // We were never woken, so update instead + if !w.will_wake(cx.waker()) { + *w = cx.waker().clone(); + } + false + } + } + } + + /// Removes the waker of a cancelled operation. + /// + /// Returns `true` if another blocked operation from the set was notified. + #[cold] + pub fn cancel(&self, key: usize) -> bool { + let mut inner = self.lock(); + + match inner.entries.remove(key) { + Some(_) => inner.notifiable -= 1, + None => { + // The operation was cancelled and notified so notify another operation instead. + for (_, opt_waker) in inner.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + inner.notifiable -= 1; + return true; + } + } + } + } + + false + } + + /// Notifies one additional blocked operation. + /// + /// Returns `true` if an operation was notified. + #[inline] + #[cfg(feature = "unstable")] + pub fn notify_one(&self) -> bool { + // Use `SeqCst` ordering to synchronize with `Lock::drop()`. + if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 { + self.notify(Notify::One) + } else { + false + } + } + + /// Notifies all blocked operations. + /// + /// Returns `true` if at least one operation was notified. + #[inline] + pub fn notify_all(&self) -> bool { + // Use `SeqCst` ordering to synchronize with `Lock::drop()`. + if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 { + self.notify(Notify::All) + } else { + false + } + } + + /// Notifies blocked operations, either one or all of them. + /// + /// Returns `true` if at least one operation was notified. + #[cold] + fn notify(&self, n: Notify) -> bool { + let inner = &mut *self.lock(); + let mut notified = false; + + for (_, opt_waker) in inner.entries.iter_mut() { + // If there is no waker in this entry, that means it was already woken. + if let Some(w) = opt_waker.take() { + w.wake(); + inner.notifiable -= 1; + notified = true; + + if n == Notify::One { + break; + } + } + + if n == Notify::Any { + break; + } + } + + notified + } + + /// Locks the list of entries. + fn lock(&self) -> Lock<'_> { + let backoff = Backoff::new(); + while self.flag.fetch_or(LOCKED, Ordering::Acquire) & LOCKED != 0 { + backoff.snooze(); + } + Lock { waker_set: self } + } +} + +/// A guard holding a `WakerSet` locked. +struct Lock<'a> { + waker_set: &'a WakerSet, +} + +impl Drop for Lock<'_> { + #[inline] + fn drop(&mut self) { + let mut flag = 0; + + // Set the `NOTIFIED` flag if there is at least one notified entry. + if self.entries.len() - self.notifiable > 0 { + flag |= NOTIFIED; + } + + // Set the `NOTIFIABLE` flag if there is at least one notifiable entry. + if self.notifiable > 0 { + flag |= NOTIFIABLE; + } + + // Use `SeqCst` ordering to synchronize with `WakerSet::lock_to_notify()`. + self.waker_set.flag.store(flag, Ordering::SeqCst); + } +} + +impl Deref for Lock<'_> { + type Target = Inner; + + #[inline] + fn deref(&self) -> &Inner { + unsafe { &*self.waker_set.inner.get() } + } +} + +impl DerefMut for Lock<'_> { + #[inline] + fn deref_mut(&mut self) -> &mut Inner { + unsafe { &mut *self.waker_set.inner.get() } + } +} + +/// Notification strategy. +#[derive(Clone, Copy, Eq, PartialEq)] +enum Notify { + /// Make sure at least one entry is notified. + Any, + /// Notify one additional entry. + One, + /// Notify all entries. + All, +} diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 032bf02d5..3ab4dc06f 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,20 +1,6 @@ -use std::cell::{Cell, UnsafeCell}; -use std::mem::{self, ManuallyDrop}; -use std::panic::{self, AssertUnwindSafe, UnwindSafe}; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{RawWaker, RawWakerVTable}; -use std::thread; +use std::future::Future; -use crossbeam_utils::sync::Parker; - -use super::task; -use super::task_local; -use super::worker; -use crate::future::Future; -use crate::task::{Context, Poll, Waker}; - -use kv_log_macro::trace; +use crate::task::Builder; /// Spawns a task and blocks the current thread on its result. /// @@ -37,141 +23,20 @@ use kv_log_macro::trace; /// println!("Hello, world!"); /// }) /// ``` +#[cfg(not(target_os = "unknown"))] pub fn block_on(future: F) -> T where F: Future, { - unsafe { - // A place on the stack where the result will be stored. - let out = &mut UnsafeCell::new(None); - - // Wrap the future into one that stores the result into `out`. - let future = { - let out = out.get(); - - async move { - let future = CatchUnwindFuture { - future: AssertUnwindSafe(future), - }; - *out = Some(future.await); - } - }; - - // Create a tag for the task. - let tag = task::Tag::new(None); - - // Log this `block_on` operation. - let child_id = tag.task_id().as_u64(); - let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); - - trace!("block_on", { - parent_id: parent_id, - child_id: child_id, - }); - - // Wrap the future into one that drops task-local variables on exit. - let future = task_local::add_finalizer(future); - - let future = async move { - future.await; - trace!("block_on completed", { - parent_id: parent_id, - child_id: child_id, - }); - }; - - // Pin the future onto the stack. - pin_utils::pin_mut!(future); - - // Transmute the future into one that is futurestatic. - let future = mem::transmute::< - Pin<&'_ mut dyn Future>, - Pin<&'static mut dyn Future>, - >(future); - - // Block on the future and and wait for it to complete. - worker::set_tag(&tag, || block(future)); - - // Take out the result. - match (*out.get()).take().unwrap() { - Ok(v) => v, - Err(err) => panic::resume_unwind(err), - } - } -} - -struct CatchUnwindFuture { - future: F, -} - -impl CatchUnwindFuture { - pin_utils::unsafe_pinned!(future: F); -} - -impl Future for CatchUnwindFuture { - type Output = thread::Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) - } + Builder::new().blocking(future) } -fn block(f: F) -> T +/// Spawns a task and waits for it to finish. +#[cfg(target_os = "unknown")] +pub fn block_on(future: F) where - F: Future, + F: Future + 'static, + T: 'static, { - thread_local! { - // May hold a pre-allocated parker that can be reused for efficiency. - // - // Note that each invocation of `block` needs its own parker. In particular, if `block` - // recursively calls itself, we must make sure that each recursive call uses a distinct - // parker instance. - static CACHE: Cell>> = Cell::new(None); - } - - pin_utils::pin_mut!(f); - - CACHE.with(|cache| { - // Reuse a cached parker or create a new one for this invocation of `block`. - let arc_parker: Arc = cache.take().unwrap_or_else(|| Arc::new(Parker::new())); - - let ptr = (&*arc_parker as *const Parker) as *const (); - let vt = vtable(); - - let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) }; - let cx = &mut Context::from_waker(&waker); - - loop { - if let Poll::Ready(t) = f.as_mut().poll(cx) { - // Save the parker for the next invocation of `block`. - cache.set(Some(arc_parker)); - return t; - } - arc_parker.park(); - } - }) -} - -fn vtable() -> &'static RawWakerVTable { - unsafe fn clone_raw(ptr: *const ()) -> RawWaker { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); - mem::forget(arc.clone()); - RawWaker::new(ptr, vtable()) - } - - unsafe fn wake_raw(ptr: *const ()) { - let arc = Arc::from_raw(ptr as *const Parker); - arc.unparker().unpark(); - } - - unsafe fn wake_by_ref_raw(ptr: *const ()) { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); - arc.unparker().unpark(); - } - - unsafe fn drop_raw(ptr: *const ()) { - drop(Arc::from_raw(ptr as *const Parker)) - } - - &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) + Builder::new().local(future).unwrap(); } diff --git a/src/task/blocking.rs b/src/task/blocking.rs deleted file mode 100644 index 3216012a7..000000000 --- a/src/task/blocking.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! A thread pool for running blocking functions asynchronously. - -use std::sync::atomic::{AtomicU64, Ordering}; -use std::thread; -use std::time::Duration; - -use crossbeam_channel::{bounded, Receiver, Sender}; -use lazy_static::lazy_static; - -use crate::task::task::{JoinHandle, Tag}; -use crate::utils::abort_on_panic; - -const MAX_THREADS: u64 = 10_000; - -static DYNAMIC_THREAD_COUNT: AtomicU64 = AtomicU64::new(0); - -struct Pool { - sender: Sender>, - receiver: Receiver>, -} - -lazy_static! { - static ref POOL: Pool = { - for _ in 0..2 { - thread::Builder::new() - .name("async-blocking-driver".to_string()) - .spawn(|| abort_on_panic(|| { - for task in &POOL.receiver { - task.run(); - } - })) - .expect("cannot start a thread driving blocking tasks"); - } - - // We want to use an unbuffered channel here to help - // us drive our dynamic control. In effect, the - // kernel's scheduler becomes the queue, reducing - // the number of buffers that work must flow through - // before being acted on by a core. This helps keep - // latency snappy in the overall async system by - // reducing bufferbloat. - let (sender, receiver) = bounded(0); - Pool { sender, receiver } - }; -} - -// Create up to MAX_THREADS dynamic blocking task worker threads. -// Dynamic threads will terminate themselves if they don't -// receive any work after between one and ten seconds. -fn maybe_create_another_blocking_thread() { - // We use a `Relaxed` atomic operation because - // it's just a heuristic, and would not lose correctness - // even if it's random. - let workers = DYNAMIC_THREAD_COUNT.load(Ordering::Relaxed); - if workers >= MAX_THREADS { - return; - } - - // We want to avoid having all threads terminate at - // exactly the same time, causing thundering herd - // effects. We want to stagger their destruction over - // 10 seconds or so to make the costs fade into - // background noise. - // - // Generate a simple random number of milliseconds - let rand_sleep_ms = u64::from(random(10_000)); - - thread::Builder::new() - .name("async-blocking-driver-dynamic".to_string()) - .spawn(move || { - let wait_limit = Duration::from_millis(1000 + rand_sleep_ms); - - DYNAMIC_THREAD_COUNT.fetch_add(1, Ordering::Relaxed); - while let Ok(task) = POOL.receiver.recv_timeout(wait_limit) { - abort_on_panic(|| task.run()); - } - DYNAMIC_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed); - }) - .expect("cannot start a dynamic thread driving blocking tasks"); -} - -// Enqueues work, attempting to send to the threadpool in a -// nonblocking way and spinning up another worker thread if -// there is not a thread ready to accept the work. -fn schedule(t: async_task::Task) { - if let Err(err) = POOL.sender.try_send(t) { - // We were not able to send to the channel without - // blocking. Try to spin up another thread and then - // retry sending while blocking. - maybe_create_another_blocking_thread(); - POOL.sender.send(err.into_inner()).unwrap(); - } -} - -/// Spawns a blocking task. -/// -/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. -pub(crate) fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - let tag = Tag::new(None); - let future = async move { f() }; - let (task, handle) = async_task::spawn(future, schedule, tag); - task.schedule(); - JoinHandle::new(handle) -} - -/// Generates a random number in `0..n`. -fn random(n: u32) -> u32 { - use std::cell::Cell; - use std::num::Wrapping; - - thread_local! { - static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); - } - - RNG.with(|rng| { - // This is the 32-bit variant of Xorshift. - // - // Source: https://en.wikipedia.org/wiki/Xorshift - let mut x = rng.get(); - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - rng.set(x); - - // This is a fast alternative to `x % n`. - // - // Author: Daniel Lemire - // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ - ((u64::from(x.0)).wrapping_mul(u64::from(n)) >> 32) as u32 - }) -} diff --git a/src/task/builder.rs b/src/task/builder.rs index a43b42bcf..aba0d6115 100644 --- a/src/task/builder.rs +++ b/src/task/builder.rs @@ -1,7 +1,12 @@ -use super::pool; -use super::JoinHandle; -use crate::future::Future; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use pin_project_lite::pin_project; + use crate::io; +use crate::task::{JoinHandle, Task, TaskLocalsWrapper}; /// Task builder that configures the settings of a new task. #[derive(Debug, Default)] @@ -11,22 +16,188 @@ pub struct Builder { impl Builder { /// Creates a new builder. + #[inline] pub fn new() -> Builder { Builder { name: None } } /// Configures the name of the task. + #[inline] pub fn name(mut self, name: String) -> Builder { self.name = Some(name); self } + fn build(self, future: F) -> SupportTaskLocals + where + F: Future, + { + let name = self.name.map(Arc::new); + + // Create a new task handle. + let task = Task::new(name); + + #[cfg(not(target_os = "unknown"))] + once_cell::sync::Lazy::force(&crate::rt::RUNTIME); + + let tag = TaskLocalsWrapper::new(task); + + SupportTaskLocals { tag, future } + } + /// Spawns a task with the configured settings. + #[cfg(not(target_os = "unknown"))] pub fn spawn(self, future: F) -> io::Result> where F: Future + Send + 'static, T: Send + 'static, { - Ok(pool::get().spawn(future, self)) + let wrapped = self.build(future); + + kv_log_macro::trace!("spawn", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + let task = wrapped.tag.task().clone(); + let handle = async_global_executor::spawn(wrapped); + + Ok(JoinHandle::new(handle, task)) + } + + /// Spawns a task locally with the configured settings. + #[cfg(all(not(target_os = "unknown"), feature = "unstable"))] + pub fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + let wrapped = self.build(future); + + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + let task = wrapped.tag.task().clone(); + let handle = async_global_executor::spawn_local(wrapped); + + Ok(JoinHandle::new(handle, task)) + } + + /// Spawns a task locally with the configured settings. + #[cfg(all(target_arch = "wasm32", feature = "unstable"))] + pub fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + use futures_channel::oneshot::channel; + let (sender, receiver) = channel(); + + let wrapped = self.build(async move { + let res = future.await; + let _ = sender.send(res); + }); + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + let task = wrapped.tag.task().clone(); + wasm_bindgen_futures::spawn_local(wrapped); + + Ok(JoinHandle::new(receiver, task)) + } + + /// Spawns a task locally with the configured settings. + #[cfg(all(target_arch = "wasm32", not(feature = "unstable")))] + pub(crate) fn local(self, future: F) -> io::Result> + where + F: Future + 'static, + T: 'static, + { + use futures_channel::oneshot::channel; + let (sender, receiver) = channel(); + + let wrapped = self.build(async move { + let res = future.await; + let _ = sender.send(res); + }); + + kv_log_macro::trace!("spawn_local", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + let task = wrapped.tag.task().clone(); + wasm_bindgen_futures::spawn_local(wrapped); + + Ok(JoinHandle::new(receiver, task)) + } + + /// Spawns a task with the configured settings, blocking on its execution. + #[cfg(not(target_os = "unknown"))] + pub fn blocking(self, future: F) -> T + where + F: Future, + { + use std::cell::Cell; + + let wrapped = self.build(future); + + // Log this `block_on` operation. + kv_log_macro::trace!("block_on", { + task_id: wrapped.tag.id().0, + parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0), + }); + + thread_local! { + /// Tracks the number of nested block_on calls. + static NUM_NESTED_BLOCKING: Cell = Cell::new(0); + } + + // Run the future as a task. + NUM_NESTED_BLOCKING.with(|num_nested_blocking| { + let count = num_nested_blocking.get(); + let should_run = count == 0; + // increase the count + num_nested_blocking.replace(count + 1); + + unsafe { + TaskLocalsWrapper::set_current(&wrapped.tag, || { + let res = if should_run { + // The first call should run the executor + async_global_executor::block_on(wrapped) + } else { + futures_lite::future::block_on(wrapped) + }; + num_nested_blocking.replace(num_nested_blocking.get() - 1); + res + }) + } + }) + } +} + +pin_project! { + /// Wrapper to add support for task locals. + struct SupportTaskLocals { + tag: TaskLocalsWrapper, + #[pin] + future: F, + } +} + +impl Future for SupportTaskLocals { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { + TaskLocalsWrapper::set_current(&self.tag, || { + let this = self.project(); + this.future.poll(cx) + }) + } } } diff --git a/src/task/current.rs b/src/task/current.rs new file mode 100644 index 000000000..ad354d629 --- /dev/null +++ b/src/task/current.rs @@ -0,0 +1,48 @@ +use crate::task::{Task, TaskLocalsWrapper}; + +/// Returns a handle to the current task. +/// +/// # Panics +/// +/// This function will panic if not called within the context of a task created by [`block_on`], +/// [`spawn`], or [`Builder::spawn`]. +/// +/// [`block_on`]: fn.block_on.html +/// [`spawn`]: fn.spawn.html +/// [`Builder::spawn`]: struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// println!("The name of this task is {:?}", task::current().name()); +/// # +/// # }) +/// ``` +pub fn current() -> Task { + try_current().expect("`task::current()` called outside the context of a task") +} + +/// Returns a handle to the current task if called within the context of a task created by [`block_on`], +/// [`spawn`], or [`Builder::spawn`], otherwise returns `None`. +/// +/// [`block_on`]: fn.block_on.html +/// [`spawn`]: fn.spawn.html +/// [`Builder::spawn`]: struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// use async_std::task; +/// +/// match task::try_current() { +/// Some(t) => println!("The name of this task is {:?}", t.name()), +/// None => println!("Not inside a task!"), +/// } +/// ``` +pub fn try_current() -> Option { + TaskLocalsWrapper::get_current(|t| t.task().clone()) +} \ No newline at end of file diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs new file mode 100644 index 000000000..9fbab44c7 --- /dev/null +++ b/src/task/join_handle.rs @@ -0,0 +1,94 @@ +use std::future::Future; +use std::pin::Pin; + +use crate::task::{Context, Poll, Task}; + +/// A handle that awaits the result of a task. +/// +/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer +/// a handle to the task and no way to `join` on it. +/// +/// Created when a task is [spawned]. +/// +/// [spawned]: fn.spawn.html +#[derive(Debug)] +pub struct JoinHandle { + handle: Option>, + task: Task, +} + +#[cfg(not(target_os = "unknown"))] +type InnerHandle = async_global_executor::Task; +#[cfg(target_arch = "wasm32")] +type InnerHandle = futures_channel::oneshot::Receiver; + +impl JoinHandle { + /// Creates a new `JoinHandle`. + pub(crate) fn new(inner: InnerHandle, task: Task) -> JoinHandle { + JoinHandle { + handle: Some(inner), + task, + } + } + + /// Returns a handle to the underlying task. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::task; + /// + /// let handle = task::spawn(async { + /// 1 + 2 + /// }); + /// println!("id = {}", handle.task().id()); + /// # + /// # }) + pub fn task(&self) -> &Task { + &self.task + } + + /// Cancel this task. + #[cfg(not(target_os = "unknown"))] + pub async fn cancel(mut self) -> Option { + let handle = self.handle.take().unwrap(); + handle.cancel().await + } + + /// Cancel this task. + #[cfg(target_arch = "wasm32")] + pub async fn cancel(mut self) -> Option { + let mut handle = self.handle.take().unwrap(); + handle.close(); + handle.await.ok() + } +} + +#[cfg(not(target_os = "unknown"))] +impl Drop for JoinHandle { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + handle.detach(); + } + } +} + +impl Future for JoinHandle { + type Output = T; + + #[cfg(not(target_os = "unknown"))] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) + } + + #[cfg(target_arch = "wasm32")] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) { + Poll::Ready(Ok(t)) => Poll::Ready(t), + Poll::Ready(Err(_)) => unreachable!("channel must not be canceled"), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/src/task/mod.rs b/src/task/mod.rs index 24eae0819..0eda72b71 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -37,7 +37,7 @@ //! outlive its parent (the task that spawned it), unless this parent is the root task. //! //! The root task can also wait on the completion of the child task; a call to [`spawn`] produces a -//! [`JoinHandle`], which provides implements `Future` and can be `await`ed: +//! [`JoinHandle`], which implements `Future` and can be `await`ed: //! //! ``` //! use async_std::task; @@ -58,7 +58,7 @@ //! ## Configuring tasks //! //! A new task can be configured before it is spawned via the [`Builder`] type, -//! which currently allows you to set the name and stack size for the child task: +//! which currently allows you to set the name for the child task: //! //! ``` //! # #![allow(unused_must_use)] @@ -103,86 +103,70 @@ //! the desired task name to [`Builder::name`]. To retrieve the task name from within the //! task, use [`Task::name`]. //! -//! [`Arc`]: ../gsync/struct.Arc.html +//! [`Arc`]: ../sync/struct.Arc.html //! [`spawn`]: fn.spawn.html //! [`JoinHandle`]: struct.JoinHandle.html //! [`JoinHandle::task`]: struct.JoinHandle.html#method.task //! [`join`]: struct.JoinHandle.html#method.join //! [`panic!`]: https://doc.rust-lang.org/std/macro.panic.html //! [`Builder`]: struct.Builder.html -//! [`Builder::stack_size`]: struct.Builder.html#method.stack_size //! [`Builder::name`]: struct.Builder.html#method.name //! [`task::current`]: fn.current.html -//! [`Task`]: struct.Thread.html +//! [`Task`]: struct.Task.html //! [`Task::name`]: struct.Task.html#method.name //! [`task_local!`]: ../macro.task_local.html //! [`with`]: struct.LocalKey.html#method.with -#[doc(inline)] -pub use std::task::{Context, Poll, Waker}; +cfg_alloc! { + #[doc(inline)] + pub use core::task::{Context, Poll, Waker}; + pub use ready::ready; -#[doc(inline)] -pub use async_macros::ready; + mod ready; +} -pub use block_on::block_on; -pub use builder::Builder; -pub use pool::spawn; -pub use sleep::sleep; -pub use task::{JoinHandle, Task, TaskId}; -pub use task_local::{AccessError, LocalKey}; -pub use worker::current; +cfg_std! { + pub use yield_now::yield_now; + mod yield_now; +} -mod block_on; -mod builder; -mod pool; -mod sleep; -mod sleepers; -mod task; -mod task_local; -mod worker; +cfg_default! { + pub use block_on::block_on; + pub use builder::Builder; + pub use current::{current, try_current}; + pub use task::Task; + pub use task_id::TaskId; + pub use join_handle::JoinHandle; + pub use sleep::sleep; + #[cfg(not(target_os = "unknown"))] + pub use spawn::spawn; + pub use task_local::{AccessError, LocalKey}; -pub(crate) mod blocking; + pub(crate) use task_local::LocalsMap; + pub(crate) use task_locals_wrapper::TaskLocalsWrapper; -cfg_unstable! { - mod yield_now; - pub use yield_now::yield_now; + mod block_on; + mod builder; + mod current; + mod join_handle; + mod sleep; + #[cfg(not(target_os = "unknown"))] + mod spawn; + #[cfg(not(target_os = "unknown"))] + mod spawn_blocking; + mod task; + mod task_id; + mod task_local; + mod task_locals_wrapper; + + #[cfg(not(target_os = "unknown"))] + pub use spawn_blocking::spawn_blocking; } -/// Spawns a blocking task. -/// -/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This -/// is useful to prevent long-running synchronous operations from blocking the main futures -/// executor. -/// -/// See also: [`task::block_on`], [`task::spawn`]. -/// -/// [`task::block_on`]: fn.block_on.html -/// [`task::spawn`]: fn.spawn.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// task::spawn_blocking(|| { -/// println!("long-running task here"); -/// }).await; -/// # -/// # }) -/// ``` -// Once this function stabilizes we should merge `blocking::spawn` into this so -// all code in our crate uses `task::blocking` too. -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[inline] -pub fn spawn_blocking(f: F) -> task::JoinHandle -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - blocking::spawn(f) +cfg_unstable! { + #[cfg(feature = "default")] + pub use spawn_local::spawn_local; + + #[cfg(feature = "default")] + mod spawn_local; } diff --git a/src/task/pool.rs b/src/task/pool.rs deleted file mode 100644 index bfaa17d48..000000000 --- a/src/task/pool.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::iter; -use std::thread; - -use crossbeam_deque::{Injector, Stealer, Worker}; -use kv_log_macro::trace; -use lazy_static::lazy_static; - -use super::sleepers::Sleepers; -use super::task; -use super::task_local; -use super::worker; -use super::{Builder, JoinHandle}; -use crate::future::Future; -use crate::utils::abort_on_panic; - -/// Spawns a task. -/// -/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task. -/// -/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// let handle = task::spawn(async { -/// 1 + 2 -/// }); -/// -/// assert_eq!(handle.await, 3); -/// # -/// # }) -/// ``` -pub fn spawn(future: F) -> JoinHandle -where - F: Future + Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(future).expect("cannot spawn future") -} - -pub(crate) struct Pool { - pub injector: Injector, - pub stealers: Vec>, - pub sleepers: Sleepers, -} - -impl Pool { - /// Spawn a future onto the pool. - pub fn spawn(&self, future: F, builder: Builder) -> JoinHandle - where - F: Future + Send + 'static, - T: Send + 'static, - { - let tag = task::Tag::new(builder.name); - - // Log this `spawn` operation. - let child_id = tag.task_id().as_u64(); - let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); - - trace!("spawn", { - parent_id: parent_id, - child_id: child_id, - }); - - // Wrap the future into one that drops task-local variables on exit. - let future = unsafe { task_local::add_finalizer(future) }; - - // Wrap the future into one that logs completion on exit. - let future = async move { - let res = future.await; - trace!("spawn completed", { - parent_id: parent_id, - child_id: child_id, - }); - res - }; - - let (task, handle) = async_task::spawn(future, worker::schedule, tag); - task.schedule(); - JoinHandle::new(handle) - } - - /// Find the next runnable task to run. - pub fn find_task(&self, local: &Worker) -> Option { - // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - iter::repeat_with(|| { - // Try stealing a batch of tasks from the injector queue. - self.injector - .steal_batch_and_pop(local) - // Or try stealing a bach of tasks from one of the other threads. - .or_else(|| { - self.stealers - .iter() - .map(|s| s.steal_batch_and_pop(local)) - .collect() - }) - }) - // Loop while no task was stolen and any steal operation needs to be retried. - .find(|s| !s.is_retry()) - // Extract the stolen task, if there is one. - .and_then(|s| s.success()) - }) - } -} - -#[inline] -pub(crate) fn get() -> &'static Pool { - lazy_static! { - static ref POOL: Pool = { - let num_threads = num_cpus::get().max(1); - let mut stealers = Vec::new(); - - // Spawn worker threads. - for _ in 0..num_threads { - let worker = Worker::new_fifo(); - stealers.push(worker.stealer()); - - thread::Builder::new() - .name("async-task-driver".to_string()) - .spawn(|| abort_on_panic(|| worker::main_loop(worker))) - .expect("cannot start a thread driving tasks"); - } - - Pool { - injector: Injector::new(), - stealers, - sleepers: Sleepers::new(), - } - }; - } - &*POOL -} diff --git a/src/task/ready.rs b/src/task/ready.rs new file mode 100644 index 000000000..aca04aa7b --- /dev/null +++ b/src/task/ready.rs @@ -0,0 +1,4 @@ +/// Extracts the successful type of a `Poll`. +/// +/// This macro bakes in propagation of `Pending` signals by returning early. +pub use futures_core::ready; diff --git a/src/task/sleepers.rs b/src/task/sleepers.rs deleted file mode 100644 index 4e7012957..000000000 --- a/src/task/sleepers.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Condvar, Mutex}; - -/// The place where worker threads go to sleep. -/// -/// Similar to how thread parking works, if a notification comes up while no threads are sleeping, -/// the next thread that attempts to go to sleep will pick up the notification immediately. -pub struct Sleepers { - /// How many threads are currently a sleep. - sleep: Mutex, - - /// A condvar for notifying sleeping threads. - wake: Condvar, - - /// Set to `true` if a notification came up while nobody was sleeping. - notified: AtomicBool, -} - -impl Sleepers { - /// Creates a new `Sleepers`. - pub fn new() -> Sleepers { - Sleepers { - sleep: Mutex::new(0), - wake: Condvar::new(), - notified: AtomicBool::new(false), - } - } - - /// Puts the current thread to sleep. - pub fn wait(&self) { - let mut sleep = self.sleep.lock().unwrap(); - - if !self.notified.swap(false, Ordering::SeqCst) { - *sleep += 1; - let _ = self.wake.wait(sleep).unwrap(); - } - } - - /// Notifies one thread. - pub fn notify_one(&self) { - if !self.notified.load(Ordering::SeqCst) { - let mut sleep = self.sleep.lock().unwrap(); - - if *sleep > 0 { - *sleep -= 1; - self.wake.notify_one(); - } else { - self.notified.store(true, Ordering::SeqCst); - } - } - } -} diff --git a/src/task/spawn.rs b/src/task/spawn.rs new file mode 100644 index 000000000..f81a483d2 --- /dev/null +++ b/src/task/spawn.rs @@ -0,0 +1,32 @@ +use std::future::Future; + +use crate::task::{Builder, JoinHandle}; + +/// Spawns a task. +/// +/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task. +/// +/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// let handle = task::spawn(async { +/// 1 + 2 +/// }); +/// +/// assert_eq!(handle.await, 3); +/// # +/// # }) +/// ``` +pub fn spawn(future: F) -> JoinHandle +where + F: Future + Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(future).expect("cannot spawn task") +} diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs new file mode 100644 index 000000000..2439c123f --- /dev/null +++ b/src/task/spawn_blocking.rs @@ -0,0 +1,37 @@ +use crate::task::{self, JoinHandle}; + +/// Spawns a blocking task. +/// +/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This +/// is useful to prevent long-running synchronous operations from blocking the main futures +/// executor. +/// +/// See also: [`task::block_on`], [`task::spawn`]. +/// +/// [`task::block_on`]: fn.block_on.html +/// [`task::spawn`]: fn.spawn.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// task::spawn_blocking(|| { +/// println!("long-running task here"); +/// }) +/// .await; +/// # +/// # }) +/// ``` +#[inline] +pub fn spawn_blocking(f: F) -> JoinHandle +where + F: FnOnce() -> T + Send + 'static, + T: Send + 'static, +{ + task::spawn(async_global_executor::spawn_blocking(f)) +} diff --git a/src/task/spawn_local.rs b/src/task/spawn_local.rs new file mode 100644 index 000000000..7e0ce6c8f --- /dev/null +++ b/src/task/spawn_local.rs @@ -0,0 +1,31 @@ +use std::future::Future; + +use crate::task::{Builder, JoinHandle}; + +/// Spawns a task onto the thread-local executor. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(feature = "unstable")] +/// # async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// let handle = task::spawn_local(async { +/// 1 + 2 +/// }); +/// +/// assert_eq!(handle.await, 3); +/// # +/// # }) +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[inline] +pub fn spawn_local(future: F) -> JoinHandle +where + F: Future + 'static, + T: 'static, +{ + Builder::new().local(future).expect("cannot spawn task") +} diff --git a/src/task/task.rs b/src/task/task.rs index ca3cac142..eba99c752 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -1,31 +1,32 @@ use std::fmt; -use std::future::Future; -use std::i64; -use std::mem; -use std::num::NonZeroU64; -use std::pin::Pin; -use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; -use super::task_local; -use crate::task::{Context, Poll}; +use crate::task::TaskId; /// A handle to a task. #[derive(Clone)] -pub struct Task(Arc); +pub struct Task { + /// The task ID. + id: TaskId, -unsafe impl Send for Task {} -unsafe impl Sync for Task {} + /// The optional task name. + name: Option>, +} impl Task { - /// Returns a reference to task metadata. - pub(crate) fn metadata(&self) -> &Metadata { - &self.0 + /// Creates a new task handle. + #[inline] + pub(crate) fn new(name: Option>) -> Task { + Task { + id: TaskId::generate(), + name, + } } /// Gets the task's unique identifier. + #[inline] pub fn id(&self) -> TaskId { - self.metadata().task_id + self.id } /// Returns the name of this task. @@ -34,176 +35,15 @@ impl Task { /// /// [`Builder::name`]: struct.Builder.html#method.name pub fn name(&self) -> Option<&str> { - self.metadata().name.as_ref().map(|s| s.as_str()) + self.name.as_ref().map(|s| s.as_str()) } } impl fmt::Debug for Task { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Task").field("name", &self.name()).finish() - } -} - -/// A handle that awaits the result of a task. -/// -/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer -/// a handle to the task and no way to `join` on it. -/// -/// Created when a task is [spawned]. -/// -/// [spawned]: fn.spawn.html -#[derive(Debug)] -pub struct JoinHandle(async_task::JoinHandle); - -unsafe impl Send for JoinHandle {} -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - pub(crate) fn new(inner: async_task::JoinHandle) -> JoinHandle { - JoinHandle(inner) - } - - /// Returns a handle to the underlying task. - /// - /// # Examples - /// - /// ``` - /// # async_std::task::block_on(async { - /// # - /// use async_std::task; - /// - /// let handle = task::spawn(async { - /// 1 + 2 - /// }); - /// println!("id = {}", handle.task().id()); - /// # - /// # }) - pub fn task(&self) -> &Task { - self.0.tag().task() - } -} - -impl Future for JoinHandle { - type Output = T; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match Pin::new(&mut self.0).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => panic!("task has panicked"), - Poll::Ready(Some(val)) => Poll::Ready(val), - } - } -} - -/// A unique identifier for a task. -/// -/// # Examples -/// -/// ``` -/// # -/// use async_std::task; -/// -/// task::block_on(async { -/// println!("id = {:?}", task::current().id()); -/// }) -/// ``` -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct TaskId(NonZeroU64); - -impl TaskId { - pub(crate) fn new() -> TaskId { - static COUNTER: AtomicU64 = AtomicU64::new(1); - - let id = COUNTER.fetch_add(1, Ordering::Relaxed); - - if id > i64::MAX as u64 { - std::process::abort(); - } - unsafe { TaskId(NonZeroU64::new_unchecked(id)) } - } - - pub(crate) fn as_u64(self) -> u64 { - self.0.get() - } -} - -impl fmt::Display for TaskId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -pub(crate) type Runnable = async_task::Task; - -pub(crate) struct Metadata { - pub task_id: TaskId, - pub name: Option, - pub local_map: task_local::Map, -} - -pub(crate) struct Tag { - task_id: TaskId, - raw_metadata: AtomicUsize, -} - -impl Tag { - pub fn new(name: Option) -> Tag { - let task_id = TaskId::new(); - - let opt_task = name.map(|name| { - Task(Arc::new(Metadata { - task_id, - name: Some(name), - local_map: task_local::Map::new(), - })) - }); - - Tag { - task_id, - raw_metadata: AtomicUsize::new(unsafe { - mem::transmute::, usize>(opt_task) - }), - } - } - - pub fn task(&self) -> &Task { - unsafe { - let raw = self.raw_metadata.load(Ordering::Acquire); - - if mem::transmute::<&usize, &Option>(&raw).is_none() { - let new = Some(Task(Arc::new(Metadata { - task_id: TaskId::new(), - name: None, - local_map: task_local::Map::new(), - }))); - - let new_raw = mem::transmute::, usize>(new); - - if self - .raw_metadata - .compare_exchange(raw, new_raw, Ordering::AcqRel, Ordering::Acquire) - .is_err() - { - let new = mem::transmute::>(new_raw); - drop(new); - } - } - - mem::transmute::<&AtomicUsize, &Option>(&self.raw_metadata) - .as_ref() - .unwrap() - } - } - - pub fn task_id(&self) -> TaskId { - self.task_id - } -} - -impl Drop for Tag { - fn drop(&mut self) { - let raw = *self.raw_metadata.get_mut(); - let opt_task = unsafe { mem::transmute::>(raw) }; - drop(opt_task); + f.debug_struct("Task") + .field("id", &self.id()) + .field("name", &self.name()) + .finish() } } diff --git a/src/task/task_id.rs b/src/task/task_id.rs new file mode 100644 index 000000000..92c607c71 --- /dev/null +++ b/src/task/task_id.rs @@ -0,0 +1,36 @@ +use std::fmt; +use std::sync::atomic::{AtomicUsize, Ordering}; + +/// A unique identifier for a task. +/// +/// # Examples +/// +/// ``` +/// use async_std::task; +/// +/// task::block_on(async { +/// println!("id = {:?}", task::current().id()); +/// }) +/// ``` +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +pub struct TaskId(pub(crate) usize); + +impl TaskId { + /// Generates a new `TaskId`. + pub(crate) fn generate() -> TaskId { + // TODO: find a good version to emulate u64 atomics on 32 bit systems. + static COUNTER: AtomicUsize = AtomicUsize::new(1); + + let id = COUNTER.fetch_add(1, Ordering::Relaxed); + if id > usize::max_value() / 2 { + std::process::abort(); + } + TaskId(id) + } +} + +impl fmt::Display for TaskId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/src/task/task_local.rs b/src/task/task_local.rs index c72937f6f..1661c0bb9 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -1,65 +1,9 @@ use std::cell::UnsafeCell; use std::error::Error; use std::fmt; -use std::future::Future; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Mutex; +use std::sync::atomic::{AtomicU32, Ordering}; -use lazy_static::lazy_static; - -use super::worker; -use crate::utils::abort_on_panic; - -/// Declares task-local values. -/// -/// The macro wraps any number of static declarations and makes them task-local. Attributes and -/// visibility modifiers are allowed. -/// -/// Each declared value is of the accessor type [`LocalKey`]. -/// -/// [`LocalKey`]: task/struct.LocalKey.html -/// -/// # Examples -/// -/// ``` -/// # -/// use std::cell::Cell; -/// -/// use async_std::task; -/// use async_std::prelude::*; -/// -/// task_local! { -/// static VAL: Cell = Cell::new(5); -/// } -/// -/// task::block_on(async { -/// let v = VAL.with(|c| c.get()); -/// assert_eq!(v, 5); -/// }); -/// ``` -#[macro_export] -macro_rules! task_local { - () => (); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( - $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = { - #[inline] - fn __init() -> $t { - $init - } - - $crate::task::LocalKey { - __init, - __key: ::std::sync::atomic::AtomicUsize::new(0), - } - }; - ); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - $crate::task_local!($(#[$attr])* $vis static $name: $t = $init); - $crate::task_local!($($rest)*); - ); -} +use crate::task::TaskLocalsWrapper; /// The key for accessing a task-local value. /// @@ -71,7 +15,7 @@ pub struct LocalKey { pub __init: fn() -> T, #[doc(hidden)] - pub __key: AtomicUsize, + pub __key: AtomicU32, } impl LocalKey { @@ -154,14 +98,13 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - worker::get_task(|task| unsafe { + TaskLocalsWrapper::get_current(|task| unsafe { // Prepare the numeric key, initialization function, and the map of task-locals. let key = self.key(); let init = || Box::new((self.__init)()) as Box; - let map = &task.metadata().local_map; // Get the value in the map of task-locals, or initialize and insert one. - let value: *const dyn Send = map.get_or_insert(key, init); + let value: *const dyn Send = task.locals().get_or_insert(key, init); // Call the closure with the value passed as an argument. f(&*(value as *const T)) @@ -171,26 +114,26 @@ impl LocalKey { /// Returns the numeric key associated with this task-local. #[inline] - fn key(&self) -> usize { + fn key(&self) -> u32 { #[cold] - fn init(key: &AtomicUsize) -> usize { - lazy_static! { - static ref COUNTER: Mutex = Mutex::new(1); - } + fn init(key: &AtomicU32) -> u32 { + static COUNTER: AtomicU32 = AtomicU32::new(1); - let mut counter = COUNTER.lock().unwrap(); - let prev = key.compare_and_swap(0, *counter, Ordering::AcqRel); + let counter = COUNTER.fetch_add(1, Ordering::Relaxed); + if counter > u32::max_value() / 2 { + std::process::abort(); + } - if prev == 0 { - *counter += 1; - *counter - 1 - } else { - prev + match key.compare_exchange(0, counter, Ordering::AcqRel, Ordering::Acquire) { + Ok(_) => counter, + Err(k) => k, } } - let key = self.__key.load(Ordering::Acquire); - if key == 0 { init(&self.__key) } else { key } + match self.__key.load(Ordering::Acquire) { + 0 => init(&self.__key), + k => k, + } } } @@ -216,51 +159,55 @@ impl fmt::Display for AccessError { impl Error for AccessError {} +/// A key-value entry in a map of task-locals. +struct Entry { + /// Key identifying the task-local variable. + key: u32, + + /// Value stored in this entry. + value: Box, +} + /// A map that holds task-locals. -pub(crate) struct Map { - /// A list of `(key, value)` entries sorted by the key. - entries: UnsafeCell)>>, +pub(crate) struct LocalsMap { + /// A list of key-value entries sorted by the key. + entries: UnsafeCell>>, } -impl Map { +impl LocalsMap { /// Creates an empty map of task-locals. - pub fn new() -> Map { - Map { - entries: UnsafeCell::new(Vec::new()), + pub fn new() -> LocalsMap { + LocalsMap { + entries: UnsafeCell::new(Some(Vec::new())), } } - /// Returns a thread-local value associated with `key` or inserts one constructed by `init`. + /// Returns a task-local value associated with `key` or inserts one constructed by `init`. #[inline] - pub fn get_or_insert(&self, key: usize, init: impl FnOnce() -> Box) -> &dyn Send { - let entries = unsafe { &mut *self.entries.get() }; - - let index = match entries.binary_search_by_key(&key, |e| e.0) { - Ok(i) => i, - Err(i) => { - entries.insert(i, (key, init())); - i + pub fn get_or_insert(&self, key: u32, init: impl FnOnce() -> Box) -> &dyn Send { + match unsafe { (*self.entries.get()).as_mut() } { + None => panic!("can't access task-locals while the task is being dropped"), + Some(entries) => { + let index = match entries.binary_search_by_key(&key, |e| e.key) { + Ok(i) => i, + Err(i) => { + let value = init(); + entries.insert(i, Entry { key, value }); + i + } + }; + &*entries[index].value } - }; - - &*entries[index].1 + } } /// Clears the map and drops all task-locals. - pub fn clear(&self) { - let entries = unsafe { &mut *self.entries.get() }; - entries.clear(); - } -} - -// Wrap the future into one that drops task-local variables on exit. -pub(crate) unsafe fn add_finalizer(f: impl Future) -> impl Future { - async move { - let res = f.await; - - // Abort on panic because thread-local variables behave the same way. - abort_on_panic(|| worker::get_task(|task| task.metadata().local_map.clear())); - - res + /// + /// This method is only safe to call at the end of the task. + pub unsafe fn clear(&self) { + // Since destructors may attempt to access task-locals, we musnt't hold a mutable reference + // to the `Vec` while dropping them. Instead, we first take the `Vec` out and then drop it. + let entries = (*self.entries.get()).take(); + drop(entries); } } diff --git a/src/task/task_locals_wrapper.rs b/src/task/task_locals_wrapper.rs new file mode 100644 index 000000000..2a7ddb7af --- /dev/null +++ b/src/task/task_locals_wrapper.rs @@ -0,0 +1,84 @@ +use std::cell::Cell; +use std::ptr; + +use crate::task::{LocalsMap, Task, TaskId}; +use crate::utils::abort_on_panic; + +thread_local! { + /// A pointer to the currently running task. + static CURRENT: Cell<*const TaskLocalsWrapper> = Cell::new(ptr::null_mut()); +} + +/// A wrapper to store task local data. +pub(crate) struct TaskLocalsWrapper { + /// The actual task details. + task: Task, + + /// The map holding task-local values. + locals: LocalsMap, +} + +impl TaskLocalsWrapper { + /// Creates a new task handle. + /// + /// If the task is unnamed, the inner representation of the task will be lazily allocated on + /// demand. + #[inline] + pub(crate) fn new(task: Task) -> Self { + Self { + task, + locals: LocalsMap::new(), + } + } + + /// Gets the task's unique identifier. + #[inline] + pub fn id(&self) -> TaskId { + self.task.id() + } + + /// Returns a reference to the inner `Task`. + pub(crate) fn task(&self) -> &Task { + &self.task + } + + /// Returns the map holding task-local values. + pub(crate) fn locals(&self) -> &LocalsMap { + &self.locals + } + + /// Set a reference to the current task. + pub(crate) unsafe fn set_current(task: *const TaskLocalsWrapper, f: F) -> R + where + F: FnOnce() -> R, + { + CURRENT.with(|current| { + let old_task = current.replace(task); + defer! { + current.set(old_task); + } + f() + }) + } + + /// Gets a reference to the current task. + pub(crate) fn get_current(f: F) -> Option + where + F: FnOnce(&TaskLocalsWrapper) -> R, + { + let res = CURRENT.try_with(|current| unsafe { current.get().as_ref().map(f) }); + match res { + Ok(Some(val)) => Some(val), + Ok(None) | Err(_) => None, + } + } +} + +impl Drop for TaskLocalsWrapper { + fn drop(&mut self) { + // Abort the process if dropping task-locals panics. + abort_on_panic(|| { + unsafe { self.locals.clear() }; + }); + } +} diff --git a/src/task/worker.rs b/src/task/worker.rs deleted file mode 100644 index f7b08e16b..000000000 --- a/src/task/worker.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::cell::Cell; -use std::ptr; - -use crossbeam_deque::Worker; - -use super::pool; -use super::task; -use super::Task; -use crate::utils::abort_on_panic; - -/// Returns a handle to the current task. -/// -/// # Panics -/// -/// This function will panic if not called within the context of a task created by [`block_on`], -/// [`spawn`], or [`Builder::spawn`]. -/// -/// [`block_on`]: fn.block_on.html -/// [`spawn`]: fn.spawn.html -/// [`Builder::spawn`]: struct.Builder.html#method.spawn -/// -/// # Examples -/// -/// ``` -/// # async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// println!("The name of this task is {:?}", task::current().name()); -/// # -/// # }) -/// ``` -pub fn current() -> Task { - get_task(|task| task.clone()).expect("`task::current()` called outside the context of a task") -} - -thread_local! { - static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut()); -} - -pub(crate) fn set_tag(tag: *const task::Tag, f: F) -> R -where - F: FnOnce() -> R, -{ - struct ResetTag<'a>(&'a Cell<*const task::Tag>); - - impl Drop for ResetTag<'_> { - fn drop(&mut self) { - self.0.set(ptr::null()); - } - } - - TAG.with(|t| { - t.set(tag); - let _guard = ResetTag(t); - - f() - }) -} - -pub(crate) fn get_task(f: F) -> Option -where - F: FnOnce(&Task) -> R, -{ - let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) }); - - match res { - Ok(Some(val)) => Some(val), - Ok(None) | Err(_) => None, - } -} - -thread_local! { - static IS_WORKER: Cell = Cell::new(false); - static QUEUE: Cell>> = Cell::new(None); -} - -pub(crate) fn is_worker() -> bool { - IS_WORKER.with(|is_worker| is_worker.get()) -} - -fn get_queue) -> T, T>(f: F) -> T { - QUEUE.with(|queue| { - let q = queue.take().unwrap(); - let ret = f(&q); - queue.set(Some(q)); - ret - }) -} - -pub(crate) fn schedule(task: task::Runnable) { - if is_worker() { - get_queue(|q| q.push(task)); - } else { - pool::get().injector.push(task); - } - pool::get().sleepers.notify_one(); -} - -pub(crate) fn main_loop(worker: Worker) { - IS_WORKER.with(|is_worker| is_worker.set(true)); - QUEUE.with(|queue| queue.set(Some(worker))); - - loop { - match get_queue(|q| pool::get().find_task(q)) { - Some(task) => set_tag(task.tag(), || abort_on_panic(|| task.run())), - None => pool::get().sleepers.wait(), - } - } -} diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 2a4788d74..2b1fd0b92 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -1,8 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - +use std::future::Future; use std::pin::Pin; +use crate::task::{Context, Poll}; + /// Cooperatively gives up a timeslice to the task scheduler. /// /// Calling this function will move the currently executing future to the back @@ -18,16 +18,14 @@ use std::pin::Pin; /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::task; /// /// task::yield_now().await; /// # -/// # }) } +/// # }) /// ``` -#[cfg(feature = "unstable")] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub async fn yield_now() { YieldNow(false).await diff --git a/src/unit/extend.rs b/src/unit/extend.rs new file mode 100644 index 000000000..ffc0c2d9d --- /dev/null +++ b/src/unit/extend.rs @@ -0,0 +1,22 @@ +use std::pin::Pin; + +use crate::prelude::*; +use crate::stream::{self, IntoStream}; + +impl stream::Extend<()> for () { + fn extend<'a, S: IntoStream + 'a>( + &'a mut self, + stream: S, + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + while let Some(_) = stream.next().await {} + }) + } +} diff --git a/src/unit/from_stream.rs b/src/unit/from_stream.rs index a238982da..e88758323 100644 --- a/src/unit/from_stream.rs +++ b/src/unit/from_stream.rs @@ -5,12 +5,12 @@ use crate::stream::{FromStream, IntoStream}; impl FromStream<()> for () { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send, { - Box::pin(stream.into_stream().for_each(|_| ())) + Box::pin(stream.into_stream().for_each(drop)) } } diff --git a/src/unit/mod.rs b/src/unit/mod.rs index cb8063d0b..bbf28987d 100644 --- a/src/unit/mod.rs +++ b/src/unit/mod.rs @@ -4,3 +4,4 @@ //! asynchronously with values of type `()`. mod from_stream; +mod extend; diff --git a/src/utils.rs b/src/utils.rs index 60516d257..d1cb063bd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,25 +1,134 @@ -use std::mem; -use std::process; - /// Calls a function and aborts if it panics. /// /// This is useful in unsafe code where we can't recover from panics. +#[cfg(feature = "default")] #[inline] pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { struct Bomb; impl Drop for Bomb { fn drop(&mut self) { - process::abort(); + std::process::abort(); } } let bomb = Bomb; let t = f(); - mem::forget(bomb); + std::mem::forget(bomb); t } +/// Generates a random number in `0..n`. +#[cfg(feature = "unstable")] +pub fn random(n: u32) -> u32 { + use std::cell::Cell; + use std::num::Wrapping; + + thread_local! { + static RNG: Cell> = { + // Take the address of a local value as seed. + let mut x = 0i32; + let r = &mut x; + let addr = r as *mut i32 as usize; + Cell::new(Wrapping(addr as u32)) + } + } + + RNG.with(|rng| { + // This is the 32-bit variant of Xorshift. + // + // Source: https://en.wikipedia.org/wiki/Xorshift + let mut x = rng.get(); + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + rng.set(x); + + // This is a fast alternative to `x % n`. + // + // Author: Daniel Lemire + // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + ((u64::from(x.0)).wrapping_mul(u64::from(n)) >> 32) as u32 + }) +} + +/// Add additional context to errors +#[cfg(feature = "std")] +pub(crate) trait Context { + fn context(self, message: impl Fn() -> String) -> Self; +} + +#[cfg(all( + not(target_os = "unknown"), + any(feature = "default", feature = "unstable") +))] +mod timer { + pub type Timer = async_io::Timer; +} + +#[cfg(any(feature = "unstable", feature = "default"))] +pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer { + Timer::after(dur) +} + +#[cfg(any(all(target_arch = "wasm32", feature = "default"),))] +mod timer { + use std::pin::Pin; + use std::task::Poll; + + use gloo_timers::future::TimeoutFuture; + + #[derive(Debug)] + pub(crate) struct Timer(TimeoutFuture); + + impl Timer { + pub(crate) fn after(dur: std::time::Duration) -> Self { + // Round up to the nearest millisecond. + let mut timeout_ms = dur.as_millis() as u32; + if std::time::Duration::from_millis(timeout_ms as u64) < dur { + timeout_ms += 1; + } + + Timer(TimeoutFuture::new(timeout_ms)) + } + } + + impl std::future::Future for Timer { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match Pin::new(&mut self.0).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => Poll::Ready(()), + } + } + } +} + +#[cfg(any(feature = "unstable", feature = "default"))] +pub(crate) use timer::*; + +/// Defers evaluation of a block of code until the end of the scope. +#[cfg(feature = "default")] +#[doc(hidden)] +macro_rules! defer { + ($($body:tt)*) => { + let _guard = { + pub struct Guard(Option); + + impl Drop for Guard { + fn drop(&mut self) { + (self.0).take().map(|f| f()); + } + } + + Guard(Some(|| { + let _ = { $($body)* }; + })) + }; + }; +} + /// Declares unstable items. #[doc(hidden)] macro_rules! cfg_unstable { @@ -32,13 +141,25 @@ macro_rules! cfg_unstable { } } +/// Declares unstable and default items. +#[doc(hidden)] +macro_rules! cfg_unstable_default { + ($($item:item)*) => { + $( + #[cfg(all(feature = "default", feature = "unstable"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + $item + )* + } +} + /// Declares Unix-specific items. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_unix { ($($item:item)*) => { $( #[cfg(any(unix, feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(unix)))] $item )* } @@ -46,11 +167,11 @@ macro_rules! cfg_unix { /// Declares Windows-specific items. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_windows { ($($item:item)*) => { $( #[cfg(any(windows, feature = "docs"))] - #[cfg_attr(feature = "docs", doc(cfg(windows)))] $item )* } @@ -58,6 +179,7 @@ macro_rules! cfg_windows { /// Declares items when the "docs" feature is enabled. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_docs { ($($item:item)*) => { $( @@ -69,6 +191,7 @@ macro_rules! cfg_docs { /// Declares items when the "docs" feature is disabled. #[doc(hidden)] +#[allow(unused_macros)] macro_rules! cfg_not_docs { ($($item:item)*) => { $( @@ -78,104 +201,50 @@ macro_rules! cfg_not_docs { } } -/// Defines an extension trait for a base trait. -/// -/// In generated docs, the base trait will contain methods from the extension trait. In actual -/// code, the base trait will be re-exported and the extension trait will be hidden. We then -/// re-export the extension trait from the prelude. -/// -/// Inside invocations of this macro, we write a definitions that looks similar to the final -/// rendered docs, and the macro then generates all the boilerplate for us. +/// Declares std items. +#[allow(unused_macros)] #[doc(hidden)] -macro_rules! extension_trait { - ( - // Interesting patterns: - // - `$name`: trait name that gets rendered in the docs - // - `$ext`: name of the hidden extension trait - // - `$base`: base trait - #[doc = $doc:tt] - pub trait $name:ident { - $($body_base:tt)* - } - - pub trait $ext:ident: $base:path { - $($body_ext:tt)* - } - - // Shim trait impls that only appear in docs. - $($imp:item)* - ) => { - // A fake `impl Future` type that doesn't borrow. - #[allow(dead_code)] - mod owned { - #[doc(hidden)] - pub struct ImplFuture(std::marker::PhantomData); - } - - // A fake `impl Future` type that borrows its environment. - #[allow(dead_code)] - mod borrowed { - #[doc(hidden)] - pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); - } - - // Render a fake trait combining the bodies of the base trait and the extension trait. - #[cfg(feature = "docs")] - #[doc = $doc] - pub trait $name { - extension_trait!(@doc () $($body_base)* $($body_ext)*); - } - - // When not rendering docs, re-export the base trait from the futures crate. - #[cfg(not(feature = "docs"))] - pub use $base as $name; - - // The extension trait that adds methods to any type implementing the base trait. - /// Extension trait. - pub trait $ext: $base { - extension_trait!(@ext () $($body_ext)*); - } - - // Blanket implementation of the extension trait for any type implementing the base trait. - impl $ext for T {} - - // Shim trait impls that only appear in docs. - $(#[cfg(feature = "docs")] $imp)* - }; - - // Parse the return type in an extension method. - (@doc ($($head:tt)*) -> impl Future [$f:ty] $($tail:tt)*) => { - extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*); - }; - (@ext ($($head:tt)*) -> impl Future $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => { - extension_trait!(@ext ($($head)* -> $f) $($tail)*); - }; - - // Parse the return type in an extension method. - (@doc ($($head:tt)*) -> impl Future + $lt:lifetime [$f:ty] $($tail:tt)*) => { - extension_trait!(@doc ($($head)* -> borrowed::ImplFuture<$lt, $out>) $($tail)*); - }; - (@ext ($($head:tt)*) -> impl Future + $lt:lifetime [$f:ty] $($tail:tt)*) => { - extension_trait!(@ext ($($head)* -> $f) $($tail)*); - }; - - // Parse a token. - (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => { - extension_trait!(@doc ($($head)* $token) $($tail)*); - }; - (@ext ($($head:tt)*) $token:tt $($tail:tt)*) => { - extension_trait!(@ext ($($head)* $token) $($tail)*); - }; +macro_rules! cfg_std { + ($($item:item)*) => { + $( + #[cfg(feature = "std")] + $item + )* + } +} - // Handle the end of the token list. - (@doc ($($head:tt)*)) => { $($head)* }; - (@ext ($($head:tt)*)) => { $($head)* }; +/// Declares no-std items. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_alloc { + ($($item:item)*) => { + $( + #[cfg(feature = "alloc")] + $item + )* + } +} - // Parse imports at the beginning of the macro. - ($import:item $($tail:tt)*) => { - #[cfg(feature = "docs")] - $import +/// Declares default items. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_default { + ($($item:item)*) => { + $( + #[cfg(feature = "default")] + $item + )* + } +} - extension_trait!($($tail)*); - }; +/// Declares items that use I/O safety. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_io_safety { + ($($item:item)*) => { + $( + #[cfg(feature = "io_safety")] + $item + )* + } } diff --git a/src/vec/extend.rs b/src/vec/extend.rs index ecf68c83f..717338ab7 100644 --- a/src/vec/extend.rs +++ b/src/vec/extend.rs @@ -1,13 +1,16 @@ use std::pin::Pin; use crate::prelude::*; -use crate::stream::{Extend, IntoStream}; +use crate::stream::{self, IntoStream}; -impl Extend for Vec { - fn stream_extend<'a, S: IntoStream + 'a>( +impl stream::Extend for Vec { + fn extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, - ) -> Pin + 'a>> { + ) -> Pin + 'a + Send>> + where + ::IntoStream: Send, + { let stream = stream.into_stream(); self.reserve(stream.size_hint().0); diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index b326b04c8..95a1f98c9 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -3,95 +3,86 @@ use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; -use crate::stream::{Extend, FromStream, IntoStream}; +use crate::prelude::*; +use crate::stream::{self, FromStream, IntoStream}; -impl FromStream for Vec { +impl FromStream for Vec { #[inline] fn from_stream<'a, S: IntoStream>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: 'a + Send, { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - let mut out = vec![]; - out.stream_extend(stream).await; + stream::extend(&mut out, stream).await; out }) } } -impl<'b, T: Clone> FromStream for Cow<'b, [T]> { +impl<'b, T: Clone + Send> FromStream for Cow<'b, [T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Cow::Owned(FromStream::from_stream(stream).await) }) } } -impl FromStream for Box<[T]> { +impl FromStream for Box<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into_boxed_slice() }) } } -impl FromStream for Rc<[T]> { +impl FromStream for Rc<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into() }) } } -impl FromStream for Arc<[T]> { +impl FromStream for Arc<[T]> { #[inline] - fn from_stream<'a, S: IntoStream>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + 'a + Send>> where - ::IntoStream: 'a, + ::IntoStream: Send { let stream = stream.into_stream(); Box::pin(async move { - pin_utils::pin_mut!(stream); - Vec::from_stream(stream).await.into() }) } diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 77a0b746b..2efd3c3f9 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -6,5 +6,6 @@ mod extend; mod from_stream; +#[allow(unused)] #[doc(inline)] pub use std::vec::Vec; diff --git a/tests/addr.rs b/tests/addr.rs index aada557c3..fcd5aa1f0 100644 --- a/tests/addr.rs +++ b/tests/addr.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use async_std::net::ToSocketAddrs; diff --git a/tests/block_on.rs b/tests/block_on.rs index c422d0630..4c264804d 100644 --- a/tests/block_on.rs +++ b/tests/block_on.rs @@ -1,16 +1,63 @@ -use async_std::task; +#![cfg(not(target_os = "unknown"))] + +use async_std::{future::ready, task::block_on}; #[test] fn smoke() { - let res = task::block_on(async { 1 + 2 }); + let res = block_on(async { 1 + 2 }); assert_eq!(res, 3); } #[test] #[should_panic = "boom"] fn panic() { - task::block_on(async { + block_on(async { // This panic should get propagated into the parent thread. panic!("boom"); }); } + +#[cfg(feature = "unstable")] +#[test] +fn nested_block_on_local() { + use async_std::task::spawn_local; + + let x = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = spawn_local(async { block_on(async { ready(2).await }) }).await; + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(x, 3 + 2 + 1); + + let y = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = spawn_local(async { block_on(async { ready(2).await }) }).await; + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(y, 3 + 2 + 1); +} + +#[test] +fn nested_block_on() { + let x = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = block_on(async { block_on(async { ready(2).await }) }); + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(x, 3 + 2 + 1); + + let y = block_on(async { + let a = block_on(async { block_on(async { ready(3).await }) }); + let b = block_on(async { block_on(async { ready(2).await }) }); + let c = block_on(async { block_on(async { ready(1).await }) }); + a + b + c + }); + + assert_eq!(y, 3 + 2 + 1); +} diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index cb2368aac..442cf8a4d 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -2,14 +2,19 @@ use async_std::io::{self, BufWriter, SeekFrom}; use async_std::prelude::*; use async_std::task; +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer() { + #![allow(clippy::cognitive_complexity)] task::block_on(async { - let inner = Vec::new(); - let mut writer = BufWriter::with_capacity(2, inner); + let inner: Vec = Vec::new(); + let mut writer = BufWriter::>::with_capacity(2, inner); writer.write(&[0, 1]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1]); writer.write(&[2]).await.unwrap(); @@ -21,7 +26,7 @@ fn test_buffered_writer() { assert_eq!(*writer.get_ref(), [0, 1]); writer.flush().await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); writer.write(&[4]).await.unwrap(); @@ -34,31 +39,33 @@ fn test_buffered_writer() { assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); writer.write(&[7, 8]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); writer.write(&[9, 10, 11]).await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); writer.flush().await.unwrap(); - assert_eq!(writer.buffer(), []); + assert!(writer.buffer().is_empty()); assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); }) } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer_inner_into_inner_flushes() { task::block_on(async { - let mut w = BufWriter::with_capacity(3, Vec::new()); + let mut w = BufWriter::with_capacity(3, Vec::::new()); w.write(&[0, 1]).await.unwrap(); - assert_eq!(*w.get_ref(), []); + assert!(w.get_ref().is_empty()); let w = w.into_inner().await.unwrap(); assert_eq!(w, [0, 1]); }) } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn test_buffered_writer_seek() { task::block_on(async { let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); diff --git a/tests/channel.rs b/tests/channel.rs new file mode 100644 index 000000000..2aa271319 --- /dev/null +++ b/tests/channel.rs @@ -0,0 +1,376 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use async_std::channel::bounded as channel; +use async_std::task; +use rand::{Rng, SeedableRng}; + +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn smoke() { + task::block_on(async { + let (s, r) = channel(1); + + s.send(7).await.unwrap(); + assert_eq!(r.recv().await.unwrap(), 7); + + s.send(8).await.unwrap(); + assert_eq!(r.recv().await.unwrap(), 8); + + drop(s); + assert!(r.recv().await.is_err()); + }); + + task::block_on(async { + let (s, r) = channel(10); + drop(r); + assert!(s.send(1).await.is_err()); + }); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn capacity() { + for i in 1..10 { + let (s, r) = channel::<()>(i); + assert_eq!(s.capacity().unwrap(), i); + assert_eq!(r.capacity().unwrap(), i); + } +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn len_empty_full() { + #![allow(clippy::cognitive_complexity)] + task::block_on(async { + let (s, r) = channel(2); + + assert_eq!(s.len(), 0); + assert_eq!(s.is_empty(), true); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 0); + assert_eq!(r.is_empty(), true); + assert_eq!(r.is_full(), false); + + s.send(()).await.unwrap(); + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + + s.send(()).await.unwrap(); + + assert_eq!(s.len(), 2); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), true); + assert_eq!(r.len(), 2); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), true); + + let _ = r.recv().await; + + assert_eq!(s.len(), 1); + assert_eq!(s.is_empty(), false); + assert_eq!(s.is_full(), false); + assert_eq!(r.len(), 1); + assert_eq!(r.is_empty(), false); + assert_eq!(r.is_full(), false); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn recv() { + task::block_on(async { + let (s, r) = channel(100); + + spawn(async move { + assert_eq!(r.recv().await.unwrap(), 7); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await.unwrap(), 8); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await.unwrap(), 9); + assert!(r.recv().await.is_err()); + }); + + task::sleep(ms(1500)).await; + s.send(7).await.unwrap(); + s.send(8).await.unwrap(); + s.send(9).await.unwrap(); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn send() { + task::block_on(async { + let (s, r) = channel(1); + + spawn(async move { + s.send(7).await.unwrap(); + task::sleep(ms(1000)).await; + s.send(8).await.unwrap(); + task::sleep(ms(1000)).await; + s.send(9).await.unwrap(); + task::sleep(ms(1000)).await; + s.send(10).await.unwrap(); + }); + + task::sleep(ms(1500)).await; + assert_eq!(r.recv().await.unwrap(), 7); + assert_eq!(r.recv().await.unwrap(), 8); + assert_eq!(r.recv().await.unwrap(), 9); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn recv_after_disconnect() { + task::block_on(async { + let (s, r) = channel(100); + + s.send(1).await.unwrap(); + s.send(2).await.unwrap(); + s.send(3).await.unwrap(); + + drop(s); + + assert_eq!(r.recv().await.unwrap(), 1); + assert_eq!(r.recv().await.unwrap(), 2); + assert_eq!(r.recv().await.unwrap(), 3); + assert!(r.recv().await.is_err()); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn len() { + const COUNT: usize = 25_000; + const CAP: usize = 1000; + + task::block_on(async { + let (s, r) = channel(CAP); + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for _ in 0..CAP / 10 { + for i in 0..50 { + s.send(i).await.unwrap(); + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + let _ = r.recv().await; + assert_eq!(r.len(), 50 - i - 1); + } + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + for i in 0..CAP { + s.send(i).await.unwrap(); + assert_eq!(s.len(), i + 1); + } + + for _ in 0..CAP { + r.recv().await.unwrap(); + } + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + + let child = spawn({ + let r = r.clone(); + async move { + for i in 0..COUNT { + assert_eq!(r.recv().await.unwrap(), i); + let len = r.len(); + assert!(len <= CAP); + } + } + }); + + for i in 0..COUNT { + s.send(i).await.unwrap(); + let len = s.len(); + assert!(len <= CAP); + } + + child.await; + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn disconnect_wakes_receiver() { + task::block_on(async { + let (s, r) = channel::<()>(1); + + let child = spawn(async move { + assert!(r.recv().await.is_err()); + }); + + task::sleep(ms(1000)).await; + drop(s); + + child.await; + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn spsc() { + const COUNT: usize = 100_000; + + task::block_on(async { + let (s, r) = channel(3); + + let child = spawn(async move { + for i in 0..COUNT { + assert_eq!(r.recv().await.unwrap(), i); + } + assert!(r.recv().await.is_err()); + }); + + for i in 0..COUNT { + s.send(i).await.unwrap(); + } + drop(s); + + child.await; + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn mpmc() { + const COUNT: usize = 25_000; + const TASKS: usize = 4; + + task::block_on(async { + let (s, r) = channel::(3); + let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::>(); + let v = Arc::new(v); + + let mut tasks = Vec::new(); + + for _ in 0..TASKS { + let r = r.clone(); + let v = v.clone(); + tasks.push(spawn(async move { + for _ in 0..COUNT { + let n = r.recv().await.unwrap(); + v[n].fetch_add(1, Ordering::SeqCst); + } + })); + } + + for _ in 0..TASKS { + let s = s.clone(); + tasks.push(spawn(async move { + for i in 0..COUNT { + s.send(i).await.unwrap(); + } + })); + } + + for t in tasks { + t.await; + } + + for c in v.iter() { + assert_eq!(c.load(Ordering::SeqCst), TASKS); + } + }); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn oneshot() { + const COUNT: usize = 10_000; + + task::block_on(async { + for _ in 0..COUNT { + let (s, r) = channel(1); + + let c1 = spawn(async move { r.recv().await.unwrap() }); + let c2 = spawn(async move { s.send(0).await.unwrap() }); + + c1.await; + c2.await; + } + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn drops() { + const RUNS: usize = 100; + + static DROPS: AtomicUsize = AtomicUsize::new(0); + + #[derive(Debug, PartialEq)] + struct DropCounter; + + impl Drop for DropCounter { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + for _ in 0..RUNS { + let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0); + task::block_on(async move { + let steps = rng.gen_range(0..10_000); + let additional = rng.gen_range(0..50); + + DROPS.store(0, Ordering::SeqCst); + let (s, r) = channel::(50); + + let child = spawn({ + let r = r.clone(); + async move { + for _ in 0..steps { + r.recv().await.unwrap(); + } + } + }); + + for _ in 0..steps { + s.send(DropCounter).await.unwrap(); + } + + child.await; + + for _ in 0..additional { + s.send(DropCounter).await.unwrap(); + } + + assert_eq!(DROPS.load(Ordering::SeqCst), steps); + drop(s); + drop(r); + assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); + }) + } +} diff --git a/tests/collect.rs b/tests/collect.rs new file mode 100644 index 000000000..7ab80ccc9 --- /dev/null +++ b/tests/collect.rs @@ -0,0 +1,18 @@ +#[cfg(feature = "unstable")] +#[test] +fn test_send() { + use async_std::prelude::*; + use async_std::{stream, task}; + + task::block_on(async { + fn test_send_trait(_: &T) {} + + let stream = stream::repeat(1u8).take(10); + test_send_trait(&stream); + + let fut = stream.collect::>(); + + // This line triggers a compilation error + test_send_trait(&fut); + }); +} diff --git a/tests/condvar.rs b/tests/condvar.rs new file mode 100644 index 000000000..b5ec12a1e --- /dev/null +++ b/tests/condvar.rs @@ -0,0 +1,103 @@ +#![cfg(feature = "unstable")] +use std::sync::Arc; +use std::time::Duration; + +use async_std::sync::{Condvar, Mutex}; +use async_std::task::{self, JoinHandle}; + +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn wait_timeout_with_lock() { + task::block_on(async { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + spawn(async move { + let (m, c) = &*pair2; + let _g = m.lock().await; + task::sleep(Duration::from_millis(200)).await; + c.notify_one(); + }); + + let (m, c) = &*pair; + let (_, wait_result) = c + .wait_timeout(m.lock().await, Duration::from_millis(50)) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn wait_timeout_without_lock() { + task::block_on(async { + let m = Mutex::new(false); + let c = Condvar::new(); + + let (_, wait_result) = c + .wait_timeout(m.lock().await, Duration::from_millis(10)) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn wait_timeout_until_timed_out() { + task::block_on(async { + let m = Mutex::new(false); + let c = Condvar::new(); + + let (_, wait_result) = c + .wait_timeout_until(m.lock().await, Duration::from_millis(100), |&mut started| { + started + }) + .await; + assert!(wait_result.timed_out()); + }) +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn notify_all() { + task::block_on(async { + let mut tasks: Vec> = Vec::new(); + let pair = Arc::new((Mutex::new(0u32), Condvar::new())); + + for _ in 0..10 { + let pair = pair.clone(); + tasks.push(spawn(async move { + let (m, c) = &*pair; + let mut count = m.lock().await; + while *count == 0 { + count = c.wait(count).await; + } + *count += 1; + })); + } + + // Give some time for tasks to start up + task::sleep(Duration::from_millis(50)).await; + + let (m, c) = &*pair; + { + let mut count = m.lock().await; + *count += 1; + c.notify_all(); + } + + for t in tasks { + t.await; + } + let count = m.lock().await; + assert_eq!(11, *count); + }) +} diff --git a/tests/io_copy.rs b/tests/io_copy.rs new file mode 100644 index 000000000..394c34f8e --- /dev/null +++ b/tests/io_copy.rs @@ -0,0 +1,72 @@ +use std::{ + io::Result, + pin::Pin, + task::{Context, Poll}, +}; + +struct ReaderThatPanicsAfterEof { + read_count: usize, + has_sent_eof: bool, + max_read: usize, +} + +impl async_std::io::Read for ReaderThatPanicsAfterEof { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + if self.has_sent_eof { + panic!("this should be unreachable because we should not poll after eof (Ready(Ok(0)))") + } else if self.read_count >= self.max_read { + self.has_sent_eof = true; + Poll::Ready(Ok(0)) + } else { + self.read_count += 1; + Poll::Ready(Ok(buf.len())) + } + } +} + +struct WriterThatTakesAWhileToFlush { + max_flush: usize, + flush_count: usize, +} + +impl async_std::io::Write for WriterThatTakesAWhileToFlush { + fn poll_write(self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + Poll::Ready(Ok(buf.len())) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.flush_count += 1; + if self.flush_count >= self.max_flush { + Poll::Ready(Ok(())) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +#[test] +fn io_copy_does_not_poll_after_eof() { + async_std::task::block_on(async { + let mut reader = ReaderThatPanicsAfterEof { + has_sent_eof: false, + max_read: 10, + read_count: 0, + }; + + let mut writer = WriterThatTakesAWhileToFlush { + flush_count: 0, + max_flush: 10, + }; + + assert!(async_std::io::copy(&mut reader, &mut writer).await.is_ok()); + }) +} diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs index 85a17ab75..371150693 100644 --- a/tests/io_timeout.rs +++ b/tests/io_timeout.rs @@ -5,6 +5,14 @@ use async_std::task; #[test] #[should_panic(expected = "timed out")] +#[cfg(not(any( + target_os = "unknown", + target_arch = "arm", + target_arch = "mips", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "x86", +)))] // stdin tests fail when running through cross fn io_timeout_timedout() { task::block_on(async { io::timeout(Duration::from_secs(1), async { diff --git a/tests/mutex.rs b/tests/mutex.rs index fd1c07b38..76f42e285 100644 --- a/tests/mutex.rs +++ b/tests/mutex.rs @@ -5,7 +5,16 @@ use async_std::sync::Mutex; use async_std::task; use futures::channel::mpsc; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn smoke() { task::block_on(async { let m = Mutex::new(()); @@ -15,18 +24,21 @@ fn smoke() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn try_lock() { let m = Mutex::new(()); *m.try_lock().unwrap() = (); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner() { let m = Mutex::new(10); assert_eq!(m.into_inner(), 10); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn get_mut() { let mut m = Mutex::new(10); *m.get_mut() = 20; @@ -34,6 +46,7 @@ fn get_mut() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn contention() { task::block_on(async { let (tx, mut rx) = mpsc::unbounded(); @@ -42,22 +55,29 @@ fn contention() { let mutex = Arc::new(Mutex::new(0)); let num_tasks = 10000; + let mut handles = Vec::new(); for _ in 0..num_tasks { let tx = tx.clone(); let mutex = mutex.clone(); - task::spawn(async move { + handles.push(spawn(async move { let mut lock = mutex.lock().await; *lock += 1; tx.unbounded_send(()).unwrap(); drop(lock); - }); + })); } for _ in 0..num_tasks { rx.next().await.unwrap(); } + for handle in handles.into_iter() { + handle.await; + } + + dbg!("wait"); + let lock = mutex.lock().await; assert_eq!(num_tasks, *lock); }); diff --git a/tests/rwlock.rs b/tests/rwlock.rs index ff25e862d..1d33a456d 100644 --- a/tests/rwlock.rs +++ b/tests/rwlock.rs @@ -10,10 +10,18 @@ use async_std::sync::RwLock; use async_std::task; use futures::channel::mpsc; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// Generates a random number in `0..n`. pub fn random(n: u32) -> u32 { thread_local! { - static RNG: Cell> = Cell::new(Wrapping(1406868647)); + static RNG: Cell> = Cell::new(Wrapping(1_406_868_647)); } RNG.with(|rng| { @@ -35,6 +43,7 @@ pub fn random(n: u32) -> u32 { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn smoke() { task::block_on(async { let lock = RwLock::new(()); @@ -46,6 +55,7 @@ fn smoke() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn try_write() { task::block_on(async { let lock = RwLock::new(0isize); @@ -56,12 +66,14 @@ fn try_write() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner() { let lock = RwLock::new(10); assert_eq!(lock.into_inner(), 10); } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn into_inner_and_drop() { struct Counter(Arc); @@ -84,6 +96,7 @@ fn into_inner_and_drop() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn get_mut() { let mut lock = RwLock::new(10); *lock.get_mut() = 20; @@ -91,6 +104,7 @@ fn get_mut() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn contention() { const N: u32 = 10; const M: usize = 1000; @@ -104,7 +118,7 @@ fn contention() { let tx = tx.clone(); let rw = rw.clone(); - task::spawn(async move { + spawn(async move { for _ in 0..M { if random(N) == 0 { drop(rw.write().await); @@ -116,7 +130,7 @@ fn contention() { }); } - task::block_on(async { + task::block_on(async move { for _ in 0..N { rx.next().await.unwrap(); } @@ -124,6 +138,7 @@ fn contention() { } #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn writer_and_readers() { #[derive(Default)] struct Yield(Cell); @@ -146,7 +161,7 @@ fn writer_and_readers() { let (tx, mut rx) = mpsc::unbounded(); // Spawn a writer task. - task::spawn({ + spawn({ let lock = lock.clone(); async move { let mut lock = lock.write().await; @@ -164,13 +179,13 @@ fn writer_and_readers() { let mut readers = Vec::new(); for _ in 0..5 { let lock = lock.clone(); - readers.push(task::spawn(async move { + readers.push(spawn(async move { let lock = lock.read().await; assert!(*lock >= 0); })); } - task::block_on(async { + task::block_on(async move { // Wait for readers to pass their asserts. for r in readers { r.await; diff --git a/tests/stream.rs b/tests/stream.rs new file mode 100644 index 000000000..654735b2a --- /dev/null +++ b/tests/stream.rs @@ -0,0 +1,183 @@ +use std::convert::identity; +use std::marker::Unpin; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use pin_project_lite::pin_project; + +use async_std::channel::bounded as channel; +use async_std::prelude::*; +use async_std::stream; +use async_std::task; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +/// Checks that streams are merged fully even if one of the components +/// experiences delay. +fn merging_delayed_streams_work() { + let (sender, receiver) = channel::(10); + + let mut s = receiver.merge(stream::empty()); + let t = spawn(async move { + let mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x); + } + xs + }); + + task::block_on(async move { + task::sleep(std::time::Duration::from_millis(500)).await; + sender.send(92).await.unwrap(); + drop(sender); + let xs = t.await; + assert_eq!(xs, vec![92]) + }); + + let (sender, receiver) = channel::(10); + + let mut s = stream::empty().merge(receiver); + let t = spawn(async move { + let mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x); + } + xs + }); + + task::block_on(async move { + task::sleep(std::time::Duration::from_millis(500)).await; + sender.send(92).await.unwrap(); + drop(sender); + let xs = t.await; + assert_eq!(xs, vec![92]) + }); +} + +pin_project! { + /// The opposite of `Fuse`: makes the stream panic if polled after termination. + struct Explode { + #[pin] + done: bool, + #[pin] + inner: S, + } +} + +impl Stream for Explode { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + if *this.done { + panic!("KABOOM!") + } + let res = this.inner.poll_next(cx); + if let Poll::Ready(None) = &res { + *this.done = true; + } + res + } +} + +fn explode(s: S) -> Explode { + Explode { + done: false, + inner: s, + } +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn merge_works_with_unfused_streams() { + let s1 = explode(stream::once(92)); + let s2 = explode(stream::once(92)); + let mut s = s1.merge(s2); + + task::block_on(async move { + let mut xs = Vec::new(); + while let Some(x) = s.next().await { + xs.push(x) + } + assert_eq!(xs, vec![92, 92]); + }); +} + +struct S(T); + +impl Stream for S { + type Item = T::Item; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + unsafe { Pin::new_unchecked(&mut self.0) }.poll_next(ctx) + } +} + +struct StrictOnce { + polled: bool, +} + +impl Stream for StrictOnce { + type Item = (); + + fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll> { + assert!(!self.polled, "Polled after completion!"); + self.polled = true; + Poll::Ready(None) + } +} + +struct Interchanger { + polled: bool, +} + +impl Stream for Interchanger { + type Item = S + Unpin>>; + + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + if self.polled { + self.polled = false; + ctx.waker().wake_by_ref(); + Poll::Pending + } else { + self.polled = true; + Poll::Ready(Some(S(Box::new(StrictOnce { polled: false })))) + } + } +} + +#[test] +fn flat_map_doesnt_poll_completed_inner_stream() { + task::block_on(async { + assert_eq!( + Interchanger { polled: false } + .take(2) + .flat_map(identity) + .count() + .await, + 0 + ); + }); +} + +#[test] +fn flatten_doesnt_poll_completed_inner_stream() { + task::block_on(async { + assert_eq!( + Interchanger { polled: false } + .take(2) + .flatten() + .count() + .await, + 0 + ); + }); +} diff --git a/tests/task_local.rs b/tests/task_local.rs index 813185c84..b5345fec3 100644 --- a/tests/task_local.rs +++ b/tests/task_local.rs @@ -3,7 +3,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; use async_std::task; use async_std::task_local; +#[cfg(not(target_os = "unknown"))] +use async_std::task::spawn; +#[cfg(target_os = "unknown")] +use async_std::task::spawn_local as spawn; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + #[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn drop_local() { static DROP_LOCAL: AtomicBool = AtomicBool::new(false); @@ -20,7 +29,7 @@ fn drop_local() { } // Spawn a task that just touches its task-local. - let handle = task::spawn(async { + let handle = spawn(async { LOCAL.with(|_| ()); }); let task = handle.task().clone(); diff --git a/tests/tcp.rs b/tests/tcp.rs index c8281d713..f21737e8f 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; @@ -49,3 +51,70 @@ fn incoming_read() -> io::Result<()> { Ok(()) }) } + +#[test] +fn smoke_std_stream_to_async_listener() -> io::Result<()> { + use std::io::Write; + + task::block_on(async { + let listener = TcpListener::bind("127.0.0.1:0").await?; + let addr = listener.local_addr()?; + + let mut std_stream = std::net::TcpStream::connect(&addr)?; + std_stream.write_all(THE_WINTERS_TALE)?; + + let mut buf = vec![0; 1024]; + let mut incoming = listener.incoming(); + let mut stream = incoming.next().await.unwrap()?; + + let n = stream.read(&mut buf).await?; + assert_eq!(&buf[..n], THE_WINTERS_TALE); + + Ok(()) + }) +} + +#[test] +fn smoke_async_stream_to_std_listener() -> io::Result<()> { + use std::io::Read; + + let std_listener = std::net::TcpListener::bind("127.0.0.1:0")?; + let addr = std_listener.local_addr()?; + + task::block_on(async move { + let mut stream = TcpStream::connect(&addr).await?; + stream.write_all(THE_WINTERS_TALE).await?; + io::Result::Ok(()) + })?; + + let mut buf = vec![0; 1024]; + let mut incoming = std_listener.incoming(); + let mut stream = incoming.next().unwrap()?; + + let n = stream.read(&mut buf).unwrap(); + assert_eq!(&buf[..n], THE_WINTERS_TALE); + + Ok(()) +} + +#[test] +fn cloned_streams() -> io::Result<()> { + task::block_on(async { + let listener = TcpListener::bind("127.0.0.1:0").await?; + let addr = listener.local_addr()?; + + let mut stream = TcpStream::connect(&addr).await?; + let mut cloned_stream = stream.clone(); + let mut incoming = listener.incoming(); + let mut write_stream = incoming.next().await.unwrap()?; + write_stream.write_all(b"Each your doing").await?; + + let mut buf = [0; 15]; + stream.read_exact(&mut buf[..8]).await?; + cloned_stream.read_exact(&mut buf[8..]).await?; + + assert_eq!(&buf[..15], b"Each your doing"); + + Ok(()) + }) +} diff --git a/tests/timeout.rs b/tests/timeout.rs new file mode 100644 index 000000000..60594d06b --- /dev/null +++ b/tests/timeout.rs @@ -0,0 +1,26 @@ +use std::time::Duration; + +use async_std::future::timeout; +use async_std::task; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +fn timeout_future_many() { + task::block_on(async { + let futures = (0..10) + .map(|i| { + timeout(Duration::from_millis(i * 50), async move { + task::sleep(Duration::from_millis(i)).await; + Ok::<(), async_std::future::TimeoutError>(()) + }) + }) + .collect::>(); + + for future in futures { + future.await.unwrap().unwrap(); + } + }); +} diff --git a/tests/udp.rs b/tests/udp.rs index 319dc74ae..cd119ddcd 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_os = "unknown"))] + use async_std::io; use async_std::net::UdpSocket; use async_std::task; @@ -10,17 +12,20 @@ const THE_MERCHANT_OF_VENICE: &[u8] = b" "; #[test] -fn send_recv() -> io::Result<()> { +fn send_recv_peek() -> io::Result<()> { task::block_on(async { let socket1 = UdpSocket::bind("127.0.0.1:0").await?; let socket2 = UdpSocket::bind("127.0.0.1:0").await?; socket1.connect(socket2.local_addr()?).await?; socket2.connect(socket1.local_addr()?).await?; - + assert_eq!(socket1.peer_addr()?, socket2.local_addr()?); socket1.send(THE_MERCHANT_OF_VENICE).await?; let mut buf = [0u8; 1024]; + let n = socket2.peek(&mut buf).await?; + assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE); + let n = socket2.recv(&mut buf).await?; assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE); diff --git a/tests/uds.rs b/tests/uds.rs index 3ab4d6ba4..dcdce443c 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -1,12 +1,10 @@ -#![cfg(unix)] +#![cfg(all(unix, not(target_os = "unknown")))] use async_std::io; use async_std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; use async_std::prelude::*; use async_std::task; -use tempdir::TempDir; - use std::time::Duration; const JULIUS_CAESAR: &[u8] = b" @@ -29,7 +27,7 @@ fn send_recv() -> io::Result<()> { } #[test] -fn into_raw_fd() -> io::Result<()> { +fn into_raw_fd_datagram() -> io::Result<()> { use async_std::os::unix::io::{FromRawFd, IntoRawFd}; task::block_on(async { let (socket1, socket2) = UnixDatagram::pair().unwrap(); @@ -45,13 +43,33 @@ fn into_raw_fd() -> io::Result<()> { }) } +#[test] +fn into_raw_fd_stream() -> io::Result<()> { + use async_std::os::unix::io::{FromRawFd, IntoRawFd}; + task::block_on(async { + let (mut socket1, socket2) = UnixStream::pair().unwrap(); + socket1.write(JULIUS_CAESAR).await?; + + let mut buf = vec![0; 1024]; + + let mut socket2 = unsafe { UnixStream::from_raw_fd(socket2.into_raw_fd()) }; + let n = socket2.read(&mut buf).await?; + assert_eq!(&buf[..n], JULIUS_CAESAR); + + Ok(()) + }) +} + const PING: &[u8] = b"ping"; const PONG: &[u8] = b"pong"; const TEST_TIMEOUT: Duration = Duration::from_secs(3); #[test] fn socket_ping_pong() { - let tmp_dir = TempDir::new("socket_ping_pong").expect("Temp dir not created"); + let tmp_dir = tempfile::Builder::new() + .prefix("socket_ping_pong") + .tempdir() + .expect("Temp dir not created"); let sock_path = tmp_dir.as_ref().join("sock"); let iter_cnt = 16; @@ -94,3 +112,30 @@ async fn ping_pong_client(socket: &std::path::PathBuf, iterations: u32) -> std:: } Ok(()) } + +#[test] +fn uds_clone() -> io::Result<()> { + task::block_on(async { + let tmp_dir = tempfile::Builder::new() + .prefix("socket_ping_pong") + .tempdir() + .expect("Temp dir not created"); + let sock_path = tmp_dir.as_ref().join("sock"); + let input = UnixListener::bind(&sock_path).await?; + + let mut writer = UnixStream::connect(&sock_path).await?; + let mut reader = input.incoming().next().await.unwrap()?; + + writer.write(b"original").await.unwrap(); + let mut original_buf = [0; 8]; + reader.read(&mut original_buf).await?; + assert_eq!(&original_buf, b"original"); + + writer.clone().write(b"clone").await.unwrap(); + let mut clone_buf = [0; 5]; + reader.clone().read(&mut clone_buf).await?; + assert_eq!(&clone_buf, b"clone"); + + Ok(()) + }) +} diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs new file mode 100644 index 000000000..2876183ef --- /dev/null +++ b/tests/verbose_errors.rs @@ -0,0 +1,33 @@ +#![cfg(not(target_os = "unknown"))] + +use async_std::{fs, io, net::ToSocketAddrs, task}; + +#[test] +fn open_file() { + task::block_on(async { + let non_existing_file = "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas"; + let res = fs::File::open(non_existing_file).await; + match res { + Ok(_) => panic!("Found file with random name: We live in a simulation"), + Err(e) => assert_eq!( + "could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`", + &format!("{}", e) + ), + } + }) +} + +#[test] +fn resolve_address() { + task::block_on(async { + let non_existing_addr = "ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80"; + let res: Result<_, io::Error> = non_existing_addr.to_socket_addrs().await; + match res { + Ok(_) => panic!("Found address with random name: We live in a simulation"), + Err(e) => assert_eq!( + "could not resolve address `\"ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80\"`", + &format!("{}", e) + ), + } + }) +}