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) = core::mem::replace(self_waker, Some(new_waker.clone()))
39                    {
40                        // We had a waker registered for another task. Wake it, so the other task can
41                        // reregister itself if it's still interested.
42                        //
43                        // If two tasks are waiting on the same thing concurrently, this will cause them
44                        // to wake each other in a loop fighting over this WakerRegistration. This wastes
45                        // CPU but things will still work.
46                        //
47                        // If the user wants to have two tasks waiting on the same thing they should use
48                        // a more appropriate primitive that can store multiple wakers.
49                        old_waker.wake()
50                    }
51                }
52            }
53        });
54    }
55
56    /// Wake the waker.
57    pub fn wake(&self) {
58        critical_section::with(|_| {
59            // SAFETY: This access is protected by the critical section.
60            let self_waker = unsafe { &mut *self.waker.get() };
61            if let Some(waker) = self_waker.take() {
62                waker.wake()
63            }
64        });
65    }
66}
67
68impl Default for CriticalSectionWakerRegistration {
69    fn default() -> Self {
70        Self::new()
71    }
72}