rtic_common/
waker_registration.rs

1//! Waker registration utility.
2
3use core::cell::UnsafeCell;
4use core::task::Waker;
5
6/// A critical section based waker handler.
7pub struct CriticalSectionWakerRegistration {
8    waker: UnsafeCell<Option<Waker>>,
9}
10
11unsafe impl Send for CriticalSectionWakerRegistration {}
12unsafe impl Sync for CriticalSectionWakerRegistration {}
13
14impl CriticalSectionWakerRegistration {
15    /// Create a new waker registration.
16    pub const fn new() -> Self {
17        Self {
18            waker: UnsafeCell::new(None),
19        }
20    }
21
22    /// Register a waker.
23    /// This will overwrite the previous waker if there was one.
24    pub fn register(&self, new_waker: &Waker) {
25        critical_section::with(|_| {
26            // SAFETY: This access is protected by the critical section.
27            let self_waker = unsafe { &mut *self.waker.get() };
28
29            // From embassy
30            // https://github.com/embassy-rs/embassy/blob/b99533607ceed225dd12ae73aaa9a0d969a7365e/embassy-sync/src/waitqueue/waker.rs#L59-L61
31            match self_waker {
32                // Optimization: If both the old and new Wakers wake the same task, we can simply
33                // keep the old waker, skipping the clone. (In most executor implementations,
34                // cloning a waker is somewhat expensive, comparable to cloning an Arc).
35                Some(ref w2) if (w2.will_wake(new_waker)) => {}
36                _ => {
37                    // clone the new waker and store it
38                    if let Some(old_waker) = self_waker.replace(new_waker.clone()) {
39                        // We had a waker registered for another task. Wake it, so the other task can
40                        // reregister itself if it's still interested.
41                        //
42                        // If two tasks are waiting on the same thing concurrently, this will cause them
43                        // to wake each other in a loop fighting over this WakerRegistration. This wastes
44                        // CPU but things will still work.
45                        //
46                        // If the user wants to have two tasks waiting on the same thing they should use
47                        // a more appropriate primitive that can store multiple wakers.
48                        old_waker.wake()
49                    }
50                }
51            }
52        });
53    }
54
55    /// Wake the waker.
56    pub fn wake(&self) {
57        critical_section::with(|_| {
58            // SAFETY: This access is protected by the critical section.
59            let self_waker = unsafe { &mut *self.waker.get() };
60            if let Some(waker) = self_waker.take() {
61                waker.wake()
62            }
63        });
64    }
65}
66
67impl Default for CriticalSectionWakerRegistration {
68    fn default() -> Self {
69        Self::new()
70    }
71}