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()
    }
}