tokio/time/
interval.rs

1use crate::future::poll_fn;
2use crate::time::{sleep_until, Duration, Instant, Sleep};
3use crate::util::trace;
4
5use std::future::Future;
6use std::panic::Location;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9
10/// Creates new [`Interval`] that yields with interval of `period`. The first
11/// tick completes immediately. The default [`MissedTickBehavior`] is
12/// [`Burst`](MissedTickBehavior::Burst), but this can be configured
13/// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
14///
15/// An interval will tick indefinitely. At any time, the [`Interval`] value can
16/// be dropped. This cancels the interval.
17///
18/// This function is equivalent to
19/// [`interval_at(Instant::now(), period)`](interval_at).
20///
21/// # Panics
22///
23/// This function panics if `period` is zero.
24///
25/// # Examples
26///
27/// ```
28/// use tokio::time::{self, Duration};
29///
30/// #[tokio::main]
31/// async fn main() {
32///     let mut interval = time::interval(Duration::from_millis(10));
33///
34///     interval.tick().await; // ticks immediately
35///     interval.tick().await; // ticks after 10ms
36///     interval.tick().await; // ticks after 10ms
37///
38///     // approximately 20ms have elapsed.
39/// }
40/// ```
41///
42/// A simple example using `interval` to execute a task every two seconds.
43///
44/// The difference between `interval` and [`sleep`] is that an [`Interval`]
45/// measures the time since the last tick, which means that [`.tick().await`]
46/// may wait for a shorter time than the duration specified for the interval
47/// if some time has passed between calls to [`.tick().await`].
48///
49/// If the tick in the example below was replaced with [`sleep`], the task
50/// would only be executed once every three seconds, and not every two
51/// seconds.
52///
53/// ```
54/// use tokio::time;
55///
56/// async fn task_that_takes_a_second() {
57///     println!("hello");
58///     time::sleep(time::Duration::from_secs(1)).await
59/// }
60///
61/// #[tokio::main]
62/// async fn main() {
63///     let mut interval = time::interval(time::Duration::from_secs(2));
64///     for _i in 0..5 {
65///         interval.tick().await;
66///         task_that_takes_a_second().await;
67///     }
68/// }
69/// ```
70///
71/// [`sleep`]: crate::time::sleep()
72/// [`.tick().await`]: Interval::tick
73#[track_caller]
74pub fn interval(period: Duration) -> Interval {
75    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
76    internal_interval_at(Instant::now(), period, trace::caller_location())
77}
78
79/// Creates new [`Interval`] that yields with interval of `period` with the
80/// first tick completing at `start`. The default [`MissedTickBehavior`] is
81/// [`Burst`](MissedTickBehavior::Burst), but this can be configured
82/// by calling [`set_missed_tick_behavior`](Interval::set_missed_tick_behavior).
83///
84/// An interval will tick indefinitely. At any time, the [`Interval`] value can
85/// be dropped. This cancels the interval.
86///
87/// # Panics
88///
89/// This function panics if `period` is zero.
90///
91/// # Examples
92///
93/// ```
94/// use tokio::time::{interval_at, Duration, Instant};
95///
96/// #[tokio::main]
97/// async fn main() {
98///     let start = Instant::now() + Duration::from_millis(50);
99///     let mut interval = interval_at(start, Duration::from_millis(10));
100///
101///     interval.tick().await; // ticks after 50ms
102///     interval.tick().await; // ticks after 10ms
103///     interval.tick().await; // ticks after 10ms
104///
105///     // approximately 70ms have elapsed.
106/// }
107/// ```
108#[track_caller]
109pub fn interval_at(start: Instant, period: Duration) -> Interval {
110    assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
111    internal_interval_at(start, period, trace::caller_location())
112}
113
114#[cfg_attr(not(all(tokio_unstable, feature = "tracing")), allow(unused_variables))]
115fn internal_interval_at(
116    start: Instant,
117    period: Duration,
118    location: Option<&'static Location<'static>>,
119) -> Interval {
120    #[cfg(all(tokio_unstable, feature = "tracing"))]
121    let resource_span = {
122        let location = location.expect("should have location if tracing");
123
124        tracing::trace_span!(
125            parent: None,
126            "runtime.resource",
127            concrete_type = "Interval",
128            kind = "timer",
129            loc.file = location.file(),
130            loc.line = location.line(),
131            loc.col = location.column(),
132        )
133    };
134
135    #[cfg(all(tokio_unstable, feature = "tracing"))]
136    let delay = resource_span.in_scope(|| Box::pin(sleep_until(start)));
137
138    #[cfg(not(all(tokio_unstable, feature = "tracing")))]
139    let delay = Box::pin(sleep_until(start));
140
141    Interval {
142        delay,
143        period,
144        missed_tick_behavior: MissedTickBehavior::default(),
145        #[cfg(all(tokio_unstable, feature = "tracing"))]
146        resource_span,
147    }
148}
149
150/// Defines the behavior of an [`Interval`] when it misses a tick.
151///
152/// Sometimes, an [`Interval`]'s tick is missed. For example, consider the
153/// following:
154///
155/// ```
156/// use tokio::time::{self, Duration};
157/// # async fn task_that_takes_one_to_three_millis() {}
158///
159/// #[tokio::main]
160/// async fn main() {
161///     // ticks every 2 milliseconds
162///     let mut interval = time::interval(Duration::from_millis(2));
163///     for _ in 0..5 {
164///         interval.tick().await;
165///         // if this takes more than 2 milliseconds, a tick will be delayed
166///         task_that_takes_one_to_three_millis().await;
167///     }
168/// }
169/// ```
170///
171/// Generally, a tick is missed if too much time is spent without calling
172/// [`Interval::tick()`].
173///
174/// By default, when a tick is missed, [`Interval`] fires ticks as quickly as it
175/// can until it is "caught up" in time to where it should be.
176/// `MissedTickBehavior` can be used to specify a different behavior for
177/// [`Interval`] to exhibit. Each variant represents a different strategy.
178///
179/// Note that because the executor cannot guarantee exact precision with timers,
180/// these strategies will only apply when the delay is greater than 5
181/// milliseconds.
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum MissedTickBehavior {
184    /// Ticks as fast as possible until caught up.
185    ///
186    /// When this strategy is used, [`Interval`] schedules ticks "normally" (the
187    /// same as it would have if the ticks hadn't been delayed), which results
188    /// in it firing ticks as fast as possible until it is caught up in time to
189    /// where it should be. Unlike [`Delay`] and [`Skip`], the ticks yielded
190    /// when `Burst` is used (the [`Instant`]s that [`tick`](Interval::tick)
191    /// yields) aren't different than they would have been if a tick had not
192    /// been missed. Like [`Skip`], and unlike [`Delay`], the ticks may be
193    /// shortened.
194    ///
195    /// This looks something like this:
196    /// ```text
197    /// Expected ticks: |     1     |     2     |     3     |     4     |     5     |     6     |
198    /// Actual ticks:   | work -----|          delay          | work | work | work -| work -----|
199    /// ```
200    ///
201    /// In code:
202    ///
203    /// ```
204    /// use tokio::time::{interval, Duration};
205    /// # async fn task_that_takes_200_millis() {}
206    ///
207    /// # #[tokio::main(flavor = "current_thread")]
208    /// # async fn main() {
209    /// let mut interval = interval(Duration::from_millis(50));
210    ///
211    /// // First tick resolves immediately after creation
212    /// interval.tick().await;
213    ///
214    /// task_that_takes_200_millis().await;
215    /// // The `Interval` has missed a tick
216    ///
217    /// // Since we have exceeded our timeout, this will resolve immediately
218    /// interval.tick().await;
219    ///
220    /// // Since we are more than 100ms after the start of `interval`, this will
221    /// // also resolve immediately.
222    /// interval.tick().await;
223    ///
224    /// // Also resolves immediately, because it was supposed to resolve at
225    /// // 150ms after the start of `interval`
226    /// interval.tick().await;
227    ///
228    /// // Resolves immediately
229    /// interval.tick().await;
230    ///
231    /// // Since we have gotten to 200ms after the start of `interval`, this
232    /// // will resolve after 50ms
233    /// interval.tick().await;
234    /// # }
235    /// ```
236    ///
237    /// This is the default behavior when [`Interval`] is created with
238    /// [`interval`] and [`interval_at`].
239    ///
240    /// [`Delay`]: MissedTickBehavior::Delay
241    /// [`Skip`]: MissedTickBehavior::Skip
242    Burst,
243
244    /// Tick at multiples of `period` from when [`tick`] was called, rather than
245    /// from `start`.
246    ///
247    /// When this strategy is used and [`Interval`] has missed a tick, instead
248    /// of scheduling ticks to fire at multiples of `period` from `start` (the
249    /// time when the first tick was fired), it schedules all future ticks to
250    /// happen at a regular `period` from the point when [`tick`] was called.
251    /// Unlike [`Burst`] and [`Skip`], ticks are not shortened, and they aren't
252    /// guaranteed to happen at a multiple of `period` from `start` any longer.
253    ///
254    /// This looks something like this:
255    /// ```text
256    /// Expected ticks: |     1     |     2     |     3     |     4     |     5     |     6     |
257    /// Actual ticks:   | work -----|          delay          | work -----| work -----| work -----|
258    /// ```
259    ///
260    /// In code:
261    ///
262    /// ```
263    /// use tokio::time::{interval, Duration, MissedTickBehavior};
264    /// # async fn task_that_takes_more_than_50_millis() {}
265    ///
266    /// # #[tokio::main(flavor = "current_thread")]
267    /// # async fn main() {
268    /// let mut interval = interval(Duration::from_millis(50));
269    /// interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
270    ///
271    /// task_that_takes_more_than_50_millis().await;
272    /// // The `Interval` has missed a tick
273    ///
274    /// // Since we have exceeded our timeout, this will resolve immediately
275    /// interval.tick().await;
276    ///
277    /// // But this one, rather than also resolving immediately, as might happen
278    /// // with the `Burst` or `Skip` behaviors, will not resolve until
279    /// // 50ms after the call to `tick` up above. That is, in `tick`, when we
280    /// // recognize that we missed a tick, we schedule the next tick to happen
281    /// // 50ms (or whatever the `period` is) from right then, not from when
282    /// // were *supposed* to tick
283    /// interval.tick().await;
284    /// # }
285    /// ```
286    ///
287    /// [`Burst`]: MissedTickBehavior::Burst
288    /// [`Skip`]: MissedTickBehavior::Skip
289    /// [`tick`]: Interval::tick
290    Delay,
291
292    /// Skips missed ticks and tick on the next multiple of `period` from
293    /// `start`.
294    ///
295    /// When this strategy is used, [`Interval`] schedules the next tick to fire
296    /// at the next-closest tick that is a multiple of `period` away from
297    /// `start` (the point where [`Interval`] first ticked). Like [`Burst`], all
298    /// ticks remain multiples of `period` away from `start`, but unlike
299    /// [`Burst`], the ticks may not be *one* multiple of `period` away from the
300    /// last tick. Like [`Delay`], the ticks are no longer the same as they
301    /// would have been if ticks had not been missed, but unlike [`Delay`], and
302    /// like [`Burst`], the ticks may be shortened to be less than one `period`
303    /// away from each other.
304    ///
305    /// This looks something like this:
306    /// ```text
307    /// Expected ticks: |     1     |     2     |     3     |     4     |     5     |     6     |
308    /// Actual ticks:   | work -----|          delay          | work ---| work -----| work -----|
309    /// ```
310    ///
311    /// In code:
312    ///
313    /// ```
314    /// use tokio::time::{interval, Duration, MissedTickBehavior};
315    /// # async fn task_that_takes_75_millis() {}
316    ///
317    /// # #[tokio::main(flavor = "current_thread")]
318    /// # async fn main() {
319    /// let mut interval = interval(Duration::from_millis(50));
320    /// interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
321    ///
322    /// task_that_takes_75_millis().await;
323    /// // The `Interval` has missed a tick
324    ///
325    /// // Since we have exceeded our timeout, this will resolve immediately
326    /// interval.tick().await;
327    ///
328    /// // This one will resolve after 25ms, 100ms after the start of
329    /// // `interval`, which is the closest multiple of `period` from the start
330    /// // of `interval` after the call to `tick` up above.
331    /// interval.tick().await;
332    /// # }
333    /// ```
334    ///
335    /// [`Burst`]: MissedTickBehavior::Burst
336    /// [`Delay`]: MissedTickBehavior::Delay
337    Skip,
338}
339
340impl MissedTickBehavior {
341    /// If a tick is missed, this method is called to determine when the next tick should happen.
342    fn next_timeout(&self, timeout: Instant, now: Instant, period: Duration) -> Instant {
343        match self {
344            Self::Burst => timeout + period,
345            Self::Delay => now + period,
346            Self::Skip => {
347                now + period
348                    - Duration::from_nanos(
349                        ((now - timeout).as_nanos() % period.as_nanos())
350                            .try_into()
351                            // This operation is practically guaranteed not to
352                            // fail, as in order for it to fail, `period` would
353                            // have to be longer than `now - timeout`, and both
354                            // would have to be longer than 584 years.
355                            //
356                            // If it did fail, there's not a good way to pass
357                            // the error along to the user, so we just panic.
358                            .expect(
359                                "too much time has elapsed since the interval was supposed to tick",
360                            ),
361                    )
362            }
363        }
364    }
365}
366
367impl Default for MissedTickBehavior {
368    /// Returns [`MissedTickBehavior::Burst`].
369    ///
370    /// For most usecases, the [`Burst`] strategy is what is desired.
371    /// Additionally, to preserve backwards compatibility, the [`Burst`]
372    /// strategy must be the default. For these reasons,
373    /// [`MissedTickBehavior::Burst`] is the default for [`MissedTickBehavior`].
374    /// See [`Burst`] for more details.
375    ///
376    /// [`Burst`]: MissedTickBehavior::Burst
377    fn default() -> Self {
378        Self::Burst
379    }
380}
381
382/// Interval returned by [`interval`] and [`interval_at`].
383///
384/// This type allows you to wait on a sequence of instants with a certain
385/// duration between each instant. Unlike calling [`sleep`] in a loop, this lets
386/// you count the time spent between the calls to [`sleep`] as well.
387///
388/// An `Interval` can be turned into a `Stream` with [`IntervalStream`].
389///
390/// [`IntervalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.IntervalStream.html
391/// [`sleep`]: crate::time::sleep()
392#[derive(Debug)]
393pub struct Interval {
394    /// Future that completes the next time the `Interval` yields a value.
395    delay: Pin<Box<Sleep>>,
396
397    /// The duration between values yielded by `Interval`.
398    period: Duration,
399
400    /// The strategy `Interval` should use when a tick is missed.
401    missed_tick_behavior: MissedTickBehavior,
402
403    #[cfg(all(tokio_unstable, feature = "tracing"))]
404    resource_span: tracing::Span,
405}
406
407impl Interval {
408    /// Completes when the next instant in the interval has been reached.
409    ///
410    /// # Cancel safety
411    ///
412    /// This method is cancellation safe. If `tick` is used as the branch in a `tokio::select!` and
413    /// another branch completes first, then no tick has been consumed.
414    ///
415    /// # Examples
416    ///
417    /// ```
418    /// use tokio::time;
419    ///
420    /// use std::time::Duration;
421    ///
422    /// #[tokio::main]
423    /// async fn main() {
424    ///     let mut interval = time::interval(Duration::from_millis(10));
425    ///
426    ///     interval.tick().await;
427    ///     // approximately 0ms have elapsed. The first tick completes immediately.
428    ///     interval.tick().await;
429    ///     interval.tick().await;
430    ///
431    ///     // approximately 20ms have elapsed.
432    /// }
433    /// ```
434    pub async fn tick(&mut self) -> Instant {
435        #[cfg(all(tokio_unstable, feature = "tracing"))]
436        let resource_span = self.resource_span.clone();
437        #[cfg(all(tokio_unstable, feature = "tracing"))]
438        let instant = trace::async_op(
439            || poll_fn(|cx| self.poll_tick(cx)),
440            resource_span,
441            "Interval::tick",
442            "poll_tick",
443            false,
444        );
445        #[cfg(not(all(tokio_unstable, feature = "tracing")))]
446        let instant = poll_fn(|cx| self.poll_tick(cx));
447
448        instant.await
449    }
450
451    /// Polls for the next instant in the interval to be reached.
452    ///
453    /// This method can return the following values:
454    ///
455    ///  * `Poll::Pending` if the next instant has not yet been reached.
456    ///  * `Poll::Ready(instant)` if the next instant has been reached.
457    ///
458    /// When this method returns `Poll::Pending`, the current task is scheduled
459    /// to receive a wakeup when the instant has elapsed. Note that on multiple
460    /// calls to `poll_tick`, only the [`Waker`](std::task::Waker) from the
461    /// [`Context`] passed to the most recent call is scheduled to receive a
462    /// wakeup.
463    pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
464        // Wait for the delay to be done
465        ready!(Pin::new(&mut self.delay).poll(cx));
466
467        // Get the time when we were scheduled to tick
468        let timeout = self.delay.deadline();
469
470        let now = Instant::now();
471
472        // If a tick was not missed, and thus we are being called before the
473        // next tick is due, just schedule the next tick normally, one `period`
474        // after `timeout`
475        //
476        // However, if a tick took excessively long and we are now behind,
477        // schedule the next tick according to how the user specified with
478        // `MissedTickBehavior`
479        let next = if now > timeout + Duration::from_millis(5) {
480            self.missed_tick_behavior
481                .next_timeout(timeout, now, self.period)
482        } else {
483            timeout
484                .checked_add(self.period)
485                .unwrap_or_else(Instant::far_future)
486        };
487
488        // When we arrive here, the internal delay returned `Poll::Ready`.
489        // Reset the delay but do not register it. It should be registered with
490        // the next call to [`poll_tick`].
491        self.delay.as_mut().reset_without_reregister(next);
492
493        // Return the time when we were scheduled to tick
494        Poll::Ready(timeout)
495    }
496
497    /// Resets the interval to complete one period after the current time.
498    ///
499    /// This method ignores [`MissedTickBehavior`] strategy.
500    ///
501    /// This is equivalent to calling `reset_at(Instant::now() + period)`.
502    ///
503    /// # Examples
504    ///
505    /// ```
506    /// use tokio::time;
507    ///
508    /// use std::time::Duration;
509    ///
510    /// #[tokio::main]
511    /// async fn main() {
512    ///     let mut interval = time::interval(Duration::from_millis(100));
513    ///
514    ///     interval.tick().await;
515    ///
516    ///     time::sleep(Duration::from_millis(50)).await;
517    ///     interval.reset();
518    ///
519    ///     interval.tick().await;
520    ///     interval.tick().await;
521    ///
522    ///     // approximately 250ms have elapsed.
523    /// }
524    /// ```
525    pub fn reset(&mut self) {
526        self.delay.as_mut().reset(Instant::now() + self.period);
527    }
528
529    /// Resets the interval immediately.
530    ///
531    /// This method ignores [`MissedTickBehavior`] strategy.
532    ///
533    /// This is equivalent to calling `reset_at(Instant::now())`.
534    ///
535    /// # Examples
536    ///
537    /// ```
538    /// use tokio::time;
539    ///
540    /// use std::time::Duration;
541    ///
542    /// #[tokio::main]
543    /// async fn main() {
544    ///     let mut interval = time::interval(Duration::from_millis(100));
545    ///
546    ///     interval.tick().await;
547    ///
548    ///     time::sleep(Duration::from_millis(50)).await;
549    ///     interval.reset_immediately();
550    ///
551    ///     interval.tick().await;
552    ///     interval.tick().await;
553    ///
554    ///     // approximately 150ms have elapsed.
555    /// }
556    /// ```
557    pub fn reset_immediately(&mut self) {
558        self.delay.as_mut().reset(Instant::now());
559    }
560
561    /// Resets the interval after the specified [`std::time::Duration`].
562    ///
563    /// This method ignores [`MissedTickBehavior`] strategy.
564    ///
565    /// This is equivalent to calling `reset_at(Instant::now() + after)`.
566    ///
567    /// # Examples
568    ///
569    /// ```
570    /// use tokio::time;
571    ///
572    /// use std::time::Duration;
573    ///
574    /// #[tokio::main]
575    /// async fn main() {
576    ///     let mut interval = time::interval(Duration::from_millis(100));
577    ///     interval.tick().await;
578    ///
579    ///     time::sleep(Duration::from_millis(50)).await;
580    ///
581    ///     let after = Duration::from_millis(20);
582    ///     interval.reset_after(after);
583    ///
584    ///     interval.tick().await;
585    ///     interval.tick().await;
586    ///
587    ///     // approximately 170ms have elapsed.
588    /// }
589    /// ```
590    pub fn reset_after(&mut self, after: Duration) {
591        self.delay.as_mut().reset(Instant::now() + after);
592    }
593
594    /// Resets the interval to a [`crate::time::Instant`] deadline.
595    ///
596    /// Sets the next tick to expire at the given instant. If the instant is in
597    /// the past, then the [`MissedTickBehavior`] strategy will be used to
598    /// catch up. If the instant is in the future, then the next tick will
599    /// complete at the given instant, even if that means that it will sleep for
600    /// longer than the duration of this [`Interval`]. If the [`Interval`] had
601    /// any missed ticks before calling this method, then those are discarded.
602    ///
603    /// # Examples
604    ///
605    /// ```
606    /// use tokio::time::{self, Instant};
607    ///
608    /// use std::time::Duration;
609    ///
610    /// #[tokio::main]
611    /// async fn main() {
612    ///     let mut interval = time::interval(Duration::from_millis(100));
613    ///     interval.tick().await;
614    ///
615    ///     time::sleep(Duration::from_millis(50)).await;
616    ///
617    ///     let deadline = Instant::now() + Duration::from_millis(30);
618    ///     interval.reset_at(deadline);
619    ///
620    ///     interval.tick().await;
621    ///     interval.tick().await;
622    ///
623    ///     // approximately 180ms have elapsed.
624    /// }
625    /// ```
626    pub fn reset_at(&mut self, deadline: Instant) {
627        self.delay.as_mut().reset(deadline);
628    }
629
630    /// Returns the [`MissedTickBehavior`] strategy currently being used.
631    pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
632        self.missed_tick_behavior
633    }
634
635    /// Sets the [`MissedTickBehavior`] strategy that should be used.
636    pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
637        self.missed_tick_behavior = behavior;
638    }
639
640    /// Returns the period of the interval.
641    pub fn period(&self) -> Duration {
642        self.period
643    }
644}