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.
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///
21/// Signals are generally declared as `static`s and then borrowed as required.
22///
23/// ```
24/// use embassy_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    state: Mutex<M, Cell<State<T>>>,
39}
40
41enum State<T> {
42    None,
43    Waiting(Waker),
44    Signaled(T),
45}
46
47impl<M, T> Signal<M, T>
48where
49    M: RawMutex,
50{
51    /// Create a new `Signal`.
52    pub const fn new() -> Self {
53        Self {
54            state: Mutex::new(Cell::new(State::None)),
55        }
56    }
57}
58
59impl<M, T> Default for Signal<M, T>
60where
61    M: RawMutex,
62{
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68impl<M, T> Signal<M, T>
69where
70    M: RawMutex,
71{
72    /// Mark this Signal as signaled.
73    pub fn signal(&self, val: T) {
74        self.state.lock(|cell| {
75            let state = cell.replace(State::Signaled(val));
76            if let State::Waiting(waker) = state {
77                waker.wake();
78            }
79        })
80    }
81
82    /// Remove the queued value in this `Signal`, if any.
83    pub fn reset(&self) {
84        self.state.lock(|cell| cell.set(State::None));
85    }
86
87    fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
88        self.state.lock(|cell| {
89            let state = cell.replace(State::None);
90            match state {
91                State::None => {
92                    cell.set(State::Waiting(cx.waker().clone()));
93                    Poll::Pending
94                }
95                State::Waiting(w) if w.will_wake(cx.waker()) => {
96                    cell.set(State::Waiting(w));
97                    Poll::Pending
98                }
99                State::Waiting(w) => {
100                    cell.set(State::Waiting(cx.waker().clone()));
101                    w.wake();
102                    Poll::Pending
103                }
104                State::Signaled(res) => Poll::Ready(res),
105            }
106        })
107    }
108
109    /// Future that completes when this Signal has been signaled.
110    pub fn wait(&self) -> impl Future<Output = T> + '_ {
111        poll_fn(move |cx| self.poll_wait(cx))
112    }
113
114    /// non-blocking method to try and take the signal value.
115    pub fn try_take(&self) -> Option<T> {
116        self.state.lock(|cell| {
117            let state = cell.replace(State::None);
118            match state {
119                State::Signaled(res) => Some(res),
120                state => {
121                    cell.set(state);
122                    None
123                }
124            }
125        })
126    }
127
128    /// non-blocking method to check whether this signal has been signaled. This does not clear the signal.  
129    pub fn signaled(&self) -> bool {
130        self.state.lock(|cell| {
131            let state = cell.replace(State::None);
132
133            let res = matches!(state, State::Signaled(_));
134
135            cell.set(state);
136
137            res
138        })
139    }
140}