embassy_sync/signal.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
//! 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<CriticalSectionRawMutex, SomeCommand> = Signal::new();
/// ```
pub struct Signal<M, T>
where
M: RawMutex,
{
state: Mutex<M, Cell<State<T>>>,
}
enum State<T> {
None,
Waiting(Waker),
Signaled(T),
}
impl<M, T> Signal<M, T>
where
M: RawMutex,
{
/// Create a new `Signal`.
pub const fn new() -> Self {
Self {
state: Mutex::new(Cell::new(State::None)),
}
}
}
impl<M, T> Default for Signal<M, T>
where
M: RawMutex,
{
fn default() -> Self {
Self::new()
}
}
impl<M, T> Signal<M, T>
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<T> {
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<Output = T> + '_ {
poll_fn(move |cx| self.poll_wait(cx))
}
/// non-blocking method to try and take the signal value.
pub fn try_take(&self) -> Option<T> {
self.state.lock(|cell| {
let state = cell.replace(State::None);
match state {
State::Signaled(res) => Some(res),
state => {
cell.set(state);
None
}
}
})
}
/// non-blocking method to check whether this signal has been signaled. This does not clear the signal.
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
})
}
}