1 use crate::future::poll_fn;
2 use crate::time::{sleep_until, Duration, Instant, Sleep};
3
4 use std::pin::Pin;
5 use std::task::{Context, Poll};
6 use std::{convert::TryInto, future::Future};
7
8 /// Creates new [`Interval`] that yields with interval of `period`. The first
9 /// tick completes immediately. The default [`MissedTickBehavior`] is
10 /// [`Burst`](MissedTickBehavior::Burst), but this can be configured
11 /// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
12 ///
13 /// An interval will tick indefinitely. At any time, the [`Interval`] value can
14 /// be dropped. This cancels the interval.
15 ///
16 /// This function is equivalent to
17 /// [`interval_at(Instant::now(), period)`](interval_at).
18 ///
19 /// # Panics
20 ///
21 /// This function panics if `period` is zero.
22 ///
23 /// # Examples
24 ///
25 /// ```
26 /// use tokio::time::{self, Duration};
27 ///
28 /// #[tokio::main]
29 /// async fn main() {
30 /// let mut interval = time::interval(Duration::from_millis(10));
31 ///
32 /// interval.tick().await; // ticks immediately
33 /// interval.tick().await; // ticks after 10ms
34 /// interval.tick().await; // ticks after 10ms
35 ///
36 /// // approximately 20ms have elapsed.
37 /// }
38 /// ```
39 ///
40 /// A simple example using `interval` to execute a task every two seconds.
41 ///
42 /// The difference between `interval` and [`sleep`] is that an [`Interval`]
43 /// measures the time since the last tick, which means that [`.tick().await`]
44 /// may wait for a shorter time than the duration specified for the interval
45 /// if some time has passed between calls to [`.tick().await`].
46 ///
47 /// If the tick in the example below was replaced with [`sleep`], the task
48 /// would only be executed once every three seconds, and not every two
49 /// seconds.
50 ///
51 /// ```
52 /// use tokio::time;
53 ///
54 /// async fn task_that_takes_a_second() {
55 /// println!("hello");
56 /// time::sleep(time::Duration::from_secs(1)).await
57 /// }
58 ///
59 /// #[tokio::main]
60 /// async fn main() {
61 /// let mut interval = time::interval(time::Duration::from_secs(2));
62 /// for _i in 0..5 {
63 /// interval.tick().await;
64 /// task_that_takes_a_second().await;
65 /// }
66 /// }
67 /// ```
68 ///
69 /// [`sleep`]: crate::time::sleep()
70 /// [`.tick().await`]: Interval::tick
interval(period: Duration) -> Interval71 pub fn interval(period: Duration) -> Interval {
72 assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
73
74 interval_at(Instant::now(), period)
75 }
76
77 /// Creates new [`Interval`] that yields with interval of `period` with the
78 /// first tick completing at `start`. The default [`MissedTickBehavior`] is
79 /// [`Burst`](MissedTickBehavior::Burst), but this can be configured
80 /// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
81 ///
82 /// An interval will tick indefinitely. At any time, the [`Interval`] value can
83 /// be dropped. This cancels the interval.
84 ///
85 /// # Panics
86 ///
87 /// This function panics if `period` is zero.
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use tokio::time::{interval_at, Duration, Instant};
93 ///
94 /// #[tokio::main]
95 /// async fn main() {
96 /// let start = Instant::now() + Duration::from_millis(50);
97 /// let mut interval = interval_at(start, Duration::from_millis(10));
98 ///
99 /// interval.tick().await; // ticks after 50ms
100 /// interval.tick().await; // ticks after 10ms
101 /// interval.tick().await; // ticks after 10ms
102 ///
103 /// // approximately 70ms have elapsed.
104 /// }
105 /// ```
interval_at(start: Instant, period: Duration) -> Interval106 pub fn interval_at(start: Instant, period: Duration) -> Interval {
107 assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
108
109 Interval {
110 delay: Box::pin(sleep_until(start)),
111 period,
112 missed_tick_behavior: Default::default(),
113 }
114 }
115
116 /// Defines the behavior of an [`Interval`] when it misses a tick.
117 ///
118 /// Sometimes, an [`Interval`]'s tick is missed. For example, consider the
119 /// following:
120 ///
121 /// ```
122 /// use tokio::time::{self, Duration};
123 /// # async fn task_that_takes_one_to_three_millis() {}
124 ///
125 /// #[tokio::main]
126 /// async fn main() {
127 /// // ticks every 2 seconds
128 /// let mut interval = time::interval(Duration::from_millis(2));
129 /// for _ in 0..5 {
130 /// interval.tick().await;
131 /// // if this takes more than 2 milliseconds, a tick will be delayed
132 /// task_that_takes_one_to_three_millis().await;
133 /// }
134 /// }
135 /// ```
136 ///
137 /// Generally, a tick is missed if too much time is spent without calling
138 /// [`Interval::tick()`].
139 ///
140 /// By default, when a tick is missed, [`Interval`] fires ticks as quickly as it
141 /// can until it is "caught up" in time to where it should be.
142 /// `MissedTickBehavior` can be used to specify a different behavior for
143 /// [`Interval`] to exhibit. Each variant represents a different strategy.
144 ///
145 /// Note that because the executor cannot guarantee exact precision with timers,
146 /// these strategies will only apply when the delay is greater than 5
147 /// milliseconds.
148 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
149 pub enum MissedTickBehavior {
150 /// Ticks as fast as possible until caught up.
151 ///
152 /// When this strategy is used, [`Interval`] schedules ticks "normally" (the
153 /// same as it would have if the ticks hadn't been delayed), which results
154 /// in it firing ticks as fast as possible until it is caught up in time to
155 /// where it should be. Unlike [`Delay`] and [`Skip`], the ticks yielded
156 /// when `Burst` is used (the [`Instant`]s that [`tick`](Interval::tick)
157 /// yields) aren't different than they would have been if a tick had not
158 /// been missed. Like [`Skip`], and unlike [`Delay`], the ticks may be
159 /// shortened.
160 ///
161 /// This looks something like this:
162 /// ```text
163 /// Expected ticks: | 1 | 2 | 3 | 4 | 5 | 6 |
164 /// Actual ticks: | work -----| delay | work | work | work -| work -----|
165 /// ```
166 ///
167 /// In code:
168 ///
169 /// ```
170 /// use tokio::time::{interval, Duration};
171 /// # async fn task_that_takes_200_millis() {}
172 ///
173 /// # #[tokio::main(flavor = "current_thread")]
174 /// # async fn main() {
175 /// let mut interval = interval(Duration::from_millis(50));
176 ///
177 /// task_that_takes_200_millis().await;
178 /// // The `Interval` has missed a tick
179 ///
180 /// // Since we have exceeded our timeout, this will resolve immediately
181 /// interval.tick().await;
182 ///
183 /// // Since we are more than 100ms after the start of `interval`, this will
184 /// // also resolve immediately.
185 /// interval.tick().await;
186 ///
187 /// // Also resolves immediately, because it was supposed to resolve at
188 /// // 150ms after the start of `interval`
189 /// interval.tick().await;
190 ///
191 /// // Resolves immediately
192 /// interval.tick().await;
193 ///
194 /// // Since we have gotten to 200ms after the start of `interval`, this
195 /// // will resolve after 50ms
196 /// interval.tick().await;
197 /// # }
198 /// ```
199 ///
200 /// This is the default behavior when [`Interval`] is created with
201 /// [`interval`] and [`interval_at`].
202 ///
203 /// [`Delay`]: MissedTickBehavior::Delay
204 /// [`Skip`]: MissedTickBehavior::Skip
205 Burst,
206
207 /// Tick at multiples of `period` from when [`tick`] was called, rather than
208 /// from `start`.
209 ///
210 /// When this strategy is used and [`Interval`] has missed a tick, instead
211 /// of scheduling ticks to fire at multiples of `period` from `start` (the
212 /// time when the first tick was fired), it schedules all future ticks to
213 /// happen at a regular `period` from the point when [`tick`] was called.
214 /// Unlike [`Burst`] and [`Skip`], ticks are not shortened, and they aren't
215 /// guaranteed to happen at a multiple of `period` from `start` any longer.
216 ///
217 /// This looks something like this:
218 /// ```text
219 /// Expected ticks: | 1 | 2 | 3 | 4 | 5 | 6 |
220 /// Actual ticks: | work -----| delay | work -----| work -----| work -----|
221 /// ```
222 ///
223 /// In code:
224 ///
225 /// ```
226 /// use tokio::time::{interval, Duration, MissedTickBehavior};
227 /// # async fn task_that_takes_more_than_50_millis() {}
228 ///
229 /// # #[tokio::main(flavor = "current_thread")]
230 /// # async fn main() {
231 /// let mut interval = interval(Duration::from_millis(50));
232 /// interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
233 ///
234 /// task_that_takes_more_than_50_millis().await;
235 /// // The `Interval` has missed a tick
236 ///
237 /// // Since we have exceeded our timeout, this will resolve immediately
238 /// interval.tick().await;
239 ///
240 /// // But this one, rather than also resolving immediately, as might happen
241 /// // with the `Burst` or `Skip` behaviors, will not resolve until
242 /// // 50ms after the call to `tick` up above. That is, in `tick`, when we
243 /// // recognize that we missed a tick, we schedule the next tick to happen
244 /// // 50ms (or whatever the `period` is) from right then, not from when
245 /// // were were *supposed* to tick
246 /// interval.tick().await;
247 /// # }
248 /// ```
249 ///
250 /// [`Burst`]: MissedTickBehavior::Burst
251 /// [`Skip`]: MissedTickBehavior::Skip
252 /// [`tick`]: Interval::tick
253 Delay,
254
255 /// Skips missed ticks and tick on the next multiple of `period` from
256 /// `start`.
257 ///
258 /// When this strategy is used, [`Interval`] schedules the next tick to fire
259 /// at the next-closest tick that is a multiple of `period` away from
260 /// `start` (the point where [`Interval`] first ticked). Like [`Burst`], all
261 /// ticks remain multiples of `period` away from `start`, but unlike
262 /// [`Burst`], the ticks may not be *one* multiple of `period` away from the
263 /// last tick. Like [`Delay`], the ticks are no longer the same as they
264 /// would have been if ticks had not been missed, but unlike [`Delay`], and
265 /// like [`Burst`], the ticks may be shortened to be less than one `period`
266 /// away from each other.
267 ///
268 /// This looks something like this:
269 /// ```text
270 /// Expected ticks: | 1 | 2 | 3 | 4 | 5 | 6 |
271 /// Actual ticks: | work -----| delay | work ---| work -----| work -----|
272 /// ```
273 ///
274 /// In code:
275 ///
276 /// ```
277 /// use tokio::time::{interval, Duration, MissedTickBehavior};
278 /// # async fn task_that_takes_75_millis() {}
279 ///
280 /// # #[tokio::main(flavor = "current_thread")]
281 /// # async fn main() {
282 /// let mut interval = interval(Duration::from_millis(50));
283 /// interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
284 ///
285 /// task_that_takes_75_millis().await;
286 /// // The `Interval` has missed a tick
287 ///
288 /// // Since we have exceeded our timeout, this will resolve immediately
289 /// interval.tick().await;
290 ///
291 /// // This one will resolve after 25ms, 100ms after the start of
292 /// // `interval`, which is the closest multiple of `period` from the start
293 /// // of `interval` after the call to `tick` up above.
294 /// interval.tick().await;
295 /// # }
296 /// ```
297 ///
298 /// [`Burst`]: MissedTickBehavior::Burst
299 /// [`Delay`]: MissedTickBehavior::Delay
300 Skip,
301 }
302
303 impl MissedTickBehavior {
304 /// If a tick is missed, this method is called to determine when the next tick should happen.
next_timeout(&self, timeout: Instant, now: Instant, period: Duration) -> Instant305 fn next_timeout(&self, timeout: Instant, now: Instant, period: Duration) -> Instant {
306 match self {
307 Self::Burst => timeout + period,
308 Self::Delay => now + period,
309 Self::Skip => {
310 now + period
311 - Duration::from_nanos(
312 ((now - timeout).as_nanos() % period.as_nanos())
313 .try_into()
314 // This operation is practically guaranteed not to
315 // fail, as in order for it to fail, `period` would
316 // have to be longer than `now - timeout`, and both
317 // would have to be longer than 584 years.
318 //
319 // If it did fail, there's not a good way to pass
320 // the error along to the user, so we just panic.
321 .expect(
322 "too much time has elapsed since the interval was supposed to tick",
323 ),
324 )
325 }
326 }
327 }
328 }
329
330 impl Default for MissedTickBehavior {
331 /// Returns [`MissedTickBehavior::Burst`].
332 ///
333 /// For most usecases, the [`Burst`] strategy is what is desired.
334 /// Additionally, to preserve backwards compatibility, the [`Burst`]
335 /// strategy must be the default. For these reasons,
336 /// [`MissedTickBehavior::Burst`] is the default for [`MissedTickBehavior`].
337 /// See [`Burst`] for more details.
338 ///
339 /// [`Burst`]: MissedTickBehavior::Burst
default() -> Self340 fn default() -> Self {
341 Self::Burst
342 }
343 }
344
345 /// Interval returned by [`interval`] and [`interval_at`].
346 ///
347 /// This type allows you to wait on a sequence of instants with a certain
348 /// duration between each instant. Unlike calling [`sleep`] in a loop, this lets
349 /// you count the time spent between the calls to [`sleep`] as well.
350 ///
351 /// An `Interval` can be turned into a `Stream` with [`IntervalStream`].
352 ///
353 /// [`IntervalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.IntervalStream.html
354 /// [`sleep`]: crate::time::sleep
355 #[derive(Debug)]
356 pub struct Interval {
357 /// Future that completes the next time the `Interval` yields a value.
358 delay: Pin<Box<Sleep>>,
359
360 /// The duration between values yielded by `Interval`.
361 period: Duration,
362
363 /// The strategy `Interval` should use when a tick is missed.
364 missed_tick_behavior: MissedTickBehavior,
365 }
366
367 impl Interval {
368 /// Completes when the next instant in the interval has been reached.
369 ///
370 /// # Cancel safety
371 ///
372 /// This method is cancellation safe. If `tick` is used as the branch in a `tokio::select!` and
373 /// another branch completes first, then no tick has been consumed.
374 ///
375 /// # Examples
376 ///
377 /// ```
378 /// use tokio::time;
379 ///
380 /// use std::time::Duration;
381 ///
382 /// #[tokio::main]
383 /// async fn main() {
384 /// let mut interval = time::interval(Duration::from_millis(10));
385 ///
386 /// interval.tick().await;
387 /// interval.tick().await;
388 /// interval.tick().await;
389 ///
390 /// // approximately 20ms have elapsed.
391 /// }
392 /// ```
tick(&mut self) -> Instant393 pub async fn tick(&mut self) -> Instant {
394 poll_fn(|cx| self.poll_tick(cx)).await
395 }
396
397 /// Polls for the next instant in the interval to be reached.
398 ///
399 /// This method can return the following values:
400 ///
401 /// * `Poll::Pending` if the next instant has not yet been reached.
402 /// * `Poll::Ready(instant)` if the next instant has been reached.
403 ///
404 /// When this method returns `Poll::Pending`, the current task is scheduled
405 /// to receive a wakeup when the instant has elapsed. Note that on multiple
406 /// calls to `poll_tick`, only the [`Waker`](std::task::Waker) from the
407 /// [`Context`] passed to the most recent call is scheduled to receive a
408 /// wakeup.
poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant>409 pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
410 // Wait for the delay to be done
411 ready!(Pin::new(&mut self.delay).poll(cx));
412
413 // Get the time when we were scheduled to tick
414 let timeout = self.delay.deadline();
415
416 let now = Instant::now();
417
418 // If a tick was not missed, and thus we are being called before the
419 // next tick is due, just schedule the next tick normally, one `period`
420 // after `timeout`
421 //
422 // However, if a tick took excessively long and we are now behind,
423 // schedule the next tick according to how the user specified with
424 // `MissedTickBehavior`
425 let next = if now > timeout + Duration::from_millis(5) {
426 self.missed_tick_behavior
427 .next_timeout(timeout, now, self.period)
428 } else {
429 timeout + self.period
430 };
431
432 self.delay.as_mut().reset(next);
433
434 // Return the time when we were scheduled to tick
435 Poll::Ready(timeout)
436 }
437
438 /// Returns the [`MissedTickBehavior`] strategy currently being used.
missed_tick_behavior(&self) -> MissedTickBehavior439 pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
440 self.missed_tick_behavior
441 }
442
443 /// Sets the [`MissedTickBehavior`] strategy that should be used.
set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior)444 pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
445 self.missed_tick_behavior = behavior;
446 }
447
448 /// Returns the period of the interval.
period(&self) -> Duration449 pub fn period(&self) -> Duration {
450 self.period
451 }
452 }
453