pub struct Notify { /* private fields */ }
Expand description
A lightweight task notification scheme that can be used to safely route events from interrupt handlers to task code.
This is the lowest level inter-task communication type in lilos
.
Any number of tasks can subscribe
to a Notify
. When
notify
is called on it, all those tasks will be awoken
(i.e. their Waker
will be triggered so that they become eligible for
polling), and their subscription is atomically ended.
A Notify
is very small (the size of a pointer), so feel free to create as
many as you like.
It is safe to call notify
from an ISR, so this is the usual method by
which interrupt handlers inform task code of events. Normally a Notify
used in this way is stored in a static
:
static EVENT: Notify = Notify::new();
You can use that style of static
Notify
to sleep waiting for interrupt
conditions in async code. Here’s an example for a made-up but typical UART
driver:
/// Event signal for waking task(s) when data arrives.
static RX_NOT_EMPTY: Notify = Notify::new();
/// UART interrupt handler.
#[interrupt]
fn UART() {
let uart = get_uart_peripheral_somehow();
let control = uart.control.read();
let status = uart.status.read();
if control.rx_irq_enabled() && status.rx_not_empty() {
// Shut off the interrupt source to keep this from reoccurring.
uart.control.modify(|_, w| w.rx_irq_enabled().clear());
// Wake up the task that requested this.
RX_NOT_EMPTY.notify();
}
}
async fn uart_recv(uart: &Uart) -> u8 {
// Enable the rx data interrupt so we get notified.
uart.control.modify(|_, w| w.rx_irq_enabled().set());
// Listen for data, using a predicate to filter out spurious wakes.
RX_NOT_EMPTY.until(|| uart.status.read().rx_not_empty());
UART.data.read()
}
Waker coalescing
A Notify
collects any number of task Waker
s into a fixed-size structure
without heap allocation. It does this by coalescing the Waker
s such that
they may become imprecise: firing the waker for task N may also spuriously
wake task M. (Implementation-wise, this is a matter of collecting a wake
bits mask from the wakers using secret knowledge.)
While this is often not the ideal strategy, it has the advantage that it can be built up cheaply and torn down atomically from interrupt context. (Contrast with e.g. a list of waiting tasks, which is more precise but harder to get right and more expensive at runtime.)
Implementations§
source§impl Notify
impl Notify
sourcepub fn notify(&self)
pub fn notify(&self)
Wakes tasks, at least all those whose waiters have been passed to
subscribe
since the last notify
, possibly more.
sourcepub fn until<'a, 'b, T: TestResult>(
&'a self,
cond: impl FnMut() -> T + 'b
) -> impl Future<Output = T::Output> + 'awhere
'b: 'a,
pub fn until<'a, 'b, T: TestResult>( &'a self, cond: impl FnMut() -> T + 'b ) -> impl Future<Output = T::Output> + 'awhere 'b: 'a,
Repeatedly calls cond
, completing when it passes. In between calls,
subscribes to self
, so that the task will wake less often and leave
CPU available for other things.
This is appropriate if you know that any change to cond
’s result will
be preceded by some task calling self.notify()
.
Cancellation
Cancel safety: Strict.
Dropping this future will drop cond
, and may leave the current task
subscribed to self
(meaning one potential spurious wakeup in the
future is possible).
sourcepub fn until_racy<'a, 'b, T: TestResult>(
&'a self,
cond: impl FnMut() -> T + 'b
) -> impl Future<Output = T::Output> + 'awhere
'b: 'a,
pub fn until_racy<'a, 'b, T: TestResult>( &'a self, cond: impl FnMut() -> T + 'b ) -> impl Future<Output = T::Output> + 'awhere 'b: 'a,
Subscribes to notify
and then calls cond
, completing if it returns
true
. Otherwise, waits and tries again. This is very similar to
until
, and is slightly more expensive, but in exchange it is correct
if the condition may be set asynchronously (i.e. you are running the OS
with preemption enabled).
Cancellation
Cancel safety: Strict.
Dropping this future will drop cond
, and will leave the current task
subscribed to self
(meaning one potential spurious wakeup in the
future is possible).
sourcepub fn until_next(&self) -> impl Future<Output = ()> + '_
pub fn until_next(&self) -> impl Future<Output = ()> + '_
Subscribes to notify
and blocks until the task is awoken. This may
produces spurious wakeups, and is appropriate only when you’re checking
some condition separately. Otherwise, use until
.
Cancellation
Cancel safety: Strict.
Dropping this future will leave the current task subscribed to self
(meaning one potential spurious wakeup in the future is possible).