aboutsummaryrefslogtreecommitdiff
path: root/embassy/src/waitqueue/waker_agnostic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy/src/waitqueue/waker_agnostic.rs')
-rw-r--r--embassy/src/waitqueue/waker_agnostic.rs84
1 files changed, 84 insertions, 0 deletions
diff --git a/embassy/src/waitqueue/waker_agnostic.rs b/embassy/src/waitqueue/waker_agnostic.rs
new file mode 100644
index 000000000..f583fa6f4
--- /dev/null
+++ b/embassy/src/waitqueue/waker_agnostic.rs
@@ -0,0 +1,84 @@
1use core::cell::Cell;
2use core::mem;
3use core::task::Waker;
4
5use crate::blocking_mutex::CriticalSectionMutex as Mutex;
6
7/// Utility struct to register and wake a waker.
8#[derive(Debug)]
9pub struct WakerRegistration {
10 waker: Option<Waker>,
11}
12
13impl WakerRegistration {
14 pub const fn new() -> Self {
15 Self { waker: None }
16 }
17
18 /// Register a waker. Overwrites the previous waker, if any.
19 pub fn register(&mut self, w: &Waker) {
20 match self.waker {
21 // Optimization: If both the old and new Wakers wake the same task, we can simply
22 // keep the old waker, skipping the clone. (In most executor implementations,
23 // cloning a waker is somewhat expensive, comparable to cloning an Arc).
24 Some(ref w2) if (w2.will_wake(w)) => {}
25 _ => {
26 // clone the new waker and store it
27 if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) {
28 // We had a waker registered for another task. Wake it, so the other task can
29 // reregister itself if it's still interested.
30 //
31 // If two tasks are waiting on the same thing concurrently, this will cause them
32 // to wake each other in a loop fighting over this WakerRegistration. This wastes
33 // CPU but things will still work.
34 //
35 // If the user wants to have two tasks waiting on the same thing they should use
36 // a more appropriate primitive that can store multiple wakers.
37 old_waker.wake()
38 }
39 }
40 }
41 }
42
43 /// Wake the registered waker, if any.
44 pub fn wake(&mut self) {
45 if let Some(w) = self.waker.take() {
46 w.wake()
47 }
48 }
49}
50
51/// Utility struct to register and wake a waker.
52pub struct AtomicWaker {
53 waker: Mutex<Cell<Option<Waker>>>,
54}
55
56impl AtomicWaker {
57 pub const fn new() -> Self {
58 Self {
59 waker: Mutex::new(Cell::new(None)),
60 }
61 }
62
63 /// Register a waker. Overwrites the previous waker, if any.
64 pub fn register(&self, w: &Waker) {
65 critical_section::with(|cs| {
66 let cell = self.waker.borrow(cs);
67 cell.set(match cell.replace(None) {
68 Some(w2) if (w2.will_wake(w)) => Some(w2),
69 _ => Some(w.clone()),
70 })
71 })
72 }
73
74 /// Wake the registered waker, if any.
75 pub fn wake(&self) {
76 critical_section::with(|cs| {
77 let cell = self.waker.borrow(cs);
78 if let Some(w) = cell.replace(None) {
79 w.wake_by_ref();
80 cell.set(Some(w));
81 }
82 })
83 }
84}