kayrx_timer/interval.rs
1use crate::driver::future::poll_fn;
2use crate::{delay_until, Delay, Duration, Instant};
3
4use std::future::Future;
5use std::pin::Pin;
6use std::task::{Context, Poll};
7
8macro_rules! ready {
9 ($e:expr $(,)?) => {
10 match $e {
11 std::task::Poll::Ready(t) => t,
12 std::task::Poll::Pending => return std::task::Poll::Pending,
13 }
14 };
15}
16
17/// Creates new `Interval` that yields with interval of `duration`. The first
18/// tick completes immediately.
19///
20/// An interval will tick indefinitely. At any time, the `Interval` value can be
21/// dropped. This cancels the interval.
22///
23/// This function is equivalent to `interval_at(Instant::now(), period)`.
24///
25/// # Panics
26///
27/// This function panics if `period` is zero.
28///
29/// # Examples
30///
31/// ```rust
32/// use kayrx_timer::{self, Duration};
33/// use kayrx_karx;
34///
35/// fn main() {
36/// kayrx_karx::exec(async {
37/// let mut interval = timer::interval(Duration::from_millis(10));
38///
39/// interval.tick().await;
40/// interval.tick().await;
41/// interval.tick().await;
42///
43/// // approximately 20ms have elapsed.
44/// });
45/// }
46/// ```
47pub fn interval(period: Duration) -> Interval {
48 assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
49
50 interval_at(Instant::now(), period)
51}
52
53/// Creates new `Interval` that yields with interval of `period` with the
54/// first tick completing at `at`.
55///
56/// An interval will tick indefinitely. At any time, the `Interval` value can be
57/// dropped. This cancels the interval.
58///
59/// # Panics
60///
61/// This function panics if `period` is zero.
62///
63/// # Examples
64///
65/// ```
66/// use kayrx_timer::{interval_at, Duration, Instant};
67/// use kayrx_karx;
68///
69/// fn main() {
70/// kayrx_karx::exec(async {
71/// let start = Instant::now() + Duration::from_millis(50);
72/// let mut interval = interval_at(start, Duration::from_millis(10));
73///
74/// interval.tick().await;
75/// interval.tick().await;
76/// interval.tick().await;
77///
78/// // approximately 70ms have elapsed.
79/// });
80/// }
81/// ```
82pub fn interval_at(start: Instant, period: Duration) -> Interval {
83 assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
84
85 Interval {
86 delay: delay_until(start),
87 period,
88 }
89}
90
91/// Stream returned by [`interval`](interval) and [`interval_at`](interval_at).
92#[derive(Debug)]
93pub struct Interval {
94 /// Future that completes the next time the `Interval` yields a value.
95 delay: Delay,
96
97 /// The duration between values yielded by `Interval`.
98 period: Duration,
99}
100
101impl Interval {
102 #[doc(hidden)] // TODO: document
103 pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
104 // Wait for the delay to be done
105 ready!(Pin::new(&mut self.delay).poll(cx));
106
107 // Get the `now` by looking at the `delay` deadline
108 let now = self.delay.deadline();
109
110 // The next interval value is `duration` after the one that just
111 // yielded.
112 let next = now + self.period;
113 self.delay.reset(next);
114
115 // Return the current instant
116 Poll::Ready(now)
117 }
118
119 /// Completes when the next instant in the interval has been reached.
120 ///
121 /// # Examples
122 ///
123 /// ```
124 /// use kayrx_timer;
125 /// use std::time::Duration;
126 /// use kayrx_karx;
127 ///
128 /// fn main() {
129 /// kayrx_karx::exec(async {
130 /// let mut interval = timer::interval(Duration::from_millis(10));
131 ///
132 /// interval.tick().await;
133 /// interval.tick().await;
134 /// interval.tick().await;
135 ///
136 /// // approximately 20ms have elapsed.
137 /// });
138 /// }
139 /// ```
140 #[allow(clippy::should_implement_trait)]
141 pub async fn tick(&mut self) -> Instant {
142 poll_fn(|cx| self.poll_tick(cx)).await
143 }
144}
145
146impl futures_core::stream::Stream for Interval {
147 type Item = Instant;
148
149 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Instant>> {
150 Poll::Ready(Some(ready!(self.poll_tick(cx))))
151 }
152}