1use core::future::{Future, poll_fn};
4use core::pin::Pin;
5use core::task::{Context, Poll};
6use std::time::Duration;
7
8use super::Instant;
9
10use super::sleep::{Sleep, sleep_until};
11
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
14pub enum MissedTickBehavior {
15 #[default]
18 Burst,
19 Delay,
21 Skip,
24}
25
26pub fn interval(period: Duration) -> Interval {
32 interval_at(Instant::now(), period)
33}
34
35pub fn interval_at(start: impl Into<Instant>, period: Duration) -> Interval {
41 let start = start.into();
42 assert!(
43 period > Duration::ZERO,
44 "an interval needs a period above zero"
45 );
46 Interval {
47 period,
48 sleep: sleep_until(start),
49 missed: MissedTickBehavior::default(),
50 }
51}
52
53#[derive(Debug)]
55pub struct Interval {
56 period: Duration,
57 sleep: Sleep,
59 missed: MissedTickBehavior,
60}
61
62impl Interval {
63 pub async fn tick(&mut self) -> Instant {
65 poll_fn(|cx| self.poll_tick(cx)).await
66 }
67
68 pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
70 if Pin::new(&mut self.sleep).poll(cx).is_pending() {
71 return Poll::Pending;
72 }
73 let due = self.sleep.deadline();
74 let now = Instant::now();
75 let next = match self.missed {
76 MissedTickBehavior::Burst => due + self.period,
77 MissedTickBehavior::Delay => now + self.period,
78 MissedTickBehavior::Skip => {
79 let late = now.saturating_duration_since(due);
80 let periods = late.as_nanos() / self.period.as_nanos() + 1;
82 due + self.period * u32::try_from(periods).unwrap_or(u32::MAX)
83 }
84 };
85 Pin::new(&mut self.sleep).reset(next);
86 Poll::Ready(due)
87 }
88
89 pub fn reset(&mut self) {
91 Pin::new(&mut self.sleep).reset(Instant::now() + self.period);
92 }
93
94 pub fn period(&self) -> Duration {
96 self.period
97 }
98
99 pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
101 self.missed
102 }
103
104 pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
106 self.missed = behavior;
107 }
108}