| 1 | //! A synchronization primitive for passing the latest value to a task. |
| 2 | use core::cell::Cell; |
| 3 | use core::future::{poll_fn, Future}; |
| 4 | use core::task::{Context, Poll, Waker}; |
| 5 | |
| 6 | use crate::blocking_mutex::raw::RawMutex; |
| 7 | use 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 | /// ``` |
| 34 | pub struct Signal<M, T> |
| 35 | where |
| 36 | M: RawMutex, |
| 37 | { |
| 38 | state: Mutex<M, Cell<State<T>>>, |
| 39 | } |
| 40 | |
| 41 | enum State<T> { |
| 42 | None, |
| 43 | Waiting(Waker), |
| 44 | Signaled(T), |
| 45 | } |
| 46 | |
| 47 | impl<M, T> Signal<M, T> |
| 48 | where |
| 49 | M: RawMutex, |
| 50 | { |
| 51 | /// Create a new `Signal`. |
| 52 | pub const fn new() -> Self { |
| 53 | Self { |
| 54 | state: Mutex::new(val:Cell::new(State::None)), |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl<M, T> Default for Signal<M, T> |
| 60 | where |
| 61 | M: RawMutex, |
| 62 | { |
| 63 | fn default() -> Self { |
| 64 | Self::new() |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | impl<M, T> Signal<M, T> |
| 69 | where |
| 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 | } |
| 141 | |