timeout_trait/
tick_impl.rs1use 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 #[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}