maitake/time/
timeout.rs

1//! [`Timeout`]s limit the amount of time a [`Future`] is allowed to run before
2//! it completes.
3//!
4//! See the documentation for the [`Timeout`] type for details.
5use super::{timer::TimerError, Sleep, Timer};
6use crate::util;
7use core::{
8    fmt,
9    future::Future,
10    pin::Pin,
11    task::{Context, Poll},
12    time::Duration,
13};
14use pin_project::pin_project;
15
16/// A [`Future`] that requires an inner [`Future`] to complete within a
17/// specified [`Duration`].
18///
19/// This `Future` is returned by the [`timeout`] and [`try_timeout`] functions,
20/// and by the [`Timer::timeout`] and [`Timer::try_timeout`] methods.
21///
22/// [`timeout`]: super::timeout()
23/// [`try_timeout`]: super::try_timeout
24///
25/// # Output
26///
27/// - [`Ok`]`(F::Output)` if the inner future completed before the specified
28///   timeout.
29/// - [`Err`]`(`[`Elapsed`]`)` if the timeout elapsed before the inner [`Future`]
30///   completed.
31///
32/// # Cancellation
33///
34/// Dropping a `Timeout` future cancels the timeout. The wrapped [`Future`] can
35/// be extracted from the `Timeout` future by calling [`Timeout::into_inner`],
36/// allowing the future to be polled without failing if the timeout elapses.
37#[derive(Debug)]
38#[pin_project]
39#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
40pub struct Timeout<'timer, F> {
41    #[pin]
42    sleep: Sleep<'timer>,
43    #[pin]
44    future: F,
45    duration: Duration,
46}
47
48/// An error indicating that a [`Timeout`] elapsed before the inner [`Future`]
49/// completed.
50#[derive(Debug, Copy, Clone, Eq, PartialEq)]
51pub struct Elapsed(Duration);
52
53// === impl Timeout ===
54
55impl<'timer, F: Future> Timeout<'timer, F> {
56    /// Returns a new [`Timeout`] future that fails if `future` does not
57    /// complete within the specified `duration`.
58    ///
59    /// The timeout will be driven by the specified `timer`.
60    ///
61    /// See the documentation for the [`Timeout`] future for details.
62    fn new(sleep: Sleep<'timer>, future: F) -> Self {
63        let duration = sleep.duration();
64        Self {
65            sleep,
66            future,
67            duration,
68        }
69    }
70
71    /// Consumes this `Timeout`, returning the inner [`Future`].
72    ///
73    /// This can be used to continue polling the inner [`Future`] without
74    /// requiring it to complete prior to the specified timeout.
75    pub fn into_inner(self) -> F {
76        self.future
77    }
78
79    /// Borrows the inner [`Future`] immutably.
80    pub fn get_ref(&self) -> &F {
81        &self.future
82    }
83
84    /// Mutably the inner [`Future`].
85    pub fn get_mut(&mut self) -> &mut F {
86        &mut self.future
87    }
88
89    /// Borrows the inner [`Future`] as a [`Pin`]ned reference, if this
90    /// `Timeout` is pinned.
91    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut F> {
92        self.project().future
93    }
94
95    /// Returns the [`Duration`] the inner [`Future`] is allowed to run for.
96    pub fn duration(&self) -> Duration {
97        self.duration
98    }
99}
100
101impl<F: Future> Future for Timeout<'_, F> {
102    type Output = Result<F::Output, Elapsed>;
103
104    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
105        let this = self.project();
106        // first, poll the sleep.
107        if this.sleep.poll(cx).is_ready() {
108            return Poll::Ready(Err(Elapsed(*this.duration)));
109        }
110
111        // then, try polling the future.
112        if let Poll::Ready(output) = this.future.poll(cx) {
113            return Poll::Ready(Ok(output));
114        }
115
116        Poll::Pending
117    }
118}
119
120// === impl Elapsed ===
121
122impl From<Elapsed> for Duration {
123    #[inline]
124    fn from(Elapsed(duration): Elapsed) -> Self {
125        duration
126    }
127}
128
129impl fmt::Display for Elapsed {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        write!(f, "timed out after {:?}", self.0)
132    }
133}
134
135impl Elapsed {
136    /// Returns the [`Duration`] the inner [`Future`] was allowed to run for.
137    pub fn duration(self) -> Duration {
138        self.0
139    }
140}
141
142feature! {
143    #![feature = "core-error"]
144    impl core::error::Error for Elapsed {}
145}
146
147// === impl Timer ===
148
149impl Timer {
150    /// Returns a new [`Timeout`] future that fails if `future` does not
151    /// complete within the specified `duration`.
152    ///
153    /// The timeout will be driven by this timer.
154    ///
155    /// # Output
156    ///
157    /// - [`Ok`]`(F::Output)` if the inner future completed before the specified
158    ///   timeout.
159    /// - [`Err`]`(`[`Elapsed`]`)` if the timeout elapsed before the inner [`Future`]
160    ///   completed.
161    ///
162    /// # Cancellation
163    ///
164    /// Dropping a `Timeout` future cancels the timeout. The wrapped [`Future`] can
165    /// be extracted from the `Timeout` future by calling [`Timeout::into_inner`],
166    /// allowing the future to be polled without failing if the timeout elapses.
167    ///
168    /// # Panics
169    ///
170    /// This method panics if the provided duration exceeds the [maximum sleep
171    /// duration][max] allowed this timer.
172    ///
173    /// For a version of this method that does not panic, use the
174    /// [`Timer::try_timeout`] method instead.
175    ///
176    /// [max]: Timer::max_duration
177    #[track_caller]
178    pub fn timeout<F: Future>(&self, duration: Duration, future: F) -> Timeout<'_, F> {
179        util::expect_display(
180            self.try_timeout(duration, future),
181            "cannot create `Timeout` future",
182        )
183    }
184
185    /// Returns a new [`Timeout`] future that fails if `future` does not
186    /// complete within the specified `duration`.
187    ///
188    /// The timeout will be driven by this timer.
189    ///
190    /// # Returns
191    ///
192    /// - [`Ok`]`(`[`Timeout`]`)` if a new [`Timeout`] future was created
193    ///   successfully.
194    /// - [`Err`]`(`[`TimerError::DurationTooLong`]`)` if the requested timeout
195    ///   duration exceeds this timer's [maximum sleep
196    ///   duration](Timer::max_duration`).
197    ///
198    /// # Output
199    ///
200    /// - [`Ok`]`(F::Output)` if the inner future completed before the specified
201    ///   timeout.
202    /// - [`Err`]`(`[`Elapsed`]`)` if the timeout elapsed before the inner [`Future`]
203    ///   completed.
204    ///
205    /// # Cancellation
206    ///
207    /// Dropping a `Timeout` future cancels the timeout. The wrapped [`Future`] can
208    /// be extracted from the `Timeout` future by calling [`Timeout::into_inner`],
209    /// allowing the future to be polled without failing if the timeout elapses.
210    ///
211    /// # Panics
212    ///
213    /// This method does not panic. For a version of this methodthat panics
214    /// rather than returning a [`TimerError`], use [`Timer::timeout`].
215    ///
216    pub fn try_timeout<F: Future>(
217        &self,
218        duration: Duration,
219        future: F,
220    ) -> Result<Timeout<'_, F>, TimerError> {
221        let sleep = self.try_sleep(duration)?;
222        Ok(Timeout::new(sleep, future))
223    }
224}