//! A synchronization primitive for passing the latest value to a task. use core::cell::Cell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; /// Single-slot signaling primitive. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except /// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead /// of waiting for the receiver to pop the previous value. /// /// It is useful for sending data between tasks when the receiver only cares about /// the latest data, and therefore it's fine to "lose" messages. This is often the case for "state" /// updates. /// /// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead. /// /// Signals are generally declared as `static`s and then borrowed as required. /// /// ``` /// use embassy_sync::signal::Signal; /// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; /// /// enum SomeCommand { /// On, /// Off, /// } /// /// static SOME_SIGNAL: Signal = Signal::new(); /// ``` pub struct Signal where M: RawMutex, { state: Mutex>>, } enum State { None, Waiting(Waker), Signaled(T), } impl Signal where M: RawMutex, { /// Create a new `Signal`. pub const fn new() -> Self { Self { state: Mutex::new(Cell::new(State::None)), } } } impl Default for Signal where M: RawMutex, { fn default() -> Self { Self::new() } } impl Signal where M: RawMutex, { /// Mark this Signal as signaled. pub fn signal(&self, val: T) { self.state.lock(|cell| { let state = cell.replace(State::Signaled(val)); if let State::Waiting(waker) = state { waker.wake(); } }) } /// Remove the queued value in this `Signal`, if any. pub fn reset(&self) { self.state.lock(|cell| cell.set(State::None)); } fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { self.state.lock(|cell| { let state = cell.replace(State::None); match state { State::None => { cell.set(State::Waiting(cx.waker().clone())); Poll::Pending } State::Waiting(w) if w.will_wake(cx.waker()) => { cell.set(State::Waiting(w)); Poll::Pending } State::Waiting(w) => { cell.set(State::Waiting(cx.waker().clone())); w.wake(); Poll::Pending } State::Signaled(res) => Poll::Ready(res), } }) } /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { poll_fn(move |cx| self.poll_wait(cx)) } /// non-blocking method to check whether this signal has been signaled. pub fn signaled(&self) -> bool { self.state.lock(|cell| { let state = cell.replace(State::None); let res = matches!(state, State::Signaled(_)); cell.set(state); res }) } }