timeout_trait/
tick_impl.rs

1use super::prelude::*;
2use core::marker::PhantomData;
3
4#[derive(Default)]
5pub struct TickTimeoutNs<T> {
6    _t: PhantomData<T>,
7}
8
9impl<T> TickTimeoutNs<T>
10where
11    T: TickInstant,
12{
13    pub const fn new() -> Self {
14        Self { _t: PhantomData }
15    }
16}
17
18impl<T> TimeoutNs for TickTimeoutNs<T>
19where
20    T: TickInstant,
21{
22    type TimeoutState = TickTimeoutState<T>;
23
24    #[inline]
25    fn start_ns(&self, timeout: u32) -> Self::TimeoutState {
26        TickTimeoutState::<T>::new_ns(timeout)
27    }
28
29    #[inline]
30    fn start_us(&self, timeout: u32) -> Self::TimeoutState {
31        TickTimeoutState::<T>::new_us(timeout)
32    }
33
34    #[inline]
35    fn start_ms(&self, timeout: u32) -> Self::TimeoutState {
36        TickTimeoutState::<T>::new_ms(timeout)
37    }
38}
39
40pub struct TickTimeoutState<T: TickInstant> {
41    tick: T,
42    timeout_tick: u32,
43    elapsed_tick: u32,
44    round: u32,
45    elapsed_round: u32,
46}
47
48impl<T> TickTimeoutState<T>
49where
50    T: TickInstant,
51{
52    pub fn new_ns(timeout: u32) -> Self {
53        let ns = timeout as u64;
54        let timeout_tick = (ns * T::frequency().to_kHz() as u64).div_ceil(1_000_000);
55        Self::new(timeout_tick)
56    }
57
58    pub fn new_us(timeout: u32) -> Self {
59        let us = timeout as u64;
60        let timeout_tick = (us * T::frequency().to_kHz() as u64).div_ceil(1_000);
61        Self::new(timeout_tick)
62    }
63
64    pub fn new_ms(timeout: u32) -> Self {
65        let ms = timeout as u64;
66        let frequency = T::frequency().to_kHz() as u64;
67        let timeout_tick = ms * frequency;
68        Self::new(timeout_tick)
69    }
70
71    fn new(mut timeout_tick: u64) -> Self {
72        let mut round = 1;
73        while timeout_tick > (u32::MAX >> 1) as u64 {
74            if (timeout_tick | 1) == 1 {
75                timeout_tick += 0b10;
76            }
77            timeout_tick >>= 1;
78            round <<= 1;
79        }
80
81        Self {
82            tick: T::now(),
83            timeout_tick: timeout_tick as u32,
84            elapsed_tick: 0,
85            round,
86            elapsed_round: 0,
87        }
88    }
89}
90
91impl<T> TimeoutState for TickTimeoutState<T>
92where
93    T: TickInstant,
94{
95    /// Can be reused without calling `restart()`.
96    #[inline]
97    fn timeout(&mut self) -> bool {
98        let now = T::now();
99        self.elapsed_tick = self.elapsed_tick.saturating_add(now.tick_since(self.tick));
100        self.tick = now;
101
102        if self.elapsed_tick >= self.timeout_tick {
103            self.elapsed_tick -= self.timeout_tick;
104            self.elapsed_round += 1;
105            if self.elapsed_round >= self.round {
106                self.elapsed_round -= self.round;
107                return true;
108            }
109        }
110        false
111    }
112
113    #[inline(always)]
114    fn restart(&mut self) {
115        self.tick = T::now();
116        self.elapsed_tick = 0;
117        self.elapsed_round = 0;
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use core::sync::atomic::{AtomicU32, Ordering};
125    use fugit::{ExtU32, KilohertzU32, NanosDurationU32, RateExtU32};
126
127    static TICK_SOURCE: AtomicU32 = AtomicU32::new(0);
128
129    #[derive(Clone, Copy)]
130    pub struct MockInstant {
131        tick: u32,
132    }
133
134    impl MockInstant {
135        fn add_time(t: NanosDurationU32) {
136            let tick = t.to_nanos();
137            TICK_SOURCE.fetch_add(tick, Ordering::Relaxed);
138        }
139    }
140
141    impl TickInstant for MockInstant {
142        fn frequency() -> KilohertzU32 {
143            1000.MHz()
144        }
145
146        fn now() -> Self {
147            Self {
148                tick: TICK_SOURCE.load(Ordering::Relaxed),
149            }
150        }
151
152        fn tick_since(self, earlier: Self) -> u32 {
153            self.tick.wrapping_sub(earlier.tick)
154        }
155    }
156
157    #[test]
158    fn tick_timeout() {
159        let now = MockInstant::now();
160        assert_eq!(now.tick_elapsed(), 0);
161        MockInstant::add_time(10.nanos());
162        assert_eq!(now.tick_elapsed(), 10);
163        MockInstant::add_time(1.millis());
164        assert_eq!(now.tick_elapsed(), 1_000_010);
165        MockInstant::add_time(10.micros());
166        let now2 = MockInstant::now();
167        assert_eq!(now2.tick_since(now), 1_010_010);
168        MockInstant::add_time(10.micros());
169        let now3 = MockInstant::now();
170        assert_eq!(now3.tick_since(now2), 10_000);
171
172        let mut t = TickTimeoutNs::<MockInstant>::new().start_ns(100);
173        assert!(!t.timeout());
174        MockInstant::add_time(10.nanos());
175        assert!(!t.timeout());
176        MockInstant::add_time(90.nanos());
177        assert!(t.timeout());
178        assert!(!t.timeout());
179        MockInstant::add_time(100.nanos());
180        assert!(t.timeout());
181
182        MockInstant::add_time(90.nanos());
183        assert!(!t.timeout());
184        t.restart();
185        MockInstant::add_time(90.nanos());
186        assert!(!t.timeout());
187        MockInstant::add_time(10.nanos());
188        assert!(t.timeout());
189
190        let mut t = TickTimeoutNs::<MockInstant>::new().start_us(100);
191        assert!(!t.timeout());
192        MockInstant::add_time(10.micros());
193        assert!(!t.timeout());
194        MockInstant::add_time(90.micros());
195        assert!(t.timeout());
196        assert!(!t.timeout());
197        MockInstant::add_time(100.micros());
198        assert!(t.timeout());
199
200        MockInstant::add_time(90.micros());
201        assert!(!t.timeout());
202        t.restart();
203        MockInstant::add_time(90.micros());
204        assert!(!t.timeout());
205        MockInstant::add_time(10.micros());
206        assert!(t.timeout());
207
208        let mut t = TickTimeoutNs::<MockInstant>::new().start_ms(100);
209        assert!(!t.timeout());
210        MockInstant::add_time(10.millis());
211        assert!(!t.timeout());
212        MockInstant::add_time(90.millis());
213        assert!(t.timeout());
214        assert!(!t.timeout());
215        MockInstant::add_time(100.millis());
216        assert!(t.timeout());
217
218        MockInstant::add_time(90.millis());
219        assert!(!t.timeout());
220        t.restart();
221        MockInstant::add_time(90.millis());
222        assert!(!t.timeout());
223        MockInstant::add_time(10.millis());
224        assert!(t.timeout());
225
226        let mut count = 0;
227        let t = TickTimeoutNs::<MockInstant>::new();
228        assert!(t.ns_with(100, || {
229            MockInstant::add_time(10.nanos());
230            count += 1;
231            true
232        }));
233        assert_eq!(count, 10);
234
235        let t = TickTimeoutState::<MockInstant>::new_us(40_000_000);
236        assert_eq!(t.round, 32);
237        assert_eq!(t.timeout_tick, 1_250_000_000);
238
239        let mut t = TickTimeoutState::<MockInstant>::new_ms(40_000);
240        assert_eq!(t.round, 32);
241        assert_eq!(t.timeout_tick, 1_250_000_000);
242
243        assert!(!t.timeout());
244
245        for _ in 0..40 {
246            MockInstant::add_time(999.millis());
247            assert!(!t.timeout());
248        }
249        assert_eq!(t.elapsed_round, 31);
250        assert!(t.elapsed_tick > 0);
251        MockInstant::add_time(100.millis());
252        assert!(t.timeout());
253        assert_eq!(t.elapsed_round, 0);
254        assert!(!t.timeout());
255
256        for _ in 0..39 {
257            MockInstant::add_time(999.millis());
258            assert!(!t.timeout());
259        }
260        t.restart();
261        for _ in 0..40 {
262            MockInstant::add_time(999.millis());
263            assert!(!t.timeout());
264        }
265        MockInstant::add_time(100.millis());
266        assert!(t.timeout());
267    }
268}
269
270#[cfg(test)]
271mod tests_fugit {
272    use core::ops::Div;
273    use fugit::{
274        ExtU32, ExtU32Ceil, KilohertzU32, MicrosDurationU32, MillisDurationU32, RateExtU32,
275    };
276
277    #[test]
278    fn duration_tick() {
279        assert_eq!(1 / 1000, 0);
280        assert_eq!(1_u32.div(1000), 0);
281        assert_eq!(1_u32.div_ceil(1000), 1);
282
283        let dur: MicrosDurationU32 = 1.micros();
284        assert_eq!(dur.ticks(), 1);
285
286        let dur: MicrosDurationU32 = 1.millis();
287        assert_eq!(dur.ticks(), 1000);
288        assert_eq!(dur.to_millis(), 1);
289
290        let dur: MillisDurationU32 = 1.millis();
291        assert_eq!(dur.ticks(), 1);
292        assert_eq!(dur.to_micros(), 1000);
293
294        let dur: MillisDurationU32 = 1.micros();
295        assert_eq!(dur.ticks(), 0);
296
297        let dur: MillisDurationU32 = 1.micros_at_least();
298        assert_eq!(dur.ticks(), 1);
299
300        let dur: MicrosDurationU32 = 1.micros();
301        assert_eq!(dur.to_millis(), 0);
302        let dur: MillisDurationU32 = dur.ticks().micros_at_least();
303        assert_eq!(dur.ticks(), 1);
304    }
305
306    #[test]
307    fn rate_tick() {
308        let r: KilohertzU32 = 1.MHz();
309        assert_eq!(r.raw(), 1_000);
310        let f: u32 = r.to_Hz();
311        assert_eq!(f, 1_000_000);
312    }
313}