diff options
| author | ivmarkov <[email protected]> | 2023-01-26 20:14:53 +0000 |
|---|---|---|
| committer | ivmarkov <[email protected]> | 2023-01-26 20:41:18 +0000 |
| commit | 34b67fe1372c535a659590744242cd4ffd52dfb6 (patch) | |
| tree | 191ba2337d17b8ebab0cb269da1d18c91efe6fc4 | |
| parent | ffa75e1e39949807317ee75459ae9d18b5376578 (diff) | |
STD driver needs a reentrant mutex; logic fixed to be reentrancy-safe
| -rw-r--r-- | embassy-time/src/driver_std.rs | 67 |
1 files changed, 45 insertions, 22 deletions
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index fc7fd1979..da46a599d 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | use std::cell::UnsafeCell; | 1 | use std::cell::{RefCell, UnsafeCell}; |
| 2 | use std::mem::MaybeUninit; | 2 | use std::mem::MaybeUninit; |
| 3 | use std::sync::{Condvar, Mutex, Once}; | 3 | use std::sync::{Condvar, Mutex, Once}; |
| 4 | use std::time::{Duration as StdDuration, Instant as StdInstant}; | 4 | use std::time::{Duration as StdDuration, Instant as StdInstant}; |
| 5 | use std::{mem, ptr, thread}; | 5 | use std::{mem, ptr, thread}; |
| 6 | 6 | ||
| 7 | use atomic_polyfill::{AtomicU8, Ordering}; | 7 | use atomic_polyfill::{AtomicU8, Ordering}; |
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 9 | use embassy_sync::blocking_mutex::Mutex as EmbassyMutex; | ||
| 8 | 10 | ||
| 9 | use crate::driver::{AlarmHandle, Driver}; | 11 | use crate::driver::{AlarmHandle, Driver}; |
| 10 | 12 | ||
| @@ -35,7 +37,10 @@ struct TimeDriver { | |||
| 35 | alarm_count: AtomicU8, | 37 | alarm_count: AtomicU8, |
| 36 | 38 | ||
| 37 | once: Once, | 39 | once: Once, |
| 38 | alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>, | 40 | // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't |
| 41 | // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections | ||
| 42 | // themselves are reentrant | ||
| 43 | alarms: UninitCell<EmbassyMutex<CriticalSectionRawMutex, RefCell<[AlarmState; ALARM_COUNT]>>>, | ||
| 39 | zero_instant: UninitCell<StdInstant>, | 44 | zero_instant: UninitCell<StdInstant>, |
| 40 | signaler: UninitCell<Signaler>, | 45 | signaler: UninitCell<Signaler>, |
| 41 | } | 46 | } |
| @@ -53,7 +58,8 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { | |||
| 53 | impl TimeDriver { | 58 | impl TimeDriver { |
| 54 | fn init(&self) { | 59 | fn init(&self) { |
| 55 | self.once.call_once(|| unsafe { | 60 | self.once.call_once(|| unsafe { |
| 56 | self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); | 61 | self.alarms |
| 62 | .write(EmbassyMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); | ||
| 57 | self.zero_instant.write(StdInstant::now()); | 63 | self.zero_instant.write(StdInstant::now()); |
| 58 | self.signaler.write(Signaler::new()); | 64 | self.signaler.write(Signaler::new()); |
| 59 | 65 | ||
| @@ -66,25 +72,37 @@ impl TimeDriver { | |||
| 66 | loop { | 72 | loop { |
| 67 | let now = DRIVER.now(); | 73 | let now = DRIVER.now(); |
| 68 | 74 | ||
| 69 | let mut next_alarm = u64::MAX; | 75 | let next_alarm = unsafe { DRIVER.alarms.as_ref() }.lock(|alarms| { |
| 70 | { | 76 | loop { |
| 71 | let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); | 77 | let pending = alarms |
| 72 | for alarm in alarms { | 78 | .borrow_mut() |
| 73 | if alarm.timestamp <= now { | 79 | .iter_mut() |
| 74 | alarm.timestamp = u64::MAX; | 80 | .find(|alarm| alarm.timestamp <= now) |
| 81 | .map(|alarm| { | ||
| 82 | alarm.timestamp = u64::MAX; | ||
| 75 | 83 | ||
| 76 | // Call after clearing alarm, so the callback can set another alarm. | 84 | (alarm.callback, alarm.ctx) |
| 85 | }); | ||
| 77 | 86 | ||
| 87 | if let Some((callback, ctx)) = pending { | ||
| 78 | // safety: | 88 | // safety: |
| 79 | // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. | 89 | // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. |
| 80 | // - other than that we only store valid function pointers into alarm.callback | 90 | // - other than that we only store valid function pointers into alarm.callback |
| 81 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; | 91 | let f: fn(*mut ()) = unsafe { mem::transmute(callback) }; |
| 82 | f(alarm.ctx); | 92 | f(ctx); |
| 83 | } else { | 93 | } else { |
| 84 | next_alarm = next_alarm.min(alarm.timestamp); | 94 | // No alarm due |
| 95 | break; | ||
| 85 | } | 96 | } |
| 86 | } | 97 | } |
| 87 | } | 98 | |
| 99 | alarms | ||
| 100 | .borrow() | ||
| 101 | .iter() | ||
| 102 | .map(|alarm| alarm.timestamp) | ||
| 103 | .min() | ||
| 104 | .unwrap_or(u64::MAX) | ||
| 105 | }); | ||
| 88 | 106 | ||
| 89 | // Ensure we don't overflow | 107 | // Ensure we don't overflow |
| 90 | let until = zero | 108 | let until = zero |
| @@ -121,18 +139,23 @@ impl Driver for TimeDriver { | |||
| 121 | 139 | ||
| 122 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | 140 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { |
| 123 | self.init(); | 141 | self.init(); |
| 124 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | 142 | unsafe { self.alarms.as_ref() }.lock(|alarms| { |
| 125 | let alarm = &mut alarms[alarm.id() as usize]; | 143 | let mut alarms = alarms.borrow_mut(); |
| 126 | alarm.callback = callback as *const (); | 144 | let alarm = &mut alarms[alarm.id() as usize]; |
| 127 | alarm.ctx = ctx; | 145 | alarm.callback = callback as *const (); |
| 146 | alarm.ctx = ctx; | ||
| 147 | }); | ||
| 128 | } | 148 | } |
| 129 | 149 | ||
| 130 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | 150 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { |
| 131 | self.init(); | 151 | self.init(); |
| 132 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | 152 | unsafe { self.alarms.as_ref() }.lock(|alarms| { |
| 133 | let alarm = &mut alarms[alarm.id() as usize]; | 153 | let mut alarms = alarms.borrow_mut(); |
| 134 | alarm.timestamp = timestamp; | 154 | |
| 135 | unsafe { self.signaler.as_ref() }.signal(); | 155 | let alarm = &mut alarms[alarm.id() as usize]; |
| 156 | alarm.timestamp = timestamp; | ||
| 157 | unsafe { self.signaler.as_ref() }.signal(); | ||
| 158 | }); | ||
| 136 | 159 | ||
| 137 | true | 160 | true |
| 138 | } | 161 | } |
