os_trait/
notifier_impls.rs

1use crate::{fugit::MicrosDurationU32, notifier::*, *};
2use core::{
3    marker::PhantomData,
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7#[derive(Default, Clone)]
8pub struct FakeNotifier;
9
10impl NotifyBuilder for FakeNotifier {
11    fn build() -> (impl Notifier, impl NotifyWaiter) {
12        (Self {}, Self {})
13    }
14
15    fn build_isr() -> (impl NotifierIsr, impl NotifyWaiter) {
16        (Self {}, Self {})
17    }
18}
19
20impl Notifier for FakeNotifier {
21    fn notify(&self) -> bool {
22        true
23    }
24}
25
26impl NotifierIsr for FakeNotifier {
27    fn notify_from_isr(&self) -> bool {
28        true
29    }
30}
31
32impl NotifyWaiter for FakeNotifier {
33    fn wait(&self, _timeout: MicrosDurationU32) -> bool {
34        true
35    }
36}
37
38// ------------------------------------------------------------------
39
40/// This [`Notifier`] implementation is for unit test
41pub struct AtomicNotifier<OS> {
42    flag: Arc<AtomicBool>,
43    _os: PhantomData<OS>,
44}
45
46impl<OS: OsInterface> Clone for AtomicNotifier<OS> {
47    fn clone(&self) -> Self {
48        Self {
49            flag: Arc::clone(&self.flag),
50            _os: PhantomData,
51        }
52    }
53}
54
55impl<OS: OsInterface> AtomicNotifier<OS> {
56    pub fn new() -> (Self, impl NotifyWaiter) {
57        let s = Self {
58            flag: Arc::new(AtomicBool::new(false)),
59            _os: PhantomData,
60        };
61        let r = AtomicNotifyReceiver::<OS> {
62            flag: Arc::clone(&s.flag),
63            _os: PhantomData,
64        };
65        (s, r)
66    }
67}
68
69impl<OS: OsInterface> NotifyBuilder for AtomicNotifier<OS> {
70    fn build() -> (impl Notifier, impl NotifyWaiter) {
71        Self::new()
72    }
73
74    fn build_isr() -> (impl NotifierIsr, impl NotifyWaiter) {
75        Self::new()
76    }
77}
78
79impl<OS: OsInterface> Notifier for AtomicNotifier<OS> {
80    fn notify(&self) -> bool {
81        self.flag.store(true, Ordering::Release);
82        true
83    }
84}
85
86impl<OS: OsInterface> NotifierIsr for AtomicNotifier<OS> {
87    fn notify_from_isr(&self) -> bool {
88        self.flag.store(true, Ordering::Release);
89        true
90    }
91}
92
93pub struct AtomicNotifyReceiver<OS> {
94    flag: Arc<AtomicBool>,
95    _os: PhantomData<OS>,
96}
97
98impl<OS: OsInterface> NotifyWaiter for AtomicNotifyReceiver<OS> {
99    fn wait(&self, timeout: MicrosDurationU32) -> bool {
100        let mut t = OS::Timeout::start_us(timeout.to_micros());
101        while !t.timeout() {
102            if self
103                .flag
104                .compare_exchange(true, false, Ordering::SeqCst, Ordering::Acquire)
105                .is_ok()
106            {
107                return true;
108            }
109            OS::yield_thread();
110        }
111        false
112    }
113}
114
115// ------------------------------------------------------------------
116
117#[cfg(feature = "std")]
118pub use std_impl::*;
119#[cfg(feature = "std")]
120mod std_impl {
121    use super::*;
122    use std::sync::{
123        Arc,
124        atomic::{AtomicBool, Ordering},
125    };
126    use std::time::Instant;
127
128    /// This implementation is only for unit testing.
129    #[derive(Clone)]
130    pub struct StdNotifier {
131        flag: Arc<AtomicBool>,
132    }
133
134    impl StdNotifier {
135        pub fn new() -> (Self, StdNotifyWaiter) {
136            let s = Self {
137                flag: Arc::new(AtomicBool::new(false)),
138            };
139            let r = StdNotifyWaiter {
140                flag: Arc::clone(&s.flag),
141            };
142            (s, r)
143        }
144    }
145
146    impl NotifyBuilder for StdNotifier {
147        fn build() -> (impl Notifier, impl NotifyWaiter) {
148            Self::new()
149        }
150
151        fn build_isr() -> (impl NotifierIsr, impl NotifyWaiter) {
152            Self::new()
153        }
154    }
155
156    impl Notifier for StdNotifier {
157        fn notify(&self) -> bool {
158            self.flag.store(true, Ordering::Release);
159            true
160        }
161    }
162
163    impl NotifierIsr for StdNotifier {
164        fn notify_from_isr(&self) -> bool {
165            self.flag.store(true, Ordering::Release);
166            true
167        }
168    }
169
170    /// This implementation is only for unit testing.
171    pub struct StdNotifyWaiter {
172        flag: Arc<AtomicBool>,
173    }
174
175    impl NotifyWaiter for StdNotifyWaiter {
176        fn wait(&self, timeout: MicrosDurationU32) -> bool {
177            let now = Instant::now();
178            while now.elapsed().as_micros() < timeout.to_micros().into() {
179                if self
180                    .flag
181                    .compare_exchange(true, false, Ordering::SeqCst, Ordering::Acquire)
182                    .is_ok()
183                {
184                    return true;
185                }
186                std::thread::yield_now();
187            }
188            false
189        }
190    }
191
192    #[cfg(test)]
193    mod tests {
194        use super::*;
195        use fugit::ExtU32;
196        use std::thread;
197
198        #[test]
199        fn notify() {
200            let (n, w) = StdNotifier::new();
201            assert!(!w.wait(1.millis()));
202            n.notify();
203            assert!(w.wait(1.millis()));
204
205            let mut handles = vec![];
206
207            let n2 = n.clone();
208
209            handles.push(thread::spawn(move || {
210                assert!(w.wait(100.millis()));
211                assert!(w.wait(100.millis()));
212            }));
213
214            handles.push(thread::spawn(move || {
215                assert!(n.notify());
216            }));
217
218            handles.push(thread::spawn(move || {
219                assert!(n2.notify());
220            }));
221
222            for h in handles {
223                h.join().unwrap();
224            }
225        }
226    }
227}