/rust/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.47.1/src/time/timeout.rs
Line | Count | Source (jump to first uncovered line) |
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 | | |
7 | | use crate::{ |
8 | | task::coop, |
9 | | time::{error::Elapsed, sleep_until, Duration, Instant, Sleep}, |
10 | | util::trace, |
11 | | }; |
12 | | |
13 | | use pin_project_lite::pin_project; |
14 | | use std::future::{Future, IntoFuture}; |
15 | | use std::pin::Pin; |
16 | | use 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 timeout is checked before polling the future, 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] |
86 | 0 | pub fn timeout<F>(duration: Duration, future: F) -> Timeout<F::IntoFuture> |
87 | 0 | where |
88 | 0 | F: IntoFuture, |
89 | 0 | { |
90 | 0 | let location = trace::caller_location(); |
91 | 0 |
|
92 | 0 | let deadline = Instant::now().checked_add(duration); |
93 | 0 | let delay = match deadline { |
94 | 0 | Some(deadline) => Sleep::new_timeout(deadline, location), |
95 | 0 | None => Sleep::far_future(location), |
96 | | }; |
97 | 0 | Timeout::new_with_delay(future.into_future(), delay) |
98 | 0 | } |
99 | | |
100 | | /// Requires a `Future` to complete before the specified instant in time. |
101 | | /// |
102 | | /// If the future completes before the instant is reached, then the completed |
103 | | /// value is returned. Otherwise, an error is returned. |
104 | | /// |
105 | | /// This function returns a future whose return type is [`Result`]`<T,`[`Elapsed`]`>`, where `T` is the |
106 | | /// return type of the provided future. |
107 | | /// |
108 | | /// If the provided future completes immediately, then the future returned from |
109 | | /// this function is guaranteed to complete immediately with an [`Ok`] variant |
110 | | /// no matter the provided deadline. |
111 | | /// |
112 | | /// [`Ok`]: std::result::Result::Ok |
113 | | /// [`Result`]: std::result::Result |
114 | | /// [`Elapsed`]: crate::time::error::Elapsed |
115 | | /// |
116 | | /// # Cancellation |
117 | | /// |
118 | | /// Cancelling a timeout is done by dropping the future. No additional cleanup |
119 | | /// or other work is required. |
120 | | /// |
121 | | /// The original future may be obtained by calling [`Timeout::into_inner`]. This |
122 | | /// consumes the `Timeout`. |
123 | | /// |
124 | | /// # Examples |
125 | | /// |
126 | | /// Create a new `Timeout` set to expire in 10 milliseconds. |
127 | | /// |
128 | | /// ```rust |
129 | | /// use tokio::time::{Instant, timeout_at}; |
130 | | /// use tokio::sync::oneshot; |
131 | | /// |
132 | | /// use std::time::Duration; |
133 | | /// |
134 | | /// # async fn dox() { |
135 | | /// let (tx, rx) = oneshot::channel(); |
136 | | /// # tx.send(()).unwrap(); |
137 | | /// |
138 | | /// // Wrap the future with a `Timeout` set to expire 10 milliseconds into the |
139 | | /// // future. |
140 | | /// if let Err(_) = timeout_at(Instant::now() + Duration::from_millis(10), rx).await { |
141 | | /// println!("did not receive value within 10 ms"); |
142 | | /// } |
143 | | /// # } |
144 | | /// ``` |
145 | 0 | pub fn timeout_at<F>(deadline: Instant, future: F) -> Timeout<F::IntoFuture> |
146 | 0 | where |
147 | 0 | F: IntoFuture, |
148 | 0 | { |
149 | 0 | let delay = sleep_until(deadline); |
150 | 0 |
|
151 | 0 | Timeout { |
152 | 0 | value: future.into_future(), |
153 | 0 | delay, |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | pin_project! { |
158 | | /// Future returned by [`timeout`](timeout) and [`timeout_at`](timeout_at). |
159 | | #[must_use = "futures do nothing unless you `.await` or poll them"] |
160 | | #[derive(Debug)] |
161 | | pub struct Timeout<T> { |
162 | | #[pin] |
163 | | value: T, |
164 | | #[pin] |
165 | | delay: Sleep, |
166 | | } |
167 | | } |
168 | | |
169 | | impl<T> Timeout<T> { |
170 | 0 | pub(crate) fn new_with_delay(value: T, delay: Sleep) -> Timeout<T> { |
171 | 0 | Timeout { value, delay } |
172 | 0 | } |
173 | | |
174 | | /// Gets a reference to the underlying value in this timeout. |
175 | 0 | pub fn get_ref(&self) -> &T { |
176 | 0 | &self.value |
177 | 0 | } |
178 | | |
179 | | /// Gets a mutable reference to the underlying value in this timeout. |
180 | 0 | pub fn get_mut(&mut self) -> &mut T { |
181 | 0 | &mut self.value |
182 | 0 | } |
183 | | |
184 | | /// Consumes this timeout, returning the underlying value. |
185 | 0 | pub fn into_inner(self) -> T { |
186 | 0 | self.value |
187 | 0 | } |
188 | | } |
189 | | |
190 | | impl<T> Future for Timeout<T> |
191 | | where |
192 | | T: Future, |
193 | | { |
194 | | type Output = Result<T::Output, Elapsed>; |
195 | | |
196 | 0 | fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { |
197 | 0 | let me = self.project(); |
198 | 0 |
|
199 | 0 | let had_budget_before = coop::has_budget_remaining(); |
200 | | |
201 | | // First, try polling the future |
202 | 0 | if let Poll::Ready(v) = me.value.poll(cx) { |
203 | 0 | return Poll::Ready(Ok(v)); |
204 | 0 | } |
205 | 0 |
|
206 | 0 | let has_budget_now = coop::has_budget_remaining(); |
207 | 0 |
|
208 | 0 | let delay = me.delay; |
209 | 0 |
|
210 | 0 | let poll_delay = || -> Poll<Self::Output> { |
211 | 0 | match delay.poll(cx) { |
212 | 0 | Poll::Ready(()) => Poll::Ready(Err(Elapsed::new())), |
213 | 0 | Poll::Pending => Poll::Pending, |
214 | | } |
215 | 0 | }; |
216 | | |
217 | 0 | if let (true, false) = (had_budget_before, has_budget_now) { |
218 | | // if it is the underlying future that exhausted the budget, we poll |
219 | | // the `delay` with an unconstrained one. This prevents pathological |
220 | | // cases where the underlying future always exhausts the budget and |
221 | | // we never get a chance to evaluate whether the timeout was hit or |
222 | | // not. |
223 | 0 | coop::with_unconstrained(poll_delay) |
224 | | } else { |
225 | 0 | poll_delay() |
226 | | } |
227 | 0 | } |
228 | | } |