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