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}