embassy_sync/
signal.rs

1//! A synchronization primitive for passing the latest value to a task.
2use core::cell::Cell;
3use core::future::{poll_fn, Future};
4use core::task::{Context, Poll, Waker};
5
6use crate::blocking_mutex::raw::RawMutex;
7use crate::blocking_mutex::Mutex;
8
9/// Single-slot signaling primitive for a _single_ consumer.
10///
11/// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except
12/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead
13/// of waiting for the receiver to pop the previous value.
14///
15/// It is useful for sending data between tasks when the receiver only cares about
16/// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state"
17/// updates.
18///
19/// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead.
20/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead.
21///
22/// Signals are generally declared as `static`s and then borrowed as required.
23///
24/// ```
25/// use embassy_sync::signal::Signal;
26/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
27///
28/// enum SomeCommand {
29///   On,
30///   Off,
31/// }
32///
33/// static SOME_SIGNAL: Signal<CriticalSectionRawMutex, SomeCommand> = Signal::new();
34/// ```
35pub struct Signal<M, T>
36where
37    M: RawMutex,
38{
39    state: Mutex<M, Cell<State<T>>>,
40}
41
42#[derive(Debug)]
43enum State<T> {
44    None,
45    Waiting(Waker),
46    Signaled(T),
47}
48
49impl<M, T> Signal<M, T>
50where
51    M: RawMutex,
52{
53    /// Create a new `Signal`.
54    pub const fn new() -> Self {
55        Self {
56            state: Mutex::new(Cell::new(State::None)),
57        }
58    }
59}
60
61impl<M, T> Default for Signal<M, T>
62where
63    M: RawMutex,
64{
65    fn default() -> Self {
66        Self::new()
67    }
68}
69
70impl<M, T> Signal<M, T>
71where
72    M: RawMutex,
73{
74    /// Mark this Signal as signaled.
75    pub fn signal(&self, val: T) {
76        self.state.lock(|cell| {
77            let state = cell.replace(State::Signaled(val));
78            if let State::Waiting(waker) = state {
79                waker.wake();
80            }
81        })
82    }
83
84    /// Remove the queued value in this `Signal`, if any.
85    pub fn reset(&self) {
86        self.state.lock(|cell| cell.set(State::None));
87    }
88
89    fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
90        self.state.lock(|cell| {
91            let state = cell.replace(State::None);
92            match state {
93                State::None => {
94                    cell.set(State::Waiting(cx.waker().clone()));
95                    Poll::Pending
96                }
97                State::Waiting(w) if w.will_wake(cx.waker()) => {
98                    cell.set(State::Waiting(w));
99                    Poll::Pending
100                }
101                State::Waiting(w) => {
102                    cell.set(State::Waiting(cx.waker().clone()));
103                    w.wake();
104                    Poll::Pending
105                }
106                State::Signaled(res) => Poll::Ready(res),
107            }
108        })
109    }
110
111    /// Future that completes when this Signal has been signaled, taking the value out of the signal.
112    pub fn wait(&self) -> impl Future<Output = T> + '_ {
113        poll_fn(move |cx| self.poll_wait(cx))
114    }
115
116    /// non-blocking method to try and take the signal value.
117    pub fn try_take(&self) -> Option<T> {
118        self.state.lock(|cell| {
119            let state = cell.replace(State::None);
120            match state {
121                State::Signaled(res) => Some(res),
122                state => {
123                    cell.set(state);
124                    None
125                }
126            }
127        })
128    }
129
130    /// non-blocking method to check whether this signal has been signaled. This does not clear the signal.  
131    pub fn signaled(&self) -> bool {
132        self.state.lock(|cell| {
133            let state = cell.replace(State::None);
134
135            let res = matches!(state, State::Signaled(_));
136
137            cell.set(state);
138
139            res
140        })
141    }
142}