bare_sync/
signal.rs

1//! A synchronization primitive for passing the latest value to a task.
2use core::cell::{Cell, UnsafeCell};
3use core::mem::MaybeUninit;
4
5use embassy_sync::blocking_mutex::raw::RawMutex;
6use embassy_sync::blocking_mutex::Mutex;
7
8/// Single-slot signaling primitive for a _single_ consumer.
9///
10/// This is similar to a [`Channel`](embassy_sync::channel::Channel) with a buffer size of 1, except
11/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead
12/// of waiting for the receiver to pop the previous value.
13///
14/// It is useful for sending data between tasks when the receiver only cares about
15/// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state"
16/// updates.
17///
18/// For more advanced use cases, you might want to use [`Channel`](embassy_sync::channel::Channel) instead.
19/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead.
20///
21/// Signals are generally declared as `static`s and then borrowed as required.
22///
23/// ```
24/// use embedded_sync::signal::Signal;
25/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
26///
27/// enum SomeCommand {
28///   On,
29///   Off,
30/// }
31///
32/// static SOME_SIGNAL: Signal<CriticalSectionRawMutex, SomeCommand> = Signal::new();
33/// ```
34pub struct Signal<M, T>
35where
36    M: RawMutex,
37{
38    inner: Mutex<M, SignalInner<T>>,
39}
40
41struct SignalInner<T> {
42    value: UnsafeCell<MaybeUninit<T>>,
43    signaled: Cell<bool>,
44}
45
46impl<T> SignalInner<T> {
47    #[inline]
48    fn write(&self, val: T) {
49        unsafe { self.value.get().write(MaybeUninit::new(val)) };
50        self.signaled.set(true);
51    }
52
53    #[inline]
54    fn take(&self) -> T {
55        self.signaled.set(false);
56        unsafe { self.value.get().read().assume_init() }
57    }
58
59    #[inline]
60    fn is_signaled(&self) -> bool {
61        self.signaled.get()
62    }
63}
64
65impl<M, T> Signal<M, T>
66where
67    M: RawMutex,
68{
69    /// Create a new `Signal`.
70    pub const fn new() -> Self {
71        Self {
72            inner: Mutex::new(SignalInner {
73                value: UnsafeCell::new(MaybeUninit::zeroed()),
74                signaled: Cell::new(false),
75            }),
76        }
77    }
78}
79
80impl<M, T> Default for Signal<M, T>
81where
82    M: RawMutex,
83{
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl<M, T> Signal<M, T>
90where
91    M: RawMutex,
92{
93    /// Mark this Signal as signaled.
94    pub fn signal(&self, val: T) {
95        self.inner.lock(|signal| {
96            signal.write(val);
97        })
98    }
99
100    /// Remove the queued value in this `Signal`, if any.
101    pub fn reset(&self) {
102        self.try_take();
103    }
104
105    /// non-blocking method to try and take the signal value.
106    pub fn try_take(&self) -> Option<T> {
107        self.inner.lock(|signal| {
108            if signal.is_signaled() {
109                Some(signal.take())
110            } else {
111                None
112            }
113        })
114    }
115
116    /// non-blocking method to check whether this signal has been signaled. This does not clear the signal.  
117    pub fn signaled(&self) -> bool {
118        self.inner.lock(|signal| signal.is_signaled())
119    }
120}