aboutsummaryrefslogtreecommitdiff
path: root/embassy-time
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time')
-rw-r--r--embassy-time/Cargo.toml1
-rw-r--r--embassy-time/src/driver_mock.rs113
-rw-r--r--embassy-time/src/driver_std.rs155
-rw-r--r--embassy-time/src/driver_wasm.rs112
4 files changed, 130 insertions, 251 deletions
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index e3074119f..9959e2863 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -384,7 +384,6 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"]
384 384
385[dependencies] 385[dependencies]
386embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } 386embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" }
387embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" }
388 387
389defmt = { version = "0.3", optional = true } 388defmt = { version = "0.3", optional = true }
390log = { version = "0.4.14", optional = true } 389log = { version = "0.4.14", optional = true }
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs
index 829eb0437..138d60499 100644
--- a/embassy-time/src/driver_mock.rs
+++ b/embassy-time/src/driver_mock.rs
@@ -1,7 +1,9 @@
1use core::cell::RefCell; 1use core::cell::RefCell;
2use core::task::Waker;
2 3
3use critical_section::Mutex as CsMutex; 4use critical_section::Mutex as CsMutex;
4use embassy_time_driver::Driver; 5use embassy_time_driver::Driver;
6use embassy_time_queue_driver::Queue;
5 7
6use crate::{Duration, Instant}; 8use crate::{Duration, Instant};
7 9
@@ -52,50 +54,12 @@ impl MockDriver {
52 /// Advances the time by the specified [`Duration`]. 54 /// Advances the time by the specified [`Duration`].
53 /// Calling any alarm callbacks that are due. 55 /// Calling any alarm callbacks that are due.
54 pub fn advance(&self, duration: Duration) { 56 pub fn advance(&self, duration: Duration) {
55 let notify = {
56 critical_section::with(|cs| {
57 let mut inner = self.0.borrow_ref_mut(cs);
58
59 inner.now += duration;
60
61 let now = inner.now.as_ticks();
62
63 if inner.alarm.timestamp <= now {
64 inner.alarm.timestamp = u64::MAX;
65
66 Some((inner.alarm.callback, inner.alarm.ctx))
67 } else {
68 None
69 }
70 })
71 };
72
73 if let Some((callback, ctx)) = notify {
74 (callback)(ctx);
75 }
76 }
77
78 /// Configures a callback to be called when the alarm fires.
79 pub fn set_alarm_callback(&self, callback: fn(*mut ()), ctx: *mut ()) {
80 critical_section::with(|cs| { 57 critical_section::with(|cs| {
81 let mut inner = self.0.borrow_ref_mut(cs); 58 let inner = &mut *self.0.borrow_ref_mut(cs);
82 59
83 inner.alarm.callback = callback; 60 inner.now += duration;
84 inner.alarm.ctx = ctx; 61 // wake expired tasks.
85 }); 62 inner.queue.next_expiration(inner.now.as_ticks());
86 }
87
88 /// Sets the alarm to fire at the specified timestamp.
89 pub fn set_alarm(&self, timestamp: u64) -> bool {
90 critical_section::with(|cs| {
91 let mut inner = self.0.borrow_ref_mut(cs);
92
93 if timestamp <= inner.now.as_ticks() {
94 false
95 } else {
96 inner.alarm.timestamp = timestamp;
97 true
98 }
99 }) 63 })
100 } 64 }
101} 65}
@@ -104,44 +68,38 @@ impl Driver for MockDriver {
104 fn now(&self) -> u64 { 68 fn now(&self) -> u64 {
105 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() 69 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
106 } 70 }
71
72 fn schedule_wake(&self, at: u64, waker: &Waker) {
73 critical_section::with(|cs| {
74 let inner = &mut *self.0.borrow_ref_mut(cs);
75 // enqueue it
76 inner.queue.schedule_wake(at, waker);
77 // wake it if it's in the past.
78 inner.queue.next_expiration(inner.now.as_ticks());
79 })
80 }
107} 81}
108 82
109struct InnerMockDriver { 83struct InnerMockDriver {
110 now: Instant, 84 now: Instant,
111 alarm: AlarmState, 85 queue: Queue,
112} 86}
113 87
114impl InnerMockDriver { 88impl InnerMockDriver {
115 const fn new() -> Self { 89 const fn new() -> Self {
116 Self { 90 Self {
117 now: Instant::from_ticks(0), 91 now: Instant::from_ticks(0),
118 alarm: AlarmState::new(), 92 queue: Queue::new(),
119 }
120 }
121}
122
123struct AlarmState {
124 timestamp: u64,
125 callback: fn(*mut ()),
126 ctx: *mut (),
127}
128
129impl AlarmState {
130 const fn new() -> Self {
131 Self {
132 timestamp: u64::MAX,
133 callback: Self::noop,
134 ctx: core::ptr::null_mut(),
135 } 93 }
136 } 94 }
137
138 fn noop(_ctx: *mut ()) {}
139} 95}
140 96
141unsafe impl Send for AlarmState {}
142
143#[cfg(test)] 97#[cfg(test)]
144mod tests { 98mod tests {
99 use core::sync::atomic::{AtomicBool, Ordering};
100 use std::sync::Arc;
101 use std::task::Wake;
102
145 use serial_test::serial; 103 use serial_test::serial;
146 104
147 use super::*; 105 use super::*;
@@ -163,24 +121,25 @@ mod tests {
163 121
164 #[test] 122 #[test]
165 #[serial] 123 #[serial]
166 fn test_set_alarm_not_in_future() { 124 fn test_schedule_wake() {
167 setup(); 125 setup();
168 126
169 let driver = MockDriver::get(); 127 static CALLBACK_CALLED: AtomicBool = AtomicBool::new(false);
170 assert_eq!(false, driver.set_alarm(driver.now()));
171 }
172 128
173 #[test] 129 struct MockWaker;
174 #[serial] 130
175 fn test_alarm() { 131 impl Wake for MockWaker {
176 setup(); 132 fn wake(self: Arc<Self>) {
133 CALLBACK_CALLED.store(true, Ordering::Relaxed);
134 }
135 }
136 let waker = Arc::new(MockWaker).into();
177 137
178 let driver = MockDriver::get(); 138 let driver = MockDriver::get();
179 static mut CALLBACK_CALLED: bool = false; 139
180 driver.set_alarm_callback(|_| unsafe { CALLBACK_CALLED = true }, core::ptr::null_mut()); 140 driver.schedule_wake(driver.now() + 1, &waker);
181 driver.set_alarm(driver.now() + 1); 141 assert_eq!(false, CALLBACK_CALLED.load(Ordering::Relaxed));
182 assert_eq!(false, unsafe { CALLBACK_CALLED });
183 driver.advance(Duration::from_secs(1)); 142 driver.advance(Duration::from_secs(1));
184 assert_eq!(true, unsafe { CALLBACK_CALLED }); 143 assert_eq!(true, CALLBACK_CALLED.load(Ordering::Relaxed));
185 } 144 }
186} 145}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index 45467f09b..35888fddd 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -1,96 +1,66 @@
1use std::cell::{RefCell, UnsafeCell}; 1use std::sync::{Condvar, Mutex};
2use std::mem::MaybeUninit; 2use std::thread;
3use std::sync::{Condvar, Mutex, Once};
4use std::time::{Duration as StdDuration, Instant as StdInstant}; 3use std::time::{Duration as StdDuration, Instant as StdInstant};
5use std::{ptr, thread};
6 4
7use critical_section::Mutex as CsMutex;
8use embassy_time_driver::Driver; 5use embassy_time_driver::Driver;
9use embassy_time_queue_driver::GlobalTimerQueue; 6use embassy_time_queue_driver::Queue;
10 7
11struct AlarmState { 8struct TimeDriver {
12 timestamp: u64, 9 signaler: Signaler,
13} 10 inner: Mutex<Inner>,
14
15unsafe impl Send for AlarmState {}
16
17impl AlarmState {
18 const fn new() -> Self {
19 Self { timestamp: u64::MAX }
20 }
21} 11}
22 12
23struct TimeDriver { 13struct Inner {
24 once: Once, 14 zero_instant: Option<StdInstant>,
25 // The STD Driver implementation requires the alarm's mutex to be reentrant, which the STD Mutex isn't 15 queue: Queue,
26 // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
27 // themselves are reentrant
28 alarm: UninitCell<CsMutex<RefCell<AlarmState>>>,
29 zero_instant: UninitCell<StdInstant>,
30 signaler: UninitCell<Signaler>,
31} 16}
32 17
33embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 18embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
34 once: Once::new(), 19 inner: Mutex::new(Inner{
35 alarm: UninitCell::uninit(), 20 zero_instant: None,
36 zero_instant: UninitCell::uninit(), 21 queue: Queue::new(),
37 signaler: UninitCell::uninit(), 22 }),
23 signaler: Signaler::new(),
38}); 24});
39 25
40impl TimeDriver { 26impl Inner {
41 fn init(&self) { 27 fn init(&mut self) -> StdInstant {
42 self.once.call_once(|| unsafe { 28 *self.zero_instant.get_or_insert_with(|| {
43 self.alarm 29 thread::spawn(alarm_thread);
44 .write(CsMutex::new(RefCell::new(const { AlarmState::new() }))); 30 StdInstant::now()
45 self.zero_instant.write(StdInstant::now()); 31 })
46 self.signaler.write(Signaler::new());
47
48 thread::spawn(Self::alarm_thread);
49 });
50 } 32 }
33}
51 34
52 fn alarm_thread() { 35impl Driver for TimeDriver {
53 let zero = unsafe { DRIVER.zero_instant.read() }; 36 fn now(&self) -> u64 {
54 loop { 37 let mut inner = self.inner.lock().unwrap();
55 let now = DRIVER.now(); 38 let zero = inner.init();
56 39 StdInstant::now().duration_since(zero).as_micros() as u64
57 let next_alarm = critical_section::with(|cs| { 40 }
58 let mut alarm = unsafe { DRIVER.alarm.as_ref() }.borrow_ref_mut(cs);
59 if alarm.timestamp <= now {
60 alarm.timestamp = u64::MAX;
61
62 TIMER_QUEUE_DRIVER.dispatch();
63 }
64 alarm.timestamp
65 });
66
67 // Ensure we don't overflow
68 let until = zero
69 .checked_add(StdDuration::from_micros(next_alarm))
70 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
71 41
72 unsafe { DRIVER.signaler.as_ref() }.wait_until(until); 42 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
43 let mut inner = self.inner.lock().unwrap();
44 inner.init();
45 if inner.queue.schedule_wake(at, waker) {
46 self.signaler.signal();
73 } 47 }
74 } 48 }
49}
75 50
76 fn set_alarm(&self, timestamp: u64) -> bool { 51fn alarm_thread() {
77 self.init(); 52 let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap();
78 critical_section::with(|cs| { 53 loop {
79 let mut alarm = unsafe { self.alarm.as_ref() }.borrow_ref_mut(cs); 54 let now = DRIVER.now();
80 alarm.timestamp = timestamp;
81 unsafe { self.signaler.as_ref() }.signal();
82 });
83 55
84 true 56 let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now);
85 }
86}
87 57
88impl Driver for TimeDriver { 58 // Ensure we don't overflow
89 fn now(&self) -> u64 { 59 let until = zero
90 self.init(); 60 .checked_add(StdDuration::from_micros(next_alarm))
61 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
91 62
92 let zero = unsafe { self.zero_instant.read() }; 63 DRIVER.signaler.wait_until(until);
93 StdInstant::now().duration_since(zero).as_micros() as u64
94 } 64 }
95} 65}
96 66
@@ -100,7 +70,7 @@ struct Signaler {
100} 70}
101 71
102impl Signaler { 72impl Signaler {
103 fn new() -> Self { 73 const fn new() -> Self {
104 Self { 74 Self {
105 mutex: Mutex::new(false), 75 mutex: Mutex::new(false),
106 condvar: Condvar::new(), 76 condvar: Condvar::new(),
@@ -132,40 +102,3 @@ impl Signaler {
132 self.condvar.notify_one(); 102 self.condvar.notify_one();
133 } 103 }
134} 104}
135
136pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
137unsafe impl<T> Send for UninitCell<T> {}
138unsafe impl<T> Sync for UninitCell<T> {}
139
140impl<T> UninitCell<T> {
141 pub const fn uninit() -> Self {
142 Self(MaybeUninit::uninit())
143 }
144
145 pub unsafe fn as_ptr(&self) -> *const T {
146 (*self.0.as_ptr()).get()
147 }
148
149 pub unsafe fn as_mut_ptr(&self) -> *mut T {
150 (*self.0.as_ptr()).get()
151 }
152
153 pub unsafe fn as_ref(&self) -> &T {
154 &*self.as_ptr()
155 }
156
157 pub unsafe fn write(&self, val: T) {
158 ptr::write(self.as_mut_ptr(), val)
159 }
160}
161
162impl<T: Copy> UninitCell<T> {
163 pub unsafe fn read(&self) -> T {
164 ptr::read(self.as_mut_ptr())
165 }
166}
167
168embassy_time_queue_driver::timer_queue_impl!(
169 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
170 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
171);
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index dcc935fde..bcdd1670b 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -1,10 +1,7 @@
1use std::cell::UnsafeCell; 1use std::sync::Mutex;
2use std::mem::MaybeUninit;
3use std::ptr;
4use std::sync::{Mutex, Once};
5 2
6use embassy_time_driver::Driver; 3use embassy_time_driver::Driver;
7use embassy_time_queue_driver::GlobalTimerQueue; 4use embassy_time_queue_driver::Queue;
8use wasm_bindgen::prelude::*; 5use wasm_bindgen::prelude::*;
9use wasm_timer::Instant as StdInstant; 6use wasm_timer::Instant as StdInstant;
10 7
@@ -12,8 +9,6 @@ struct AlarmState {
12 token: Option<f64>, 9 token: Option<f64>,
13} 10}
14 11
15unsafe impl Send for AlarmState {}
16
17impl AlarmState { 12impl AlarmState {
18 const fn new() -> Self { 13 const fn new() -> Self {
19 Self { token: None } 14 Self { token: None }
@@ -27,33 +22,38 @@ extern "C" {
27} 22}
28 23
29struct TimeDriver { 24struct TimeDriver {
30 once: Once, 25 inner: Mutex<Inner>,
31 alarm: UninitCell<Mutex<AlarmState>>, 26}
32 zero_instant: UninitCell<StdInstant>, 27
33 closure: UninitCell<Closure<dyn FnMut()>>, 28struct Inner {
29 alarm: AlarmState,
30 zero_instant: Option<StdInstant>,
31 queue: Queue,
32 closure: Option<Closure<dyn FnMut()>>,
34} 33}
35 34
35unsafe impl Send for Inner {}
36
36embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 37embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
37 once: Once::new(), 38 inner: Mutex::new(Inner{
38 alarm: UninitCell::uninit(), 39 zero_instant: None,
39 zero_instant: UninitCell::uninit(), 40 queue: Queue::new(),
40 closure: UninitCell::uninit() 41 alarm: AlarmState::new(),
42 closure: None,
43 }),
41}); 44});
42 45
43impl TimeDriver { 46impl Inner {
44 fn init(&self) { 47 fn init(&mut self) -> StdInstant {
45 self.once.call_once(|| unsafe { 48 *self.zero_instant.get_or_insert_with(StdInstant::now)
46 self.alarm.write(Mutex::new(const { AlarmState::new() })); 49 }
47 self.zero_instant.write(StdInstant::now()); 50
48 self.closure 51 fn now(&mut self) -> u64 {
49 .write(Closure::new(Box::new(|| TIMER_QUEUE_DRIVER.dispatch()))); 52 StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64
50 });
51 } 53 }
52 54
53 fn set_alarm(&self, timestamp: u64) -> bool { 55 fn set_alarm(&mut self, timestamp: u64) -> bool {
54 self.init(); 56 if let Some(token) = self.alarm.token {
55 let mut alarm = unsafe { self.alarm.as_ref() }.lock().unwrap();
56 if let Some(token) = alarm.token {
57 clearTimeout(token); 57 clearTimeout(token);
58 } 58 }
59 59
@@ -62,7 +62,8 @@ impl TimeDriver {
62 false 62 false
63 } else { 63 } else {
64 let timeout = (timestamp - now) as u32; 64 let timeout = (timestamp - now) as u32;
65 alarm.token = Some(setTimeout(unsafe { self.closure.as_ref() }, timeout / 1000)); 65 let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch));
66 self.alarm.token = Some(setTimeout(closure, timeout / 1000));
66 67
67 true 68 true
68 } 69 }
@@ -71,45 +72,32 @@ impl TimeDriver {
71 72
72impl Driver for TimeDriver { 73impl Driver for TimeDriver {
73 fn now(&self) -> u64 { 74 fn now(&self) -> u64 {
74 self.init(); 75 let mut inner = self.inner.lock().unwrap();
75 76 let zero = inner.init();
76 let zero = unsafe { self.zero_instant.read() };
77 StdInstant::now().duration_since(zero).as_micros() as u64 77 StdInstant::now().duration_since(zero).as_micros() as u64
78 } 78 }
79}
80 79
81pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); 80 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
82unsafe impl<T> Send for UninitCell<T> {} 81 let mut inner = self.inner.lock().unwrap();
83unsafe impl<T> Sync for UninitCell<T> {} 82 inner.init();
84 83 if inner.queue.schedule_wake(at, waker) {
85impl<T> UninitCell<T> { 84 let now = inner.now();
86 pub const fn uninit() -> Self { 85 let mut next = inner.queue.next_expiration(now);
87 Self(MaybeUninit::uninit()) 86 while !inner.set_alarm(next) {
88 } 87 let now = inner.now();
89 unsafe fn as_ptr(&self) -> *const T { 88 next = inner.queue.next_expiration(now);
90 (*self.0.as_ptr()).get() 89 }
91 } 90 }
92
93 pub unsafe fn as_mut_ptr(&self) -> *mut T {
94 (*self.0.as_ptr()).get()
95 }
96
97 pub unsafe fn as_ref(&self) -> &T {
98 &*self.as_ptr()
99 }
100
101 pub unsafe fn write(&self, val: T) {
102 ptr::write(self.as_mut_ptr(), val)
103 } 91 }
104} 92}
105 93
106impl<T: Copy> UninitCell<T> { 94fn dispatch() {
107 pub unsafe fn read(&self) -> T { 95 let inner = &mut *DRIVER.inner.lock().unwrap();
108 ptr::read(self.as_mut_ptr()) 96
97 let now = inner.now();
98 let mut next = inner.queue.next_expiration(now);
99 while !inner.set_alarm(next) {
100 let now = inner.now();
101 next = inner.queue.next_expiration(now);
109 } 102 }
110} 103}
111
112embassy_time_queue_driver::timer_queue_impl!(
113 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
114 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
115);