127 lines
3.3 KiB
Rust
127 lines
3.3 KiB
Rust
//! 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: Send> 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 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
|
|
})
|
|
}
|
|
}
|