aboutsummaryrefslogtreecommitdiff
path: root/embassy-time
diff options
context:
space:
mode:
authorivmarkov <[email protected]>2023-01-26 20:14:53 +0000
committerivmarkov <[email protected]>2023-01-26 20:41:18 +0000
commit34b67fe1372c535a659590744242cd4ffd52dfb6 (patch)
tree191ba2337d17b8ebab0cb269da1d18c91efe6fc4 /embassy-time
parentffa75e1e39949807317ee75459ae9d18b5376578 (diff)
STD driver needs a reentrant mutex; logic fixed to be reentrancy-safe
Diffstat (limited to 'embassy-time')
-rw-r--r--embassy-time/src/driver_std.rs67
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 @@
1use std::cell::UnsafeCell; 1use std::cell::{RefCell, UnsafeCell};
2use std::mem::MaybeUninit; 2use std::mem::MaybeUninit;
3use std::sync::{Condvar, Mutex, Once}; 3use std::sync::{Condvar, Mutex, Once};
4use std::time::{Duration as StdDuration, Instant as StdInstant}; 4use std::time::{Duration as StdDuration, Instant as StdInstant};
5use std::{mem, ptr, thread}; 5use std::{mem, ptr, thread};
6 6
7use atomic_polyfill::{AtomicU8, Ordering}; 7use atomic_polyfill::{AtomicU8, Ordering};
8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9use embassy_sync::blocking_mutex::Mutex as EmbassyMutex;
8 10
9use crate::driver::{AlarmHandle, Driver}; 11use 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 {
53impl TimeDriver { 58impl 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 }