From 735d604cd14422fcb7f974130d616993d73ce190 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 5 Oct 2019 22:17:21 +0300 Subject: [PATCH 01/38] Adds stream::repeat_with --- src/stream/mod.rs | 2 + src/stream/repeat_with.rs | 103 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/stream/repeat_with.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8aa12a2..f2fcdeb 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,6 +26,7 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; 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, Zip}; pub(crate) mod stream; @@ -33,6 +34,7 @@ pub(crate) mod stream; mod empty; mod once; mod repeat; +mod repeat_with; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs new file mode 100644 index 0000000..b682dfa --- /dev/null +++ b/src/stream/repeat_with.rs @@ -0,0 +1,103 @@ +use std::marker::PhantomData; +use std::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 clousre. +/// +/// This stream is constructed by the [`repeat_with`] function. +/// +/// [`repeat_with`]: fn.repeat_with.html +#[derive(Debug)] +pub struct RepeatWith { + f: F, + future: Option, + __a: PhantomData, +} + +/// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::repeat_with(|| async { 1 }); +/// +/// +/// 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(1)); +/// assert_eq!(s.next().await, Some(1)); +/// +/// # }) } +/// ``` +/// +/// Going finite: +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream; +/// +/// let s = stream::repeat_with(|| async { 1u8 }).take(2); +/// +/// +/// pin_utils::pin_mut!(s); +/// +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, Some(1)); +/// assert_eq!(s.next().await, None); +/// +/// # }) } +/// ``` +pub fn repeat_with(repeater: F) -> RepeatWith { + RepeatWith { + f: repeater, + future: None, + __a: PhantomData, + } +} + +impl RepeatWith { + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_pinned!(future: Option); +} + +impl Stream for RepeatWith +where + F: FnMut() -> Fut, + Fut: Future, +{ + type Item = A; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match self.future.is_some() { + true => { + 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)); + } + false => { + let fut = (self.as_mut().f())(); + + self.as_mut().future().set(Some(fut)); + } + } + } + } +} From 2384df11ed27e186ae7cab6db7938165db58898f Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 6 Oct 2019 08:27:44 +0300 Subject: [PATCH 02/38] Apply suggestions from code review Co-Authored-By: Yoshua Wuyts --- src/stream/repeat_with.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index b682dfa..ce249dd 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -5,7 +5,7 @@ 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 clousre. +/// A stream that repeats elements of type `T` endlessly by applying a provided closure. /// /// This stream is constructed by the [`repeat_with`] function. /// @@ -31,14 +31,12 @@ pub struct RepeatWith { /// /// let s = stream::repeat_with(|| async { 1 }); /// -/// /// 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(1)); /// assert_eq!(s.next().await, Some(1)); -/// /// # }) } /// ``` /// @@ -52,13 +50,11 @@ pub struct RepeatWith { /// /// let s = stream::repeat_with(|| async { 1u8 }).take(2); /// -/// /// pin_utils::pin_mut!(s); /// /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, None); -/// /// # }) } /// ``` pub fn repeat_with(repeater: F) -> RepeatWith { From 49d123c7f9b5b5a1d1e63310631dda7c57ac1100 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 6 Oct 2019 08:32:44 +0300 Subject: [PATCH 03/38] Fix review nits --- src/stream/repeat_with.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index ce249dd..f38b323 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -57,7 +57,11 @@ pub struct RepeatWith { /// 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, +{ RepeatWith { f: repeater, future: None, @@ -79,8 +83,8 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { - match self.future.is_some() { - true => { + match &self.future { + Some(_) => { let res = futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); @@ -88,7 +92,7 @@ where return Poll::Ready(Some(res)); } - false => { + None => { let fut = (self.as_mut().f())(); self.as_mut().future().set(Some(fut)); From aed9e2efdf779f2127fbd8350a7b51c856e68d04 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:27:27 +0200 Subject: [PATCH 04/38] removes Travis CI (#333) Signed-off-by: Yoshua Wuyts --- .travis.yml | 68 ----------------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bb3c836..0000000 --- a/.travis.yml +++ /dev/null @@ -1,68 +0,0 @@ -language: rust - -env: - - RUSTFLAGS="-D warnings" - -# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`. -cache: - directories: - - /home/travis/.cargo - -# Don't cache the cargo registry because it's too big. -before_cache: - - rm -rf /home/travis/.cargo/registry - - -branches: - only: - - master - - staging - - trying - -matrix: - fast_finish: true - include: - - rust: nightly - os: linux - - - rust: nightly - os: osx - osx_image: xcode9.2 - - - rust: nightly-x86_64-pc-windows-msvc - os: windows - - - name: fmt - rust: nightly - os: linux - before_script: | - if ! rustup component add rustfmt; then - target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`; - echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead"; - rustup toolchain install nightly-$target; - rustup default nightly-$target; - rustup component add rustfmt; - fi - script: - - cargo fmt --all -- --check - - - name: docs - rust: nightly - os: linux - script: - - cargo doc --features docs - - - name: book - rust: nightly - os: linux - before_script: - - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh - - cargo build # to find 'extern crate async_std' by `mdbook test` - script: - - mdbook build docs - - mdbook test -L ./target/debug/deps docs - -script: - - cargo check --all --benches --bins --examples --tests - - cargo check --features unstable --all --benches --bins --examples --tests - - cargo test --all --doc --features unstable From 35fc85a157b961aade8ee1d985af2174083fc0bf Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:27:41 +0200 Subject: [PATCH 05/38] clean readme (#331) * clean readme Signed-off-by: Yoshua Wuyts * add back features Signed-off-by: Yoshua Wuyts * Update README.md Co-Authored-By: Stjepan Glavina --- README.md | 203 ++++++++++++++++++++++++++---------------------------- 1 file changed, 96 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index ae129aa..7aeaed8 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,78 @@ -# Async version of the Rust standard library - -[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std) -[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std) -[![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std) -[![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std) -[![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe) - -This crate provides an async version of [`std`]. It provides all the interfaces you -are used to, but in an async version and ready for Rust's `async`/`await` syntax. +

async-std

+
+ + Async version of the Rust standard library + +
+ +
+ +
+ + + Crates.io version + + + + Download + + + + docs.rs docs + + + + chat + +
+ +
+

+ + API Docs + + | + + Book + + | + + Releases + + | + + Contributing + +

+
+ +
+ +This crate provides an async version of [`std`]. It provides all the interfaces +you are used to, but in an async version and ready for Rust's `async`/`await` +syntax. [`std`]: https://doc.rust-lang.org/std/index.html -## Documentation +## Features -`async-std` comes with [extensive API documentation][docs] and a [book][book]. +- __Modern:__ Built from the ground up for `std::future` and `async/await` with + blazing fast compilation times. +- __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 + APIs once. +- __Clear:__ [Detailed documentation][docs] and [accessible guides][book] mean + using async Rust was never easier. [docs]: https://docs.rs/async-std [book]: https://book.async.rs -## Quickstart - -Add the following lines to your `Cargo.toml`: - -```toml -[dependencies] -async-std = "0.99" -``` - -Or use [cargo add][cargo-add] if you have it installed: - -```sh -$ cargo add async-std -``` - -[cargo-add]: https://github.com/killercup/cargo-edit - -## Hello world +## Examples ```rust use async_std::task; @@ -47,96 +84,48 @@ fn main() { } ``` -## Low-Friction Sockets with Built-In Timeouts - -```rust -use std::time::Duration; - -use async_std::{ - prelude::*, - task, - io, - net::TcpStream, -}; - -async fn get() -> io::Result> { - let mut stream = TcpStream::connect("example.com:80").await?; - stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?; - - let mut buf = vec![]; - - io::timeout(Duration::from_secs(5), async { - stream.read_to_end(&mut buf).await?; - Ok(buf) - }).await -} - -fn main() { - task::block_on(async { - let raw_response = get().await.expect("request"); - let response = String::from_utf8(raw_response) - .expect("utf8 conversion"); - println!("received: {}", response); - }); -} -``` - -## Features - -`async-std` is strongly commited to following semver. This means your code won't -break unless _you_ decide to upgrade. - -However every now and then we come up with something that we think will work -_great_ for `async-std`, and we want to provide a sneak-peek so you can try it -out. This is what we call _"unstable"_ features. You can try out the unstable -features by enabling the `unstable` feature in your `Cargo.toml` file: - -```toml -[dependencies.async-std] -version = "0.99" -features = ["unstable"] -``` - -Just be careful when using these features, as they may change between -versions. +More examples, including networking and file access, can be found in our +[`examples`] directory. -## Take a look around +[`examples`]: https://github.com/async-rs/async-std/tree/master/examples -Clone the repo: +## Philosophy -``` -git clone git@github.com:async-rs/async-std.git && cd async-std -``` +We believe Async Rust should be as easy to pick up as Sync Rust. We also believe +that the best API is the one you already know. And finally, we believe that +providing an asynchronous counterpart to the standard library is the best way +stdlib provides a reliable basis for both performance and productivity. -Generate docs: +Async-std is the embodiment of that vision. It combines single-allocation task +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. -``` -cargo +nightly doc --features docs --open -``` +## Installation -Check out the [examples](examples). To run an example: +With [cargo add][cargo-add] installed run: +```sh +$ cargo add async-std ``` -cargo +nightly run --example hello-world -``` - -## Contributing -See [our contribution document][contribution]. +We also provide a set of "unstable" features with async-std. See the [features +documentation] on how to enable them. -[contribution]: https://async.rs/contribute +[cargo-add]: https://github.com/killercup/cargo-edit +[features documentation]: https://docs.rs/async-std/#features ## License -Licensed under either of - - * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + -#### Contribution +
+ Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + From e938527f660647ab0efecf5c9e1ff71191f33901 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 15:30:24 +0200 Subject: [PATCH 06/38] add stream::interval (#298) * add stream::interval Signed-off-by: Yoshua Wuyts * fix tests Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts * cross-docs Signed-off-by: Yoshua Wuyts * update deps Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- src/io/timeout.rs | 22 ++--- src/stream/interval.rs | 198 +++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 4 +- src/task/sleep.rs | 4 + 5 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 src/stream/interval.rs diff --git a/Cargo.toml b/Cargo.toml index 0c65298..dc319f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" futures-core-preview = "=0.3.0-alpha.19" futures-io-preview = "=0.3.0-alpha.19" -futures-timer = "0.4.0" +futures-timer = "1.0.2" lazy_static = "1.4.0" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 69b6892..9fcc15e 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -2,8 +2,8 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use futures_core::future::TryFuture; use futures_timer::Delay; +use pin_utils::unsafe_pinned; use crate::future::Future; use crate::io; @@ -36,16 +36,16 @@ pub async fn timeout(dur: Duration, f: F) -> io::Result where F: Future>, { - let f = TimeoutFuture { + Timeout { timeout: Delay::new(dur), future: f, - }; - f.await + } + .await } -// Future returned by the [`io::timeout`](./fn.timeout.html) function. +/// Future returned by the `FutureExt::timeout` method. #[derive(Debug)] -pub struct TimeoutFuture +pub struct Timeout where F: Future>, { @@ -53,22 +53,22 @@ where timeout: Delay, } -impl TimeoutFuture +impl Timeout where F: Future>, { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(timeout: Delay); + unsafe_pinned!(future: F); + unsafe_pinned!(timeout: Delay); } -impl Future for TimeoutFuture +impl Future for Timeout where F: Future>, { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().try_poll(cx) { + match self.as_mut().future().poll(cx) { Poll::Pending => {} other => return other, } diff --git a/src/stream/interval.rs b/src/stream/interval.rs new file mode 100644 index 0000000..271cd81 --- /dev/null +++ b/src/stream/interval.rs @@ -0,0 +1,198 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; + +use futures_core::future::Future; +use futures_core::stream::Stream; +use pin_utils::unsafe_pinned; + +use futures_timer::Delay; + +/// Creates a new stream that yields at a set interval. +/// +/// The stream first yields after `dur`, and continues to yield every +/// `dur` after that. The stream accounts for time elapsed between calls, and +/// will adjust accordingly to prevent time skews. +/// +/// Each interval may be slightly longer than the specified duration, but never +/// less. +/// +/// Note that intervals are not intended for high resolution timers, but rather +/// they will likely fire some granularity after the exact instant that they're +/// otherwise indicated to fire at. +/// +/// See also: [`task::sleep`]. +/// +/// [`task::sleep`]: ../task/fn.sleep.html +/// +/// # Examples +/// +/// Basic example: +/// +/// ```no_run +/// use async_std::prelude::*; +/// use async_std::stream; +/// use std::time::Duration; +/// +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// let mut interval = stream::interval(Duration::from_secs(4)); +/// while let Some(_) = interval.next().await { +/// println!("prints every four seconds"); +/// } +/// # +/// # Ok(()) }) } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub fn interval(dur: Duration) -> Interval { + Interval { + delay: Delay::new(dur), + interval: dur, + } +} + +/// A stream representing notifications at fixed interval +/// +#[derive(Debug)] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[doc(inline)] +pub struct Interval { + delay: Delay, + 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() { + return Poll::Pending; + } + let when = Instant::now(); + let next = next_interval(when, Instant::now(), self.interval); + self.delay.reset(next); + 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 070983e..f34e9dc 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -44,13 +44,15 @@ cfg_if! { mod extend; mod from_stream; mod into_stream; + mod interval; pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; - pub use into_stream::IntoStream; pub use fused_stream::FusedStream; + pub use into_stream::IntoStream; + pub use interval::{interval, Interval}; pub use stream::Merge; } diff --git a/src/task/sleep.rs b/src/task/sleep.rs index 3e98755..380ee3e 100644 --- a/src/task/sleep.rs +++ b/src/task/sleep.rs @@ -11,6 +11,10 @@ use crate::io; /// /// [`std::thread::sleep`]: https://doc.rust-lang.org/std/thread/fn.sleep.html /// +/// See also: [`stream::interval`]. +/// +/// [`stream::interval`]: ../stream/fn.interval.html +/// /// # Examples /// /// ``` From 23beab412506dd441acce18b3599fb4cd5abbb24 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 15 Oct 2019 16:50:17 +0300 Subject: [PATCH 07/38] Adds a from_fn stream implementation (#277) * Adds a from_fn stream implementation * Update src/stream/from_fn.rs Co-Authored-By: Yoshua Wuyts * Fix review nits * Use async_std Mutex --- src/stream/from_fn.rs | 100 ++++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 + 2 files changed, 102 insertions(+) create mode 100644 src/stream/from_fn.rs diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs new file mode 100644 index 0000000..c1cb97a --- /dev/null +++ b/src/stream/from_fn.rs @@ -0,0 +1,100 @@ +use std::marker::PhantomData; +use std::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. +/// +/// [`from_fn`]: fn.from_fn.html +#[derive(Debug)] +pub struct FromFn { + f: F, + future: Option, + __t: PhantomData, +} + +/// 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 +/// syntax of creating a dedicated type and implementing a `Stream` trait for it. +/// +/// # Examples +/// +/// ``` +/// # fn main() { 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 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) +/// } +/// } +/// }); +/// +/// 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 +where + F: FnMut() -> Fut, + Fut: Future>, +{ + FromFn { + f, + future: None, + __t: PhantomData, + } +} + +impl FromFn { + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_pinned!(future: Option); +} + +impl Stream for FromFn +where + F: FnMut() -> Fut, + Fut: Future>, +{ + 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)); + } + } + } + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f34e9dc..87f3f84 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -24,6 +24,7 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; +pub use from_fn::{from_fn, FromFn}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{ @@ -33,6 +34,7 @@ pub use stream::{ pub(crate) mod stream; mod empty; +mod from_fn; mod once; mod repeat; From 1a3429655c4f9208891756483e6ca87ba71d08bb Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:03:28 +0200 Subject: [PATCH 08/38] init blocking-updates Signed-off-by: Yoshua Wuyts --- src/task/blocking.rs | 5 +++-- src/task/mod.rs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 53b52c8..012158d 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -96,12 +96,13 @@ fn schedule(t: async_task::Task) { /// Spawns a blocking task. /// /// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. -pub(crate) fn spawn(future: F) -> JoinHandle +pub(crate) fn spawn(f: F) -> JoinHandle where - F: Future + Send + 'static, + 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) diff --git a/src/task/mod.rs b/src/task/mod.rs index 727dc59..21b85fd 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -73,7 +73,7 @@ cfg_if::cfg_if! { /// # /// use async_std::task; /// -/// task::blocking(async { +/// task::blocking(|| { /// println!("long-running task here"); /// }).await; /// # @@ -84,10 +84,10 @@ cfg_if::cfg_if! { #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] -pub fn blocking(future: F) -> task::JoinHandle +pub fn blocking(f: F) -> task::JoinHandle where - F: crate::future::Future + Send + 'static, + F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - blocking::spawn(future) + blocking::spawn_blocking(future) } From b4c1c63fd27f3ce6350d9ba11689fe801fb8312e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:21:49 +0200 Subject: [PATCH 09/38] task::blocking async closure -> FnOnce Signed-off-by: Yoshua Wuyts --- src/fs/canonicalize.rs | 2 +- src/fs/copy.rs | 2 +- src/fs/create_dir.rs | 2 +- src/fs/create_dir_all.rs | 2 +- src/fs/dir_builder.rs | 2 +- src/fs/dir_entry.rs | 4 ++-- src/fs/file.rs | 20 ++++++++++---------- src/fs/hard_link.rs | 2 +- src/fs/metadata.rs | 2 +- src/fs/open_options.rs | 2 +- src/fs/read.rs | 2 +- src/fs/read_dir.rs | 4 ++-- src/fs/read_link.rs | 2 +- src/fs/read_to_string.rs | 2 +- src/fs/remove_dir.rs | 2 +- src/fs/remove_dir_all.rs | 2 +- src/fs/remove_file.rs | 2 +- src/fs/rename.rs | 2 +- src/fs/set_permissions.rs | 2 +- src/fs/symlink_metadata.rs | 2 +- src/fs/write.rs | 2 +- src/io/stderr.rs | 4 ++-- src/io/stdin.rs | 4 ++-- src/io/stdout.rs | 4 ++-- src/net/addr.rs | 5 ++--- src/net/tcp/stream.rs | 2 +- src/os/unix/fs.rs | 2 +- src/os/unix/net/datagram.rs | 2 +- src/os/unix/net/listener.rs | 2 +- src/os/unix/net/stream.rs | 2 +- src/task/blocking.rs | 1 - 31 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index a763e4e..601d477 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -32,5 +32,5 @@ use crate::task::blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::canonicalize(&path).map(Into::into) }).await + blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index c0c6b9e..733fb64 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -41,5 +41,5 @@ 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(async move { std::fs::copy(&from, &to) }).await + blocking::spawn(move || std::fs::copy(&from, &to)).await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 99f4ac4..740d303 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -34,5 +34,5 @@ use crate::task::blocking; /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::create_dir(path) }).await + blocking::spawn(move || std::fs::create_dir(path)).await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 0dc446e..76604de 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::create_dir_all(path) }).await + blocking::spawn(move || std::fs::create_dir_all(path)).await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 1fb0850..738ddbc 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -108,7 +108,7 @@ impl DirBuilder { } let path = path.as_ref().to_owned(); - async move { blocking::spawn(async move { builder.create(path) }).await } + async move { blocking::spawn(move || builder.create(path)).await } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 3d42f83..80d4bbd 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -89,7 +89,7 @@ impl DirEntry { /// ``` pub async fn metadata(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(async move { inner.metadata() }).await + blocking::spawn(move || inner.metadata()).await } /// Reads the file type for this entry. @@ -127,7 +127,7 @@ impl DirEntry { /// ``` pub async fn file_type(&self) -> io::Result { let inner = self.0.clone(); - blocking::spawn(async move { inner.file_type() }).await + blocking::spawn(move || inner.file_type()).await } /// Returns the bare name of this entry without the leading path. diff --git a/src/fs/file.rs b/src/fs/file.rs index b52bcd0..3916234 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -97,7 +97,7 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?; + let file = blocking::spawn(move || std::fs::File::open(&path)).await?; Ok(file.into()) } @@ -132,7 +132,7 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?; + let file = blocking::spawn(move || std::fs::File::create(&path)).await?; Ok(file.into()) } @@ -165,7 +165,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.sync_all() }).await + blocking::spawn(move || state.file.sync_all()).await } /// Synchronizes OS-internal buffered contents to disk. @@ -201,7 +201,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.sync_data() }).await + blocking::spawn(move || state.file.sync_data()).await } /// Truncates or extends the file. @@ -234,7 +234,7 @@ impl File { }) .await?; - blocking::spawn(async move { state.file.set_len(size) }).await + blocking::spawn(move || state.file.set_len(size)).await } /// Reads the file's metadata. @@ -253,7 +253,7 @@ impl File { /// ``` pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); - blocking::spawn(async move { file.metadata() }).await + blocking::spawn(move || file.metadata()).await } /// Changes the permissions on the file. @@ -282,7 +282,7 @@ impl File { /// ``` pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); - blocking::spawn(async move { file.set_permissions(perm) }).await + blocking::spawn(move || file.set_permissions(perm)).await } } @@ -702,7 +702,7 @@ impl LockGuard { self.register(cx); // Start a read operation asynchronously. - blocking::spawn(async move { + blocking::spawn(move || { // Read some data from the file into the cache. let res = { let State { file, cache, .. } = &mut *self; @@ -811,7 +811,7 @@ impl LockGuard { self.register(cx); // Start a write operation asynchronously. - blocking::spawn(async move { + blocking::spawn(move || { match (&*self.file).write_all(&self.cache) { Ok(_) => { // Switch to idle mode. @@ -844,7 +844,7 @@ impl LockGuard { self.register(cx); // Start a flush operation asynchronously. - blocking::spawn(async move { + blocking::spawn(move || { match (&*self.file).flush() { Ok(()) => { // Mark the file as flushed. diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 2ae2cad..8b09b5d 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -32,5 +32,5 @@ 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(async move { std::fs::hard_link(&from, &to) }).await + blocking::spawn(move || std::fs::hard_link(&from, &to)).await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 8c92381..65f494b 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -36,7 +36,7 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::metadata(path) }).await + blocking::spawn(move || std::fs::metadata(path)).await } cfg_if! { diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index d7e0454..a9511f1 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -285,7 +285,7 @@ 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(async move { options.open(path).map(|f| f.into()) }).await } + async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await } } } diff --git a/src/fs/read.rs b/src/fs/read.rs index b562ea3..a0eb130 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -36,5 +36,5 @@ use crate::task::blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::read(path) }).await + blocking::spawn(move || std::fs::read(path)).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index fd1a21f..6e47801 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -45,7 +45,7 @@ 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(async move { std::fs::read_dir(path) }) + blocking::spawn(move || std::fs::read_dir(path)) .await .map(ReadDir::new) } @@ -91,7 +91,7 @@ impl Stream for ReadDir { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - self.0 = State::Busy(blocking::spawn(async move { + self.0 = State::Busy(blocking::spawn(move || { let next = inner.next(); (inner, next) })); diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 53080dd..eaa7b62 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -28,5 +28,5 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::read_link(path).map(Into::into) }).await + blocking::spawn(move || std::fs::read_link(path).map(Into::into)).await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index a4d175f..40c4b6b 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -37,5 +37,5 @@ use crate::task::blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::read_to_string(path) }).await + blocking::spawn(move || std::fs::read_to_string(path)).await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index f457124..d1fa7bf 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::remove_dir(path) }).await + blocking::spawn(move || std::fs::remove_dir(path)).await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 3b12d26..0a0fceb 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::remove_dir_all(path) }).await + blocking::spawn(move || std::fs::remove_dir_all(path)).await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 216209f..5bc0608 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -29,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { std::fs::remove_file(path) }).await + blocking::spawn(move || std::fs::remove_file(path)).await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index f517a26..c2aa77b 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -34,5 +34,5 @@ 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(async move { std::fs::rename(&from, &to) }).await + blocking::spawn(move || std::fs::rename(&from, &to)).await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 68dd8d4..d14ced9 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -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(async move { std::fs::set_permissions(path, perm) }).await + blocking::spawn(move || std::fs::set_permissions(path, perm)).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index e2bc12d..bc5cce8 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -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(async move { std::fs::symlink_metadata(path) }).await + blocking::spawn(move || std::fs::symlink_metadata(path)).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index 4db5221..3df5604 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -33,5 +33,5 @@ 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(async move { std::fs::write(path, contents) }).await + blocking::spawn(move || std::fs::write(path, contents)).await } diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 5706aa2..34d4d1d 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -116,7 +116,7 @@ impl Write for Stderr { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::write(&mut inner.stderr, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -144,7 +144,7 @@ impl Write for Stderr { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::flush(&mut inner.stderr); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 95a77b8..178e819 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -119,7 +119,7 @@ impl Stdin { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { inner.line.clear(); let res = inner.stdin.read_line(&mut inner.line); inner.last_op = Some(Operation::ReadLine(res)); @@ -172,7 +172,7 @@ impl Read for Stdin { } // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); inner.last_op = Some(Operation::Read(res)); State::Idle(Some(inner)) diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 7849f1c..4128aae 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -116,7 +116,7 @@ impl Write for Stdout { inner.buf[..buf.len()].copy_from_slice(buf); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::write(&mut inner.stdout, &inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) @@ -144,7 +144,7 @@ impl Write for Stdout { let mut inner = opt.take().unwrap(); // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { + *state = State::Busy(blocking::spawn(move || { let res = std::io::Write::flush(&mut inner.stdout); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) diff --git a/src/net/addr.rs b/src/net/addr.rs index 64b22cf..adc2408 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -196,7 +196,7 @@ impl ToSocketAddrs for (&str, u16) { } let host = host.to_string(); - let task = blocking::spawn(async move { + let task = blocking::spawn(move || { std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) }); ToSocketAddrsFuture::Resolving(task) @@ -217,8 +217,7 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = - blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) }); + let task = blocking::spawn(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1056943..1ffd636 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -76,7 +76,7 @@ impl TcpStream { let mut last_err = None; for addr in addrs.to_socket_addrs().await? { - let res = blocking::spawn(async move { + 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 { diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index f00aaec..4a15510 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -28,7 +28,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(async move { std::os::unix::fs::symlink(&src, &dst) }).await + blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await } cfg_if! { diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 61adc09..c96afd5 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -67,7 +67,7 @@ impl UnixDatagram { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let socket = blocking::spawn(async move { mio_uds::UnixDatagram::bind(path) }).await?; + let socket = blocking::spawn(move || mio_uds::UnixDatagram::bind(path)).await?; Ok(UnixDatagram::new(socket)) } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 2d68a6b..b6e6a29 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -68,7 +68,7 @@ impl UnixListener { /// ``` pub async fn bind>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let listener = blocking::spawn(async move { mio_uds::UnixListener::bind(path) }).await?; + let listener = blocking::spawn(move || mio_uds::UnixListener::bind(path)).await?; Ok(UnixListener { watcher: Watcher::new(listener), diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 8245e63..b16f2a3 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -58,7 +58,7 @@ impl UnixStream { pub async fn connect>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { + 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 { diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 012158d..3216012 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -7,7 +7,6 @@ use std::time::Duration; use crossbeam_channel::{bounded, Receiver, Sender}; use lazy_static::lazy_static; -use crate::future::Future; use crate::task::task::{JoinHandle, Tag}; use crate::utils::abort_on_panic; From 12fdc1232ddd83f43e46659eca95229cc346873f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 22:24:38 +0200 Subject: [PATCH 10/38] rename task::blocking to task::spawn_blocking Signed-off-by: Yoshua Wuyts --- src/task/block_on.rs | 2 +- src/task/mod.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 31588c4..e1b6d02 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -21,7 +21,7 @@ use kv_log_macro::trace; /// /// See also: [`task::blocking`]. /// -/// [`task::blocking`]: fn.blocking.html +/// [`task::spawn_blocking`]: fn.spawn_blocking.html /// /// [spawning]: https://doc.rust-lang.org/std/thread/fn.spawn.html /// [joining]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join diff --git a/src/task/mod.rs b/src/task/mod.rs index 21b85fd..fc702df 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -60,9 +60,10 @@ cfg_if::cfg_if! { /// is useful to prevent long-running synchronous operations from blocking the main futures /// executor. /// -/// See also: [`task::block_on`]. +/// See also: [`task::block_on`], [`task::spawn`]. /// /// [`task::block_on`]: fn.block_on.html +/// [`task::spawn`]: fn.spawn.html /// /// # Examples /// @@ -73,7 +74,7 @@ cfg_if::cfg_if! { /// # /// use async_std::task; /// -/// task::blocking(|| { +/// task::spawn_blocking(|| { /// println!("long-running task here"); /// }).await; /// # @@ -84,10 +85,10 @@ cfg_if::cfg_if! { #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] -pub fn blocking(f: F) -> task::JoinHandle +pub fn spawn_blocking(f: F) -> task::JoinHandle where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { - blocking::spawn_blocking(future) + blocking::spawn(f) } From 33806ad44ce7a80ae49dd2da57f60ad9680d4293 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 9 Oct 2019 23:44:26 +0200 Subject: [PATCH 11/38] fix warning Signed-off-by: Yoshua Wuyts --- src/task/block_on.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index e1b6d02..db46f02 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -19,7 +19,7 @@ use kv_log_macro::trace; /// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an /// asynchronous task will be spawned. /// -/// See also: [`task::blocking`]. +/// See also: [`task::spawn_blocking`]. /// /// [`task::spawn_blocking`]: fn.spawn_blocking.html /// From 237cfa0315185b339dc23a14faf96cebde478743 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:03:49 +0200 Subject: [PATCH 12/38] add IntoFuture (#259) * add IntoFuture Signed-off-by: Yoshua Wuyts * blanket impl for IntoFuture Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts * example Signed-off-by: Yoshua Wuyts * mark as unstable Signed-off-by: Yoshua Wuyts --- src/future/into_future.rs | 54 +++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 3 +++ 2 files changed, 57 insertions(+) create mode 100644 src/future/into_future.rs diff --git a/src/future/into_future.rs b/src/future/into_future.rs new file mode 100644 index 0000000..58b6766 --- /dev/null +++ b/src/future/into_future.rs @@ -0,0 +1,54 @@ +use crate::future::Future; + +/// Convert a type into a `Future`. +/// +/// # Examples +/// +/// ``` +/// use async_std::future::{Future, IntoFuture}; +/// use async_std::io; +/// use async_std::pin::Pin; +/// +/// struct Client; +/// +/// impl Client { +/// pub async fn send(self) -> io::Result<()> { +/// // Send a request +/// Ok(()) +/// } +/// } +/// +/// impl IntoFuture for Client { +/// type Output = io::Result<()>; +/// +/// type Future = Pin>>; +/// +/// fn into_future(self) -> Self::Future { +/// Box::pin(async { +/// self.send().await +/// }) +/// } +/// } +/// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub trait IntoFuture { + /// The type of value produced on completion. + type Output; + + /// Which kind of future are we turning this into? + type Future: Future; + + /// Create a future from a value + fn into_future(self) -> Self::Future; +} + +impl IntoFuture for T { + type Output = T::Output; + + type Future = T; + + fn into_future(self) -> Self::Future { + self + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index e5e696d..dc9b466 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -63,7 +63,10 @@ mod ready; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod into_future; mod timeout; + + pub use into_future::IntoFuture; pub use timeout::{timeout, TimeoutError}; } } From 49faea2023396c240b91b6b4cffc56de038ce336 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:27:06 +0200 Subject: [PATCH 13/38] init FutureExt (#308) * init FutureExt Signed-off-by: Yoshua Wuyts * prelude Signed-off-by: Yoshua Wuyts * Refactor extension_trait Signed-off-by: Yoshua Wuyts * Fix rustdoc Signed-off-by: Yoshua Wuyts --- src/fs/dir_builder.rs | 3 +- src/fs/open_options.rs | 3 +- src/future/future.rs | 145 +++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 5 +- src/io/buf_read/mod.rs | 4 +- src/io/read/mod.rs | 4 +- src/io/seek.rs | 6 +- src/io/write/mod.rs | 4 +- src/prelude.rs | 2 + src/stream/stream/mod.rs | 4 +- src/task/blocking.rs | 1 + src/task/task.rs | 2 +- src/task/task_local.rs | 2 +- src/utils.rs | 56 +++++---------- 14 files changed, 190 insertions(+), 51 deletions(-) create mode 100644 src/future/future.rs diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 738ddbc..6dfddcb 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,6 +1,7 @@ +use std::future::Future; + use cfg_if::cfg_if; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::task::blocking; diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index a9511f1..fe65e2f 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,7 +1,8 @@ +use std::future::Future; + use cfg_if::cfg_if; use crate::fs::File; -use crate::future::Future; use crate::io; use crate::path::Path; use crate::task::blocking; diff --git a/src/future/future.rs b/src/future/future.rs new file mode 100644 index 0000000..556dc1a --- /dev/null +++ b/src/future/future.rs @@ -0,0 +1,145 @@ +use crate::utils::extension_trait; + +cfg_if::cfg_if! { + if #[cfg(feature = "docs")] { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; + + use crate::task::{Context, Poll}; + } +} + +extension_trait! { + #[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/mod.rs b/src/future/mod.rs index dc9b466..06d0633 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -41,9 +41,6 @@ //! | `future::select` | `Result` | Return on first value //! | `future::try_select` | `Result` | Return on first `Ok`, reject on last Err -#[doc(inline)] -pub use std::future::Future; - #[doc(inline)] pub use async_macros::{join, try_join}; @@ -53,10 +50,12 @@ pub use async_macros::{select, try_select}; use cfg_if::cfg_if; +pub use future::Future; 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; diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 987522b..dff10fa 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -44,7 +44,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html [provided methods]: #provided-methods "#] - pub trait BufRead [BufReadExt: futures_io::AsyncBufRead] { + 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. @@ -67,7 +67,9 @@ extension_trait! { should no longer be returned in calls to `read`. "#] fn consume(self: Pin<&mut Self>, amt: usize); + } + pub trait BufReadExt: futures_io::AsyncBufRead { #[doc = r#" Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 6fd95c1..40cb3ca 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -50,7 +50,7 @@ extension_trait! { [`poll_read`]: #tymethod.poll_read [`poll_read_vectored`]: #method.poll_read_vectored "#] - pub trait Read [ReadExt: futures_io::AsyncRead] { + pub trait Read { #[doc = r#" Attempt to read from the `AsyncRead` into `buf`. "#] @@ -70,7 +70,9 @@ extension_trait! { ) -> Poll> { unreachable!("this impl only appears in the rendered docs") } + } + pub trait ReadExt: futures_io::AsyncRead { #[doc = r#" Reads some bytes from the byte stream. diff --git a/src/io/seek.rs b/src/io/seek.rs index 274eee7..2d1c4d3 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -33,7 +33,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html [provided methods]: #provided-methods "#] - pub trait Seek [SeekExt: futures_io::AsyncSeek] { + pub trait Seek { #[doc = r#" Attempt to seek to an offset, in bytes, in a stream. "#] @@ -42,7 +42,9 @@ extension_trait! { cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll>; + } + pub trait SeekExt: futures_io::AsyncSeek { #[doc = r#" Seeks to a new position in a byte stream. @@ -70,7 +72,7 @@ extension_trait! { fn seek( &mut self, pos: SeekFrom, - ) -> impl Future> [SeekFuture<'_, Self>] + ) -> impl Future> + '_ [SeekFuture<'_, Self>] where Self: Unpin, { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 7914ccc..be69b7e 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -49,7 +49,7 @@ extension_trait! { [`poll_flush`]: #tymethod.poll_flush [`poll_close`]: #tymethod.poll_close "#] - pub trait Write [WriteExt: futures_io::AsyncWrite] { + pub trait Write { #[doc = r#" Attempt to write bytes from `buf` into the object. "#] @@ -80,7 +80,9 @@ extension_trait! { Attempt to close the object. "#] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } + pub trait WriteExt: futures_io::AsyncWrite { #[doc = r#" Writes some bytes into the byte stream. diff --git a/src/prelude.rs b/src/prelude.rs index f808da0..6c670cc 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -28,6 +28,8 @@ pub use crate::stream::Stream; #[doc(no_inline)] pub use crate::task_local; +#[doc(hidden)] +pub use crate::future::future::FutureExt as _; #[doc(hidden)] pub use crate::io::buf_read::BufReadExt as _; #[doc(hidden)] diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 0e563f6..2ee01d7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -122,7 +122,7 @@ extension_trait! { https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html [provided methods]: #provided-methods "#] - pub trait Stream [StreamExt: futures_core::stream::Stream] { + pub trait Stream { #[doc = r#" The type of items yielded by this stream. "#] @@ -180,7 +180,9 @@ extension_trait! { ``` "#] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } + pub trait StreamExt: futures_core::stream::Stream { #[doc = r#" Advances the stream and returns the next value. diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 3216012..5c183b3 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -1,5 +1,6 @@ //! A thread pool for running blocking functions asynchronously. +use std::future::Future; use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; diff --git a/src/task/task.rs b/src/task/task.rs index 5100af4..ca3cac1 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::future::Future; use std::i64; use std::mem; use std::num::NonZeroU64; @@ -7,7 +8,6 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; use super::task_local; -use crate::future::Future; use crate::task::{Context, Poll}; /// A handle to a task. diff --git a/src/task/task_local.rs b/src/task/task_local.rs index 8347e34..c72937f 100644 --- a/src/task/task_local.rs +++ b/src/task/task_local.rs @@ -1,13 +1,13 @@ 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 lazy_static::lazy_static; use super::worker; -use crate::future::Future; use crate::utils::abort_on_panic; /// Declares task-local values. diff --git a/src/utils.rs b/src/utils.rs index bdf0f3b..76db50b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,7 +20,7 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } -/// Defines an extension trait for a base trait from the `futures` crate. +/// 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 @@ -35,10 +35,14 @@ 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 from the `futures` crate - $(#[$attr:meta])* - pub trait $name:ident [$ext:ident: $base:path] { - $($body:tt)* + // - `$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. @@ -58,11 +62,11 @@ macro_rules! extension_trait { pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); } - // Render a fake trait containing all methods from the base trait and the extension trait. + // Render a fake trait combining the bodies of the base trait and the extension trait. #[cfg(feature = "docs")] - $(#[$attr])* + #[doc = $doc] pub trait $name { - extension_trait!(@doc () $($body)*); + extension_trait!(@doc () $($body_base)* $($body_ext)*); } // When not rendering docs, re-export the base trait from the futures crate. @@ -70,9 +74,9 @@ macro_rules! extension_trait { pub use $base as $name; // The extension trait that adds methods to any type implementing the base trait. - $(#[$attr])* + /// Extension trait. pub trait $ext: $base { - extension_trait!(@ext () $($body)*); + extension_trait!(@ext () $($body_ext)*); } // Blanket implementation of the extension trait for any type implementing the base trait. @@ -82,39 +86,15 @@ macro_rules! extension_trait { $(#[cfg(feature = "docs")] $imp)* }; - // Parse an associated type. - (@doc ($($head:tt)*) type $name:ident; $($tail:tt)*) => { - extension_trait!(@doc ($($head)* type $name;) $($tail)*); - }; - (@ext ($($head:tt)*) type $ident:ty; $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse a required method. - (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => { - extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)?;) $($tail)*); - }; - (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse a provided method that exists in the base trait. - (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => { - extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)? { $($body)* }) $($tail)*); - }; - (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => { - extension_trait!(@ext ($($head)*) $($tail)*); - }; - - // Parse the return type in an extension method where the future doesn't borrow. + // 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 [$f:ty] $($tail:tt)*) => { + (@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 where the future borrows its environment. + // 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)*); }; @@ -122,7 +102,7 @@ macro_rules! extension_trait { extension_trait!(@ext ($($head)* -> $f) $($tail)*); }; - // Parse a token that doesn't fit into any of the previous patterns. + // Parse a token. (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => { extension_trait!(@doc ($($head)* $token) $($tail)*); }; From 00d936488b8909fdfc8896f485d7520ef0f3b0a4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:33:23 +0200 Subject: [PATCH 14/38] stabilize future::timeout (#335) Signed-off-by: Yoshua Wuyts --- src/future/mod.rs | 4 ++-- src/future/timeout.rs | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index 06d0633..cc3b7a5 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -54,18 +54,18 @@ pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; pub use ready::ready; +pub use timeout::{timeout, TimeoutError}; pub(crate) mod future; mod pending; mod poll_fn; mod ready; +mod timeout; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod into_future; - mod timeout; pub use into_future::IntoFuture; - pub use timeout::{timeout, TimeoutError}; } } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index aa88f64..a8338fb 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -28,8 +28,6 @@ use crate::task::{Context, Poll}; /// # /// # Ok(()) }) } /// ``` -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, @@ -42,8 +40,6 @@ where } /// A future that times out after a duration of time. -#[doc(hidden)] -#[allow(missing_debug_implementations)] struct TimeoutFuture { future: F, delay: Delay, @@ -69,8 +65,6 @@ impl Future for TimeoutFuture { } /// An error returned when a future 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: (), From 4911f4599b02dbc3e08c9cbf2b583f267752e6b1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 17:50:24 +0200 Subject: [PATCH 15/38] init changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ab025..6979d51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,93 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [0.99.10] - 2019-10-16 + +This patch stabilizes several core concurrency macros, introduces async versions +of `Path` and `PathBuf`, and adds almost 100 other commits. + +## Examples + +__Asynchronously read directories from the filesystem__ +```rust +use async_std::fs; +use async_std::path::Path; +use async_std::prelude::*; + +let path = Path::new("/laputa"); +let mut dir = fs::read_dir(&path).await.unwrap(); +while let Some(entry) = dir.next().await { + if let Ok(entry) = entry { + println!("{:?}", entry.path()); + } +} +``` + +__Cooperatively reschedule the current task on the executor__ +```rust +use async_std::prelude::*; +use async_std::task; + +task::spawn(async { + let x = fibonnacci(1000); // Do expensive work + task::yield_now().await; // Allow other tasks to run + x + fibonnacci(100) // Do more work +}) +``` + +__Create an interval stream__ +```rust +use async_std::prelude::*; +use async_std::stream; +use std::time::Duration; + +let mut interval = stream::interval(Duration::from_secs(4)); +while let Some(_) = interval.next().await { + println!("prints every four seconds"); +} +``` + +## Added + +- Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::merge` as "unstable", replacing `stream::join!` +- Added `Stream::partial_cmp` +- Added `Stream::take_while` +- Added `future::IntoFuture` as "unstable" +- Added `io::BufRead::split` +- Added `io::Write::write_fmt` +- Added `stream::from_fn` +- Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable" +- Added `process` as "unstable", re-exporting std types only for now +- Added `std::path::PathBuf` with all associated methods +- Added `std::path::Path` with all associated methods +- Added `stream::ExactSizeStream` as "unstable" +- Added `stream::FusedStream` as "unstable" +- Added `stream::interval` as "unstable" +- Added `task::spawn_blocking` as "unstable", replacing `task::blocking` +- Added `task::yield_now` +- Added `write!` and `writeln!` macros as "unstable" +- Added `std::net` re-exports to the `net` submodule +- Stabilized `future::join!` and `future::try_join!` +- Stabilized `future::timeout` +- Stabilized `path` +- Stabilized `task::ready!` + +## Changed + +- Fixed `BufWriter::into_inner` so it calls `flush` before yielding +- Refactored `io::BufWriter` internals +- Refactored `net::ToSocketAddrs` internals +- Removed Travis CI entirely +- Rewrote the README.md +- Stabilized `io::Cursor` +- Switched bors over to use GitHub actions + +## Removed + +- Removed the "unstable" `stream::join!` in favor of `Stream::merge` +- Removed the "unstable" `task::blocking` in favor of `task::spawn_blocking` + # [0.99.9] - 2019-10-08 This patch upgrades our `futures-rs` version, allowing us to build on the 1.39 @@ -183,7 +270,8 @@ task::blocking(async { - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.9...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD +[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 [0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7 From 609a5780a22c4c7b8d8080c04ef76f93dff47b83 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 17:51:32 +0200 Subject: [PATCH 16/38] 0.99.10 Signed-off-by: Yoshua Wuyts --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dc319f7..d1c10a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.9" +version = "0.99.10" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 24cdb2d4899f63d41726a8c32c32b2727eb28e42 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 00:31:49 +0200 Subject: [PATCH 17/38] add stream::{Sum,Product} (#343) Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 10 +++++++--- src/stream/product.rs | 23 +++++++++++++++++++++++ src/stream/sum.rs | 23 +++++++++++++++++++++++ src/task/blocking.rs | 1 - 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/stream/product.rs create mode 100644 src/stream/sum.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 87f3f84..c41ceb6 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -42,19 +42,23 @@ cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; mod exact_size_stream; - mod fused_stream; mod extend; mod from_stream; - mod into_stream; + mod fused_stream; mod interval; + mod into_stream; + mod product; + mod sum; pub use double_ended_stream::DoubleEndedStream; pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; pub use fused_stream::FusedStream; - pub use into_stream::IntoStream; pub use interval::{interval, Interval}; + pub use into_stream::IntoStream; + pub use product::Product; + pub use sum::Sum; pub use stream::Merge; } diff --git a/src/stream/product.rs b/src/stream/product.rs new file mode 100644 index 0000000..b322776 --- /dev/null +++ b/src/stream/product.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::stream::Stream; + +/// Trait to represent types that can be created by productming up 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 +/// [`FromStream`] this trait should rarely be called directly and instead +/// interacted with through [`Stream::product`]. +/// +/// [`product`]: trait.Product.html#tymethod.product +/// [`FromStream`]: trait.FromStream.html +/// [`Stream::product`]: trait.Stream.html#method.product +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +pub trait Product: Sized { + /// Method which takes a stream and generates `Self` from the elements by + /// multiplying the items. + fn product(stream: S) -> F + where + S: Stream, + F: Future; +} diff --git a/src/stream/sum.rs b/src/stream/sum.rs new file mode 100644 index 0000000..fd5d7d5 --- /dev/null +++ b/src/stream/sum.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::stream::Stream; + +/// Trait to represent types that can be created by summing up a stream. +/// +/// This trait is used to implement the [`sum`] method on streams. Types which +/// implement the trait can be generated by the [`sum`] method. Like +/// [`FromStream`] this trait should rarely be called directly and instead +/// interacted with through [`Stream::sum`]. +/// +/// [`sum`]: trait.Sum.html#tymethod.sum +/// [`FromStream`]: trait.FromStream.html +/// [`Stream::sum`]: trait.Stream.html#method.sum +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +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 + where + S: Stream, + F: Future; +} diff --git a/src/task/blocking.rs b/src/task/blocking.rs index 5c183b3..3216012 100644 --- a/src/task/blocking.rs +++ b/src/task/blocking.rs @@ -1,6 +1,5 @@ //! A thread pool for running blocking functions asynchronously. -use std::future::Future; use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; use std::time::Duration; From a8dc2c6f9b8516109d7e301ca9de22e1aee41d4c Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 18:32:54 -0400 Subject: [PATCH 18/38] Adds Stream::lt (#337) --- src/stream/stream/lt.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/stream/stream/lt.rs diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs new file mode 100644 index 0000000..b774d7b --- /dev/null +++ b/src/stream/stream/lt.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +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, +} + +impl LtFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + LtFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for LtFuture +where + L: Stream + Sized, + R: Stream + Sized, + 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)); + + match result { + Some(Ordering::Less) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 2ee01d7..dfd8d49 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod fold; mod for_each; mod fuse; mod inspect; +mod lt; mod map; mod min_by; mod next; @@ -55,6 +56,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use lt::LtFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; @@ -1258,6 +1260,43 @@ extension_trait! { { PartialCmpFuture::new(self, other) } + + #[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 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) + } } impl Stream for Box { From f0f279ec04bf025f5e9aa6c57002245c2f1bb013 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 18:46:06 -0400 Subject: [PATCH 19/38] Adds Stream::le (#336) --- src/stream/stream/le.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 37 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/stream/stream/le.rs diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs new file mode 100644 index 0000000..37b62d8 --- /dev/null +++ b/src/stream/stream/le.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +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, +} + +impl LeFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + LeFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for LeFuture +where + L: Stream + Sized, + R: Stream + Sized, + 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)); + + match result { + Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index dfd8d49..30b6031 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod fold; mod for_each; mod fuse; mod inspect; +mod le; mod lt; mod map; mod min_by; @@ -56,6 +57,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; use next::NextFuture; @@ -1261,6 +1263,41 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[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 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) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically less than those of another. From 5f7238eec6bdc921ce32f79da4afeec8ed28a3a8 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 19:11:48 -0400 Subject: [PATCH 20/38] [Draft PR] Adds Stream::gt (#304) * [Draft PR] Adds Stream::gt * Applies cargo format and fixes incorrect comment * cargo fmt * fixes rustdoc related issues --- src/stream/stream/gt.rs | 47 +++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 53 +++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/stream/stream/gt.rs diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs new file mode 100644 index 0000000..6c480a2 --- /dev/null +++ b/src/stream/stream/gt.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +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, +} + +impl GtFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + GtFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for GtFuture +where + L: Stream + Sized, + R: Stream + Sized, + 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)); + + match result { + Some(Ordering::Greater) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 30b6031..90fde15 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -32,6 +32,7 @@ mod find_map; mod fold; mod for_each; mod fuse; +mod gt; mod inspect; mod le; mod lt; @@ -57,6 +58,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use gt::GtFuture; use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; @@ -1230,14 +1232,16 @@ extension_trait! { #[doc = r#" Lexicographically compares the elements of this `Stream` with those of another. - + # Examples + ``` # 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]); @@ -1263,17 +1267,54 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than those of another. + + # 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().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) + } + #[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 std::collections::VecDeque; - + let single = VecDeque::from(vec![1]); let single_gt = VecDeque::from(vec![10]); let multi = VecDeque::from(vec![1,2]); @@ -1301,14 +1342,15 @@ extension_trait! { #[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 std::collections::VecDeque; - + let single = VecDeque::from(vec![1]); let single_gt = VecDeque::from(vec![10]); let multi = VecDeque::from(vec![1,2]); @@ -1318,7 +1360,6 @@ extension_trait! { 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); - # # }) } ``` From a7041be6f291db6f465e9ac8a823ed387c7066fe Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 19:24:27 -0400 Subject: [PATCH 21/38] Adds Stream:ge (#285) * Adds partial_cmp.rs file and partial_cmp signature to mod.rs * adds tests that compare streams of same length * Adds Stream::ge * cargo fmt * fixes rustdoc error --- src/stream/stream/ge.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/stream/stream/ge.rs diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs new file mode 100644 index 0000000..eb9786b --- /dev/null +++ b/src/stream/stream/ge.rs @@ -0,0 +1,47 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::partial_cmp::PartialCmpFuture; +use crate::future::Future; +use crate::prelude::*; +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, +} + +impl GeFuture +where + L::Item: PartialOrd, +{ + pin_utils::unsafe_pinned!(partial_cmp: PartialCmpFuture); + + pub(super) fn new(l: L, r: R) -> Self { + GeFuture { + partial_cmp: l.partial_cmp(r), + } + } +} + +impl Future for GeFuture +where + L: Stream + Sized, + R: Stream + Sized, + 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)); + + match result { + Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true), + _ => Poll::Ready(false), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 90fde15..fbbd8f6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -32,6 +32,7 @@ mod find_map; mod fold; mod for_each; mod fuse; +mod ge; mod gt; mod inspect; mod le; @@ -58,6 +59,7 @@ use find::FindFuture; use find_map::FindMapFuture; use fold::FoldFuture; use for_each::ForEachFuture; +use ge::GeFuture; use gt::GtFuture; use le::LeFuture; use lt::LtFuture; @@ -1240,6 +1242,7 @@ extension_trait! { # use async_std::prelude::*; use std::collections::VecDeque; + use std::cmp::Ordering; let s1 = VecDeque::from(vec![1]); @@ -1267,6 +1270,43 @@ extension_trait! { PartialCmpFuture::new(self, other) } + + #[doc = r#" + Determines if the elements of this `Stream` are lexicographically + greater than or equal to those of another. + + # Examples + + ``` + # 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) + } + #[doc = r#" Determines if the elements of this `Stream` are lexicographically greater than those of another. From 2bd82ac2497ba5a710c28476c2797c4af82c3957 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 01:48:40 +0200 Subject: [PATCH 22/38] updates Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6979d51..f7e8731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,24 +56,29 @@ while let Some(_) = interval.next().await { ## Added - Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::ge` +- Added `Stream::le` +- Added `Stream::lt` - Added `Stream::merge` as "unstable", replacing `stream::join!` - Added `Stream::partial_cmp` - Added `Stream::take_while` - Added `future::IntoFuture` as "unstable" - Added `io::BufRead::split` - Added `io::Write::write_fmt` -- Added `stream::from_fn` - Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable" - Added `process` as "unstable", re-exporting std types only for now +- Added `std::net` re-exports to the `net` submodule - Added `std::path::PathBuf` with all associated methods - Added `std::path::Path` with all associated methods - Added `stream::ExactSizeStream` as "unstable" - Added `stream::FusedStream` as "unstable" +- Added `stream::Product` +- Added `stream::Sum` +- Added `stream::from_fn` - Added `stream::interval` as "unstable" - Added `task::spawn_blocking` as "unstable", replacing `task::blocking` - Added `task::yield_now` - Added `write!` and `writeln!` macros as "unstable" -- Added `std::net` re-exports to the `net` submodule - Stabilized `future::join!` and `future::try_join!` - Stabilized `future::timeout` - Stabilized `path` From 9f8fa45dc7f6c2c0bb744aba752cd7d33fa7cd42 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 02:03:26 +0200 Subject: [PATCH 23/38] io docs Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 812c97a..7f9a40f 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,24 +1,74 @@ -//! Basic input and output. +//! Traits, helpers, and type definitions for core I/O functionality. +//! +//! The `async_std::io` module contains a number of common things you'll need +//! when doing input and output. The most core part of this module is +//! the [`Read`] and [`Write`] traits, which provide the +//! most general interface for reading and writing input and output. //! //! This module is an async version of [`std::io`]. //! //! [`std::io`]: https://doc.rust-lang.org/std/io/index.html //! -//! # Examples +//! # Read and Write +//! +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number +//! of other types, and you can implement them for your types too. As such, +//! you'll see a few different types of I/O throughout the documentation in +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For +//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on +//! [`File`]s: +//! +//! ```no_run +//! use async_std::prelude::*; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut f = File::open("foo.txt").await?; +//! let mut buffer = [0; 10]; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer).await?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a +//! nickname: readers and writers. So you'll sometimes see 'a reader' instead +//! of 'a type that implements the [`Read`] trait'. Much easier! +//! +//! ## Seek and BufRead //! -//! Read a line from the standard input: +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is +//! coming from: //! //! ```no_run +//! use async_std::prelude::*; +//! use async_std::io::SeekFrom; +//! use async_std::fs::File; +//! //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # -//! use async_std::io; +//! let mut f = File::open("foo.txt").await?; +//! let mut buffer = [0; 10]; +//! +//! // skip to the last 10 bytes of the file +//! f.seek(SeekFrom::End(-10)).await?; //! -//! let stdin = io::stdin(); -//! let mut line = String::new(); -//! stdin.read_line(&mut line).await?; +//! // read up to 10 bytes +//! let n = f.read(&mut buffer).await?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); //! # //! # Ok(()) }) } //! ``` +//! +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but +//! to show it off, we'll need to talk about buffers in general. Keep reading! #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; From d250eee55688b2d084fcd0a1bd433eb62629d87f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 02:15:20 +0200 Subject: [PATCH 24/38] port the std::io docs to async_std::io Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 7f9a40f..9a125b2 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -47,7 +47,7 @@ //! coming from: //! //! ```no_run -//! use async_std::prelude::*; +//! use async_std::io::prelude::*; //! use async_std::io::SeekFrom; //! use async_std::fs::File; //! @@ -69,6 +69,205 @@ //! //! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but //! to show it off, we'll need to talk about buffers in general. Keep reading! +//! +//! ## BufReader and BufWriter +//! +//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be +//! making near-constant calls to the operating system. To help with this, +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap +//! readers and writers. The wrapper uses a buffer, reducing the number of +//! calls and providing nicer methods for accessing exactly what you want. +//! +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra +//! methods to any reader: +//! +//! ```no_run +//! use async_std::io::prelude::*; +//! use async_std::io::BufReader; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let f = File::open("foo.txt").await?; +//! let mut reader = BufReader::new(f); +//! let mut buffer = String::new(); +//! +//! // read a line into buffer +//! reader.read_line(&mut buffer).await?; +//! +//! println!("{}", buffer); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write`][`Write::write`]: +//! +//! ```no_run +//! 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 { +//! # +//! let f = File::create("foo.txt").await?; +//! { +//! let mut writer = BufWriter::new(f); +//! +//! // write a byte to the buffer +//! writer.write(&[42]).await?; +//! +//! } // the buffer is flushed once writer goes out of scope +//! # +//! # Ok(()) }) } +//! ``` +//! +//! ## Standard input and output +//! +//! A very common source of input is standard input: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; +//! +//! println!("You typed: {}", input.trim()); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Note that you cannot use the [`?` operator] in functions that do not return +//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] +//! or `match` on the return value to catch any possible errors: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await.unwrap(); +//! # +//! # Ok(()) }) } +//! ``` +//! +//! And a very common source of output is standard output: +//! +//! ```no_run +//! use async_std::io; +//! use async_std::io::prelude::*; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! io::stdout().write(&[42]).await?; +//! # +//! # Ok(()) }) } +//! ``` +//! +//! Of course, using [`io::stdout`] directly is less common than something like +//! [`println!`]. +//! +//! ## Iterator types +//! +//! A large number of the structures provided by `std::io` are for various +//! ways of iterating over I/O. For example, [`Lines`] is used to split over +//! lines: +//! +//! ```no_run +//! use async_std::prelude::*; +//! use async_std::io::BufReader; +//! use async_std::fs::File; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! let f = File::open("foo.txt").await?; +//! let reader = BufReader::new(f); +//! +//! let mut lines = reader.lines(); +//! while let Some(line) = lines.next().await { +//! println!("{}", line?); +//! } +//! # +//! # Ok(()) }) } +//! ``` +//! +//! ## Functions +//! +//! There are a number of [functions][functions-list] that offer access to various +//! features. For example, we can use three of these functions to copy everything +//! from standard input to standard output: +//! +//! ```no_run +//! use async_std::io; +//! +//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +//! # +//! io::copy(&mut io::stdin(), &mut io::stdout()).await?; +//! # +//! # Ok(()) }) } +//! ``` +//! +//! [functions-list]: #functions-1 +//! +//! ## io::Result +//! +//! Last, but certainly not least, is [`io::Result`]. This type is used +//! as the return type of many `std::io` functions that can cause an error, and +//! can be returned from your own functions as well. Many of the examples in this +//! module use the [`?` operator]: +//! +//! ``` +//! #![allow(dead_code)] +//! use async_std::io; +//! +//! async fn read_input() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).await?; +//! +//! println!("You typed: {}", input.trim()); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The return type of `read_input`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is +//! to read the line and print it, so we use `()`. +//! +//! ## Platform-specific behavior +//! +//! Many I/O functions throughout the standard library are documented to indicate +//! what various library or syscalls they are delegated to. This is done to help +//! applications both understand what's happening under the hood as well as investigate +//! any possibly unclear semantics. Note, however, that this is informative, not a binding +//! contract. The implementation of many of these functions are subject to change over +//! time and may call fewer or more syscalls/library functions. +//! +//! [`Read`]: trait.Read.html +//! [`Write`]: trait.Write.html +//! [`Seek`]: trait.Seek.html +//! [`BufRead`]: trait.BufRead.html +//! [`File`]: ../fs/struct.File.html +//! [`TcpStream`]: ../net/struct.TcpStream.html +//! [`Vec`]: ../vec/struct.Vec.html +//! [`BufReader`]: struct.BufReader.html +//! [`BufWriter`]: struct.BufWriter.html +//! [`Write::write`]: trait.Write.html#tymethod.write +//! [`io::stdout`]: fn.stdout.html +//! [`println!`]: ../macro.println.html +//! [`Lines`]: struct.Lines.html +//! [`io::Result`]: type.Result.html +//! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html +//! [`Read::read`]: trait.Read.html#tymethod.read +//! [`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}; From 4b96ea127366bca42f6143e7cdf681884e9d6857 Mon Sep 17 00:00:00 2001 From: assemblaj <7599535+assemblaj@users.noreply.github.com> Date: Tue, 15 Oct 2019 20:23:41 -0400 Subject: [PATCH 25/38] Adds Stream::cmp (#273) * Adds cmp * Fixes formatting * cleans up examples * attempts to fix rustdoc issue * formats with cargo fmt * Adds proper trait bounds for cmp --- src/stream/stream/cmp.rs | 91 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 39 +++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/stream/stream/cmp.rs diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs new file mode 100644 index 0000000..fc7161a --- /dev/null +++ b/src/stream/stream/cmp.rs @@ -0,0 +1,91 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::fuse::Fuse; +use crate::future::Future; +use crate::prelude::*; +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, +} + +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 { + l: l.fuse(), + r: r.fuse(), + l_cache: None, + r_cache: None, + } + } +} + +impl Future for CmpFuture +where + L: Stream + Sized, + R: Stream + Sized, + L::Item: Ord, +{ + type Output = Ordering; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + 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(); + + if l_complete && r_complete { + return Poll::Ready(Ordering::Equal); + } else if l_complete { + return Poll::Ready(Ordering::Less); + } else if r_complete { + 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)); + if let Some(item) = l_next { + *self.as_mut().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 let Some(item) = r_next { + *self.as_mut().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(); + 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; + } else { + // Return non equal value + return Poll::Ready(result); + } + } + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index fbbd8f6..f2b9830 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod chain; +mod cmp; mod enumerate; mod filter; mod filter_map; @@ -53,6 +54,7 @@ mod zip; use all::AllFuture; use any::AnyFuture; +use cmp::CmpFuture; use enumerate::Enumerate; use filter_map::FilterMap; use find::FindFuture; @@ -1270,6 +1272,43 @@ extension_trait! { PartialCmpFuture::new(self, other) } + #[doc = r#" + Lexicographically compares the elements of this `Stream` with those + of another using 'Ord'. + + # Examples + + ``` + # 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) + } #[doc = r#" Determines if the elements of this `Stream` are lexicographically From 6b00e5e66cae4bda1017dd4aab494744409c4a20 Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 16 Oct 2019 02:32:27 +0200 Subject: [PATCH 26/38] Implemented StreamExt::try_fold (#344) --- src/stream/stream/mod.rs | 42 +++++++++++++++++++++++++ src/stream/stream/try_fold.rs | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/stream/stream/try_fold.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f2b9830..d582d70 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -49,6 +49,7 @@ mod skip_while; mod step_by; mod take; mod take_while; +mod try_fold; mod try_for_each; mod zip; @@ -69,6 +70,7 @@ use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; use partial_cmp::PartialCmpFuture; +use try_fold::TryFoldFuture; use try_for_each::TryForEeachFuture; pub use chain::Chain; @@ -1042,6 +1044,46 @@ extension_trait! { Skip::new(self, n) } + #[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 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#" Applies a falliable function to each element in a stream, stopping at first error and returning it. diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs new file mode 100644 index 0000000..212b058 --- /dev/null +++ b/src/stream/stream/try_fold.rs @@ -0,0 +1,59 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct TryFoldFuture { + stream: 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); + + pub(super) fn new(stream: S, init: T, f: F) -> Self { + TryFoldFuture { + stream, + f, + acc: Some(init), + __t: PhantomData, + } + } +} + +impl Future for TryFoldFuture +where + S: Stream + Sized, + 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)); + + match next { + Some(v) => { + let old = self.as_mut().acc().take().unwrap(); + let new = (self.as_mut().f())(old, v); + + match new { + Ok(o) => { + *self.as_mut().acc() = Some(o); + } + Err(e) => return Poll::Ready(Err(e)), + } + } + None => return Poll::Ready(Ok(self.as_mut().acc().take().unwrap())), + } + } + } +} From faff1f7370a9bc31e7cedd7dc0cf7a6bd6966c09 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 10:28:14 +0200 Subject: [PATCH 27/38] task docs (#346) Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 2 +- src/task/mod.rs | 110 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index c41ceb6..7e8939b 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -1,4 +1,4 @@ -//! Asynchronous iteration. +//! Composable asynchronous iteration. //! //! This module is an async version of [`std::iter`]. //! diff --git a/src/task/mod.rs b/src/task/mod.rs index fc702df..069aa3a 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -1,26 +1,122 @@ -//! Asynchronous tasks. +//! Types and Traits for working with asynchronous tasks.. //! //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of //! threads. //! -//! [`std::thread`]: https://doc.rust-lang.org/std/thread/index.html +//! [`std::thread`]: https://doc.rust-lang.org/std/thread //! -//! # Examples +//! ## The task model //! -//! Spawn a task and await its result: +//! An executing asynchronous Rust program consists of a collection of native OS threads, on top of +//! which multiple stackless coroutines are multiplexed. We refer to these as "tasks". Tasks can +//! be named, and provide some built-in support for synchronization. //! +//! Communication between tasks can be done through channels, Rust's message-passing types, along +//! with [other forms of tasks synchronization](../sync/index.html) and shared-memory data +//! structures. In particular, types that are guaranteed to be threadsafe are easily shared between +//! tasks using the atomically-reference-counted container, [`Arc`]. +//! +//! Fatal logic errors in Rust cause *thread panic*, during which a thread will unwind the stack, +//! running destructors and freeing owned resources. If a panic occurs inside a task, there is no +//! meaningful way of recovering, so the panic will propagate through any thread boundaries all the +//! way to the root task. This is also known as a "panic = abort" model. +//! +//! ## Spawning a task +//! +//! A new task can be spawned using the [`task::spawn`][`spawn`] function: +//! +//! ```no_run +//! use async_std::task; +//! +//! task::spawn(async { +//! // some work here +//! }); //! ``` +//! +//! In this example, the spawned task is "detached" from the current task. This means that it can +//! 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: +//! +//! ``` +//! use async_std::task; +//! //! # async_std::task::block_on(async { //! # +//! let child = task::spawn(async { +//! // some work here +//! }); +//! // some work here +//! let res = child.await; +//! # +//! # }) +//! ``` +//! +//! The `await` operator returns the final value produced by the child task. +//! +//! ## 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: +//! +//! ``` +//! # #![allow(unused_must_use)] //! use async_std::task; //! -//! let handle = task::spawn(async { -//! 1 + 2 +//! # async_std::task::block_on(async { +//! # +//! task::Builder::new().name("child1".to_string()).spawn(async { +//! println!("Hello, world!"); //! }); -//! assert_eq!(handle.await, 3); //! # //! # }) //! ``` +//! +//! ## The `Task` type +//! +//! Tasks are represented via the [`Task`] type, which you can get in one of +//! two ways: +//! +//! * By spawning a new task, e.g., using the [`task::spawn`][`spawn`] +//! function, and calling [`task`][`JoinHandle::task`] on the [`JoinHandle`]. +//! * By requesting the current task, using the [`task::current`] function. +//! +//! ## Task-local storage +//! +//! This module also provides an implementation of task-local storage for Rust +//! programs. Task-local storage is a method of storing data into a global +//! variable that each task in the program will have its own copy of. +//! Tasks do not share this data, so accesses do not need to be synchronized. +//! +//! A task-local key owns the value it contains and will destroy the value when the +//! task exits. It is created with the [`task_local!`] macro and can contain any +//! value that is `'static` (no borrowed pointers). It provides an accessor function, +//! [`with`], that yields a shared reference to the value to the specified +//! closure. Task-local keys allow only shared access to values, as there would be no +//! way to guarantee uniqueness if mutable borrows were allowed. +//! +//! ## Naming tasks +//! +//! Tasks are able to have associated names for identification purposes. By default, spawned +//! tasks are unnamed. To specify a name for a task, build the task with [`Builder`] and pass +//! the desired task name to [`Builder::name`]. To retrieve the task name from within the +//! task, use [`Task::name`]. +//! +//! [`Arc`]: ../gsync/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::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}; From add6863185a12dbc415536720ad43cb31d2b7601 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 16 Oct 2019 12:24:18 +0200 Subject: [PATCH 28/38] Fix typos --- src/task/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/mod.rs b/src/task/mod.rs index 069aa3a..e2c89ae 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -1,4 +1,4 @@ -//! Types and Traits for working with asynchronous tasks.. +//! Types and traits for working with asynchronous tasks. //! //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of //! threads. From aaa1b6ca39fc1f81cc6b56193480d113116b44f2 Mon Sep 17 00:00:00 2001 From: Johannes Weissmann Date: Wed, 16 Oct 2019 13:14:54 +0200 Subject: [PATCH 29/38] add Stream::last (#347) * add stream::LastFuture (not compiling) Struggling with the associated type, pinning and how to move/copy LastFuture.last. * fix type signature -> still cannot assign still problems assigning the new value to self.last * remove unused bound * add doctest * unpin LastFuture.last * RustFmt * add static lifetime * remove redundant lifetime --- src/stream/stream/last.rs | 42 ++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/stream/stream/last.rs diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs new file mode 100644 index 0000000..c58dd66 --- /dev/null +++ b/src/stream/stream/last.rs @@ -0,0 +1,42 @@ +use std::pin::Pin; + +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, +} + +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 } + } +} + +impl Future for LastFuture +where + S: Stream + Unpin + Sized, + S::Item: Copy, +{ + 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)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + *self.as_mut().last() = Some(new); + Poll::Pending + } + None => Poll::Ready(self.last), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d582d70..764dc97 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -36,6 +36,7 @@ mod fuse; mod ge; mod gt; mod inspect; +mod last; mod le; mod lt; mod map; @@ -64,6 +65,7 @@ use fold::FoldFuture; use for_each::ForEachFuture; use ge::GeFuture; use gt::GtFuture; +use last::LastFuture; use le::LeFuture; use lt::LtFuture; use min_by::MinByFuture; @@ -456,6 +458,54 @@ extension_trait! { Inspect::new(self, f) } + #[doc = r#" + Returns the last element of the stream. + + # Examples + + Basic usage: + + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + + let last = s.last().await; + assert_eq!(last, Some(3)); + # + # }) } + ``` + + An empty stream will return `None: + ``` + # fn main() { async_std::task::block_on(async { + # + use std::collections::VecDeque; + + use async_std::prelude::*; + + let s: VecDeque = vec![].into_iter().collect(); + + let last = s.last().await; + assert_eq!(last, None); + # + # }) } + ``` + + "#] + fn last( + self, + ) -> impl Future> [LastFuture] + where + Self: Sized, + { + LastFuture::new(self) + } + #[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 From 6e0905d3caf10848cf3bab90485c5df9579907f2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 14:13:35 +0200 Subject: [PATCH 30/38] correctly mark stream::Merge as unstable (#352) Signed-off-by: Yoshua Wuyts --- src/stream/interval.rs | 1 + src/stream/stream/merge.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 271cd81..21ac032 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -43,6 +43,7 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` +#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[doc(inline)] pub fn interval(dur: Duration) -> Interval { diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index 5e7b226..ab97d2c 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -8,6 +8,8 @@ use futures_core::Stream; /// This stream is returned by [`Stream::merge`]. /// /// [`Stream::merge`]: trait.Stream.html#method.merge +#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { left: L, From 4d34a153633ad5b4de5e68becc5bfee9b5f858a6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 14:34:11 +0200 Subject: [PATCH 31/38] fix macros, take II Signed-off-by: Yoshua Wuyts --- src/macros.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index d70b779..12ca7ed 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -86,7 +86,10 @@ macro_rules! print { #[macro_export] macro_rules! println { () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) + ($($arg:tt)*) => (async { + $crate::io::_print(format_args!($($arg)*)).await; + $crate::io::_print(format_args!("\n")).await; + }) } /// Prints to the standard error. @@ -158,6 +161,7 @@ macro_rules! eprintln { ($($arg:tt)*) => ( async { $crate::io::_eprint(format_args!($($arg)*)).await; + $crate::io::_eprint(format_args!("\n")).await; } ); } From 802d4dfc3ba83f13ce1074f1eea60aec996d39f1 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 16 Oct 2019 15:24:35 +0200 Subject: [PATCH 32/38] finalize changelog Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e8731..f0e735a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,12 +56,15 @@ while let Some(_) = interval.next().await { ## Added - Added `FutureExt` to the `prelude`, allowing us to extend `Future` +- Added `Stream::cmp` - Added `Stream::ge` +- Added `Stream::last` - Added `Stream::le` - Added `Stream::lt` - Added `Stream::merge` as "unstable", replacing `stream::join!` - Added `Stream::partial_cmp` - Added `Stream::take_while` +- Added `Stream::try_fold` - Added `future::IntoFuture` as "unstable" - Added `io::BufRead::split` - Added `io::Write::write_fmt` @@ -76,6 +79,7 @@ while let Some(_) = interval.next().await { - Added `stream::Sum` - Added `stream::from_fn` - Added `stream::interval` as "unstable" +- Added `stream::repeat_with` - Added `task::spawn_blocking` as "unstable", replacing `task::blocking` - Added `task::yield_now` - Added `write!` and `writeln!` macros as "unstable" @@ -93,6 +97,8 @@ while let Some(_) = interval.next().await { - Rewrote the README.md - Stabilized `io::Cursor` - Switched bors over to use GitHub actions +- Updated the `io` documentation to match std's `io` docs +- Updated the `task` documentation to match std's `thread` docs ## Removed From e405544ea0b6cac9fd599ab6f86383db261ff601 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 17 Oct 2019 07:06:29 +0900 Subject: [PATCH 33/38] Enable tests on CI (#357) * Enable tests on CI * Fix failed test --- .github/workflows/ci.yml | 2 +- tests/buf_writer.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b92e50b..c622a59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --doc --features unstable + args: --all --features unstable check_fmt_and_docs: name: Checking fmt and docs diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs index fa0e1ed..cb2368a 100644 --- a/tests/buf_writer.rs +++ b/tests/buf_writer.rs @@ -48,13 +48,13 @@ fn test_buffered_writer() { } #[test] -fn test_buffered_writer_inner_into_inner_does_not_flush() { +fn test_buffered_writer_inner_into_inner_flushes() { task::block_on(async { let mut w = BufWriter::with_capacity(3, Vec::new()); w.write(&[0, 1]).await.unwrap(); assert_eq!(*w.get_ref(), []); let w = w.into_inner().await.unwrap(); - assert_eq!(w, []); + assert_eq!(w, [0, 1]); }) } From 46f0fb1c6407a5417eeedfedfed5fdf5a5e9cef6 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 11:52:42 +0200 Subject: [PATCH 34/38] Make sure each invocation of block_on uses its own Parker (#358) --- Cargo.toml | 1 + src/task/block_on.rs | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1c10a6..e8e2ccc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ async-task = "1.0.0" cfg-if = "0.1.9" 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" diff --git a/src/task/block_on.rs b/src/task/block_on.rs index db46f02..032bf02 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -1,10 +1,12 @@ -use std::cell::UnsafeCell; +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::{self, Thread}; +use std::thread; + +use crossbeam_utils::sync::Parker; use super::task; use super::task_local; @@ -119,13 +121,21 @@ where F: Future, { thread_local! { - static ARC_THREAD: Arc = Arc::new(thread::current()); + // 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); - ARC_THREAD.with(|arc_thread: &Arc| { - let ptr = (&**arc_thread as *const Thread) as *const (); + 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))) }; @@ -133,32 +143,34 @@ where 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; } - thread::park(); + arc_parker.park(); } }) } fn vtable() -> &'static RawWakerVTable { unsafe fn clone_raw(ptr: *const ()) -> RawWaker { - let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); + 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 Thread); - arc.unpark(); + 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 Thread)); - arc.unpark(); + 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 Thread)) + drop(Arc::from_raw(ptr as *const Parker)) } &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) From ec23632f3e7d2d8f9ed49995b85741eeb0a5be69 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 17 Oct 2019 19:17:49 +0200 Subject: [PATCH 35/38] Cleanup: replace cfg-if with our macros (#361) * Cleanup: replace cfg-if with our macros * Prefix macros with cfg_ * Remove #[macro_export] from internal macros --- Cargo.toml | 3 +- src/fs/dir_builder.rs | 23 +- src/fs/dir_entry.rs | 21 +- src/fs/file.rs | 85 +++----- src/fs/file_type.rs | 148 +++++++------ src/fs/metadata.rs | 350 +++++++++++++++--------------- src/fs/open_options.rs | 31 +-- src/fs/permissions.rs | 98 ++++----- src/future/future.rs | 14 +- src/future/into_future.rs | 2 +- src/future/mod.rs | 16 +- src/io/buf_read/mod.rs | 11 +- src/io/read/mod.rs | 66 +++--- src/io/{seek.rs => seek/mod.rs} | 37 +--- src/io/seek/seek.rs | 21 ++ src/io/stderr.rs | 37 +--- src/io/stdin.rs | 37 +--- src/io/stdout.rs | 37 +--- src/io/write/mod.rs | 18 +- src/lib.rs | 34 ++- src/macros.rs | 8 +- src/net/addr.rs | 24 +- src/net/tcp/listener.rs | 84 +++---- src/net/tcp/stream.rs | 81 +++---- src/net/udp/mod.rs | 83 +++---- src/os/mod.rs | 12 +- src/os/unix/fs.rs | 72 +++--- src/os/unix/io.rs | 90 ++++---- src/os/unix/net/mod.rs | 142 ++++++------ src/os/windows/io.rs | 80 ++++--- src/{pin.rs => pin/mod.rs} | 0 src/prelude.rs | 15 +- src/stream/double_ended_stream.rs | 2 +- src/stream/exact_size_stream.rs | 2 +- src/stream/extend.rs | 1 + src/stream/from_stream.rs | 2 +- src/stream/fused_stream.rs | 2 +- src/stream/interval.rs | 7 +- src/stream/into_stream.rs | 2 +- src/stream/mod.rs | 45 ++-- src/stream/product.rs | 2 +- src/stream/stream/merge.rs | 2 +- src/stream/stream/mod.rs | 44 ++-- src/stream/sum.rs | 2 +- src/sync/barrier.rs | 2 + src/sync/mod.rs | 11 +- src/task/mod.rs | 10 +- src/task/yield_now.rs | 2 +- src/utils.rs | 69 +++++- 49 files changed, 917 insertions(+), 1070 deletions(-) rename src/io/{seek.rs => seek/mod.rs} (80%) create mode 100644 src/io/seek/seek.rs rename src/{pin.rs => pin/mod.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index e8e2ccc..2741a19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,13 +21,12 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -docs = ["broadcaster"] +docs = ["unstable"] unstable = ["broadcaster"] [dependencies] async-macros = "1.0.0" async-task = "1.0.0" -cfg-if = "0.1.9" crossbeam-channel = "0.3.9" crossbeam-deque = "0.7.1" crossbeam-utils = "0.6.6" diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 6dfddcb..a55a9a9 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,7 +1,5 @@ use std::future::Future; -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -113,22 +111,13 @@ impl DirBuilder { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::DirBuilderExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::DirBuilderExt; - } -} +cfg_unix! { + use crate::os::unix::fs::DirBuilderExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl DirBuilderExt for DirBuilder { - fn mode(&mut self, mode: u32) -> &mut Self { - self.mode = Some(mode); - self - } + impl DirBuilderExt for DirBuilder { + fn mode(&mut self, mode: u32) -> &mut Self { + self.mode = Some(mode); + self } } } diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 80d4bbd..959e2ad 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -2,8 +2,6 @@ use std::ffi::OsString; use std::fmt; use std::sync::Arc; -use cfg_if::cfg_if; - use crate::fs::{FileType, Metadata}; use crate::io; use crate::path::PathBuf; @@ -160,21 +158,12 @@ impl fmt::Debug for DirEntry { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::DirEntryExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::DirEntryExt; - } -} +cfg_unix! { + use crate::os::unix::fs::DirEntryExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl DirEntryExt for DirEntry { - fn ino(&self) -> u64 { - self.0.ino() - } + impl DirEntryExt for DirEntry { + fn ino(&self) -> u64 { + self.0.ino() } } } diff --git a/src/fs/file.rs b/src/fs/file.rs index 3916234..3129e96 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -7,8 +7,6 @@ use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use cfg_if::cfg_if; - use crate::fs::{Metadata, Permissions}; use crate::future; use crate::io::{self, Read, Seek, SeekFrom, Write}; @@ -401,67 +399,54 @@ impl From for File { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for File { - fn as_raw_fd(&self) -> RawFd { - self.file.as_raw_fd() - } + impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() } + } - impl FromRawFd for File { - unsafe fn from_raw_fd(fd: RawFd) -> File { - std::fs::File::from_raw_fd(fd).into() - } + impl FromRawFd for File { + unsafe fn from_raw_fd(fd: RawFd) -> File { + std::fs::File::from_raw_fd(fd).into() } + } - 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() - } + 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() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for File { - fn as_raw_handle(&self) -> RawHandle { - self.file.as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; + + impl AsRawHandle for File { + fn as_raw_handle(&self) -> RawHandle { + self.file.as_raw_handle() } + } - impl FromRawHandle for File { - unsafe fn from_raw_handle(handle: RawHandle) -> File { - std::fs::File::from_raw_handle(handle).into() - } + impl FromRawHandle for File { + unsafe fn from_raw_handle(handle: RawHandle) -> File { + std::fs::File::from_raw_handle(handle).into() } + } - impl IntoRawHandle for File { - fn into_raw_handle(self) -> RawHandle { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") - .into_raw_handle() - } + impl IntoRawHandle for File { + fn into_raw_handle(self) -> RawHandle { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect("cannot acquire ownership of the file handle after drop") + .into_raw_handle() } } } diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs index a162798..11f47d1 100644 --- a/src/fs/file_type.rs +++ b/src/fs/file_type.rs @@ -1,86 +1,84 @@ -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::fs::FileType; +} + +cfg_docs! { + /// The type of a file or directory. + /// + /// A file type is returned by [`Metadata::file_type`]. + /// + /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], + /// [`is_file`], and [`is_symlink`] can return `true`. + /// + /// This type is a re-export of [`std::fs::FileType`]. + /// + /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`is_symlink`]: #method.is_symlink + /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct FileType { + _private: (), + } -cfg_if! { - if #[cfg(feature = "docs")] { - /// The type of a file or directory. + impl FileType { + /// Returns `true` if this file type represents a regular directory. /// - /// A file type is returned by [`Metadata::file_type`]. + /// If this file type represents a symbolic link, this method returns `false`. /// - /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], - /// [`is_file`], and [`is_symlink`] can return `true`. + /// # Examples /// - /// This type is a re-export of [`std::fs::FileType`]. + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; /// - /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type - /// [`is_dir`]: #method.is_dir - /// [`is_file`]: #method.is_file - /// [`is_symlink`]: #method.is_symlink - /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub struct FileType { - _private: (), + /// let file_type = fs::metadata(".").await?.file_type(); + /// println!("{:?}", file_type.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() } - impl FileType { - /// Returns `true` if this file type represents a regular directory. - /// - /// If this file type represents a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata(".").await?.file_type(); - /// println!("{:?}", file_type.is_dir()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_dir(&self) -> bool { - unimplemented!() - } - - /// Returns `true` if this file type represents a regular file. - /// - /// If this file type represents a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata("a.txt").await?.file_type(); - /// println!("{:?}", file_type.is_file()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_file(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this file type represents a regular file. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } - /// Returns `true` if this file type represents a symbolic link. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let file_type = fs::metadata("a.txt").await?.file_type(); - /// println!("{:?}", file_type.is_symlink()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_symlink(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this file type represents a symbolic link. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_symlink()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_symlink(&self) -> bool { + unimplemented!() } - } else { - pub use std::fs::FileType; } } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 65f494b..4afc559 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,5 +1,3 @@ -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -39,193 +37,193 @@ pub async fn metadata>(path: P) -> io::Result { blocking::spawn(move || std::fs::metadata(path)).await } -cfg_if! { - if #[cfg(feature = "docs")] { - use std::time::SystemTime; +cfg_not_docs! { + pub use std::fs::Metadata; +} + +cfg_docs! { + use std::time::SystemTime; - use crate::fs::{FileType, Permissions}; + use crate::fs::{FileType, Permissions}; - /// Metadata for a file or directory. + /// Metadata for a file or directory. + /// + /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// + /// This type is a re-export of [`std::fs::Metadata`]. + /// + /// [`metadata`]: fn.metadata.html + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html + #[derive(Clone, Debug)] + pub struct Metadata { + _private: (), + } + + impl Metadata { + /// Returns the file type from this metadata. /// - /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// # Examples /// - /// This type is a re-export of [`std::fs::Metadata`]. + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; /// - /// [`metadata`]: fn.metadata.html - /// [`symlink_metadata`]: fn.symlink_metadata.html - /// [`is_dir`]: #method.is_dir - /// [`is_file`]: #method.is_file - /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html - #[derive(Clone, Debug)] - pub struct Metadata { - _private: (), + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn file_type(&self) -> FileType { + unimplemented!() } - impl Metadata { - /// Returns the file type from this metadata. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.file_type()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn file_type(&self) -> FileType { - unimplemented!() - } - - /// Returns `true` if this metadata is for a regular directory. - /// - /// If this metadata is for a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata(".").await?; - /// println!("{:?}", metadata.is_dir()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_dir(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this metadata is for a regular directory. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata(".").await?; + /// println!("{:?}", metadata.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } - /// Returns `true` if this metadata is for a regular file. - /// - /// If this metadata is for a symbolic link, this method returns `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.is_file()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn is_file(&self) -> bool { - unimplemented!() - } + /// Returns `true` if this metadata is for a regular file. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } - /// Returns the file size in bytes. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{}", metadata.len()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn len(&self) -> u64 { - unimplemented!() - } + /// Returns the file size in bytes. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{}", metadata.len()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn len(&self) -> u64 { + unimplemented!() + } - /// Returns the permissions from this metadata. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.permissions()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn permissions(&self) -> Permissions { - unimplemented!() - } + /// Returns the permissions from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.permissions()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn permissions(&self) -> Permissions { + unimplemented!() + } - /// Returns the last modification time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.modified()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn modified(&self) -> io::Result { - unimplemented!() - } + /// Returns the last modification time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.modified()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn modified(&self) -> io::Result { + unimplemented!() + } - /// Returns the last access time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.accessed()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn accessed(&self) -> io::Result { - unimplemented!() - } + /// Returns the last access time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.accessed()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn accessed(&self) -> io::Result { + unimplemented!() + } - /// Returns the creation time. - /// - /// # Errors - /// - /// This data may not be available on all platforms, in which case an error will be - /// returned. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let metadata = fs::metadata("a.txt").await?; - /// println!("{:?}", metadata.created()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn created(&self) -> io::Result { - unimplemented!() - } + /// Returns the creation time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.created()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn created(&self) -> io::Result { + unimplemented!() } - } else { - pub use std::fs::Metadata; } } diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index fe65e2f..a2eb9e7 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,7 +1,5 @@ use std::future::Future; -use cfg_if::cfg_if; - use crate::fs::File; use crate::io; use crate::path::Path; @@ -296,27 +294,18 @@ impl Default for OpenOptions { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::fs::OpenOptionsExt; - } else if #[cfg(unix)] { - use std::os::unix::fs::OpenOptionsExt; - } -} +cfg_unix! { + use crate::os::unix::fs::OpenOptionsExt; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut Self { - self.0.mode(mode); - self - } + impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut Self { + self.0.mode(mode); + self + } - fn custom_flags(&mut self, flags: i32) -> &mut Self { - self.0.custom_flags(flags); - self - } + fn custom_flags(&mut self, flags: i32) -> &mut Self { + self.0.custom_flags(flags); + self } } } diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs index 628dd39..1339a7c 100644 --- a/src/fs/permissions.rs +++ b/src/fs/permissions.rs @@ -1,58 +1,56 @@ -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::fs::Permissions; +} + +cfg_docs! { + /// A set of permissions on a file or directory. + /// + /// This type is a re-export of [`std::fs::Permissions`]. + /// + /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Permissions { + _private: (), + } -cfg_if! { - if #[cfg(feature = "docs")] { - /// A set of permissions on a file or directory. + impl Permissions { + /// Returns the read-only flag. /// - /// This type is a re-export of [`std::fs::Permissions`]. + /// # Examples /// - /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html - #[derive(Clone, PartialEq, Eq, Debug)] - pub struct Permissions { - _private: (), + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let perm = fs::metadata("a.txt").await?.permissions(); + /// println!("{:?}", perm.readonly()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn readonly(&self) -> bool { + unimplemented!() } - impl Permissions { - /// Returns the read-only flag. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let perm = fs::metadata("a.txt").await?.permissions(); - /// println!("{:?}", perm.readonly()); - /// # - /// # Ok(()) }) } - /// ``` - pub fn readonly(&self) -> bool { - unimplemented!() - } - - /// Configures the read-only flag. - /// - /// [`fs::set_permissions`]: fn.set_permissions.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs; - /// - /// let mut perm = fs::metadata("a.txt").await?.permissions(); - /// perm.set_readonly(true); - /// fs::set_permissions("a.txt", perm).await?; - /// # - /// # Ok(()) }) } - /// ``` - pub fn set_readonly(&mut self, readonly: bool) { - unimplemented!() - } + /// Configures the read-only flag. + /// + /// [`fs::set_permissions`]: fn.set_permissions.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let mut perm = fs::metadata("a.txt").await?.permissions(); + /// perm.set_readonly(true); + /// fs::set_permissions("a.txt", perm).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn set_readonly(&mut self, readonly: bool) { + unimplemented!() } - } else { - pub use std::fs::Permissions; } } diff --git a/src/future/future.rs b/src/future/future.rs index 556dc1a..8c9c12a 100644 --- a/src/future/future.rs +++ b/src/future/future.rs @@ -1,15 +1,9 @@ -use crate::utils::extension_trait; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; -cfg_if::cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; + use crate::task::{Context, Poll}; - use crate::task::{Context, Poll}; - } -} - -extension_trait! { #[doc = r#" A future represents an asynchronous computation. diff --git a/src/future/into_future.rs b/src/future/into_future.rs index 58b6766..42839a2 100644 --- a/src/future/into_future.rs +++ b/src/future/into_future.rs @@ -30,7 +30,7 @@ use crate::future::Future; /// } /// } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait IntoFuture { /// The type of value produced on completion. diff --git a/src/future/mod.rs b/src/future/mod.rs index cc3b7a5..a45bf96 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -44,12 +44,6 @@ #[doc(inline)] pub use async_macros::{join, try_join}; -#[doc(inline)] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -pub use async_macros::{select, try_select}; - -use cfg_if::cfg_if; - pub use future::Future; pub use pending::pending; pub use poll_fn::poll_fn; @@ -62,10 +56,10 @@ mod poll_fn; mod ready; mod timeout; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod into_future; +cfg_unstable! { + #[doc(inline)] + pub use async_macros::{select, try_select}; - pub use into_future::IntoFuture; - } + pub use into_future::IntoFuture; + mod into_future; } diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index dff10fa..b82971f 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -12,19 +12,12 @@ use read_until::ReadUntilFuture; use std::mem; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::io; use crate::task::{Context, Poll}; -use crate::utils::extension_trait; - -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; - } -} extension_trait! { + use std::ops::{Deref, DerefMut}; + #[doc = r#" Allows reading from a buffered byte stream. diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 40cb3ca..ed61590 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -13,23 +13,17 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture}; use read_to_string::ReadToStringFuture; use read_vectored::ReadVectoredFuture; -use cfg_if::cfg_if; use std::mem; use crate::io::IoSliceMut; -use crate::utils::extension_trait; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; - use crate::io; - use crate::task::{Context, Poll}; - } -} + use crate::io; + use crate::task::{Context, Poll}; -extension_trait! { #[doc = r#" Allows reading from a byte stream. @@ -309,34 +303,34 @@ extension_trait! { #[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. - + # 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 mut f = File::open("foo.txt").await?; let mut buffer = Vec::new(); let mut other_buffer = Vec::new(); - + { let reference = f.by_ref(); - + // read at most 5 bytes reference.take(5).read_to_end(&mut buffer).await?; - + } // 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?; # @@ -348,27 +342,27 @@ extension_trait! { #[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()); } @@ -382,29 +376,29 @@ extension_trait! { #[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`. - + # 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 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?; diff --git a/src/io/seek.rs b/src/io/seek/mod.rs similarity index 80% rename from src/io/seek.rs rename to src/io/seek/mod.rs index 2d1c4d3..ec2dd8f 100644 --- a/src/io/seek.rs +++ b/src/io/seek/mod.rs @@ -1,19 +1,16 @@ -use std::pin::Pin; +mod seek; -use cfg_if::cfg_if; +use seek::SeekFuture; -use crate::future::Future; -use crate::io::{self, SeekFrom}; -use crate::task::{Context, Poll}; -use crate::utils::extension_trait; - -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; - } -} +use crate::io::SeekFrom; extension_trait! { + use std::ops::{Deref, DerefMut}; + use std::pin::Pin; + + use crate::io; + use crate::task::{Context, Poll}; + #[doc = r#" Allows seeking through a byte stream. @@ -114,19 +111,3 @@ extension_trait! { } } } - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct SeekFuture<'a, T: Unpin + ?Sized> { - seeker: &'a mut T, - pos: SeekFrom, -} - -impl Future for SeekFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let pos = self.pos; - Pin::new(&mut *self.seeker).poll_seek(cx, pos) - } -} diff --git a/src/io/seek/seek.rs b/src/io/seek/seek.rs new file mode 100644 index 0000000..65743be --- /dev/null +++ b/src/io/seek/seek.rs @@ -0,0 +1,21 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::io::{self, Seek, SeekFrom}; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct SeekFuture<'a, T: Unpin + ?Sized> { + pub(crate) seeker: &'a mut T, + pub(crate) pos: SeekFrom, +} + +impl Future for SeekFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let pos = self.pos; + Pin::new(&mut *self.seeker).poll_seek(cx, pos) + } +} diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 34d4d1d..1ec28b2 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io::{self, Write}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -162,35 +160,22 @@ impl Write for Stderr { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stderr { - fn as_raw_fd(&self) -> RawFd { - std::io::stderr().as_raw_fd() - } + impl AsRawFd for Stderr { + fn as_raw_fd(&self) -> RawFd { + std::io::stderr().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stderr { - fn as_raw_handle(&self) -> RawHandle { - std::io::stderr().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stderr { + fn as_raw_handle(&self) -> RawHandle { + std::io::stderr().as_raw_handle() } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 178e819..dd3991f 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::{self, Future}; use crate::io::{self, Read}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -186,35 +184,22 @@ impl Read for Stdin { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stdin { - fn as_raw_fd(&self) -> RawFd { - std::io::stdin().as_raw_fd() - } + impl AsRawFd for Stdin { + fn as_raw_fd(&self) -> RawFd { + std::io::stdin().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stdin { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdin().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdin { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdin().as_raw_handle() } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 4128aae..7945bfd 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,8 +1,6 @@ use std::pin::Pin; use std::sync::Mutex; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io::{self, Write}; use crate::task::{blocking, Context, JoinHandle, Poll}; @@ -162,35 +160,22 @@ impl Write for Stdout { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, RawFd}; - use crate::os::windows::io::{AsRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, RawFd}; - } else if #[cfg(windows)] { - use std::os::windows::io::{AsRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for Stdout { - fn as_raw_fd(&self) -> RawFd { - std::io::stdout().as_raw_fd() - } + impl AsRawFd for Stdout { + fn as_raw_fd(&self) -> RawFd { + std::io::stdout().as_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - impl AsRawHandle for Stdout { - fn as_raw_handle(&self) -> RawHandle { - std::io::stdout().as_raw_handle() - } +cfg_windows! { + use crate::os::windows::io::{AsRawHandle, RawHandle}; + + impl AsRawHandle for Stdout { + fn as_raw_handle(&self) -> RawHandle { + std::io::stdout().as_raw_handle() } } } diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index be69b7e..a07c896 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -10,22 +10,14 @@ use write_all::WriteAllFuture; use write_fmt::WriteFmtFuture; use write_vectored::WriteVectoredFuture; -use cfg_if::cfg_if; +use crate::io::{self, IoSlice}; -use crate::io::IoSlice; -use crate::utils::extension_trait; +extension_trait! { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; -use crate::io; + use crate::task::{Context, Poll}; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::pin::Pin; - use std::ops::{Deref, DerefMut}; - use crate::task::{Context, Poll}; - } -} - -extension_trait! { #[doc = r#" Allows writing to a byte stream. diff --git a/src/lib.rs b/src/lib.rs index e138c87..7f888a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,8 @@ #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![recursion_limit = "1024"] -use cfg_if::cfg_if; +#[macro_use] +mod utils; pub mod fs; pub mod future; @@ -61,26 +62,19 @@ pub mod stream; pub mod sync; pub mod task; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub mod pin; - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - pub mod process; +cfg_unstable! { + pub mod pin; + pub mod process; - mod unit; - mod vec; - mod result; - mod option; - mod string; - mod collections; - } + mod unit; + mod vec; + mod result; + mod option; + mod string; + mod collections; + + #[doc(inline)] + pub use std::{write, writeln}; } mod macros; -pub(crate) mod utils; - -#[cfg(any(feature = "unstable", feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] -pub use std::{write, writeln}; diff --git a/src/macros.rs b/src/macros.rs index 12ca7ed..f932e47 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -43,7 +43,7 @@ /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! print { @@ -81,7 +81,7 @@ macro_rules! print { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! println { @@ -119,7 +119,7 @@ macro_rules! println { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprint { @@ -153,7 +153,7 @@ macro_rules! eprint { /// # /// # }) /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[macro_export] macro_rules! eprintln { diff --git a/src/net/addr.rs b/src/net/addr.rs index adc2408..1dada64 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -3,24 +3,22 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::future::Future; use crate::io; use crate::task::{blocking, Context, JoinHandle, Poll}; -cfg_if! { - if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct ImplFuture(std::marker::PhantomData); +cfg_not_docs! { + macro_rules! ret { + (impl Future, $fut:ty) => ($fut); + } +} + +cfg_docs! { + #[doc(hidden)] + pub struct ImplFuture(std::marker::PhantomData); - macro_rules! ret { - (impl Future, $fut:ty) => (ImplFuture<$out>); - } - } else { - macro_rules! ret { - (impl Future, $fut:ty) => ($fut); - } + macro_rules! ret { + (impl Future, $fut:ty) => (ImplFuture<$out>); } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 26e19d7..6fd27f0 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,13 +1,10 @@ use std::net::SocketAddr; use std::pin::Pin; -use cfg_if::cfg_if; - -use super::TcpStream; use crate::future::{self, Future}; use crate::io; use crate::net::driver::Watcher; -use crate::net::ToSocketAddrs; +use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -213,59 +210,46 @@ impl From for TcpListener { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for TcpListener { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { - std::net::TcpListener::from_raw_fd(fd).into() - } + impl FromRawFd for TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { + std::net::TcpListener::from_raw_fd(fd).into() } + } - impl IntoRawFd for TcpListener { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for TcpListener { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // 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 - // } - // } - } +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 + // } + // } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 1ffd636..5988194 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -2,8 +2,6 @@ use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::net::SocketAddr; use std::pin::Pin; -use cfg_if::cfg_if; - use crate::future; use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; @@ -367,59 +365,46 @@ impl From for TcpStream { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for TcpStream { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { - std::net::TcpStream::from_raw_fd(fd).into() - } + impl FromRawFd for TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { + std::net::TcpStream::from_raw_fd(fd).into() } + } - impl IntoRawFd for TcpStream { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for TcpStream { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // 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 - // } - // } - } +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 + // } + // } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 4588be1..37c9d50 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -1,7 +1,5 @@ use std::io; use std::net::SocketAddr; - -use cfg_if::cfg_if; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; @@ -463,61 +461,46 @@ impl From for UdpSocket { } } -cfg_if! { - if #[cfg(feature = "docs")] { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } else if #[cfg(unix)] { - use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - } else if #[cfg(windows)] { - // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - } -} +cfg_unix! { + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -cfg_if! { - if #[cfg(any(unix, feature = "docs"))] { - impl AsRawFd for UdpSocket { - fn as_raw_fd(&self) -> RawFd { - self.watcher.get_ref().as_raw_fd() - } + impl AsRawFd for UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.watcher.get_ref().as_raw_fd() } + } - impl FromRawFd for UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { - std::net::UdpSocket::from_raw_fd(fd).into() - } + impl FromRawFd for UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { + std::net::UdpSocket::from_raw_fd(fd).into() } + } - impl IntoRawFd for UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.watcher.into_inner().into_raw_fd() - } + impl IntoRawFd for UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.watcher.into_inner().into_raw_fd() } } } -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -cfg_if! { - if #[cfg(any(windows, feature = "docs"))] { - // use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; - // - // 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 - // } - // } - } +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 + // } + // } } diff --git a/src/os/mod.rs b/src/os/mod.rs index fb9b941..5b836ae 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,9 +1,9 @@ //! OS-specific extensions. -#[cfg(any(unix, feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unix)))] -pub mod unix; +cfg_unix! { + pub mod unix; +} -#[cfg(any(windows, feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(windows)))] -pub mod windows; +cfg_windows! { + pub mod windows; +} diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index 4a15510..d3e8523 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -1,7 +1,5 @@ //! Unix-specific filesystem extensions. -use cfg_if::cfg_if; - use crate::io; use crate::path::Path; use crate::task::blocking; @@ -31,43 +29,43 @@ pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Resu blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await } -cfg_if! { - if #[cfg(feature = "docs")] { - /// Unix-specific extensions to `DirBuilder`. - pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// `0o777`. - fn mode(&mut self, mode: u32) -> &mut Self; - } +cfg_not_docs! { + pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt}; +} - /// Unix-specific extension methods for `DirEntry`. - pub trait DirEntryExt { - /// Returns the underlying `d_ino` field in the contained `dirent` - /// structure. - fn ino(&self) -> u64; - } +cfg_docs! { + /// Unix-specific extensions to `DirBuilder`. + pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// `0o777`. + fn mode(&mut self, mode: u32) -> &mut Self; + } + + /// Unix-specific extension methods for `DirEntry`. + pub trait DirEntryExt { + /// Returns the underlying `d_ino` field in the contained `dirent` + /// structure. + fn ino(&self) -> u64; + } - /// Unix-specific extensions to `OpenOptions`. - pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of a `File::open_opts` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the systems `umask`, to produce - /// the final permissions. - fn mode(&mut self, mode: u32) -> &mut Self; + /// Unix-specific extensions to `OpenOptions`. + pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the systems `umask`, to produce + /// the final permissions. + fn mode(&mut self, mode: u32) -> &mut Self; - /// Pass custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - fn custom_flags(&mut self, flags: i32) -> &mut Self; - } - } else { - pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt}; + /// Pass custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rusts options. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + fn custom_flags(&mut self, flags: i32) -> &mut Self; } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 820d509..0b98460 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -1,56 +1,54 @@ //! Unix-specific I/O extensions. -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +} -cfg_if! { - if #[cfg(feature = "docs")] { - /// Raw file descriptors. - pub type RawFd = std::os::raw::c_int; +cfg_docs! { + /// Raw file descriptors. + pub type RawFd = std::os::raw::c_int; - /// A trait to extract the raw unix file descriptor from an underlying - /// object. + /// A trait to extract the raw unix file descriptor from an underlying + /// object. + /// + /// This is only available on unix platforms and must be imported in order + /// to call the method. Windows platforms have a corresponding `AsRawHandle` + /// and `AsRawSocket` set of traits. + pub trait AsRawFd { + /// Extracts the raw file descriptor. /// - /// This is only available on unix platforms and must be imported in order - /// to call the method. Windows platforms have a corresponding `AsRawHandle` - /// and `AsRawSocket` set of traits. - pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - fn as_raw_fd(&self) -> RawFd; - } + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + fn as_raw_fd(&self) -> RawFd; + } - /// A trait to express the ability to construct an object from a raw file + /// A trait to express the ability to construct an object from a raw file + /// descriptor. + pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file /// descriptor. - pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the 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_fd(fd: RawFd) -> Self; - } + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the 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_fd(fd: RawFd) -> Self; + } - /// A trait to express the ability to consume an object and acquire ownership of - /// its raw file descriptor. - pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. - fn into_raw_fd(self) -> RawFd; - } - } else { - pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw file descriptor. + pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + fn into_raw_fd(self) -> RawFd; } } diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs index 2c79e8c..e0a3b9b 100644 --- a/src/os/unix/net/mod.rs +++ b/src/os/unix/net/mod.rs @@ -1,7 +1,5 @@ //! Unix-specific networking extensions. -use cfg_if::cfg_if; - pub use datagram::UnixDatagram; pub use listener::{Incoming, UnixListener}; pub use stream::UnixStream; @@ -10,90 +8,90 @@ mod datagram; mod listener; mod stream; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::fmt; +cfg_not_docs! { + pub use std::os::unix::net::SocketAddr; +} + +cfg_docs! { + use std::fmt; + + use crate::path::Path; - use crate::path::Path; + /// An address associated with a Unix socket. + /// + /// # Examples + /// + /// ``` + /// use async_std::os::unix::net::UnixListener; + /// + /// let socket = UnixListener::bind("/tmp/socket").await?; + /// let addr = socket.local_addr()?; + /// ``` + #[derive(Clone)] + pub struct SocketAddr { + _private: (), + } - /// An address associated with a Unix socket. + impl SocketAddr { + /// Returns `true` if the address is unnamed. /// /// # Examples /// - /// ``` + /// A named address: + /// + /// ```no_run /// use async_std::os::unix::net::UnixListener; /// /// let socket = UnixListener::bind("/tmp/socket").await?; /// let addr = socket.local_addr()?; + /// assert_eq!(addr.is_unnamed(), false); /// ``` - #[derive(Clone)] - pub struct SocketAddr { - _private: (), + /// + /// An unnamed address: + /// + /// ```no_run + /// use async_std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().await?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.is_unnamed(), true); + /// ``` + pub fn is_unnamed(&self) -> bool { + unreachable!("this impl only appears in the rendered docs") } - impl SocketAddr { - /// Returns `true` if the address is unnamed. - /// - /// # Examples - /// - /// A named address: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixListener; - /// - /// let socket = UnixListener::bind("/tmp/socket").await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.is_unnamed(), false); - /// ``` - /// - /// An unnamed address: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound().await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.is_unnamed(), true); - /// ``` - pub fn is_unnamed(&self) -> bool { - unreachable!("this impl only appears in the rendered docs") - } - - /// Returns the contents of this address if it is a `pathname` address. - /// - /// # Examples - /// - /// With a pathname: - /// - /// ```no_run - /// use async_std::os::unix::net::UnixListener; - /// use async_std::path::Path; - /// - /// let socket = UnixListener::bind("/tmp/socket").await?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket"))); - /// ``` - /// - /// Without a pathname: - /// - /// ``` - /// use async_std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr()?; - /// assert_eq!(addr.as_pathname(), None); - /// ``` - pub fn as_pathname(&self) -> Option<&Path> { - unreachable!("this impl only appears in the rendered docs") - } + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use async_std::os::unix::net::UnixListener; + /// use async_std::path::Path; + /// + /// let socket = UnixListener::bind("/tmp/socket").await?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket"))); + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use async_std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr()?; + /// assert_eq!(addr.as_pathname(), None); + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + unreachable!("this impl only appears in the rendered docs") } + } - impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - unreachable!("this impl only appears in the rendered docs") - } + impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + unreachable!("this impl only appears in the rendered docs") } - } else { - pub use std::os::unix::net::SocketAddr; } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 20f87d2..e83d557 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -1,50 +1,48 @@ //! Windows-specific I/O extensions. -use cfg_if::cfg_if; +cfg_not_docs! { + pub use std::os::windows::io::{ + AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, + }; +} -cfg_if! { - if #[cfg(feature = "docs")] { - /// Raw HANDLEs. - pub type RawHandle = *mut std::os::raw::c_void; +cfg_docs! { + /// Raw HANDLEs. + pub type RawHandle = *mut std::os::raw::c_void; - /// Raw SOCKETs. - pub type RawSocket = u64; + /// Raw SOCKETs. + pub type RawSocket = u64; - /// Extracts raw handles. - pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. - fn as_raw_handle(&self) -> RawHandle; - } + /// Extracts raw handles. + pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + fn as_raw_handle(&self) -> RawHandle; + } - /// Construct I/O objects from raw handles. - pub trait FromRawHandle { - /// Constructs a new I/O object from the specified raw handle. - /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. - /// - /// 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_handle(handle: RawHandle) -> Self; - } + /// Construct I/O objects from raw handles. + pub trait FromRawHandle { + /// Constructs a new I/O object from the specified raw handle. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// 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_handle(handle: RawHandle) -> Self; + } - /// A trait to express the ability to consume an object and acquire ownership of - /// its raw `HANDLE`. - pub trait IntoRawHandle { - /// Consumes this object, returning the raw underlying handle. - /// - /// This function **transfers ownership** of the underlying handle to the - /// caller. Callers are then the unique owners of the handle and must close - /// it once it's no longer needed. - fn into_raw_handle(self) -> RawHandle; - } - } else { - pub use std::os::windows::io::{ - AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, - }; + /// A trait to express the ability to consume an object and acquire ownership of + /// its raw `HANDLE`. + pub trait IntoRawHandle { + /// Consumes this object, returning the raw underlying handle. + /// + /// This function **transfers ownership** of the underlying handle to the + /// caller. Callers are then the unique owners of the handle and must close + /// it once it's no longer needed. + fn into_raw_handle(self) -> RawHandle; } } diff --git a/src/pin.rs b/src/pin/mod.rs similarity index 100% rename from src/pin.rs rename to src/pin/mod.rs diff --git a/src/prelude.rs b/src/prelude.rs index 6c670cc..91432e0 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,8 +11,6 @@ //! use async_std::prelude::*; //! ``` -use cfg_if::cfg_if; - #[doc(no_inline)] pub use crate::future::Future; #[doc(no_inline)] @@ -41,12 +39,9 @@ pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - #[doc(no_inline)] - pub use crate::stream::DoubleEndedStream; - - #[doc(no_inline)] - pub use crate::stream::ExactSizeStream; - } +cfg_unstable! { + #[doc(no_inline)] + pub use crate::stream::DoubleEndedStream; + #[doc(no_inline)] + pub use crate::stream::ExactSizeStream; } diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 6fab77c..129bb1c 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -10,8 +10,8 @@ use std::task::{Context, Poll}; /// `Item`s from the back, as well as the front. /// /// [`Stream`]: trait.Stream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait DoubleEndedStream: Stream { /// Removes and returns an element from the end of the stream. /// diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index ef23691..7d2e7cb 100644 --- a/src/stream/exact_size_stream.rs +++ b/src/stream/exact_size_stream.rs @@ -76,8 +76,8 @@ pub use crate::stream::Stream; /// # }); /// # } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait ExactSizeStream: Stream { /// Returns the exact number of times the stream will iterate. /// diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 27efd8b..d9e1481 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -27,6 +27,7 @@ use crate::stream::IntoStream; /// # /// # }) } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 047dab8..54a2229 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -106,8 +106,8 @@ use std::pin::Pin; ///``` /// /// [`IntoStream`]: trait.IntoStream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait FromStream { /// Creates a value from a stream. /// diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs index 42e7e6f..e14ab5b 100644 --- a/src/stream/fused_stream.rs +++ b/src/stream/fused_stream.rs @@ -14,7 +14,7 @@ use crate::stream::Stream; /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`Stream::fuse`]: trait.Stream.html#method.fuse /// [`Fuse`]: struct.Fuse.html -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait FusedStream: Stream {} diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 21ac032..043d307 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -43,9 +43,8 @@ use futures_timer::Delay; /// # /// # Ok(()) }) } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] pub fn interval(dur: Duration) -> Interval { Interval { delay: Delay::new(dur), @@ -55,9 +54,9 @@ pub fn interval(dur: Duration) -> Interval { /// A stream representing notifications at fixed interval /// -#[derive(Debug)] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[doc(inline)] +#[derive(Debug)] pub struct Interval { delay: Delay, interval: Duration, diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 9233181..f96dc59 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -13,8 +13,8 @@ use crate::stream::Stream; /// See also: [`FromStream`]. /// /// [`FromStream`]: trait.FromStream.html +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait IntoStream { /// The type of the elements being iterated over. type Item; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 3b5f461..e796510 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -21,8 +21,6 @@ //! # }) //! ``` -use cfg_if::cfg_if; - pub use empty::{empty, Empty}; pub use from_fn::{from_fn, FromFn}; pub use once::{once, Once}; @@ -40,28 +38,25 @@ mod once; mod repeat; mod repeat_with; -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod double_ended_stream; - mod exact_size_stream; - mod extend; - mod from_stream; - mod fused_stream; - mod interval; - mod into_stream; - mod product; - mod sum; - - pub use double_ended_stream::DoubleEndedStream; - pub use exact_size_stream::ExactSizeStream; - pub use extend::Extend; - pub use from_stream::FromStream; - pub use fused_stream::FusedStream; - pub use interval::{interval, Interval}; - pub use into_stream::IntoStream; - pub use product::Product; - pub use sum::Sum; +cfg_unstable! { + mod double_ended_stream; + mod exact_size_stream; + mod extend; + mod from_stream; + mod fused_stream; + mod interval; + mod into_stream; + mod product; + mod sum; - pub use stream::Merge; - } + pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; + pub use extend::Extend; + pub use from_stream::FromStream; + pub use fused_stream::FusedStream; + pub use interval::{interval, Interval}; + pub use into_stream::IntoStream; + pub use product::Product; + pub use stream::Merge; + pub use sum::Sum; } diff --git a/src/stream/product.rs b/src/stream/product.rs index b322776..5799990 100644 --- a/src/stream/product.rs +++ b/src/stream/product.rs @@ -11,8 +11,8 @@ use crate::stream::Stream; /// [`product`]: trait.Product.html#tymethod.product /// [`FromStream`]: trait.FromStream.html /// [`Stream::product`]: trait.Stream.html#method.product +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait Product: Sized { /// Method which takes a stream and generates `Self` from the elements by /// multiplying the items. diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs index ab97d2c..9889dc7 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -8,7 +8,7 @@ use futures_core::Stream; /// This stream is returned by [`Stream::merge`]. /// /// [`Stream::merge`]: trait.Stream.html#method.merge -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Merge { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 764dc97..501ece1 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -91,32 +91,22 @@ pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use cfg_if::cfg_if; +cfg_unstable! { + use std::pin::Pin; -use crate::utils::extension_trait; + use crate::future::Future; + use crate::stream::FromStream; -cfg_if! { - if #[cfg(feature = "docs")] { - use std::ops::{Deref, DerefMut}; + pub use merge::Merge; - use crate::task::{Context, Poll}; - } + mod merge; } -cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod merge; - - use std::pin::Pin; - - use crate::future::Future; - use crate::stream::FromStream; +extension_trait! { + use std::ops::{Deref, DerefMut}; - pub use merge::Merge; - } -} + use crate::task::{Context, Poll}; -extension_trait! { #[doc = r#" An asynchronous stream of values. @@ -495,7 +485,7 @@ extension_trait! { # # }) } ``` - + "#] fn last( self, @@ -1276,7 +1266,7 @@ extension_trait! { [`stream`]: trait.Stream.html#tymethod.next "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[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>( @@ -1315,7 +1305,7 @@ extension_trait! { # }); ``` "#] - #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] fn merge(self, other: U) -> Merge where @@ -1345,9 +1335,9 @@ extension_trait! { 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!(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)); + assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); # # }) } ``` @@ -1366,7 +1356,7 @@ extension_trait! { #[doc = r#" Lexicographically compares the elements of this `Stream` with those - of another using 'Ord'. + of another using 'Ord'. # Examples @@ -1383,9 +1373,9 @@ extension_trait! { 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!(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); + assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); # # }) } ``` diff --git a/src/stream/sum.rs b/src/stream/sum.rs index fd5d7d5..a87ade1 100644 --- a/src/stream/sum.rs +++ b/src/stream/sum.rs @@ -11,8 +11,8 @@ use crate::stream::Stream; /// [`sum`]: trait.Sum.html#tymethod.sum /// [`FromStream`]: trait.FromStream.html /// [`Stream::sum`]: trait.Stream.html#method.sum +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(any(feature = "unstable", feature = "docs"))] pub trait Sum: Sized { /// Method which takes a stream and generates `Self` from the elements by /// "summing up" the items. diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 43488ee..080eff8 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -32,6 +32,7 @@ use crate::sync::Mutex; /// # }); /// # } /// ``` +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Debug)] pub struct Barrier { @@ -61,6 +62,7 @@ struct BarrierState { /// 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); diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 3d3b7b8..be74d8f 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -32,14 +32,13 @@ #[doc(inline)] pub use std::sync::{Arc, Weak}; -#[cfg(any(feature = "unstable", feature = "docs"))] -pub use barrier::{Barrier, BarrierWaitResult}; - pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -#[cfg(any(feature = "unstable", feature = "docs"))] -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -mod barrier; mod mutex; mod rwlock; + +cfg_unstable! { + pub use barrier::{Barrier, BarrierWaitResult}; + mod barrier; +} diff --git a/src/task/mod.rs b/src/task/mod.rs index e2c89ae..24eae08 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -143,11 +143,9 @@ mod worker; pub(crate) mod blocking; -cfg_if::cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - mod yield_now; - pub use yield_now::yield_now; - } +cfg_unstable! { + mod yield_now; + pub use yield_now::yield_now; } /// Spawns a blocking task. @@ -178,7 +176,7 @@ cfg_if::cfg_if! { /// ``` // Once this function stabilizes we should merge `blocking::spawn` into this so // all code in our crate uses `task::blocking` too. -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub fn spawn_blocking(f: F) -> task::JoinHandle diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 6f59638..2a4788d 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -26,7 +26,7 @@ use std::pin::Pin; /// # /// # }) } /// ``` -#[cfg(any(feature = "unstable", feature = "docs"))] +#[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[inline] pub async fn yield_now() { diff --git a/src/utils.rs b/src/utils.rs index 76db50b..60516d2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,6 +20,64 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { t } +/// Declares unstable items. +#[doc(hidden)] +macro_rules! cfg_unstable { + ($($item:item)*) => { + $( + #[cfg(feature = "unstable")] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + $item + )* + } +} + +/// Declares Unix-specific items. +#[doc(hidden)] +macro_rules! cfg_unix { + ($($item:item)*) => { + $( + #[cfg(any(unix, feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unix)))] + $item + )* + } +} + +/// Declares Windows-specific items. +#[doc(hidden)] +macro_rules! cfg_windows { + ($($item:item)*) => { + $( + #[cfg(any(windows, feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(windows)))] + $item + )* + } +} + +/// Declares items when the "docs" feature is enabled. +#[doc(hidden)] +macro_rules! cfg_docs { + ($($item:item)*) => { + $( + #[cfg(feature = "docs")] + $item + )* + } +} + +/// Declares items when the "docs" feature is disabled. +#[doc(hidden)] +macro_rules! cfg_not_docs { + ($($item:item)*) => { + $( + #[cfg(not(feature = "docs"))] + $item + )* + } +} + /// Defines an extension trait for a base trait. /// /// In generated docs, the base trait will contain methods from the extension trait. In actual @@ -29,7 +87,6 @@ pub fn abort_on_panic(f: impl FnOnce() -> T) -> T { /// 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. #[doc(hidden)] -#[macro_export] macro_rules! extension_trait { ( // Interesting patterns: @@ -113,6 +170,12 @@ macro_rules! extension_trait { // Handle the end of the token list. (@doc ($($head:tt)*)) => { $($head)* }; (@ext ($($head:tt)*)) => { $($head)* }; -} -pub use crate::extension_trait; + // Parse imports at the beginning of the macro. + ($import:item $($tail:tt)*) => { + #[cfg(feature = "docs")] + $import + + extension_trait!($($tail)*); + }; +} From 944e43d4bf1e228b9e9243c79a3a600d97855308 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 18:35:02 +0900 Subject: [PATCH 36/38] =?UTF-8?q?Remove=20Pin=20API=20related=20unsafe=20c?= =?UTF-8?q?ode=20by=20using=20pin-project-lite=20cra=E2=80=A6=20(#381)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/future/timeout.rs | 25 ++-- src/io/buf_read/lines.rs | 63 +++++----- src/io/buf_read/split.rs | 59 ++++----- src/io/buf_reader.rs | 141 +++++++++++---------- src/io/buf_writer.rs | 195 ++++++++++++++++-------------- src/io/copy.rs | 43 +++---- src/io/read/chain.rs | 89 +++++++------- src/io/read/take.rs | 54 +++++---- src/io/timeout.rs | 37 +++--- src/stream/from_fn.rs | 52 ++++---- src/stream/interval.rs | 8 +- src/stream/once.rs | 24 ++-- src/stream/repeat_with.rs | 51 ++++---- src/stream/stream/chain.rs | 32 ++--- src/stream/stream/cmp.rs | 58 ++++----- src/stream/stream/enumerate.rs | 29 +++-- src/stream/stream/filter.rs | 27 +++-- src/stream/stream/filter_map.rs | 29 +++-- src/stream/stream/fold.rs | 36 +++--- src/stream/stream/for_each.rs | 27 +++-- src/stream/stream/fuse.rs | 33 +++-- src/stream/stream/ge.rs | 27 +++-- src/stream/stream/gt.rs | 23 ++-- src/stream/stream/inspect.rs | 27 +++-- src/stream/stream/last.rs | 27 +++-- src/stream/stream/le.rs | 23 ++-- src/stream/stream/lt.rs | 23 ++-- src/stream/stream/map.rs | 29 +++-- src/stream/stream/merge.rs | 40 +++--- src/stream/stream/min_by.rs | 38 +++--- src/stream/stream/partial_cmp.rs | 58 ++++----- src/stream/stream/scan.rs | 27 +++-- src/stream/stream/skip.rs | 27 +++-- src/stream/stream/skip_while.rs | 29 +++-- src/stream/stream/step_by.rs | 32 ++--- src/stream/stream/take.rs | 33 +++-- src/stream/stream/take_while.rs | 27 +++-- src/stream/stream/try_fold.rs | 38 +++--- src/stream/stream/try_for_each.rs | 29 +++-- src/stream/stream/zip.rs | 35 +++--- src/task/block_on.rs | 14 +-- 42 files changed, 894 insertions(+), 825 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2741a19..e353386 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ 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"] } +pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" diff --git a/src/future/timeout.rs b/src/future/timeout.rs index a8338fb..c745d73 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -4,6 +4,7 @@ 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}; @@ -39,24 +40,24 @@ where f.await } -/// A future that times out after a duration of time. -struct TimeoutFuture { - future: F, - delay: Delay, -} - -impl TimeoutFuture { - pin_utils::unsafe_pinned!(future: F); - pin_utils::unsafe_pinned!(delay: Delay); +pin_project! { + /// A future that times out after a duration of time. + struct TimeoutFuture { + #[pin] + future: F, + #[pin] + delay: Delay, + } } 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 6cb4a07..c60529c 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/split.rs b/src/io/buf_read/split.rs index aa3b6fb..229a99b 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 13cb4cc..1d00b52 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,51 +2,56 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; +use pin_project_lite::pin_project; + use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// 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 { @@ -95,10 +100,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 +142,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 +193,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 +210,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 +227,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 +244,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 +310,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 f12aacb..6327ca7 100644 --- a/src/io/buf_writer.rs +++ b/src/io/buf_writer.rs @@ -2,6 +2,7 @@ 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}; @@ -9,88 +10,88 @@ use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; -/// 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 -/// -/// # 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?; -/// }; -/// # -/// # 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, +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`. + /// + /// 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 + /// + /// # 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?; + /// }; + /// # + /// # 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 { + #[pin] + inner: W, + buf: Vec, + written: usize, + } } #[derive(Debug)] pub struct IntoInnerError(W, std::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. /// @@ -178,6 +179,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 +242,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 +258,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 +267,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,20 +285,20 @@ 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) } } @@ -314,6 +321,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 3840d2a..098df8d 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::future::Future; use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; @@ -46,47 +48,38 @@ where R: Read + Unpin + ?Sized, W: Write + Unpin + ?Sized, { - pub struct CopyFuture<'a, R, W: ?Sized> { - reader: R, - writer: &'a mut W, - amt: u64, - } - - 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, - ) - } + pin_project! { + struct CopyFuture { + #[pin] + reader: R, + #[pin] + writer: W, + amt: u64, } } - impl Future for CopyFuture<'_, R, W> + 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))?; + 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)); + futures_core::ready!(this.writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*this.amt)); } - 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); } } } diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs index 09517cc..335cac2 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,76 +103,64 @@ 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) } } } diff --git a/src/io/read/take.rs b/src/io/read/take.rs index def4e24..09b02c2 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,31 +191,30 @@ 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); } } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 9fcc15e..8ef844d 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -3,7 +3,7 @@ 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; @@ -43,22 +43,18 @@ where .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: Delay, + } } impl Future for Timeout @@ -67,13 +63,14 @@ 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() { + if this.timeout.poll(cx).is_ready() { let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); Poll::Ready(err) } else { diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index c1cb97a..f53f3e5 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + 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. -/// -/// [`from_fn`]: fn.from_fn.html -#[derive(Debug)] -pub struct FromFn { - f: F, - future: Option, - __t: PhantomData, +pin_project! { + /// A stream that yields elements by calling a closure. + /// + /// This stream is constructed by [`from_fn`] function. + /// + /// [`from_fn`]: fn.from_fn.html + #[derive(Debug)] + pub struct FromFn { + f: F, + #[pin] + future: Option, + __t: PhantomData, + } } /// Creates a new stream where to produce each new element a provided closure is called. @@ -68,11 +73,6 @@ where } } -impl FromFn { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for FromFn where F: FnMut() -> Fut, @@ -80,20 +80,18 @@ where { type Item = T; - 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 { - 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); + if this.future.is_some() { + let next = + futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); + this.future.set(None); - return Poll::Ready(next); - } - None => { - let fut = (self.as_mut().f())(); - self.as_mut().future().set(Some(fut)); - } + return Poll::Ready(next); + } else { + let fut = (this.f)(); + this.future.set(Some(fut)); } } } diff --git a/src/stream/interval.rs b/src/stream/interval.rs index 043d307..2f7fe9e 100644 --- a/src/stream/interval.rs +++ b/src/stream/interval.rs @@ -4,8 +4,6 @@ use std::time::{Duration, Instant}; use futures_core::future::Future; use futures_core::stream::Stream; -use pin_utils::unsafe_pinned; - use futures_timer::Delay; /// Creates a new stream that yields at a set interval. @@ -62,15 +60,11 @@ pub struct Interval { 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(); diff --git a/src/stream/once.rs b/src/stream/once.rs index be875e4..ae90d63 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,5 +1,7 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use crate::stream::Stream; use crate::task::{Context, Poll}; @@ -24,20 +26,22 @@ 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 constructed by the [`once`] function. + /// + /// [`once`]: fn.once.html + #[derive(Debug)] + pub struct Once { + value: Option, + } } 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()) } } diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index f38b323..fda30fe 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -1,20 +1,25 @@ use std::marker::PhantomData; use std::pin::Pin; +use pin_project_lite::pin_project; + 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. -/// -/// [`repeat_with`]: fn.repeat_with.html -#[derive(Debug)] -pub struct RepeatWith { - f: F, - future: Option, - __a: PhantomData, +pin_project! { + /// A stream that repeats elements of type `T` endlessly by applying a provided closure. + /// + /// This stream is constructed by the [`repeat_with`] function. + /// + /// [`repeat_with`]: fn.repeat_with.html + #[derive(Debug)] + pub struct RepeatWith { + f: F, + #[pin] + future: Option, + __a: PhantomData, + } } /// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure. @@ -69,11 +74,6 @@ where } } -impl RepeatWith { - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_pinned!(future: Option); -} - impl Stream for RepeatWith where F: FnMut() -> Fut, @@ -81,22 +81,19 @@ where { type Item = A; - 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 { - match &self.future { - Some(_) => { - let res = - futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); + if this.future.is_some() { + let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); + this.future.set(None); - return Poll::Ready(Some(res)); - } - None => { - let fut = (self.as_mut().f())(); + return Poll::Ready(Some(res)); + } else { + let fut = (this.f)(); - self.as_mut().future().set(Some(fut)); - } + this.future.set(Some(fut)); } } } diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 2693382..df31615 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,20 +1,23 @@ use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::prelude::*; use crate::task::{Context, Poll}; -/// Chains two streams one after another. -#[derive(Debug)] -pub struct Chain { - first: Fuse, - second: Fuse, +pin_project! { + /// Chains two streams one after another. + #[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 { first: first.fuse(), @@ -26,22 +29,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/cmp.rs b/src/stream/stream/cmp.rs index fc7161a..df08e9d 100644 --- a/src/stream/stream/cmp.rs +++ b/src/stream/stream/cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; 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 { l: l.fuse(), @@ -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); @@ -57,30 +59,30 @@ where } // 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)); + 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/enumerate.rs b/src/stream/stream/enumerate.rs index 7d5a3d6..2a3afa8 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,19 +1,21 @@ -use crate::task::{Context, Poll}; use std::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! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + 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 } } @@ -25,13 +27,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/filter.rs b/src/stream/stream/filter.rs index 8ed282c..eb4153f 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::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. + #[derive(Debug)] + pub struct Filter { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl Filter { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { Filter { stream, @@ -32,11 +34,12 @@ where { 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 756efff..6a4593f 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -2,21 +2,23 @@ use std::marker::PhantomData; use std::pin::Pin; use std::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! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FilterMap { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl FilterMap { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { FilterMap { stream, @@ -34,10 +36,11 @@ where { 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/fold.rs b/src/stream/stream/fold.rs index 18ddcd8..5b0eb12 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::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! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct FoldFuture { + #[pin] + stream: S, + f: F, + acc: Option, + __t: PhantomData, + } } impl FoldFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pin_utils::unsafe_unpinned!(acc: Option); - pub(super) fn new(stream: S, init: B, f: F) -> Self { FoldFuture { stream, @@ -36,17 +37,18 @@ where { 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 0406a50..4696529 100644 --- a/src/stream/stream/for_each.rs +++ b/src/stream/stream/for_each.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::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 ForEachFuture { - stream: S, - f: F, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct ForEachFuture { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl ForEachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { ForEachFuture { stream, @@ -33,12 +35,13 @@ where { 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 ff5bdab..1162970 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,33 +1,32 @@ use std::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, -} - -impl Unpin for Fuse {} - -impl Fuse { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(done: bool); +pin_project! { + /// 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 { + #[pin] + pub(crate) stream: S, + pub(crate) done: bool, + } } 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 eb9786b..3dc6031 100644 --- a/src/stream/stream/ge.rs +++ b/src/stream/stream/ge.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; 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 { 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 6c480a2..513ca76 100644 --- a/src/stream/stream/gt.rs +++ b/src/stream/stream/gt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; 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 { 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 e63b584..5de60fb 100644 --- a/src/stream/stream/inspect.rs +++ b/src/stream/stream/inspect.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::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. + #[derive(Debug)] + pub struct Inspect { + #[pin] + stream: S, + f: F, + __t: PhantomData, + } } impl Inspect { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: S, f: F) -> Self { Inspect { stream, @@ -32,11 +34,12 @@ where { 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); + (this.f)(&x); Some(x) })) } diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs index c58dd66..eba01e5 100644 --- a/src/stream/stream/last.rs +++ b/src/stream/stream/last.rs @@ -1,20 +1,22 @@ use std::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 } } @@ -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 37b62d8..af72700 100644 --- a/src/stream/stream/le.rs +++ b/src/stream/stream/le.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; 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 { 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 b774d7b..524f268 100644 --- a/src/stream/stream/lt.rs +++ b/src/stream/stream/lt.rs @@ -1,26 +1,29 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::partial_cmp::PartialCmpFuture; use crate::future::Future; use crate::prelude::*; 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 { 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 4bc2e36..a1fafc3 100644 --- a/src/stream/stream/map.rs +++ b/src/stream/stream/map.rs @@ -1,22 +1,24 @@ use std::marker::PhantomData; use std::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! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct Map { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl Map { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { Map { stream, @@ -34,8 +36,9 @@ where { 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/merge.rs b/src/stream/stream/merge.rs index 9889dc7..3ccc223 100644 --- a/src/stream/stream/merge.rs +++ b/src/stream/stream/merge.rs @@ -2,22 +2,25 @@ use std::pin::Pin; use std::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, +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 { + #[pin] + left: L, + #[pin] + right: R, + } } -impl Unpin for Merge {} - impl Merge { pub(crate) fn new(left: L, right: R) -> Self { Self { left, right } @@ -26,19 +29,20 @@ impl Merge { 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) { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + if let Poll::Ready(Some(item)) = this.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)) } else { - Pin::new(&mut self.right).poll_next(cx) + this.right.poll_next(cx) } } } diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index a68cf31..ab12aa0 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,23 +1,24 @@ use std::cmp::Ordering; use std::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 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 { stream, @@ -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/partial_cmp.rs b/src/stream/stream/partial_cmp.rs index fac2705..e30c6ea 100644 --- a/src/stream/stream/partial_cmp.rs +++ b/src/stream/stream/partial_cmp.rs @@ -1,29 +1,30 @@ use std::cmp::Ordering; use std::pin::Pin; +use pin_project_lite::pin_project; + use super::fuse::Fuse; use crate::future::Future; use crate::prelude::*; 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 { l: l.fuse(), @@ -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)); @@ -58,30 +60,30 @@ where } // 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)); + 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/scan.rs b/src/stream/stream/scan.rs index 7897516..c4771d8 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,13 +1,18 @@ use std::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. + #[derive(Debug)] + pub struct Scan { + #[pin] + stream: S, + state_f: (St, F), + } } impl Scan { @@ -17,13 +22,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 +31,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 8a2d966..6562b99 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -1,19 +1,21 @@ use std::pin::Pin; use std::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. + #[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 } } @@ -25,14 +27,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 b1a8d9e..0499df2 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::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. + #[derive(Debug)] + pub struct SkipWhile { + #[pin] + stream: S, + predicate: Option

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

); - pub(crate) fn new(stream: S, predicate: P) -> Self { SkipWhile { stream, @@ -32,15 +34,16 @@ 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.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 f84feec..ab9e45b 100644 --- a/src/stream/stream/step_by.rs +++ b/src/stream/stream/step_by.rs @@ -1,21 +1,22 @@ use std::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. + #[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 { stream, @@ -31,17 +32,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 81d48d2..835bc44 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,33 +1,32 @@ use std::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, -} - -impl Unpin for Take {} - -impl Take { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(remaining: usize); +pin_project! { + /// A stream that yields the first `n` items of another stream. + #[derive(Clone, Debug)] + pub struct Take { + #[pin] + pub(crate) stream: S, + pub(crate) remaining: usize, + } } 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 6f3cc8f..bf89458 100644 --- a/src/stream/stream/take_while.rs +++ b/src/stream/stream/take_while.rs @@ -1,21 +1,23 @@ use std::marker::PhantomData; use std::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. + #[derive(Debug)] + pub struct TakeWhile { + #[pin] + stream: S, + predicate: P, + __t: PhantomData, + } } impl TakeWhile { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(predicate: P); - pub(super) fn new(stream: S, predicate: P) -> Self { TakeWhile { stream, @@ -32,11 +34,12 @@ where { 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/try_fold.rs b/src/stream/stream/try_fold.rs index 212b058..80392e1 100644 --- a/src/stream/stream/try_fold.rs +++ b/src/stream/stream/try_fold.rs @@ -1,24 +1,25 @@ use std::marker::PhantomData; use std::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 TryFoldFuture { - stream: S, - f: F, - acc: Option, - __t: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryFoldFuture { + #[pin] + stream: 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); - pub(super) fn new(stream: S, init: T, f: F) -> Self { TryFoldFuture { stream, @@ -36,23 +37,22 @@ where { type Output = Result; - 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); + let old = this.acc.take().unwrap(); + let new = (this.f)(old, v); match new { - Ok(o) => { - *self.as_mut().acc() = Some(o); - } + Ok(o) => *this.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(this.acc.take().unwrap())), } } } diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs index ae3d5ea..578b854 100644 --- a/src/stream/stream/try_for_each.rs +++ b/src/stream/stream/try_for_each.rs @@ -1,23 +1,25 @@ use std::marker::PhantomData; use std::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 TryForEeachFuture { - stream: S, - f: F, - __from: PhantomData, - __to: PhantomData, +pin_project! { + #[doc(hidden)] + #[allow(missing_debug_implementations)] + pub struct TryForEeachFuture { + #[pin] + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, + } } impl TryForEeachFuture { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(f: F); - pub(crate) fn new(stream: S, f: F) -> Self { TryForEeachFuture { stream, @@ -36,14 +38,15 @@ where { type Output = Result<(), E>; - 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 item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(this.stream.as_mut().poll_next(cx)); match item { None => return Poll::Ready(Ok(())), Some(v) => { - let res = (self.as_mut().f())(v); + let res = (this.f)(v); if let Err(e) = res { return Poll::Ready(Err(e)); } diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 4c66aef..9b7c299 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -1,14 +1,20 @@ use std::fmt; use std::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! { + /// An iterator that iterates two other iterators simultaneously. + pub struct Zip { + item_slot: Option, + #[pin] + first: A, + #[pin] + second: B, + } } impl fmt::Debug for Zip { @@ -20,8 +26,6 @@ impl fmt::Debug for Zip { } } -impl Unpin for Zip {} - impl Zip { pub(crate) fn new(first: A, second: B) -> Self { Zip { @@ -30,25 +34,22 @@ impl Zip { 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/task/block_on.rs b/src/task/block_on.rs index 032bf02..b0adc38 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -7,6 +7,7 @@ use std::task::{RawWaker, RawWakerVTable}; use std::thread; use crossbeam_utils::sync::Parker; +use pin_project_lite::pin_project; use super::task; use super::task_local; @@ -100,19 +101,18 @@ where } } -struct CatchUnwindFuture { - future: F, -} - -impl CatchUnwindFuture { - pin_utils::unsafe_pinned!(future: F); +pin_project! { + struct CatchUnwindFuture { + #[pin] + 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) + panic::catch_unwind(AssertUnwindSafe(|| self.project().future.poll(cx)))?.map(Ok) } } From 2abf5ca891b72bcab0afa3adcdc2b99ba2f59f85 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 23 Oct 2019 20:20:59 +0900 Subject: [PATCH 37/38] Deny warnings on CI (#378) * Deny warnings on CI * Fix some clippy warnings --- .github/workflows/ci.yml | 7 +++++++ src/io/timeout.rs | 2 +- src/io/write/write_fmt.rs | 4 ++-- src/net/addr.rs | 2 +- src/path/path.rs | 2 +- src/path/pathbuf.rs | 4 ++-- src/stream/exact_size_stream.rs | 3 +-- src/stream/extend.rs | 4 ++-- src/stream/from_fn.rs | 5 ++--- src/stream/repeat_with.rs | 8 ++++---- src/sync/barrier.rs | 9 +++------ src/task/yield_now.rs | 4 ++-- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c622a59..565134d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ jobs: build_and_test: name: Build and test runs-on: ${{ matrix.os }} + env: + RUSTFLAGS: -Dwarnings strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] @@ -46,6 +48,8 @@ jobs: check_fmt_and_docs: name: Checking fmt and docs runs-on: ubuntu-latest + env: + RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@master @@ -77,6 +81,9 @@ jobs: clippy_check: name: Clippy check runs-on: ubuntu-latest + # TODO: There is a lot of warnings + # env: + # RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@v1 - id: component diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 8ef844d..ec3668e 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -71,7 +71,7 @@ where } if this.timeout.poll(cx).is_ready() { - let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); + let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out")); Poll::Ready(err) } else { Poll::Pending diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs index a1149cd..ad3e94a 100644 --- a/src/io/write/write_fmt.rs +++ b/src/io/write/write_fmt.rs @@ -32,7 +32,7 @@ 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 { @@ -40,7 +40,7 @@ impl Future for WriteFmtFuture<'_, T> { 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))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } diff --git a/src/net/addr.rs b/src/net/addr.rs index 1dada64..519b184 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -210,7 +210,7 @@ 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())); } diff --git a/src/path/path.rs b/src/path/path.rs index aa15ab2..22dab76 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -799,7 +799,7 @@ impl AsRef for String { impl AsRef for std::path::PathBuf { fn as_ref(&self) -> &Path { - Path::new(self.into()) + Path::new(self) } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 64744e1..38018c9 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -5,7 +5,7 @@ use crate::path::Path; /// 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(Debug, PartialEq, Default)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -206,7 +206,7 @@ impl From for PathBuf { impl Into for PathBuf { fn into(self) -> std::path::PathBuf { - self.inner.into() + self.inner } } diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs index 7d2e7cb..32a1eb3 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,7 +74,6 @@ pub use crate::stream::Stream; /// /// assert_eq!(5, counter.len()); /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/extend.rs b/src/stream/extend.rs index d9e1481..350418d 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -14,7 +14,7 @@ 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}; @@ -25,7 +25,7 @@ use crate::stream::IntoStream; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # -/// # }) } +/// # }) /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs index f53f3e5..7fee892 100644 --- a/src/stream/from_fn.rs +++ b/src/stream/from_fn.rs @@ -30,7 +30,7 @@ pin_project! { /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::sync::Mutex; @@ -58,8 +58,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(3)); /// assert_eq!(s.next().await, None); /// # -/// # }) } -/// +/// # }) /// ``` pub fn from_fn(f: F) -> FromFn where diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs index fda30fe..15d4aa1 100644 --- a/src/stream/repeat_with.rs +++ b/src/stream/repeat_with.rs @@ -29,7 +29,7 @@ pin_project! { /// Basic usage: /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; @@ -42,13 +42,13 @@ pin_project! { /// 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; @@ -60,7 +60,7 @@ pin_project! { /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, Some(1)); /// assert_eq!(s.next().await, None); -/// # }) } +/// # }) /// ``` pub fn repeat_with(repeater: F) -> RepeatWith where diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 080eff8..0163e3f 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -8,7 +8,7 @@ use crate::sync::Mutex; /// # Examples /// /// ``` -/// # fn main() { async_std::task::block_on(async { +/// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -30,7 +30,6 @@ use crate::sync::Mutex; /// handle.await; /// } /// # }); -/// # } /// ``` #[cfg(feature = "unstable")] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] @@ -120,7 +119,7 @@ impl Barrier { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::{Arc, Barrier}; /// use async_std::task; @@ -142,7 +141,6 @@ impl Barrier { /// handle.await; /// } /// # }); - /// # } /// ``` pub async fn wait(&self) -> BarrierWaitResult { let mut lock = self.state.lock().await; @@ -190,7 +188,7 @@ impl BarrierWaitResult { /// # Examples /// /// ``` - /// # fn main() { async_std::task::block_on(async { + /// # async_std::task::block_on(async { /// # /// use async_std::sync::Barrier; /// @@ -198,7 +196,6 @@ impl BarrierWaitResult { /// let barrier_wait_result = barrier.wait().await; /// println!("{:?}", barrier_wait_result.is_leader()); /// # }); - /// # } /// ``` pub fn is_leader(&self) -> bool { self.0 diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs index 2a4788d..c11408a 100644 --- a/src/task/yield_now.rs +++ b/src/task/yield_now.rs @@ -18,13 +18,13 @@ 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)))] From b2fe91385b08cbcd3046294decce9136df904d8c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 23 Oct 2019 17:02:03 +0100 Subject: [PATCH 38/38] Add channel behind unstable feature flag (#380) * Add channel behind unstable feature flag * Don't check tests without unstable feature flag * Fix typos * Remove useless attribute --- .github/workflows/ci.yml | 2 +- Cargo.toml | 1 + src/sync/channel.rs | 1132 ++++++++++++++++++++++++++++++++++++++ src/sync/mod.rs | 3 + tests/channel.rs | 350 ++++++++++++ 5 files changed, 1487 insertions(+), 1 deletion(-) create mode 100644 src/sync/channel.rs create mode 100644 tests/channel.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 565134d..d47eabc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --benches --bins --examples --tests + args: --all --bins --examples - name: check unstable uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index e353386..7aaba68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ pin-project-lite = "0.1" [dev-dependencies] femme = "1.2.0" +rand = "0.7.2" # surf = "1.0.2" tempdir = "0.3.7" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } diff --git a/src/sync/channel.rs b/src/sync/channel.rs new file mode 100644 index 0000000..6751417 --- /dev/null +++ b/src/sync/channel.rs @@ -0,0 +1,1132 @@ +use std::cell::UnsafeCell; +use std::fmt; +use std::future::Future; +use std::isize; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::pin::Pin; +use std::process; +use std::ptr; +use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::task::{Context, Poll, Waker}; + +use crossbeam_utils::{Backoff, CachePadded}; +use futures_core::stream::Stream; +use slab::Slab; + +/// Creates a bounded multi-producer multi-consumer channel. +/// +/// This channel has a buffer that can hold at most `cap` messages at a time. +/// +/// Senders and receivers can be cloned. When all senders associated with a channel get dropped, it +/// becomes closed. Receive operations on a closed and empty channel return `None` instead of +/// blocking. +/// +/// # Panics +/// +/// If `cap` is zero, this function will panic. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// 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; +/// }); +/// +/// task::sleep(Duration::from_secs(1)).await; +/// assert_eq!(r.recv().await, Some(1)); +/// assert_eq!(r.recv().await, Some(2)); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub fn channel(cap: usize) -> (Sender, Receiver) { + let channel = Arc::new(Channel::with_capacity(cap)); + let s = Sender { + channel: channel.clone(), + }; + let r = Receiver { + channel, + opt_key: None, + }; + (s, r) +} + +/// The sending side of a channel. +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// let (s1, r) = channel(100); +/// let s2 = s1.clone(); +/// +/// task::spawn(async move { s1.send(1).await }); +/// task::spawn(async move { s2.send(2).await }); +/// +/// let msg1 = r.recv().await.unwrap(); +/// let msg2 = r.recv().await.unwrap(); +/// +/// assert_eq!(msg1 + msg2, 3); +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub struct Sender { + /// The inner channel. + channel: Arc>, +} + +impl Sender { + /// Sends a message into the channel. + /// + /// If the channel is full, this method will block the current task until the send operation + /// can proceed. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// use async_std::task; + /// + /// let (s, r) = channel(1); + /// + /// task::spawn(async move { + /// s.send(1).await; + /// s.send(2).await; + /// }); + /// + /// assert_eq!(r.recv().await, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn send(&self, msg: T) { + struct SendFuture<'a, T> { + sender: &'a Sender, + msg: Option, + opt_key: Option, + } + + impl Unpin for SendFuture<'_, T> {} + + impl Future for SendFuture<'_, T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let msg = self.msg.take().unwrap(); + + // Try sending the message. + let poll = match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + Err(PushError::Full(msg)) => { + // Register the current task. + match self.opt_key { + None => self.opt_key = Some(self.sender.channel.sends.register(cx)), + Some(key) => self.sender.channel.sends.reregister(key, cx), + } + + // Try sending the message again. + match self.sender.channel.push(msg) { + Ok(()) => Poll::Ready(()), + Err(PushError::Disconnected(msg)) | Err(PushError::Full(msg)) => { + self.msg = Some(msg); + Poll::Pending + } + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key.take() { + // `true` means the send operation is completed. + self.sender.channel.sends.unregister(key, true); + } + } + + poll + } + } + + impl Drop for SendFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the send operation is cancelled. + self.sender.channel.sends.unregister(key, false); + } + } + } + + SendFuture { + sender: self, + msg: Some(msg), + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::channel; + /// + /// let (s, _) = channel::(5); + /// assert_eq!(s.capacity(), 5); + /// ``` + pub fn capacity(&self) -> usize { + self.channel.cap + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(s.is_empty()); + /// s.send(0).await; + /// assert!(!s.is_empty()); + /// # + /// # }) + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(!s.is_full()); + /// s.send(0).await; + /// assert!(s.is_full()); + /// # + /// # }) + /// ``` + pub fn is_full(&self) -> bool { + self.channel.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(2); + /// assert_eq!(s.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(s.len(), 2); + /// # + /// # }) + /// ``` + pub fn len(&self) -> usize { + self.channel.len() + } +} + +impl Drop for Sender { + fn drop(&mut self) { + // Decrement the sender count and disconnect the channel if it drops down to zero. + if self.channel.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.disconnect(); + } + } +} + +impl Clone for Sender { + fn clone(&self) -> Sender { + let count = self.channel.sender_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of sender clones are leaked. + if count > isize::MAX as usize { + process::abort(); + } + + Sender { + channel: self.channel.clone(), + } + } +} + +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Sender { .. }") + } +} + +/// The receiving side of a channel. +/// +/// This type implements the [`Stream`] trait, which means it can act as an asynchronous iterator. +/// +/// [`Stream`]: ../stream/trait.Stream.html +/// +/// # Examples +/// +/// ``` +/// # async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::sync::channel; +/// use async_std::task; +/// +/// let (s, r) = channel(100); +/// +/// task::spawn(async move { +/// s.send(1).await; +/// task::sleep(Duration::from_secs(1)).await; +/// s.send(2).await; +/// }); +/// +/// assert_eq!(r.recv().await, Some(1)); // Received immediately. +/// assert_eq!(r.recv().await, Some(2)); // Received after 1 second. +/// # +/// # }) +/// ``` +#[cfg(feature = "unstable")] +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub struct Receiver { + /// The inner channel. + channel: Arc>, + + /// The registration key for this receiver in the `channel.streams` registry. + opt_key: Option, +} + +impl Receiver { + /// Receives a message from the channel. + /// + /// If the channel is empty and it still has senders, this method will block the current task + /// until the receive operation can proceed. If the channel is empty and there are no more + /// senders, this method returns `None`. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// use async_std::task; + /// + /// let (s, r) = channel(1); + /// + /// task::spawn(async move { + /// s.send(1).await; + /// s.send(2).await; + /// }); + /// + /// assert_eq!(r.recv().await, Some(1)); + /// assert_eq!(r.recv().await, Some(2)); + /// assert_eq!(r.recv().await, None); + /// # + /// # }) + /// ``` + pub async fn recv(&self) -> Option { + struct RecvFuture<'a, T> { + channel: &'a Channel, + opt_key: Option, + } + + impl Future for RecvFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + poll_recv(&self.channel, &self.channel.recvs, &mut self.opt_key, cx) + } + } + + impl Drop for RecvFuture<'_, T> { + fn drop(&mut self) { + // If the current task was registered, unregister now. + if let Some(key) = self.opt_key { + // `false` means the receive operation is cancelled. + self.channel.recvs.unregister(key, false); + } + } + } + + RecvFuture { + channel: &self.channel, + opt_key: None, + } + .await + } + + /// Returns the channel capacity. + /// + /// # Examples + /// + /// ``` + /// use async_std::sync::channel; + /// + /// let (_, r) = channel::(5); + /// assert_eq!(r.capacity(), 5); + /// ``` + pub fn capacity(&self) -> usize { + self.channel.cap + } + + /// Returns `true` if the channel is empty. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(r.is_empty()); + /// s.send(0).await; + /// assert!(!r.is_empty()); + /// # + /// # }) + /// ``` + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns `true` if the channel is full. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(1); + /// + /// assert!(!r.is_full()); + /// s.send(0).await; + /// assert!(r.is_full()); + /// # + /// # }) + /// ``` + pub fn is_full(&self) -> bool { + self.channel.is_full() + } + + /// Returns the number of messages in the channel. + /// + /// # Examples + /// + /// ``` + /// # async_std::task::block_on(async { + /// # + /// use async_std::sync::channel; + /// + /// let (s, r) = channel(2); + /// assert_eq!(r.len(), 0); + /// + /// s.send(1).await; + /// s.send(2).await; + /// assert_eq!(r.len(), 2); + /// # + /// # }) + /// ``` + pub fn len(&self) -> usize { + self.channel.len() + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + // If the current task was registered as blocked on this stream, unregister now. + if let Some(key) = self.opt_key { + // `false` means the last request for a stream item is cancelled. + self.channel.streams.unregister(key, false); + } + + // Decrement the receiver count and disconnect the channel if it drops down to zero. + if self.channel.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.channel.disconnect(); + } + } +} + +impl Clone for Receiver { + fn clone(&self) -> Receiver { + let count = self.channel.receiver_count.fetch_add(1, Ordering::Relaxed); + + // Make sure the count never overflows, even if lots of receiver clones are leaked. + if count > isize::MAX as usize { + process::abort(); + } + + Receiver { + channel: self.channel.clone(), + opt_key: None, + } + } +} + +impl Stream for Receiver { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = &mut *self; + poll_recv(&this.channel, &this.channel.streams, &mut this.opt_key, cx) + } +} + +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Receiver { .. }") + } +} + +/// Polls a receive operation on a channel. +/// +/// If the receive operation is blocked, the current task will be registered in `registry` and its +/// registration key will then be stored in `opt_key`. +fn poll_recv( + channel: &Channel, + registry: &Registry, + opt_key: &mut Option, + cx: &mut Context<'_>, +) -> Poll> { + // Try receiving a message. + let poll = match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => { + // Register the current task. + match *opt_key { + None => *opt_key = Some(registry.register(cx)), + Some(key) => registry.reregister(key, cx), + } + + // Try receiving a message again. + match channel.pop() { + Ok(msg) => Poll::Ready(Some(msg)), + Err(PopError::Disconnected) => Poll::Ready(None), + Err(PopError::Empty) => Poll::Pending, + } + } + }; + + if poll.is_ready() { + // If the current task was registered, unregister now. + if let Some(key) = opt_key.take() { + // `true` means the receive operation is completed. + registry.unregister(key, true); + } + } + + poll +} + +/// A slot in a channel. +struct Slot { + /// The current stamp. + stamp: AtomicUsize, + + /// The message in this slot. + msg: UnsafeCell, +} + +/// Bounded channel based on a preallocated array. +struct Channel { + /// The head of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit in the head is always zero. + /// + /// Messages are popped from the head of the channel. + head: CachePadded, + + /// The tail of the channel. + /// + /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but + /// packed into a single `usize`. The lower bits represent the index, while the upper bits + /// represent the lap. The mark bit indicates that the channel is disconnected. + /// + /// Messages are pushed into the tail of the channel. + tail: CachePadded, + + /// The buffer holding slots. + buffer: *mut Slot, + + /// The channel capacity. + cap: usize, + + /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. + one_lap: usize, + + /// If this bit is set in the tail, that means either all senders were dropped or all receivers + /// were dropped. + mark_bit: usize, + + /// Send operations waiting while the channel is full. + sends: Registry, + + /// Receive operations waiting while the channel is empty and not disconnected. + recvs: Registry, + + /// Streams waiting while the channel is empty and not disconnected. + streams: Registry, + + /// The number of currently active `Sender`s. + sender_count: AtomicUsize, + + /// The number of currently active `Receivers`s. + receiver_count: AtomicUsize, + + /// Indicates that dropping a `Channel` may drop values of type `T`. + _marker: PhantomData, +} + +unsafe impl Send for Channel {} +unsafe impl Sync for Channel {} +impl Unpin for Channel {} + +impl Channel { + /// Creates a bounded channel of capacity `cap`. + fn with_capacity(cap: usize) -> Self { + assert!(cap > 0, "capacity must be positive"); + + // Compute constants `mark_bit` and `one_lap`. + let mark_bit = (cap + 1).next_power_of_two(); + let one_lap = mark_bit * 2; + + // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let head = 0; + // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. + let tail = 0; + + // Allocate a buffer of `cap` slots. + let buffer = { + let mut v = Vec::>::with_capacity(cap); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + + // Initialize stamps in the slots. + for i in 0..cap { + unsafe { + // Set the stamp to `{ lap: 0, mark: 0, index: i }`. + let slot = buffer.add(i); + ptr::write(&mut (*slot).stamp, AtomicUsize::new(i)); + } + } + + Channel { + buffer, + cap, + one_lap, + mark_bit, + head: CachePadded::new(AtomicUsize::new(head)), + tail: CachePadded::new(AtomicUsize::new(tail)), + sends: Registry::new(), + recvs: Registry::new(), + streams: Registry::new(), + sender_count: AtomicUsize::new(1), + receiver_count: AtomicUsize::new(1), + _marker: PhantomData, + } + } + + /// Attempts to push a message. + fn push(&self, msg: T) -> Result<(), PushError> { + let backoff = Backoff::new(); + let mut tail = self.tail.load(Ordering::Relaxed); + + loop { + // Deconstruct the tail. + let index = tail & (self.mark_bit - 1); + let lap = tail & !(self.one_lap - 1); + + // Inspect the corresponding slot. + let slot = unsafe { &*self.buffer.add(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the tail and the stamp match, we may attempt to push. + if tail == stamp { + let new_tail = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + tail + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the tail. + match self.tail.compare_exchange_weak( + tail, + new_tail, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Write the message into the slot and update the stamp. + unsafe { slot.msg.get().write(msg) }; + let stamp = tail + 1; + slot.stamp.store(stamp, Ordering::Release); + + // Wake a blocked receive operation. + self.recvs.notify_one(); + + // Wake all blocked streams. + self.streams.notify_all(); + + return Ok(()); + } + Err(t) => { + tail = t; + backoff.spin(); + } + } + } else if stamp.wrapping_add(self.one_lap) == tail + 1 { + atomic::fence(Ordering::SeqCst); + let head = self.head.load(Ordering::Relaxed); + + // If the head lags one lap behind the tail as well... + if head.wrapping_add(self.one_lap) == tail { + // ...then the channel is full. + + // Check if the channel is disconnected. + if tail & self.mark_bit != 0 { + return Err(PushError::Disconnected(msg)); + } else { + return Err(PushError::Full(msg)); + } + } + + backoff.spin(); + tail = self.tail.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + tail = self.tail.load(Ordering::Relaxed); + } + } + } + + /// Attempts to pop a message. + fn pop(&self) -> Result { + let backoff = Backoff::new(); + let mut head = self.head.load(Ordering::Relaxed); + + loop { + // Deconstruct the head. + let index = head & (self.mark_bit - 1); + let lap = head & !(self.one_lap - 1); + + // Inspect the corresponding slot. + let slot = unsafe { &*self.buffer.add(index) }; + let stamp = slot.stamp.load(Ordering::Acquire); + + // If the the stamp is ahead of the head by 1, we may attempt to pop. + if head + 1 == stamp { + let new = if index + 1 < self.cap { + // Same lap, incremented index. + // Set to `{ lap: lap, mark: 0, index: index + 1 }`. + head + 1 + } else { + // One lap forward, index wraps around to zero. + // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. + lap.wrapping_add(self.one_lap) + }; + + // Try moving the head. + match self.head.compare_exchange_weak( + head, + new, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => { + // Read the message from the slot and update the stamp. + let msg = unsafe { slot.msg.get().read() }; + let stamp = head.wrapping_add(self.one_lap); + slot.stamp.store(stamp, Ordering::Release); + + // Wake a blocked send operation. + self.sends.notify_one(); + + return Ok(msg); + } + Err(h) => { + head = h; + backoff.spin(); + } + } + } else if stamp == head { + atomic::fence(Ordering::SeqCst); + let tail = self.tail.load(Ordering::Relaxed); + + // If the tail equals the head, that means the channel is empty. + if (tail & !self.mark_bit) == head { + // If the channel is disconnected... + if tail & self.mark_bit != 0 { + return Err(PopError::Disconnected); + } else { + // Otherwise, the receive operation is not ready. + return Err(PopError::Empty); + } + } + + backoff.spin(); + head = self.head.load(Ordering::Relaxed); + } else { + // Snooze because we need to wait for the stamp to get updated. + backoff.snooze(); + head = self.head.load(Ordering::Relaxed); + } + } + } + + /// Returns the current number of messages inside the channel. + fn len(&self) -> usize { + loop { + // Load the tail, then load the head. + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // If the tail didn't change, we've got consistent values to work with. + if self.tail.load(Ordering::SeqCst) == tail { + let hix = head & (self.mark_bit - 1); + let tix = tail & (self.mark_bit - 1); + + return if hix < tix { + tix - hix + } else if hix > tix { + self.cap - hix + tix + } else if (tail & !self.mark_bit) == head { + 0 + } else { + self.cap + }; + } + } + } + + /// Returns `true` if the channel is empty. + fn is_empty(&self) -> bool { + let head = self.head.load(Ordering::SeqCst); + let tail = self.tail.load(Ordering::SeqCst); + + // Is the tail equal to the head? + // + // Note: If the head changes just before we load the tail, that means there was a moment + // when the channel was not empty, so it is safe to just return `false`. + (tail & !self.mark_bit) == head + } + + /// Returns `true` if the channel is full. + fn is_full(&self) -> bool { + let tail = self.tail.load(Ordering::SeqCst); + let head = self.head.load(Ordering::SeqCst); + + // Is the head lagging one lap behind tail? + // + // Note: If the tail changes just before we load the head, that means there was a moment + // when the channel was not full, so it is safe to just return `false`. + head.wrapping_add(self.one_lap) == tail & !self.mark_bit + } + + /// Disconnects the channel and wakes up all blocked operations. + fn disconnect(&self) { + let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); + + if tail & self.mark_bit == 0 { + // Notify everyone blocked on this channel. + self.sends.notify_all(); + self.recvs.notify_all(); + self.streams.notify_all(); + } + } +} + +impl Drop for Channel { + fn drop(&mut self) { + // Get the index of the head. + let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1); + + // Loop over all slots that hold a message and drop them. + for i in 0..self.len() { + // Compute the index of the next slot holding a message. + let index = if hix + i < self.cap { + hix + i + } else { + hix + i - self.cap + }; + + unsafe { + self.buffer.add(index).drop_in_place(); + } + } + + // Finally, deallocate the buffer, but don't run any destructors. + unsafe { + Vec::from_raw_parts(self.buffer, 0, self.cap); + } + } +} + +/// An error returned from the `push()` method. +enum PushError { + /// The channel is full but not disconnected. + Full(T), + + /// The channel is full and disconnected. + Disconnected(T), +} + +/// An error returned from the `pop()` method. +enum PopError { + /// The channel is empty but not disconnected. + Empty, + + /// The channel is empty and disconnected. + Disconnected, +} + +/// A list of blocked channel operations. +struct Blocked { + /// A list of registered channel operations. + /// + /// Each entry has a 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 registry yet. + entries: Slab>, + + /// The number of wakers in the entry list. + waker_count: usize, +} + +/// A registry of blocked channel operations. +/// +/// Blocked operations register themselves in a registry. Successful operations on the opposite +/// side of the channel wake blocked operations in the registry. +struct Registry { + /// A list of blocked channel operations. + blocked: Spinlock, + + /// Set to `true` if there are no wakers in the registry. + /// + /// Note that this either means there are no entries in the registry, or that all entries have + /// been notified. + is_empty: AtomicBool, +} + +impl Registry { + /// Creates a new registry. + fn new() -> Registry { + Registry { + blocked: Spinlock::new(Blocked { + entries: Slab::new(), + waker_count: 0, + }), + is_empty: AtomicBool::new(true), + } + } + + /// Registers a blocked channel operation and returns a key associated with it. + fn register(&self, cx: &Context<'_>) -> usize { + let mut blocked = self.blocked.lock(); + + // Insert a new entry into the list of blocked tasks. + let w = cx.waker().clone(); + let key = blocked.entries.insert(Some(w)); + + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + + key + } + + /// Re-registers a blocked channel operation by filling in its waker. + fn reregister(&self, key: usize, cx: &Context<'_>) { + let mut blocked = self.blocked.lock(); + + let was_none = blocked.entries[key].is_none(); + let w = cx.waker().clone(); + blocked.entries[key] = Some(w); + + if was_none { + blocked.waker_count += 1; + if blocked.waker_count == 1 { + self.is_empty.store(false, Ordering::SeqCst); + } + } + } + + /// Unregisters a channel operation. + /// + /// If `completed` is `true`, the operation will be removed from the registry. If `completed` + /// is `false`, that means the operation was cancelled so another one will be notified. + fn unregister(&self, key: usize, completed: bool) { + let mut blocked = self.blocked.lock(); + let mut removed = false; + + match blocked.entries.remove(key) { + Some(_) => removed = true, + None => { + if !completed { + // This operation was cancelled. Notify another one. + if let Some((_, opt_waker)) = blocked.entries.iter_mut().next() { + if let Some(w) = opt_waker.take() { + w.wake(); + removed = true; + } + } + } + } + } + + if removed { + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + + /// Notifies one blocked channel operation. + #[inline] + fn notify_one(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + if let Some((_, opt_waker)) = blocked.entries.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(); + + blocked.waker_count -= 1; + if blocked.waker_count == 0 { + self.is_empty.store(true, Ordering::SeqCst); + } + } + } + } + } + + /// Notifies all blocked channel operations. + #[inline] + fn notify_all(&self) { + if !self.is_empty.load(Ordering::SeqCst) { + let mut blocked = self.blocked.lock(); + + for (_, opt_waker) in blocked.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(); + } + } + + blocked.waker_count = 0; + self.is_empty.store(true, Ordering::SeqCst); + } + } +} + +/// A simple spinlock. +struct Spinlock { + flag: AtomicBool, + value: UnsafeCell, +} + +impl Spinlock { + /// Returns a new spinlock initialized with `value`. + fn new(value: T) -> Spinlock { + Spinlock { + flag: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Locks the spinlock. + fn lock(&self) -> SpinlockGuard<'_, T> { + let backoff = Backoff::new(); + while self.flag.swap(true, Ordering::Acquire) { + backoff.snooze(); + } + SpinlockGuard { parent: self } + } +} + +/// A guard holding a spinlock locked. +struct SpinlockGuard<'a, T> { + parent: &'a Spinlock, +} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.parent.flag.store(false, Ordering::Release); + } +} + +impl<'a, T> Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.parent.value.get() } + } +} + +impl<'a, T> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.parent.value.get() } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index be74d8f..3ad2776 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -40,5 +40,8 @@ mod rwlock; cfg_unstable! { pub use barrier::{Barrier, BarrierWaitResult}; + pub use channel::{channel, Sender, Receiver}; + mod barrier; + mod channel; } diff --git a/tests/channel.rs b/tests/channel.rs new file mode 100644 index 0000000..91622b0 --- /dev/null +++ b/tests/channel.rs @@ -0,0 +1,350 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use async_std::sync::channel; +use async_std::task; +use rand::{thread_rng, Rng}; + +fn ms(ms: u64) -> Duration { + Duration::from_millis(ms) +} + +#[test] +fn smoke() { + task::block_on(async { + let (s, r) = channel(1); + + s.send(7).await; + assert_eq!(r.recv().await, Some(7)); + + s.send(8).await; + assert_eq!(r.recv().await, Some(8)); + + drop(s); + assert_eq!(r.recv().await, None); + }) +} + +#[test] +fn capacity() { + for i in 1..10 { + let (s, r) = channel::<()>(i); + assert_eq!(s.capacity(), i); + assert_eq!(r.capacity(), i); + } +} + +#[test] +fn len_empty_full() { + 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; + + 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; + + 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); + + 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] +fn recv() { + task::block_on(async { + let (s, r) = channel(100); + + task::spawn(async move { + assert_eq!(r.recv().await, Some(7)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(8)); + task::sleep(ms(1000)).await; + assert_eq!(r.recv().await, Some(9)); + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1500)).await; + s.send(7).await; + s.send(8).await; + s.send(9).await; + }) +} + +#[test] +fn send() { + task::block_on(async { + let (s, r) = channel(1); + + task::spawn(async move { + s.send(7).await; + task::sleep(ms(1000)).await; + s.send(8).await; + task::sleep(ms(1000)).await; + s.send(9).await; + task::sleep(ms(1000)).await; + s.send(10).await; + }); + + task::sleep(ms(1500)).await; + assert_eq!(r.recv().await, Some(7)); + assert_eq!(r.recv().await, Some(8)); + assert_eq!(r.recv().await, Some(9)); + }) +} + +#[test] +fn recv_after_disconnect() { + task::block_on(async { + let (s, r) = channel(100); + + s.send(1).await; + s.send(2).await; + s.send(3).await; + + drop(s); + + assert_eq!(r.recv().await, Some(1)); + assert_eq!(r.recv().await, Some(2)); + assert_eq!(r.recv().await, Some(3)); + assert_eq!(r.recv().await, None); + }) +} + +#[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; + assert_eq!(s.len(), i + 1); + } + + for i in 0..50 { + 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; + 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 = task::spawn({ + let r = r.clone(); + async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + let len = r.len(); + assert!(len <= CAP); + } + } + }); + + for i in 0..COUNT { + s.send(i).await; + let len = s.len(); + assert!(len <= CAP); + } + + child.await; + + assert_eq!(s.len(), 0); + assert_eq!(r.len(), 0); + }) +} + +#[test] +fn disconnect_wakes_receiver() { + task::block_on(async { + let (s, r) = channel::<()>(1); + + let child = task::spawn(async move { + assert_eq!(r.recv().await, None); + }); + + task::sleep(ms(1000)).await; + drop(s); + + child.await; + }) +} + +#[test] +fn spsc() { + const COUNT: usize = 100_000; + + task::block_on(async { + let (s, r) = channel(3); + + let child = task::spawn(async move { + for i in 0..COUNT { + assert_eq!(r.recv().await, Some(i)); + } + assert_eq!(r.recv().await, None); + }); + + for i in 0..COUNT { + s.send(i).await; + } + drop(s); + + child.await; + }) +} + +#[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(task::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(task::spawn(async move { + for i in 0..COUNT { + s.send(i).await; + } + })); + } + + for t in tasks { + t.await; + } + + for c in v.iter() { + assert_eq!(c.load(Ordering::SeqCst), TASKS); + } + }); +} + +#[test] +fn oneshot() { + const COUNT: usize = 10_000; + + task::block_on(async { + for _ in 0..COUNT { + let (s, r) = channel(1); + + let c1 = task::spawn(async move { r.recv().await.unwrap() }); + let c2 = task::spawn(async move { s.send(0).await }); + + c1.await; + c2.await; + } + }) +} + +#[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); + } + } + + let mut rng = thread_rng(); + + for _ in 0..RUNS { + task::block_on(async { + 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 = task::spawn({ + let r = r.clone(); + async move { + for _ in 0..steps { + r.recv().await.unwrap(); + } + } + }); + + for _ in 0..steps { + s.send(DropCounter).await; + } + + child.await; + + for _ in 0..additional { + s.send(DropCounter).await; + } + + assert_eq!(DROPS.load(Ordering::SeqCst), steps); + drop(s); + drop(r); + assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional); + }) + } +}