waiter_trait/
tick_waiter.rs

1use super::*;
2use core::marker::PhantomData;
3
4/// A [`Waiter`] implementation for embedded system.
5///
6/// # Examples
7///
8/// ```
9/// use std::time::{Duration, Instant};
10/// use waiter_trait::{Waiter, WaiterTime, TickWaiter, StdInterval};
11///
12/// let w = TickWaiter::<Instant, _, _>::ns(
13///     Duration::from_millis(10).as_nanos() as u64,
14///     StdInterval::new(Duration::from_millis(8)),
15///     Duration::from_secs(1).as_nanos() as u32,
16/// );
17///
18/// let mut t = w.start();
19/// assert!(!t.timeout());
20/// assert!(!t.timeout());
21/// assert!(t.timeout());
22///
23/// let mut t = w.start();
24/// assert!(!t.timeout());
25/// assert!(!t.timeout());
26/// t.restart();
27/// assert!(!t.timeout());
28/// assert!(!t.timeout());
29/// assert!(t.timeout());
30/// ```
31pub struct TickWaiter<T, I, N> {
32    timeout_tick: N,
33    interval: I,
34    _t: PhantomData<T>,
35}
36
37impl<T, I> TickWaiter<T, I, u32>
38where
39    T: TickInstant,
40    I: Interval,
41{
42    pub fn us(timeout_us: u32, interval: I, frequency: u32) -> Self {
43        let timeout_tick = (timeout_us as u64)
44            .checked_mul(frequency as u64)
45            .unwrap()
46            .div_ceil(1_000_000);
47        assert!(timeout_tick <= u32::MAX as u64);
48        Self {
49            timeout_tick: timeout_tick as u32,
50            interval,
51            _t: PhantomData,
52        }
53    }
54}
55
56impl<T, I> TickWaiter<T, I, u64>
57where
58    T: TickInstant,
59    I: Interval,
60{
61    /// It can wait longer with a nanosecond timeout.
62    pub fn ns(timeout_ns: u64, interval: I, frequency: u32) -> Self {
63        assert_eq!(frequency % 1_000_000, 0);
64        let timeout_tick = timeout_ns
65            .checked_mul((frequency / 1_000_000) as u64)
66            .unwrap()
67            .div_ceil(1_000);
68        Self {
69            timeout_tick,
70            interval,
71            _t: PhantomData,
72        }
73    }
74}
75
76impl<T, I, N> Waiter for TickWaiter<T, I, N>
77where
78    N: Num,
79    T: TickInstant,
80    I: Interval,
81{
82    fn start(&self) -> impl WaiterTime {
83        TickWaiterTime::<T, I, N> {
84            tick: T::now(),
85            elapsed_tick: N::ZERO,
86            waiter: self,
87        }
88    }
89}
90
91pub struct TickWaiterTime<'a, T: TickInstant, I: Interval, N: Num> {
92    tick: T,
93    elapsed_tick: N,
94    waiter: &'a TickWaiter<T, I, N>,
95}
96
97impl<'a, T, I, N> WaiterTime for TickWaiterTime<'a, T, I, N>
98where
99    N: Num,
100    T: TickInstant,
101    I: Interval,
102{
103    /// Can be reused without calling `restart()`.
104    #[inline]
105    fn timeout(&mut self) -> bool {
106        let now = T::now();
107        self.elapsed_tick = self.elapsed_tick.add_u32(now.tick_since(self.tick));
108        self.tick = now;
109
110        if self.elapsed_tick >= self.waiter.timeout_tick {
111            self.elapsed_tick -= self.waiter.timeout_tick;
112            true
113        } else {
114            self.waiter.interval.interval();
115            false
116        }
117    }
118
119    #[inline(always)]
120    fn restart(&mut self) {
121        self.tick = T::now();
122        self.elapsed_tick = N::ZERO;
123    }
124}
125
126pub trait Num: Sized + Copy + core::cmp::Ord + core::ops::SubAssign {
127    const ZERO: Self;
128    fn add_u32(self, v: u32) -> Self;
129}
130
131impl Num for u32 {
132    const ZERO: Self = 0;
133    fn add_u32(self, v: u32) -> Self {
134        self.saturating_add(v)
135    }
136}
137
138impl Num for u64 {
139    const ZERO: Self = 0u64;
140    fn add_u32(self, v: u32) -> Self {
141        self.saturating_add(v as u64)
142    }
143}