aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src/queue_generic.rs
diff options
context:
space:
mode:
authorivmarkov <[email protected]>2022-10-24 09:17:43 +0300
committerivmarkov <[email protected]>2022-10-24 09:17:43 +0300
commit4d5550070fe5e80ff2296a71239c568c774b9ceb (patch)
tree3248eb5c70b9dd5402c5edc049cafc31a5f66ed3 /embassy-time/src/queue_generic.rs
parent53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 (diff)
Change time Driver contract to never fire the alarm synchronously
Diffstat (limited to 'embassy-time/src/queue_generic.rs')
-rw-r--r--embassy-time/src/queue_generic.rs77
1 files changed, 28 insertions, 49 deletions
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
index 1c4e5398b..83f734848 100644
--- a/embassy-time/src/queue_generic.rs
+++ b/embassy-time/src/queue_generic.rs
@@ -2,7 +2,6 @@ use core::cell::RefCell;
2use core::cmp::Ordering; 2use core::cmp::Ordering;
3use core::task::Waker; 3use core::task::Waker;
4 4
5use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering};
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::Mutex; 6use embassy_sync::blocking_mutex::Mutex;
8use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; 7use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList};
@@ -71,7 +70,7 @@ impl InnerQueue {
71 } 70 }
72 } 71 }
73 72
74 fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { 73 fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
75 self.queue 74 self.queue
76 .find_mut(|timer| timer.waker.will_wake(waker)) 75 .find_mut(|timer| timer.waker.will_wake(waker))
77 .map(|mut timer| { 76 .map(|mut timer| {
@@ -98,50 +97,54 @@ impl InnerQueue {
98 // dispatch all timers that are already due 97 // dispatch all timers that are already due
99 // 98 //
100 // Then update the alarm if necessary 99 // Then update the alarm if necessary
101 self.dispatch(alarm_schedule); 100 self.dispatch();
102 } 101 }
103 102
104 fn dispatch(&mut self, alarm_schedule: &AtomicU64) { 103 fn dispatch(&mut self) {
105 let now = Instant::now(); 104 loop {
105 let now = Instant::now();
106 106
107 while self.queue.peek().filter(|timer| timer.at <= now).is_some() { 107 while self.queue.peek().filter(|timer| timer.at <= now).is_some() {
108 self.queue.pop().unwrap().waker.wake(); 108 self.queue.pop().unwrap().waker.wake();
109 } 109 }
110 110
111 self.update_alarm(alarm_schedule); 111 if self.update_alarm() {
112 break;
113 }
114 }
112 } 115 }
113 116
114 fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { 117 fn update_alarm(&mut self) -> bool {
115 if let Some(timer) = self.queue.peek() { 118 if let Some(timer) = self.queue.peek() {
116 let new_at = timer.at; 119 let new_at = timer.at;
117 120
118 if self.alarm_at != new_at { 121 if self.alarm_at != new_at {
119 self.alarm_at = new_at; 122 self.alarm_at = new_at;
120 alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); 123
124 return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks());
121 } 125 }
122 } else { 126 } else {
123 self.alarm_at = Instant::MAX; 127 self.alarm_at = Instant::MAX;
124 alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst);
125 } 128 }
129
130 true
126 } 131 }
127 132
128 fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { 133 fn handle_alarm(&mut self) {
129 self.alarm_at = Instant::MAX; 134 self.alarm_at = Instant::MAX;
130 135
131 self.dispatch(alarm_schedule); 136 self.dispatch();
132 } 137 }
133} 138}
134 139
135struct Queue { 140struct Queue {
136 inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>, 141 inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>,
137 alarm_schedule: AtomicU64,
138} 142}
139 143
140impl Queue { 144impl Queue {
141 const fn new() -> Self { 145 const fn new() -> Self {
142 Self { 146 Self {
143 inner: Mutex::new(RefCell::new(InnerQueue::new())), 147 inner: Mutex::new(RefCell::new(InnerQueue::new())),
144 alarm_schedule: AtomicU64::new(u64::MAX),
145 } 148 }
146 } 149 }
147 150
@@ -156,28 +159,12 @@ impl Queue {
156 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); 159 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
157 } 160 }
158 161
159 inner.schedule_wake(at, waker, &self.alarm_schedule) 162 inner.schedule_wake(at, waker)
160 }); 163 });
161
162 self.update_alarm();
163 }
164
165 fn update_alarm(&self) {
166 // Need to set the alarm when we are *not* holding the mutex on the inner queue
167 // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately
168 // call us back if the timestamp is in the past.
169 let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst);
170
171 if alarm_at < u64::MAX {
172 set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at);
173 }
174 } 164 }
175 165
176 fn handle_alarm(&self) { 166 fn handle_alarm(&self) {
177 self.inner 167 self.inner.lock(|inner| inner.borrow_mut().handle_alarm());
178 .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule));
179
180 self.update_alarm();
181 } 168 }
182 169
183 fn handle_alarm_callback(ctx: *mut ()) { 170 fn handle_alarm_callback(ctx: *mut ()) {
@@ -196,7 +183,6 @@ crate::timer_queue_impl!(static QUEUE: Queue = Queue::new());
196#[cfg(test)] 183#[cfg(test)]
197mod tests { 184mod tests {
198 use core::cell::Cell; 185 use core::cell::Cell;
199 use core::sync::atomic::Ordering;
200 use core::task::{RawWaker, RawWakerVTable, Waker}; 186 use core::task::{RawWaker, RawWakerVTable, Waker};
201 use std::rc::Rc; 187 use std::rc::Rc;
202 use std::sync::Mutex; 188 use std::sync::Mutex;
@@ -282,20 +268,14 @@ mod tests {
282 inner.ctx = ctx; 268 inner.ctx = ctx;
283 } 269 }
284 270
285 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { 271 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
286 let notify = { 272 let mut inner = self.0.lock().unwrap();
287 let mut inner = self.0.lock().unwrap();
288
289 if timestamp <= inner.now {
290 Some((inner.callback, inner.ctx))
291 } else {
292 inner.alarm = timestamp;
293 None
294 }
295 };
296 273
297 if let Some((callback, ctx)) = notify { 274 if timestamp <= inner.now {
298 (callback)(ctx); 275 false
276 } else {
277 inner.alarm = timestamp;
278 true
299 } 279 }
300 } 280 }
301 } 281 }
@@ -344,7 +324,6 @@ mod tests {
344 fn setup() { 324 fn setup() {
345 DRIVER.reset(); 325 DRIVER.reset();
346 326
347 QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst);
348 QUEUE.inner.lock(|inner| { 327 QUEUE.inner.lock(|inner| {
349 *inner.borrow_mut() = InnerQueue::new(); 328 *inner.borrow_mut() = InnerQueue::new();
350 }); 329 });