use std::error::Error; use std::fmt; use std::pin::Pin; use std::time::Duration; use cfg_if::cfg_if; use futures_timer::Delay; use pin_utils::unsafe_pinned; use crate::future::Future; use crate::io; use crate::task::{Context, Poll}; cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] pub struct ImplFuture(std::marker::PhantomData); macro_rules! ret { ($f:tt, $o:ty) => (ImplFuture<$o>); } } else { macro_rules! ret { ($f:tt, $o:ty) => ($f); } } } /// An error returned when a future times out. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError; impl Error for TimeoutError {} impl fmt::Display for TimeoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "future has timed out".fmt(f) } } impl From for io::Error { fn from(_: TimeoutError) -> io::Error { io::Error::new(io::ErrorKind::TimedOut, "future has timed out") } } /// An extension trait that configures timeouts for futures. pub trait Timeout: Future + Sized { /// Awaits a future to completion or times out after a duration of time. /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::{io, prelude::*}; /// use std::time::Duration; /// /// let stdin = io::stdin(); /// let mut line = String::new(); /// /// let n = stdin /// .read_line(&mut line) /// .timeout(Duration::from_secs(5)) /// .await??; /// # /// # Ok(()) }) } /// ``` fn timeout(self, dur: Duration) -> ret!(TimeoutFuture, Result) { TimeoutFuture { future: self, delay: Delay::new(dur), } } } /// A future that times out after a duration of time. #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct TimeoutFuture { future: F, delay: Delay, } impl TimeoutFuture { unsafe_pinned!(future: F); 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)), Poll::Pending => Poll::Pending, }, } } } impl Timeout for F {}