timeout_trait/
tick_impl.rs

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