aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/ci/test.sh2
-rw-r--r--embassy-nrf/src/time_driver.rs115
-rw-r--r--embassy-rp/src/time_driver.rs78
-rw-r--r--embassy-stm32/src/time_driver.rs104
-rw-r--r--embassy-time-driver/src/lib.rs17
-rw-r--r--embassy-time-queue-driver/src/lib.rs140
-rw-r--r--embassy-time-queue-driver/src/queue_integrated.rs12
-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
11 files changed, 330 insertions, 519 deletions
diff --git a/.github/ci/test.sh b/.github/ci/test.sh
index 285f3f29e..0fd6820d2 100755
--- a/.github/ci/test.sh
+++ b/.github/ci/test.sh
@@ -17,7 +17,7 @@ cargo test --manifest-path ./embassy-futures/Cargo.toml
17cargo test --manifest-path ./embassy-sync/Cargo.toml 17cargo test --manifest-path ./embassy-sync/Cargo.toml
18cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml 18cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
19cargo test --manifest-path ./embassy-hal-internal/Cargo.toml 19cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
20cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver 20cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver,embassy-time-queue-driver/generic-queue-8
21cargo test --manifest-path ./embassy-time-driver/Cargo.toml 21cargo test --manifest-path ./embassy-time-driver/Cargo.toml
22 22
23cargo test --manifest-path ./embassy-boot/Cargo.toml 23cargo test --manifest-path ./embassy-boot/Cargo.toml
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index f8b3c4bbc..a27fae9a8 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -1,11 +1,11 @@
1use core::cell::Cell; 1use core::cell::{Cell, RefCell};
2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; 2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
3 3
4use critical_section::CriticalSection; 4use critical_section::CriticalSection;
5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; 6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
7use embassy_time_driver::Driver; 7use embassy_time_driver::Driver;
8use embassy_time_queue_driver::GlobalTimerQueue; 8use embassy_time_queue_driver::Queue;
9 9
10use crate::interrupt::InterruptExt; 10use crate::interrupt::InterruptExt;
11use crate::{interrupt, pac}; 11use crate::{interrupt, pac};
@@ -111,11 +111,13 @@ struct RtcDriver {
111 period: AtomicU32, 111 period: AtomicU32,
112 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. 112 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
113 alarms: Mutex<AlarmState>, 113 alarms: Mutex<AlarmState>,
114 queue: Mutex<RefCell<Queue>>,
114} 115}
115 116
116embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 117embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
117 period: AtomicU32::new(0), 118 period: AtomicU32::new(0),
118 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 119 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
120 queue: Mutex::new(RefCell::new(Queue::new())),
119}); 121});
120 122
121impl RtcDriver { 123impl RtcDriver {
@@ -194,59 +196,60 @@ impl RtcDriver {
194 alarm.timestamp.set(u64::MAX); 196 alarm.timestamp.set(u64::MAX);
195 197
196 // Call after clearing alarm, so the callback can set another alarm. 198 // Call after clearing alarm, so the callback can set another alarm.
197 TIMER_QUEUE_DRIVER.dispatch(); 199 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
200 while !self.set_alarm(cs, next) {
201 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
202 }
198 } 203 }
199 204
200 fn set_alarm(&self, timestamp: u64) -> bool { 205 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
201 critical_section::with(|cs| { 206 let n = 0;
202 let n = 0; 207 let alarm = &self.alarms.borrow(cs);
203 let alarm = &self.alarms.borrow(cs); 208 alarm.timestamp.set(timestamp);
204 alarm.timestamp.set(timestamp);
205 209
206 let r = rtc(); 210 let r = rtc();
207 211
208 let t = self.now(); 212 let t = self.now();
209 if timestamp <= t { 213 if timestamp <= t {
210 // If alarm timestamp has passed the alarm will not fire. 214 // If alarm timestamp has passed the alarm will not fire.
211 // Disarm the alarm and return `false` to indicate that. 215 // Disarm the alarm and return `false` to indicate that.
212 r.intenclr().write(|w| w.0 = compare_n(n)); 216 r.intenclr().write(|w| w.0 = compare_n(n));
213 217
214 alarm.timestamp.set(u64::MAX); 218 alarm.timestamp.set(u64::MAX);
215 219
216 return false; 220 return false;
217 } 221 }
218 222
219 // If it hasn't triggered yet, setup it in the compare channel. 223 // If it hasn't triggered yet, setup it in the compare channel.
220 224
221 // Write the CC value regardless of whether we're going to enable it now or not. 225 // Write the CC value regardless of whether we're going to enable it now or not.
222 // This way, when we enable it later, the right value is already set. 226 // This way, when we enable it later, the right value is already set.
223 227
224 // nrf52 docs say: 228 // nrf52 docs say:
225 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. 229 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
226 // To workaround this, we never write a timestamp smaller than N+3. 230 // To workaround this, we never write a timestamp smaller than N+3.
227 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. 231 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
228 // 232 //
229 // It is impossible for rtc to tick more than once because 233 // It is impossible for rtc to tick more than once because
230 // - this code takes less time than 1 tick 234 // - this code takes less time than 1 tick
231 // - it runs with interrupts disabled so nothing else can preempt it. 235 // - it runs with interrupts disabled so nothing else can preempt it.
232 // 236 //
233 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed 237 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
234 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, 238 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
235 // and we don't do that here. 239 // and we don't do that here.
236 let safe_timestamp = timestamp.max(t + 3); 240 let safe_timestamp = timestamp.max(t + 3);
237 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); 241 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
238 242
239 let diff = timestamp - t; 243 let diff = timestamp - t;
240 if diff < 0xc00000 { 244 if diff < 0xc00000 {
241 r.intenset().write(|w| w.0 = compare_n(n)); 245 r.intenset().write(|w| w.0 = compare_n(n));
242 } else { 246 } else {
243 // If it's too far in the future, don't setup the compare channel yet. 247 // If it's too far in the future, don't setup the compare channel yet.
244 // It will be setup later by `next_period`. 248 // It will be setup later by `next_period`.
245 r.intenclr().write(|w| w.0 = compare_n(n)); 249 r.intenclr().write(|w| w.0 = compare_n(n));
246 } 250 }
247 251
248 true 252 true
249 })
250 } 253 }
251} 254}
252 255
@@ -258,6 +261,19 @@ impl Driver for RtcDriver {
258 let counter = rtc().counter().read().0; 261 let counter = rtc().counter().read().0;
259 calc_now(period, counter) 262 calc_now(period, counter)
260 } 263 }
264
265 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
266 critical_section::with(|cs| {
267 let mut queue = self.queue.borrow(cs).borrow_mut();
268
269 if queue.schedule_wake(at, waker) {
270 let mut next = queue.next_expiration(self.now());
271 while !self.set_alarm(cs, next) {
272 next = queue.next_expiration(self.now());
273 }
274 }
275 })
276 }
261} 277}
262 278
263#[cfg(feature = "_nrf54l")] 279#[cfg(feature = "_nrf54l")]
@@ -277,8 +293,3 @@ fn RTC1() {
277pub(crate) fn init(irq_prio: crate::interrupt::Priority) { 293pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
278 DRIVER.init(irq_prio) 294 DRIVER.init(irq_prio)
279} 295}
280
281embassy_time_queue_driver::timer_queue_impl!(
282 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
283 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
284);
diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs
index 17ae5fff3..a0eaec10e 100644
--- a/embassy-rp/src/time_driver.rs
+++ b/embassy-rp/src/time_driver.rs
@@ -1,10 +1,11 @@
1//! Timer driver. 1//! Timer driver.
2use core::cell::Cell; 2use core::cell::{Cell, RefCell};
3 3
4use critical_section::CriticalSection;
4use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
5use embassy_sync::blocking_mutex::Mutex; 6use embassy_sync::blocking_mutex::Mutex;
6use embassy_time_driver::Driver; 7use embassy_time_driver::Driver;
7use embassy_time_queue_driver::GlobalTimerQueue; 8use embassy_time_queue_driver::Queue;
8#[cfg(feature = "rp2040")] 9#[cfg(feature = "rp2040")]
9use pac::TIMER; 10use pac::TIMER;
10#[cfg(feature = "_rp235x")] 11#[cfg(feature = "_rp235x")]
@@ -20,12 +21,14 @@ unsafe impl Send for AlarmState {}
20 21
21struct TimerDriver { 22struct TimerDriver {
22 alarms: Mutex<CriticalSectionRawMutex, AlarmState>, 23 alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
24 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
23} 25}
24 26
25embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ 27embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
26 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState { 28 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
27 timestamp: Cell::new(0), 29 timestamp: Cell::new(0),
28 }), 30 }),
31 queue: Mutex::new(RefCell::new(Queue::new()))
29}); 32});
30 33
31impl Driver for TimerDriver { 34impl Driver for TimerDriver {
@@ -39,35 +42,46 @@ impl Driver for TimerDriver {
39 } 42 }
40 } 43 }
41 } 44 }
42}
43 45
44impl TimerDriver { 46 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
45 fn set_alarm(&self, timestamp: u64) -> bool {
46 let n = 0;
47 critical_section::with(|cs| { 47 critical_section::with(|cs| {
48 let alarm = &self.alarms.borrow(cs); 48 let mut queue = self.queue.borrow(cs).borrow_mut();
49 alarm.timestamp.set(timestamp);
50 49
51 // Arm it. 50 if queue.schedule_wake(at, waker) {
52 // Note that we're not checking the high bits at all. This means the irq may fire early 51 let mut next = queue.next_expiration(self.now());
53 // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire 52 while !self.set_alarm(cs, next) {
54 // it is checked if the alarm time has passed. 53 next = queue.next_expiration(self.now());
55 TIMER.alarm(n).write_value(timestamp as u32); 54 }
56
57 let now = self.now();
58 if timestamp <= now {
59 // If alarm timestamp has passed the alarm will not fire.
60 // Disarm the alarm and return `false` to indicate that.
61 TIMER.armed().write(|w| w.set_armed(1 << n));
62
63 alarm.timestamp.set(u64::MAX);
64
65 false
66 } else {
67 true
68 } 55 }
69 }) 56 })
70 } 57 }
58}
59
60impl TimerDriver {
61 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
62 let n = 0;
63 let alarm = &self.alarms.borrow(cs);
64 alarm.timestamp.set(timestamp);
65
66 // Arm it.
67 // Note that we're not checking the high bits at all. This means the irq may fire early
68 // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire
69 // it is checked if the alarm time has passed.
70 TIMER.alarm(n).write_value(timestamp as u32);
71
72 let now = self.now();
73 if timestamp <= now {
74 // If alarm timestamp has passed the alarm will not fire.
75 // Disarm the alarm and return `false` to indicate that.
76 TIMER.armed().write(|w| w.set_armed(1 << n));
77
78 alarm.timestamp.set(u64::MAX);
79
80 false
81 } else {
82 true
83 }
84 }
71 85
72 fn check_alarm(&self) { 86 fn check_alarm(&self) {
73 let n = 0; 87 let n = 0;
@@ -75,7 +89,7 @@ impl TimerDriver {
75 let alarm = &self.alarms.borrow(cs); 89 let alarm = &self.alarms.borrow(cs);
76 let timestamp = alarm.timestamp.get(); 90 let timestamp = alarm.timestamp.get();
77 if timestamp <= self.now() { 91 if timestamp <= self.now() {
78 self.trigger_alarm() 92 self.trigger_alarm(cs)
79 } else { 93 } else {
80 // Not elapsed, arm it again. 94 // Not elapsed, arm it again.
81 // This can happen if it was set more than 2^32 us in the future. 95 // This can happen if it was set more than 2^32 us in the future.
@@ -87,8 +101,11 @@ impl TimerDriver {
87 TIMER.intr().write(|w| w.set_alarm(n, true)); 101 TIMER.intr().write(|w| w.set_alarm(n, true));
88 } 102 }
89 103
90 fn trigger_alarm(&self) { 104 fn trigger_alarm(&self, cs: CriticalSection) {
91 TIMER_QUEUE_DRIVER.dispatch(); 105 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
106 while !self.set_alarm(cs, next) {
107 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
108 }
92 } 109 }
93} 110}
94 111
@@ -125,8 +142,3 @@ fn TIMER_IRQ_0() {
125fn TIMER0_IRQ_0() { 142fn TIMER0_IRQ_0() {
126 DRIVER.check_alarm() 143 DRIVER.check_alarm()
127} 144}
128
129embassy_time_queue_driver::timer_queue_impl!(
130 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
131 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
132);
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 290f857ad..a4c333d82 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -1,13 +1,13 @@
1#![allow(non_snake_case)] 1#![allow(non_snake_case)]
2 2
3use core::cell::Cell; 3use core::cell::{Cell, RefCell};
4use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; 4use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
5 5
6use critical_section::CriticalSection; 6use critical_section::CriticalSection;
7use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 7use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
8use embassy_sync::blocking_mutex::Mutex; 8use embassy_sync::blocking_mutex::Mutex;
9use embassy_time_driver::{Driver, TICK_HZ}; 9use embassy_time_driver::{Driver, TICK_HZ};
10use embassy_time_queue_driver::GlobalTimerQueue; 10use embassy_time_queue_driver::Queue;
11use stm32_metapac::timer::{regs, TimGp16}; 11use stm32_metapac::timer::{regs, TimGp16};
12 12
13use crate::interrupt::typelevel::Interrupt; 13use crate::interrupt::typelevel::Interrupt;
@@ -214,6 +214,7 @@ pub(crate) struct RtcDriver {
214 alarm: Mutex<CriticalSectionRawMutex, AlarmState>, 214 alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
215 #[cfg(feature = "low-power")] 215 #[cfg(feature = "low-power")]
216 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, 216 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
217 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
217} 218}
218 219
219embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 220embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
@@ -221,6 +222,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
221 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 222 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
222 #[cfg(feature = "low-power")] 223 #[cfg(feature = "low-power")]
223 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 224 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
225 queue: Mutex::new(RefCell::new(Queue::new()))
224}); 226});
225 227
226impl RtcDriver { 228impl RtcDriver {
@@ -266,8 +268,7 @@ impl RtcDriver {
266 fn on_interrupt(&self) { 268 fn on_interrupt(&self) {
267 let r = regs_gp16(); 269 let r = regs_gp16();
268 270
269 // XXX: reduce the size of this critical section ? 271 critical_section::with(|cs| {
270 critical_section::with(|_cs| {
271 let sr = r.sr().read(); 272 let sr = r.sr().read();
272 let dier = r.dier().read(); 273 let dier = r.dier().read();
273 274
@@ -288,7 +289,7 @@ impl RtcDriver {
288 289
289 let n = 0; 290 let n = 0;
290 if sr.ccif(n + 1) && dier.ccie(n + 1) { 291 if sr.ccif(n + 1) && dier.ccie(n + 1) {
291 self.trigger_alarm(); 292 self.trigger_alarm(cs);
292 } 293 }
293 }) 294 })
294 } 295 }
@@ -315,8 +316,11 @@ impl RtcDriver {
315 }) 316 })
316 } 317 }
317 318
318 fn trigger_alarm(&self) { 319 fn trigger_alarm(&self, cs: CriticalSection) {
319 TIMER_QUEUE_DRIVER.dispatch(); 320 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
321 while !self.set_alarm(cs, next) {
322 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
323 }
320 } 324 }
321 325
322 /* 326 /*
@@ -366,9 +370,9 @@ impl RtcDriver {
366 // Now, recompute alarm 370 // Now, recompute alarm
367 let alarm = self.alarm.borrow(cs); 371 let alarm = self.alarm.borrow(cs);
368 372
369 if !self.set_alarm(alarm.timestamp.get()) { 373 if !self.set_alarm(cs, alarm.timestamp.get()) {
370 // If the alarm timestamp has passed, we need to trigger it 374 // If the alarm timestamp has passed, we need to trigger it
371 self.trigger_alarm(); 375 self.trigger_alarm(cs);
372 } 376 }
373 } 377 }
374 378
@@ -441,49 +445,47 @@ impl RtcDriver {
441 }) 445 })
442 } 446 }
443 447
444 fn set_alarm(&self, timestamp: u64) -> bool { 448 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
445 critical_section::with(|cs| { 449 let r = regs_gp16();
446 let r = regs_gp16();
447 450
448 let n = 0; 451 let n = 0;
449 self.alarm.borrow(cs).timestamp.set(timestamp); 452 self.alarm.borrow(cs).timestamp.set(timestamp);
450 453
451 let t = self.now(); 454 let t = self.now();
452 if timestamp <= t { 455 if timestamp <= t {
453 // If alarm timestamp has passed the alarm will not fire. 456 // If alarm timestamp has passed the alarm will not fire.
454 // Disarm the alarm and return `false` to indicate that. 457 // Disarm the alarm and return `false` to indicate that.
455 r.dier().modify(|w| w.set_ccie(n + 1, false)); 458 r.dier().modify(|w| w.set_ccie(n + 1, false));
456 459
457 self.alarm.borrow(cs).timestamp.set(u64::MAX); 460 self.alarm.borrow(cs).timestamp.set(u64::MAX);
458 461
459 return false; 462 return false;
460 } 463 }
461 464
462 // Write the CCR value regardless of whether we're going to enable it now or not. 465 // Write the CCR value regardless of whether we're going to enable it now or not.
463 // This way, when we enable it later, the right value is already set. 466 // This way, when we enable it later, the right value is already set.
464 r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); 467 r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
465 468
466 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. 469 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
467 let diff = timestamp - t; 470 let diff = timestamp - t;
468 r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); 471 r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
469 472
470 // Reevaluate if the alarm timestamp is still in the future 473 // Reevaluate if the alarm timestamp is still in the future
471 let t = self.now(); 474 let t = self.now();
472 if timestamp <= t { 475 if timestamp <= t {
473 // If alarm timestamp has passed since we set it, we have a race condition and 476 // If alarm timestamp has passed since we set it, we have a race condition and
474 // the alarm may or may not have fired. 477 // the alarm may or may not have fired.
475 // Disarm the alarm and return `false` to indicate that. 478 // Disarm the alarm and return `false` to indicate that.
476 // It is the caller's responsibility to handle this ambiguity. 479 // It is the caller's responsibility to handle this ambiguity.
477 r.dier().modify(|w| w.set_ccie(n + 1, false)); 480 r.dier().modify(|w| w.set_ccie(n + 1, false));
478 481
479 self.alarm.borrow(cs).timestamp.set(u64::MAX); 482 self.alarm.borrow(cs).timestamp.set(u64::MAX);
480 483
481 return false; 484 return false;
482 } 485 }
483 486
484 // We're confident the alarm will ring in the future. 487 // We're confident the alarm will ring in the future.
485 true 488 true
486 })
487 } 489 }
488} 490}
489 491
@@ -496,6 +498,19 @@ impl Driver for RtcDriver {
496 let counter = r.cnt().read().cnt(); 498 let counter = r.cnt().read().cnt();
497 calc_now(period, counter) 499 calc_now(period, counter)
498 } 500 }
501
502 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
503 critical_section::with(|cs| {
504 let mut queue = self.queue.borrow(cs).borrow_mut();
505
506 if queue.schedule_wake(at, waker) {
507 let mut next = queue.next_expiration(self.now());
508 while !self.set_alarm(cs, next) {
509 next = queue.next_expiration(self.now());
510 }
511 }
512 })
513 }
499} 514}
500 515
501#[cfg(feature = "low-power")] 516#[cfg(feature = "low-power")]
@@ -506,8 +521,3 @@ pub(crate) fn get_driver() -> &'static RtcDriver {
506pub(crate) fn init(cs: CriticalSection) { 521pub(crate) fn init(cs: CriticalSection) {
507 DRIVER.init(cs) 522 DRIVER.init(cs)
508} 523}
509
510embassy_time_queue_driver::timer_queue_impl!(
511 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
512 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
513);
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs
index ffb363cd7..090969d8c 100644
--- a/embassy-time-driver/src/lib.rs
+++ b/embassy-time-driver/src/lib.rs
@@ -38,6 +38,8 @@
38//! # Example 38//! # Example
39//! 39//!
40//! ``` 40//! ```
41//! use core::task::Waker;
42//!
41//! use embassy_time_driver::Driver; 43//! use embassy_time_driver::Driver;
42//! 44//!
43//! struct MyDriver{} // not public! 45//! struct MyDriver{} // not public!
@@ -46,6 +48,10 @@
46//! fn now(&self) -> u64 { 48//! fn now(&self) -> u64 {
47//! todo!() 49//! todo!()
48//! } 50//! }
51//!
52//! fn schedule_wake(&self, at: u64, waker: &Waker) {
53//! todo!()
54//! }
49//! } 55//! }
50//! 56//!
51//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); 57//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
@@ -54,6 +60,8 @@
54//! ## Feature flags 60//! ## Feature flags
55#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] 61#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
56 62
63use core::task::Waker;
64
57mod tick; 65mod tick;
58 66
59/// Ticks per second of the global timebase. 67/// Ticks per second of the global timebase.
@@ -74,6 +82,10 @@ pub trait Driver: Send + Sync + 'static {
74 /// you MUST extend them to 64-bit, for example by counting overflows in software, 82 /// you MUST extend them to 64-bit, for example by counting overflows in software,
75 /// or chaining multiple timers together. 83 /// or chaining multiple timers together.
76 fn now(&self) -> u64; 84 fn now(&self) -> u64;
85
86 /// Schedules a waker to be awoken at moment `at`.
87 /// If this moment is in the past, the waker might be awoken immediately.
88 fn schedule_wake(&self, at: u64, waker: &Waker);
77} 89}
78 90
79extern "Rust" { 91extern "Rust" {
@@ -97,5 +109,10 @@ macro_rules! time_driver_impl {
97 fn _embassy_time_now() -> u64 { 109 fn _embassy_time_now() -> u64 {
98 <$t as $crate::Driver>::now(&$name) 110 <$t as $crate::Driver>::now(&$name)
99 } 111 }
112
113 #[no_mangle]
114 fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {
115 <$t as $crate::Driver>::schedule_wake(&$name, at, waker);
116 }
100 }; 117 };
101} 118}
diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs
index 2d5fd449a..ed490a0ef 100644
--- a/embassy-time-queue-driver/src/lib.rs
+++ b/embassy-time-queue-driver/src/lib.rs
@@ -49,23 +49,18 @@
49//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); 49//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{});
50//! ``` 50//! ```
51 51
52use core::task::Waker;
53
52#[cfg(not(feature = "integrated-timers"))] 54#[cfg(not(feature = "integrated-timers"))]
53pub mod queue_generic; 55pub mod queue_generic;
54#[cfg(feature = "integrated-timers")] 56#[cfg(feature = "integrated-timers")]
55pub mod queue_integrated; 57pub mod queue_integrated;
56 58
57use core::cell::RefCell; 59#[cfg(feature = "integrated-timers")]
58use core::task::Waker; 60pub use queue_integrated::Queue;
59
60use critical_section::Mutex;
61 61
62/// Timer queue 62#[cfg(not(feature = "integrated-timers"))]
63pub trait TimerQueue { 63pub use queue_generic::Queue;
64 /// Schedules a waker in the queue to be awoken at moment `at`.
65 ///
66 /// If this moment is in the past, the waker might be awoken immediately.
67 fn schedule_wake(&'static self, at: u64, waker: &Waker);
68}
69 64
70extern "Rust" { 65extern "Rust" {
71 fn _embassy_time_schedule_wake(at: u64, waker: &Waker); 66 fn _embassy_time_schedule_wake(at: u64, waker: &Waker);
@@ -73,7 +68,10 @@ extern "Rust" {
73 68
74/// Schedule the given waker to be woken at `at`. 69/// Schedule the given waker to be woken at `at`.
75pub fn schedule_wake(at: u64, waker: &Waker) { 70pub fn schedule_wake(at: u64, waker: &Waker) {
76 #[cfg(feature = "integrated-timers")] 71 // This function is not implemented in embassy-time-driver because it needs access to executor
72 // internals. The function updates task state, then delegates to the implementation provided
73 // by the time driver.
74 #[cfg(not(feature = "_generic-queue"))]
77 { 75 {
78 use embassy_executor::raw::task_from_waker; 76 use embassy_executor::raw::task_from_waker;
79 use embassy_executor::raw::timer_queue::TimerEnqueueOperation; 77 use embassy_executor::raw::timer_queue::TimerEnqueueOperation;
@@ -89,121 +87,3 @@ pub fn schedule_wake(at: u64, waker: &Waker) {
89 } 87 }
90 unsafe { _embassy_time_schedule_wake(at, waker) } 88 unsafe { _embassy_time_schedule_wake(at, waker) }
91} 89}
92
93/// Set the TimerQueue implementation.
94///
95/// See the module documentation for an example.
96#[macro_export]
97macro_rules! timer_queue_impl {
98 (static $name:ident: $t: ty = $val:expr) => {
99 static $name: $t = $val;
100
101 #[no_mangle]
102 fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {
103 <$t as $crate::TimerQueue>::schedule_wake(&$name, at, waker);
104 }
105 };
106}
107
108#[cfg(feature = "integrated-timers")]
109type InnerQueue = queue_integrated::TimerQueue;
110
111#[cfg(not(feature = "integrated-timers"))]
112type InnerQueue = queue_generic::Queue;
113
114/// A timer queue implementation that can be used as a global timer queue.
115///
116/// This implementation is not thread-safe, and should be protected by a mutex of some sort.
117pub struct GenericTimerQueue<F: Fn(u64) -> bool> {
118 queue: InnerQueue,
119 set_alarm: F,
120}
121
122impl<F: Fn(u64) -> bool> GenericTimerQueue<F> {
123 /// Creates a new timer queue.
124 ///
125 /// `set_alarm` is a function that should set the next alarm time. The function should
126 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
127 pub const fn new(set_alarm: F) -> Self {
128 Self {
129 queue: InnerQueue::new(),
130 set_alarm,
131 }
132 }
133
134 /// Schedules a task to run at a specific time, and returns whether any changes were made.
135 pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) {
136 #[cfg(feature = "integrated-timers")]
137 let waker = embassy_executor::raw::task_from_waker(waker);
138
139 if self.queue.schedule_wake(at, waker) {
140 self.dispatch()
141 }
142 }
143
144 /// Dequeues expired timers and returns the next alarm time.
145 pub fn next_expiration(&mut self, now: u64) -> u64 {
146 self.queue.next_expiration(now)
147 }
148
149 /// Handle the alarm.
150 ///
151 /// Call this function when the next alarm is due.
152 pub fn dispatch(&mut self) {
153 let mut next_expiration = self.next_expiration(embassy_time_driver::now());
154
155 while !(self.set_alarm)(next_expiration) {
156 // next_expiration is in the past, dequeue and find a new expiration
157 next_expiration = self.next_expiration(next_expiration);
158 }
159 }
160}
161
162/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`].
163pub struct GlobalTimerQueue {
164 inner: Mutex<RefCell<GenericTimerQueue<fn(u64) -> bool>>>,
165}
166
167impl GlobalTimerQueue {
168 /// Creates a new timer queue.
169 ///
170 /// `set_alarm` is a function that should set the next alarm time. The function should
171 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
172 pub const fn new(set_alarm: fn(u64) -> bool) -> Self {
173 Self {
174 inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))),
175 }
176 }
177
178 /// Schedules a task to run at a specific time, and returns whether any changes were made.
179 pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
180 critical_section::with(|cs| {
181 let mut inner = self.inner.borrow_ref_mut(cs);
182 inner.schedule_wake(at, waker);
183 });
184 }
185
186 /// Dequeues expired timers and returns the next alarm time.
187 pub fn next_expiration(&self, now: u64) -> u64 {
188 critical_section::with(|cs| {
189 let mut inner = self.inner.borrow_ref_mut(cs);
190 inner.next_expiration(now)
191 })
192 }
193
194 /// Handle the alarm.
195 ///
196 /// Call this function when the next alarm is due.
197 pub fn dispatch(&self) {
198 critical_section::with(|cs| {
199 let mut inner = self.inner.borrow_ref_mut(cs);
200 inner.dispatch()
201 })
202 }
203}
204
205impl TimerQueue for GlobalTimerQueue {
206 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
207 GlobalTimerQueue::schedule_wake(self, at, waker)
208 }
209}
diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs
index b905c00c3..6bb4c0c1a 100644
--- a/embassy-time-queue-driver/src/queue_integrated.rs
+++ b/embassy-time-queue-driver/src/queue_integrated.rs
@@ -1,15 +1,16 @@
1//! Timer queue operations. 1//! Timer queue operations.
2use core::cell::Cell; 2use core::cell::Cell;
3use core::cmp::min; 3use core::cmp::min;
4use core::task::Waker;
4 5
5use embassy_executor::raw::TaskRef; 6use embassy_executor::raw::TaskRef;
6 7
7/// A timer queue, with items integrated into tasks. 8/// A timer queue, with items integrated into tasks.
8pub struct TimerQueue { 9pub struct Queue {
9 head: Cell<Option<TaskRef>>, 10 head: Cell<Option<TaskRef>>,
10} 11}
11 12
12impl TimerQueue { 13impl Queue {
13 /// Creates a new timer queue. 14 /// Creates a new timer queue.
14 pub const fn new() -> Self { 15 pub const fn new() -> Self {
15 Self { head: Cell::new(None) } 16 Self { head: Cell::new(None) }
@@ -19,11 +20,12 @@ impl TimerQueue {
19 /// 20 ///
20 /// If this function returns `true`, the called should find the next expiration time and set 21 /// If this function returns `true`, the called should find the next expiration time and set
21 /// a new alarm for that time. 22 /// a new alarm for that time.
22 pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { 23 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
23 let item = p.timer_queue_item(); 24 let task = embassy_executor::raw::task_from_waker(waker);
25 let item = task.timer_queue_item();
24 if item.next.get().is_none() { 26 if item.next.get().is_none() {
25 // If not in the queue, add it and update. 27 // If not in the queue, add it and update.
26 let prev = self.head.replace(Some(p)); 28 let prev = self.head.replace(Some(task));
27 item.next.set(if prev.is_none() { 29 item.next.set(if prev.is_none() {
28 Some(unsafe { TaskRef::dangling() }) 30 Some(unsafe { TaskRef::dangling() })
29 } else { 31 } else {
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);