wasm_timer/timer/
interval.rs1use pin_utils::unsafe_pinned;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4use std::time::Duration;
5
6use futures::prelude::*;
7
8use crate::timer::delay;
9use crate::{Delay, Instant, TimerHandle};
10
11#[derive(Debug)]
21pub struct Interval {
22 delay: Delay,
23 interval: Duration,
24}
25
26impl Interval {
27 unsafe_pinned!(delay: Delay);
28
29 pub fn new(dur: Duration) -> Interval {
35 Interval::new_at(Instant::now() + dur, dur)
36 }
37
38 pub fn new_at(at: Instant, dur: Duration) -> Interval {
44 Interval {
45 delay: Delay::new_at(at),
46 interval: dur,
47 }
48 }
49
50 pub fn new_handle(at: Instant, dur: Duration, handle: TimerHandle) -> Interval {
55 Interval {
56 delay: Delay::new_handle(at, handle),
57 interval: dur,
58 }
59 }
60}
61
62impl Stream for Interval {
63 type Item = ();
64
65 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
66 if Pin::new(&mut *self).delay().poll(cx).is_pending() {
67 return Poll::Pending;
68 }
69 let next = next_interval(delay::fires_at(&self.delay), Instant::now(), self.interval);
70 self.delay.reset_at(next);
71 Poll::Ready(Some(()))
72 }
73}
74
75fn duration_to_nanos(dur: Duration) -> Option<u64> {
84 dur.as_secs()
85 .checked_mul(1_000_000_000)
86 .and_then(|v| v.checked_add(dur.subsec_nanos() as u64))
87}
88
89fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant {
90 let new = prev + interval;
91 if new > now {
92 return new;
93 } else {
94 let spent_ns =
95 duration_to_nanos(now.duration_since(prev)).expect("interval should be expired");
96 let interval_ns =
97 duration_to_nanos(interval).expect("interval is less that 427 thousand years");
98 let mult = spent_ns / interval_ns + 1;
99 assert!(
100 mult < (1 << 32),
101 "can't skip more than 4 billion intervals of {:?} \
102 (trying to skip {})",
103 interval,
104 mult
105 );
106 return prev + interval * (mult as u32);
107 }
108}
109
110#[cfg(test)]
111mod test {
112 use super::next_interval;
113 use std::time::{Duration, Instant};
114
115 struct Timeline(Instant);
116
117 impl Timeline {
118 fn new() -> Timeline {
119 Timeline(Instant::now())
120 }
121 fn at(&self, millis: u64) -> Instant {
122 self.0 + Duration::from_millis(millis)
123 }
124 fn at_ns(&self, sec: u64, nanos: u32) -> Instant {
125 self.0 + Duration::new(sec, nanos)
126 }
127 }
128
129 fn dur(millis: u64) -> Duration {
130 Duration::from_millis(millis)
131 }
132
133 fn almost_eq(a: Instant, b: Instant) -> bool {
136 if a == b {
137 true
138 } else if a > b {
139 a - b < Duration::from_millis(1)
140 } else {
141 b - a < Duration::from_millis(1)
142 }
143 }
144
145 #[test]
146 fn norm_next() {
147 let tm = Timeline::new();
148 assert!(almost_eq(
149 next_interval(tm.at(1), tm.at(2), dur(10)),
150 tm.at(11)
151 ));
152 assert!(almost_eq(
153 next_interval(tm.at(7777), tm.at(7788), dur(100)),
154 tm.at(7877)
155 ));
156 assert!(almost_eq(
157 next_interval(tm.at(1), tm.at(1000), dur(2100)),
158 tm.at(2101)
159 ));
160 }
161
162 #[test]
163 fn fast_forward() {
164 let tm = Timeline::new();
165 assert!(almost_eq(
166 next_interval(tm.at(1), tm.at(1000), dur(10)),
167 tm.at(1001)
168 ));
169 assert!(almost_eq(
170 next_interval(tm.at(7777), tm.at(8888), dur(100)),
171 tm.at(8977)
172 ));
173 assert!(almost_eq(
174 next_interval(tm.at(1), tm.at(10000), dur(2100)),
175 tm.at(10501)
176 ));
177 }
178
179 #[test]
183 #[should_panic(expected = "can't skip more than 4 billion intervals")]
184 fn large_skip() {
185 let tm = Timeline::new();
186 assert_eq!(
187 next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)),
188 tm.at_ns(25, 1)
189 );
190 }
191}