diff --git a/.travis.yml b/.travis.yml index 125dc1690..9d78f7f5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ before_script: - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi script: - - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi - - if [[ -n "$BUILD_BOOK" ]]; then cargo test --all --benches --bins --examples --tests; fi + - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --features unstable --all --benches --bins --examples --tests && cargo test --features unstable --all; fi + - if [[ -n "$BUILD_BOOK" ]]; then cargo test --features unstable --all --benches --bins --examples --tests; fi - cargo fmt --all -- --check - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi diff --git a/Cargo.toml b/Cargo.toml index eba38cca2..230de6402 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] docs = [] +unstable = [] [dependencies] async-task = "1.0.0" diff --git a/src/future/mod.rs b/src/future/mod.rs index 29e2b047f..5d510a449 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -3,8 +3,17 @@ #[doc(inline)] pub use std::future::Future; +use cfg_if::cfg_if; + pub use pending::pending; pub use ready::ready; mod pending; mod ready; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod timeout; + pub use timeout::{timeout, TimeoutError}; + } +} diff --git a/src/future/timeout.rs b/src/future/timeout.rs new file mode 100644 index 000000000..cf146aeb6 --- /dev/null +++ b/src/future/timeout.rs @@ -0,0 +1,80 @@ +use std::error::Error; +use std::fmt; +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::future::Future; +use crate::task::{Context, Poll}; + +/// Awaits a future or times out after a duration of time. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::future; +/// +/// let never = future::pending::<()>(); +/// let dur = Duration::from_millis(5); +/// assert!(future::timeout(dur, never).await.is_err()); +/// # +/// # Ok(()) }) } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub async fn timeout(dur: Duration, f: F) -> Result +where + F: Future, +{ + let f = TimeoutFuture { + future: f, + delay: Delay::new(dur), + }; + f.await +} + +/// A future that times out after a duration of time. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +struct TimeoutFuture { + future: F, + delay: Delay, +} + +impl TimeoutFuture { + pin_utils::unsafe_pinned!(future: F); + pin_utils::unsafe_pinned!(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) { + Poll::Ready(v) => Poll::Ready(Ok(v)), + Poll::Pending => match self.delay().poll(cx) { + Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _priv: () })), + Poll::Pending => Poll::Pending, + }, + } + } +} + +/// An error returned when a future times out. +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct TimeoutError { + _priv: (), +} + +impl Error for TimeoutError {} + +impl fmt::Display for TimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "future has timed out".fmt(f) + } +}