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}