diff options
| author | ivmarkov <[email protected]> | 2022-10-24 09:17:43 +0300 |
|---|---|---|
| committer | ivmarkov <[email protected]> | 2022-10-24 09:17:43 +0300 |
| commit | 4d5550070fe5e80ff2296a71239c568c774b9ceb (patch) | |
| tree | 3248eb5c70b9dd5402c5edc049cafc31a5f66ed3 /embassy-time/src/queue_generic.rs | |
| parent | 53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 (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.rs | 77 |
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; | |||
| 2 | use core::cmp::Ordering; | 2 | use core::cmp::Ordering; |
| 3 | use core::task::Waker; | 3 | use core::task::Waker; |
| 4 | 4 | ||
| 5 | use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; | ||
| 6 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 7 | use embassy_sync::blocking_mutex::Mutex; | 6 | use embassy_sync::blocking_mutex::Mutex; |
| 8 | use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; | 7 | use 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 | ||
| 135 | struct Queue { | 140 | struct Queue { |
| 136 | inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>, | 141 | inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>, |
| 137 | alarm_schedule: AtomicU64, | ||
| 138 | } | 142 | } |
| 139 | 143 | ||
| 140 | impl Queue { | 144 | impl 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)] |
| 197 | mod tests { | 184 | mod 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 | }); |
