aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src
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
parent53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 (diff)
Change time Driver contract to never fire the alarm synchronously
Diffstat (limited to 'embassy-time/src')
-rw-r--r--embassy-time/src/driver.rs13
-rw-r--r--embassy-time/src/driver_std.rs4
-rw-r--r--embassy-time/src/driver_wasm.rs4
-rw-r--r--embassy-time/src/queue_generic.rs77
4 files changed, 41 insertions, 57 deletions
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
index 79ae14b91..5c2ad3b23 100644
--- a/embassy-time/src/driver.rs
+++ b/embassy-time/src/driver.rs
@@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static {
105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm 105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
106 /// timestamp, the provided callback function will be called. 106 /// timestamp, the provided callback function will be called.
107 /// 107 ///
108 /// If `timestamp` is already in the past, the alarm callback must be immediately fired. 108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
109 /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. 109 /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
110 /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
110 /// 111 ///
111 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 112 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
112 /// 113 ///
113 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. 114 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
114 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); 115 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
115} 116}
116 117
117extern "Rust" { 118extern "Rust" {
118 fn _embassy_time_now() -> u64; 119 fn _embassy_time_now() -> u64;
119 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; 120 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
120 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); 121 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
121 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); 122 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool;
122} 123}
123 124
124/// See [`Driver::now`] 125/// See [`Driver::now`]
@@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut (
139} 140}
140 141
141/// See [`Driver::set_alarm`] 142/// See [`Driver::set_alarm`]
142pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { 143pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool {
143 unsafe { _embassy_time_set_alarm(alarm, timestamp) } 144 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
144} 145}
145 146
@@ -167,7 +168,7 @@ macro_rules! time_driver_impl {
167 } 168 }
168 169
169 #[no_mangle] 170 #[no_mangle]
170 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { 171 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool {
171 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) 172 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
172 } 173 }
173 }; 174 };
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index 2ddb2e604..fc7fd1979 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -127,12 +127,14 @@ impl Driver for TimeDriver {
127 alarm.ctx = ctx; 127 alarm.ctx = ctx;
128 } 128 }
129 129
130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
131 self.init(); 131 self.init();
132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
133 let alarm = &mut alarms[alarm.id() as usize]; 133 let alarm = &mut alarms[alarm.id() as usize];
134 alarm.timestamp = timestamp; 134 alarm.timestamp = timestamp;
135 unsafe { self.signaler.as_ref() }.signal(); 135 unsafe { self.signaler.as_ref() }.signal();
136
137 true
136 } 138 }
137} 139}
138 140
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index e4497e6a2..d7a6b0d8d 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -81,13 +81,15 @@ impl Driver for TimeDriver {
81 } 81 }
82 } 82 }
83 83
84 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 84 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool {
85 self.init(); 85 self.init();
86 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 86 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
87 let alarm = &mut alarms[alarm.id() as usize]; 87 let alarm = &mut alarms[alarm.id() as usize];
88 alarm.closure.replace(Closure::new(move || { 88 alarm.closure.replace(Closure::new(move || {
89 callback(ctx); 89 callback(ctx);
90 })); 90 }));
91
92 true
91 } 93 }
92 94
93 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 95 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
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 });