From 49faea2023396c240b91b6b4cffc56de038ce336 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 15 Oct 2019 16:27:06 +0200 Subject: [PATCH] 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)*); };