• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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