Skip to main content

rtic_sync/
signal.rs

1//! A "latest only" value store with unlimited writers and async waiting.
2
3use core::{cell::UnsafeCell, future::poll_fn, task::Poll};
4use portable_atomic::{
5    AtomicBool,
6    Ordering::{AcqRel, Release},
7};
8use rtic_common::waker_registration::CriticalSectionWakerRegistration;
9
10/// Basically an Option but for indicating
11/// whether the store has been set or not
12#[derive(Clone, Copy)]
13pub(crate) enum Store<T> {
14    Set(T),
15    Unset,
16}
17
18/// A "latest only" value store with unlimited writers and async waiting.
19pub struct Signal<T: Copy> {
20    pub(crate) waker: CriticalSectionWakerRegistration,
21    pub(crate) store: UnsafeCell<Store<T>>,
22    pub(crate) seen: AtomicBool,
23    pub(crate) already_split: AtomicBool,
24}
25
26impl<T> core::fmt::Debug for Signal<T>
27where
28    T: core::marker::Copy,
29{
30    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31        fmt.write_fmt(format_args!(
32            "Signal<{}>{{ .. }}",
33            core::any::type_name::<T>()
34        ))
35    }
36}
37
38impl<T: Copy> Default for Signal<T> {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44unsafe impl<T: Copy> Send for Signal<T> {}
45unsafe impl<T: Copy> Sync for Signal<T> {}
46
47impl<T: Copy> Signal<T> {
48    /// Create a new signal.
49    pub const fn new() -> Self {
50        Self {
51            waker: CriticalSectionWakerRegistration::new(),
52            store: UnsafeCell::new(Store::Unset),
53            seen: AtomicBool::new(false),
54            already_split: AtomicBool::new(false),
55        }
56    }
57
58    /// Split the signal into a writer and reader.
59    pub fn split(&self) -> (SignalWriter<'_, T>, SignalReader<'_, T>) {
60        if self.already_split.swap(true, AcqRel) {
61            panic!("`Signal` cannot be split twice");
62        }
63        (SignalWriter { parent: self }, SignalReader { parent: self })
64    }
65}
66
67/// Facilitates the writing of values to a Signal.
68#[derive(Clone)]
69pub struct SignalWriter<'a, T: Copy> {
70    pub(crate) parent: &'a Signal<T>,
71}
72
73impl<T> core::fmt::Debug for SignalWriter<'_, T>
74where
75    T: core::marker::Copy,
76{
77    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78        fmt.write_fmt(format_args!(
79            "SignalWriter<{}>{{ .. }}",
80            core::any::type_name::<T>()
81        ))
82    }
83}
84
85impl<T: Copy> SignalWriter<'_, T> {
86    /// Write a raw Store value to the Signal.
87    pub(crate) fn write_inner(&mut self, value: Store<T>) {
88        critical_section::with(|_| {
89            // SAFETY: in a cs: exclusive access
90            unsafe { self.parent.store.get().replace(value) };
91        });
92        self.parent.seen.store(false, Release);
93
94        self.parent.waker.wake();
95    }
96
97    /// Write a value to the Signal.
98    pub fn write(&mut self, value: T) {
99        self.write_inner(Store::Set(value));
100    }
101
102    /// Clear the stored value in the Signal (if any).
103    pub fn clear(&mut self) {
104        self.write_inner(Store::Unset);
105    }
106}
107
108/// Facilitates the async reading of values from the Signal.
109pub struct SignalReader<'a, T: Copy> {
110    parent: &'a Signal<T>,
111}
112
113impl<T> core::fmt::Debug for SignalReader<'_, T>
114where
115    T: core::marker::Copy,
116{
117    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        fmt.write_fmt(format_args!(
119            "SignalReader<{}>{{ .. }}",
120            core::any::type_name::<T>()
121        ))
122    }
123}
124
125impl<T: Copy> SignalReader<'_, T> {
126    /// Immediately read and evict the latest value stored in the Signal.
127    fn take(&mut self) -> Store<T> {
128        critical_section::with(|_| {
129            // SAFETY: in a cs: exclusive access
130            unsafe { self.parent.store.get().replace(Store::Unset) }
131        })
132    }
133
134    /// Returns a pending value if present, or None if no value is available.
135    ///
136    /// Upon read, the stored value is evicted.
137    pub fn try_read(&mut self) -> Option<T> {
138        match self.take() {
139            Store::Unset => None,
140            Store::Set(value) => Some(value),
141        }
142    }
143
144    /// Wait for a new value to be written and read it.
145    ///
146    /// If a value is already pending it will be returned immediately.
147    ///
148    /// Upon read, the stored value is evicted.
149    pub async fn wait(&mut self) -> T {
150        poll_fn(|ctx| {
151            self.parent.waker.register(ctx.waker());
152            match self.take() {
153                Store::Unset => Poll::Pending,
154                Store::Set(value) => Poll::Ready(value),
155            }
156        })
157        .await
158    }
159
160    /// Wait for a new value to be written and read it.
161    ///
162    /// If a value is already pending, it will be evicted and a new
163    /// value must be written for the wait to resolve.
164    ///
165    /// Upon read, the stored value is evicted.
166    pub async fn wait_fresh(&mut self) -> T {
167        self.take();
168        self.wait().await
169    }
170}
171
172/// Creates a split signal with `'static` lifetime.
173#[macro_export]
174macro_rules! make_signal {
175    ( $T:ty ) => {{
176        static SIGNAL: $crate::signal::Signal<$T> = $crate::signal::Signal::new();
177
178        SIGNAL.split()
179    }};
180}
181
182#[cfg(test)]
183#[cfg(not(loom))]
184mod tests {
185    use super::*;
186    use static_cell::StaticCell;
187
188    #[test]
189    fn empty() {
190        let (_writer, mut reader) = make_signal!(u32);
191
192        assert!(reader.try_read().is_none());
193    }
194
195    #[test]
196    fn ping_pong() {
197        let (mut writer, mut reader) = make_signal!(u32);
198
199        writer.write(0xde);
200        assert!(reader.try_read().is_some_and(|value| value == 0xde));
201    }
202
203    #[test]
204    fn latest() {
205        let (mut writer, mut reader) = make_signal!(u32);
206
207        writer.write(0xde);
208        writer.write(0xad);
209        writer.write(0xbe);
210        writer.write(0xef);
211        assert!(reader.try_read().is_some_and(|value| value == 0xef));
212    }
213
214    #[test]
215    fn consumption() {
216        let (mut writer, mut reader) = make_signal!(u32);
217
218        writer.write(0xaa);
219        assert!(reader.try_read().is_some_and(|value| value == 0xaa));
220        assert!(reader.try_read().is_none());
221    }
222
223    #[test]
224    #[should_panic]
225    fn no_multi_split() {
226        fn scary_helper<'a>() -> (SignalWriter<'a, u32>, SignalReader<'a, u32>) {
227            make_signal!(u32)
228        }
229        let (mut _writer1, mut _reader1) = scary_helper();
230        let (mut _writer2, mut _reader2) = scary_helper();
231    }
232
233    #[tokio::test]
234    async fn pending() {
235        let (mut writer, mut reader) = make_signal!(u32);
236
237        writer.write(0xaa);
238
239        assert_eq!(reader.wait().await, 0xaa);
240    }
241
242    #[tokio::test]
243    async fn waiting() {
244        static READER: StaticCell<SignalReader<u32>> = StaticCell::new();
245        let (mut writer, reader) = make_signal!(u32);
246
247        writer.write(0xaa);
248
249        let reader = READER.init(reader);
250        let handle = tokio::spawn(reader.wait_fresh());
251
252        tokio::task::yield_now().await; // encourage tokio executor to poll reader future
253        assert!(!handle.is_finished()); // verify reader future did not resolve after poll
254
255        writer.write(0xab);
256
257        assert!(handle.await.is_ok_and(|value| value == 0xab));
258    }
259}