tokio/time/timeout.rs
1//! Allows a future to execute for a maximum amount of time.
2//!
3//! See [`Timeout`] documentation for more details.
4//!
5//! [`Timeout`]: struct@Timeout
6
7use crate::{
8 task::coop,
9 time::{error::Elapsed, Duration, Instant, Sleep},
10 util::trace,
11};
12
13use pin_project_lite::pin_project;
14use std::future::{Future, IntoFuture};
15use std::pin::Pin;
16use std::task::{self, Poll};
17
18/// Requires a `Future` to complete before the specified duration has elapsed.
19///
20/// If the future completes before the duration has elapsed, then the completed
21/// value is returned. Otherwise, an error is returned and the future is
22/// canceled.
23///
24/// Note that the future is polled before the timeout is checked, so if the future
25/// does not yield during execution then it is possible for the future to complete
26/// and exceed the timeout _without_ returning an error.
27///
28/// This function returns a future whose return type is [`Result`]`<T,`[`Elapsed`]`>`, where `T` is the
29/// return type of the provided future.
30///
31/// If the provided future completes immediately, then the future returned from
32/// this function is guaranteed to complete immediately with an [`Ok`] variant
33/// no matter the provided duration.
34///
35/// [`Ok`]: std::result::Result::Ok
36/// [`Result`]: std::result::Result
37/// [`Elapsed`]: crate::time::error::Elapsed
38///
39/// # Cancellation
40///
41/// Cancelling a timeout is done by dropping the future. No additional cleanup
42/// or other work is required.
43///
44/// The original future may be obtained by calling [`Timeout::into_inner`]. This
45/// consumes the `Timeout`.
46///
47/// # Examples
48///
49/// Create a new `Timeout` set to expire in 10 milliseconds.
50///
51/// ```rust
52/// use tokio::time::timeout;
53/// use tokio::sync::oneshot;
54///
55/// use std::time::Duration;
56///
57/// # async fn dox() {
58/// let (tx, rx) = oneshot::channel();
59/// # tx.send(()).unwrap();
60///
61/// // Wrap the future with a `Timeout` set to expire in 10 milliseconds.
62/// if let Err(_) = timeout(Duration::from_millis(10), rx).await {
63/// println!("did not receive value within 10 ms");
64/// }
65/// # }
66/// ```
67///
68/// # Panics
69///
70/// This function panics if there is no current timer set.
71///
72/// It can be triggered when [`Builder::enable_time`] or
73/// [`Builder::enable_all`] are not included in the builder.
74///
75/// It can also panic whenever a timer is created outside of a
76/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
77/// since the function is executed outside of the runtime.
78/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
79/// And this is because wrapping the function on an async makes it lazy,
80/// and so gets executed inside the runtime successfully without
81/// panicking.
82///
83/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
84/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
85#[track_caller]
86pub fn timeout<F>(duration: Duration, future: F) -> Timeout<F::IntoFuture>
87where
88 F: IntoFuture,
89{
90 // Closures don't preserve `#[track_caller]`.
91 #[allow(clippy::manual_map)]
92 Timeout {
93 value: future.into_future(),
94 delay: match Instant::now().checked_add(duration) {
95 Some(deadline) => Some(Sleep::new_timeout(deadline, trace::caller_location())),
96 None => None,
97 },
98 }
99}
100
101/// Requires a `Future` to complete before the specified instant in time.
102///
103/// If the future completes before the instant is reached, then the completed
104/// value is returned. Otherwise, an error is returned.
105///
106/// This function returns a future whose return type is [`Result`]`<T,`[`Elapsed`]`>`, where `T` is the
107/// return type of the provided future.
108///
109/// If the provided future completes immediately, then the future returned from
110/// this function is guaranteed to complete immediately with an [`Ok`] variant
111/// no matter the provided deadline.
112///
113/// [`Ok`]: std::result::Result::Ok
114/// [`Result`]: std::result::Result
115/// [`Elapsed`]: crate::time::error::Elapsed
116///
117/// # Cancellation
118///
119/// Cancelling a timeout is done by dropping the future. No additional cleanup
120/// or other work is required.
121///
122/// The original future may be obtained by calling [`Timeout::into_inner`]. This
123/// consumes the `Timeout`.
124///
125/// # Examples
126///
127/// Create a new `Timeout` set to expire in 10 milliseconds.
128///
129/// ```rust
130/// use tokio::time::{Instant, timeout_at};
131/// use tokio::sync::oneshot;
132///
133/// use std::time::Duration;
134///
135/// # async fn dox() {
136/// let (tx, rx) = oneshot::channel();
137/// # tx.send(()).unwrap();
138///
139/// // Wrap the future with a `Timeout` set to expire 10 milliseconds into the
140/// // future.
141/// if let Err(_) = timeout_at(Instant::now() + Duration::from_millis(10), rx).await {
142/// println!("did not receive value within 10 ms");
143/// }
144/// # }
145/// ```
146///
147/// # Panics
148///
149/// This function panics if there is no current timer set.
150///
151/// It can be triggered when [`Builder::enable_time`] or
152/// [`Builder::enable_all`] are not included in the builder.
153///
154/// It can also panic whenever a timer is created outside of a
155/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic,
156/// since the function is executed outside of the runtime.
157/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic.
158/// And this is because wrapping the function on an async makes it lazy,
159/// and so gets executed inside the runtime successfully without
160/// panicking.
161///
162/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time
163/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all
164#[track_caller]
165pub fn timeout_at<F>(deadline: Instant, future: F) -> Timeout<F::IntoFuture>
166where
167 F: IntoFuture,
168{
169 Timeout {
170 value: future.into_future(),
171 delay: Some(Sleep::new_timeout(deadline, trace::caller_location())),
172 }
173}
174
175pin_project! {
176 /// Future returned by [`timeout`](timeout) and [`timeout_at`](timeout_at).
177 #[must_use = "futures do nothing unless you `.await` or poll them"]
178 #[derive(Debug)]
179 pub struct Timeout<T> {
180 #[pin]
181 value: T,
182 #[pin]
183 delay: Option<Sleep>,
184 }
185}
186
187impl<T> Timeout<T> {
188 /// Gets a reference to the underlying value in this timeout.
189 pub fn get_ref(&self) -> &T {
190 &self.value
191 }
192
193 /// Gets a mutable reference to the underlying value in this timeout.
194 pub fn get_mut(&mut self) -> &mut T {
195 &mut self.value
196 }
197
198 /// Consumes this timeout, returning the underlying value.
199 pub fn into_inner(self) -> T {
200 self.value
201 }
202}
203
204impl<T> Future for Timeout<T>
205where
206 T: Future,
207{
208 type Output = Result<T::Output, Elapsed>;
209
210 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
211 let me = self.project();
212
213 let had_budget_before = coop::has_budget_remaining();
214
215 // First, try polling the future
216 if let Poll::Ready(v) = me.value.poll(cx) {
217 return Poll::Ready(Ok(v));
218 }
219
220 match me.delay.as_pin_mut() {
221 Some(delay) => poll_delay(had_budget_before, delay, cx).map(Err),
222 None => Poll::Pending,
223 }
224 }
225}
226
227// The T-invariant portion of Timeout::<T>::poll. Pulling this out reduces the
228// amount of code that gets duplicated during monomorphization.
229fn poll_delay(
230 had_budget_before: bool,
231 delay: Pin<&mut Sleep>,
232 cx: &mut task::Context<'_>,
233) -> Poll<Elapsed> {
234 let delay_poll = || match delay.poll(cx) {
235 Poll::Ready(()) => Poll::Ready(Elapsed::new()),
236 Poll::Pending => Poll::Pending,
237 };
238
239 let has_budget_now = coop::has_budget_remaining();
240
241 if let (true, false) = (had_budget_before, has_budget_now) {
242 // if it is the underlying future that exhausted the budget, we poll
243 // the `delay` with an unconstrained one. This prevents pathological
244 // cases where the underlying future always exhausts the budget and
245 // we never get a chance to evaluate whether the timeout was hit or
246 // not.
247 coop::with_unconstrained(delay_poll)
248 } else {
249 delay_poll()
250 }
251}