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::cell::Cell;
125    use fugit::{ExtU32, KilohertzU32, NanosDurationU32, RateExtU32};
126
127    static TICK_SOURCE: critical_section::Mutex<Cell<u32>> =
128        critical_section::Mutex::new(Cell::new(0));
129
130    #[derive(Clone, Copy)]
131    pub struct MockInstant {
132        tick: u32,
133    }
134
135    impl MockInstant {
136        fn add_time(t: NanosDurationU32) {
137            let tick = t.to_nanos();
138            critical_section::with(|cs| {
139                let v = TICK_SOURCE.borrow(cs).get().wrapping_add(tick);
140                TICK_SOURCE.borrow(cs).set(v);
141            })
142        }
143    }
144
145    impl TickInstant for MockInstant {
146        fn frequency() -> KilohertzU32 {
147            1000.MHz()
148        }
149
150        fn now() -> Self {
151            critical_section::with(|cs| Self {
152                tick: TICK_SOURCE.borrow(cs).get(),
153            })
154        }
155
156        fn tick_since(self, earlier: Self) -> u32 {
157            self.tick.wrapping_sub(earlier.tick)
158        }
159    }
160
161    #[test]
162    fn tick_timeout() {
163        let now = MockInstant::now();
164        assert_eq!(now.tick_elapsed(), 0);
165        MockInstant::add_time(10.nanos());
166        assert_eq!(now.tick_elapsed(), 10);
167        MockInstant::add_time(1.millis());
168        assert_eq!(now.tick_elapsed(), 1_000_010);
169        MockInstant::add_time(10.micros());
170        let now2 = MockInstant::now();
171        assert_eq!(now2.tick_since(now), 1_010_010);
172        MockInstant::add_time(10.micros());
173        let now3 = MockInstant::now();
174        assert_eq!(now3.tick_since(now2), 10_000);
175
176        let mut t = TickTimeoutNs::<MockInstant>::new().start_ns(100);
177        assert!(!t.timeout());
178        MockInstant::add_time(10.nanos());
179        assert!(!t.timeout());
180        MockInstant::add_time(90.nanos());
181        assert!(t.timeout());
182        assert!(!t.timeout());
183        MockInstant::add_time(100.nanos());
184        assert!(t.timeout());
185
186        MockInstant::add_time(90.nanos());
187        assert!(!t.timeout());
188        t.restart();
189        MockInstant::add_time(90.nanos());
190        assert!(!t.timeout());
191        MockInstant::add_time(10.nanos());
192        assert!(t.timeout());
193
194        let mut t = TickTimeoutNs::<MockInstant>::new().start_us(100);
195        assert!(!t.timeout());
196        MockInstant::add_time(10.micros());
197        assert!(!t.timeout());
198        MockInstant::add_time(90.micros());
199        assert!(t.timeout());
200        assert!(!t.timeout());
201        MockInstant::add_time(100.micros());
202        assert!(t.timeout());
203
204        MockInstant::add_time(90.micros());
205        assert!(!t.timeout());
206        t.restart();
207        MockInstant::add_time(90.micros());
208        assert!(!t.timeout());
209        MockInstant::add_time(10.micros());
210        assert!(t.timeout());
211
212        let mut t = TickTimeoutNs::<MockInstant>::new().start_ms(100);
213        assert!(!t.timeout());
214        MockInstant::add_time(10.millis());
215        assert!(!t.timeout());
216        MockInstant::add_time(90.millis());
217        assert!(t.timeout());
218        assert!(!t.timeout());
219        MockInstant::add_time(100.millis());
220        assert!(t.timeout());
221
222        MockInstant::add_time(90.millis());
223        assert!(!t.timeout());
224        t.restart();
225        MockInstant::add_time(90.millis());
226        assert!(!t.timeout());
227        MockInstant::add_time(10.millis());
228        assert!(t.timeout());
229
230        let mut count = 0;
231        let t = TickTimeoutNs::<MockInstant>::new();
232        assert!(t.ns_with(100, || {
233            MockInstant::add_time(10.nanos());
234            count += 1;
235            true
236        }));
237        assert_eq!(count, 10);
238
239        let t = TickTimeoutState::<MockInstant>::new_us(40_000_000);
240        assert_eq!(t.round, 32);
241        assert_eq!(t.timeout_tick, 1_250_000_000);
242
243        let mut t = TickTimeoutState::<MockInstant>::new_ms(40_000);
244        assert_eq!(t.round, 32);
245        assert_eq!(t.timeout_tick, 1_250_000_000);
246
247        assert!(!t.timeout());
248
249        for _ in 0..40 {
250            MockInstant::add_time(999.millis());
251            assert!(!t.timeout());
252        }
253        assert_eq!(t.elapsed_round, 31);
254        assert!(t.elapsed_tick > 0);
255        MockInstant::add_time(100.millis());
256        assert!(t.timeout());
257        assert_eq!(t.elapsed_round, 0);
258        assert!(!t.timeout());
259
260        for _ in 0..39 {
261            MockInstant::add_time(999.millis());
262            assert!(!t.timeout());
263        }
264        t.restart();
265        for _ in 0..40 {
266            MockInstant::add_time(999.millis());
267            assert!(!t.timeout());
268        }
269        MockInstant::add_time(100.millis());
270        assert!(t.timeout());
271    }
272}
273
274#[cfg(test)]
275mod tests_fugit {
276    use core::ops::Div;
277    use fugit::{
278        ExtU32, ExtU32Ceil, KilohertzU32, MicrosDurationU32, MillisDurationU32, RateExtU32,
279    };
280
281    #[test]
282    fn duration_tick() {
283        assert_eq!(1 / 1000, 0);
284        assert_eq!(1_u32.div(1000), 0);
285        assert_eq!(1_u32.div_ceil(1000), 1);
286
287        let dur: MicrosDurationU32 = 1.micros();
288        assert_eq!(dur.ticks(), 1);
289
290        let dur: MicrosDurationU32 = 1.millis();
291        assert_eq!(dur.ticks(), 1000);
292        assert_eq!(dur.to_millis(), 1);
293
294        let dur: MillisDurationU32 = 1.millis();
295        assert_eq!(dur.ticks(), 1);
296        assert_eq!(dur.to_micros(), 1000);
297
298        let dur: MillisDurationU32 = 1.micros();
299        assert_eq!(dur.ticks(), 0);
300
301        let dur: MillisDurationU32 = 1.micros_at_least();
302        assert_eq!(dur.ticks(), 1);
303
304        let dur: MicrosDurationU32 = 1.micros();
305        assert_eq!(dur.to_millis(), 0);
306        let dur: MillisDurationU32 = dur.ticks().micros_at_least();
307        assert_eq!(dur.ticks(), 1);
308    }
309
310    #[test]
311    fn rate_tick() {
312        let r: KilohertzU32 = 1.MHz();
313        assert_eq!(r.raw(), 1_000);
314        let f: u32 = r.to_Hz();
315        assert_eq!(f, 1_000_000);
316    }
317}