rtic_common/waker_registration.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
//! Waker registration utility.
use core::cell::UnsafeCell;
use core::task::Waker;
/// A critical section based waker handler.
pub struct CriticalSectionWakerRegistration {
waker: UnsafeCell<Option<Waker>>,
}
unsafe impl Send for CriticalSectionWakerRegistration {}
unsafe impl Sync for CriticalSectionWakerRegistration {}
impl CriticalSectionWakerRegistration {
/// Create a new waker registration.
pub const fn new() -> Self {
Self {
waker: UnsafeCell::new(None),
}
}
/// Register a waker.
/// This will overwrite the previous waker if there was one.
pub fn register(&self, new_waker: &Waker) {
critical_section::with(|_| {
// SAFETY: This access is protected by the critical section.
let self_waker = unsafe { &mut *self.waker.get() };
// From embassy
// https://github.com/embassy-rs/embassy/blob/b99533607ceed225dd12ae73aaa9a0d969a7365e/embassy-sync/src/waitqueue/waker.rs#L59-L61
match self_waker {
// Optimization: If both the old and new Wakers wake the same task, we can simply
// keep the old waker, skipping the clone. (In most executor implementations,
// cloning a waker is somewhat expensive, comparable to cloning an Arc).
Some(ref w2) if (w2.will_wake(new_waker)) => {}
_ => {
// clone the new waker and store it
if let Some(old_waker) = core::mem::replace(self_waker, Some(new_waker.clone()))
{
// We had a waker registered for another task. Wake it, so the other task can
// reregister itself if it's still interested.
//
// If two tasks are waiting on the same thing concurrently, this will cause them
// to wake each other in a loop fighting over this WakerRegistration. This wastes
// CPU but things will still work.
//
// If the user wants to have two tasks waiting on the same thing they should use
// a more appropriate primitive that can store multiple wakers.
old_waker.wake()
}
}
}
});
}
/// Wake the waker.
pub fn wake(&self) {
critical_section::with(|_| {
// SAFETY: This access is protected by the critical section.
let self_waker = unsafe { &mut *self.waker.get() };
if let Some(waker) = self_waker.take() {
waker.wake()
}
});
}
}
impl Default for CriticalSectionWakerRegistration {
fn default() -> Self {
Self::new()
}
}