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