aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src/driver_std.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time/src/driver_std.rs')
-rw-r--r--embassy-time/src/driver_std.rs208
1 files changed, 41 insertions, 167 deletions
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index cbef7aae1..35888fddd 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -1,160 +1,66 @@
1use core::sync::atomic::{AtomicU8, Ordering}; 1use std::sync::{Condvar, Mutex};
2use std::cell::{RefCell, UnsafeCell}; 2use std::thread;
3use std::mem::MaybeUninit;
4use std::sync::{Condvar, Mutex, Once};
5use std::time::{Duration as StdDuration, Instant as StdInstant}; 3use std::time::{Duration as StdDuration, Instant as StdInstant};
6use std::{mem, ptr, thread};
7 4
8use critical_section::Mutex as CsMutex; 5use embassy_time_driver::Driver;
9use embassy_time_driver::{AlarmHandle, Driver}; 6use embassy_time_queue_driver::Queue;
10 7
11const ALARM_COUNT: usize = 4; 8struct TimeDriver {
12 9 signaler: Signaler,
13struct AlarmState { 10 inner: Mutex<Inner>,
14 timestamp: u64,
15
16 // This is really a Option<(fn(*mut ()), *mut ())>
17 // but fn pointers aren't allowed in const yet
18 callback: *const (),
19 ctx: *mut (),
20} 11}
21 12
22unsafe impl Send for AlarmState {} 13struct Inner {
23 14 zero_instant: Option<StdInstant>,
24impl AlarmState { 15 queue: Queue,
25 const fn new() -> Self {
26 Self {
27 timestamp: u64::MAX,
28 callback: ptr::null(),
29 ctx: ptr::null_mut(),
30 }
31 }
32}
33
34struct TimeDriver {
35 alarm_count: AtomicU8,
36
37 once: Once,
38 // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't
39 // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
40 // themselves are reentrant
41 alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>,
42 zero_instant: UninitCell<StdInstant>,
43 signaler: UninitCell<Signaler>,
44} 16}
45 17
46embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 18embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
47 alarm_count: AtomicU8::new(0), 19 inner: Mutex::new(Inner{
48 20 zero_instant: None,
49 once: Once::new(), 21 queue: Queue::new(),
50 alarms: UninitCell::uninit(), 22 }),
51 zero_instant: UninitCell::uninit(), 23 signaler: Signaler::new(),
52 signaler: UninitCell::uninit(),
53}); 24});
54 25
55impl TimeDriver { 26impl Inner {
56 fn init(&self) { 27 fn init(&mut self) -> StdInstant {
57 self.once.call_once(|| unsafe { 28 *self.zero_instant.get_or_insert_with(|| {
58 self.alarms 29 thread::spawn(alarm_thread);
59 .write(CsMutex::new(RefCell::new([const { AlarmState::new() }; ALARM_COUNT]))); 30 StdInstant::now()
60 self.zero_instant.write(StdInstant::now()); 31 })
61 self.signaler.write(Signaler::new());
62
63 thread::spawn(Self::alarm_thread);
64 });
65 }
66
67 fn alarm_thread() {
68 let zero = unsafe { DRIVER.zero_instant.read() };
69 loop {
70 let now = DRIVER.now();
71
72 let next_alarm = critical_section::with(|cs| {
73 let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs);
74 loop {
75 let pending = alarms
76 .borrow_mut()
77 .iter_mut()
78 .find(|alarm| alarm.timestamp <= now)
79 .map(|alarm| {
80 alarm.timestamp = u64::MAX;
81
82 (alarm.callback, alarm.ctx)
83 });
84
85 if let Some((callback, ctx)) = pending {
86 // safety:
87 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
88 // - other than that we only store valid function pointers into alarm.callback
89 let f: fn(*mut ()) = unsafe { mem::transmute(callback) };
90 f(ctx);
91 } else {
92 // No alarm due
93 break;
94 }
95 }
96
97 alarms
98 .borrow()
99 .iter()
100 .map(|alarm| alarm.timestamp)
101 .min()
102 .unwrap_or(u64::MAX)
103 });
104
105 // Ensure we don't overflow
106 let until = zero
107 .checked_add(StdDuration::from_micros(next_alarm))
108 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
109
110 unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
111 }
112 } 32 }
113} 33}
114 34
115impl Driver for TimeDriver { 35impl Driver for TimeDriver {
116 fn now(&self) -> u64 { 36 fn now(&self) -> u64 {
117 self.init(); 37 let mut inner = self.inner.lock().unwrap();
118 38 let zero = inner.init();
119 let zero = unsafe { self.zero_instant.read() };
120 StdInstant::now().duration_since(zero).as_micros() as u64 39 StdInstant::now().duration_since(zero).as_micros() as u64
121 } 40 }
122 41
123 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 42 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
124 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 43 let mut inner = self.inner.lock().unwrap();
125 if x < ALARM_COUNT as u8 { 44 inner.init();
126 Some(x + 1) 45 if inner.queue.schedule_wake(at, waker) {
127 } else { 46 self.signaler.signal();
128 None
129 }
130 });
131
132 match id {
133 Ok(id) => Some(AlarmHandle::new(id)),
134 Err(_) => None,
135 } 47 }
136 } 48 }
49}
137 50
138 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 51fn alarm_thread() {
139 self.init(); 52 let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap();
140 critical_section::with(|cs| { 53 loop {
141 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); 54 let now = DRIVER.now();
142 let alarm = &mut alarms[alarm.id() as usize]; 55
143 alarm.callback = callback as *const (); 56 let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now);
144 alarm.ctx = ctx;
145 });
146 }
147 57
148 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 58 // Ensure we don't overflow
149 self.init(); 59 let until = zero
150 critical_section::with(|cs| { 60 .checked_add(StdDuration::from_micros(next_alarm))
151 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); 61 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
152 let alarm = &mut alarms[alarm.id() as usize];
153 alarm.timestamp = timestamp;
154 unsafe { self.signaler.as_ref() }.signal();
155 });
156 62
157 true 63 DRIVER.signaler.wait_until(until);
158 } 64 }
159} 65}
160 66
@@ -164,7 +70,7 @@ struct Signaler {
164} 70}
165 71
166impl Signaler { 72impl Signaler {
167 fn new() -> Self { 73 const fn new() -> Self {
168 Self { 74 Self {
169 mutex: Mutex::new(false), 75 mutex: Mutex::new(false),
170 condvar: Condvar::new(), 76 condvar: Condvar::new(),
@@ -196,35 +102,3 @@ impl Signaler {
196 self.condvar.notify_one(); 102 self.condvar.notify_one();
197 } 103 }
198} 104}
199
200pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
201unsafe impl<T> Send for UninitCell<T> {}
202unsafe impl<T> Sync for UninitCell<T> {}
203
204impl<T> UninitCell<T> {
205 pub const fn uninit() -> Self {
206 Self(MaybeUninit::uninit())
207 }
208
209 pub unsafe fn as_ptr(&self) -> *const T {
210 (*self.0.as_ptr()).get()
211 }
212
213 pub unsafe fn as_mut_ptr(&self) -> *mut T {
214 (*self.0.as_ptr()).get()
215 }
216
217 pub unsafe fn as_ref(&self) -> &T {
218 &*self.as_ptr()
219 }
220
221 pub unsafe fn write(&self, val: T) {
222 ptr::write(self.as_mut_ptr(), val)
223 }
224}
225
226impl<T: Copy> UninitCell<T> {
227 pub unsafe fn read(&self) -> T {
228 ptr::read(self.as_mut_ptr())
229 }
230}