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 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 #[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}