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