aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/ci/test.sh2
-rwxr-xr-xci-xtensa.sh4
-rwxr-xr-xci.sh4
-rw-r--r--embassy-executor/Cargo.toml2
-rw-r--r--embassy-executor/src/arch/avr.rs4
-rw-r--r--embassy-executor/src/arch/cortex_m.rs6
-rw-r--r--embassy-executor/src/arch/riscv32.rs4
-rw-r--r--embassy-executor/src/arch/spin.rs4
-rw-r--r--embassy-executor/src/arch/std.rs4
-rw-r--r--embassy-executor/src/arch/wasm.rs4
-rw-r--r--embassy-executor/src/raw/mod.rs135
-rw-r--r--embassy-executor/src/raw/timer_queue.rs89
-rw-r--r--embassy-executor/src/raw/util.rs5
-rw-r--r--embassy-executor/tests/test.rs3
-rw-r--r--embassy-nrf/Cargo.toml3
-rw-r--r--embassy-nrf/src/time_driver.rs117
-rw-r--r--embassy-rp/Cargo.toml3
-rw-r--r--embassy-rp/src/time_driver.rs130
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/low_power.rs3
-rw-r--r--embassy-stm32/src/time_driver.rs156
-rw-r--r--embassy-time-driver/src/lib.rs135
-rw-r--r--embassy-time-queue-driver/Cargo.toml33
-rw-r--r--embassy-time-queue-driver/src/lib.rs136
-rw-r--r--embassy-time-queue-driver/src/queue_generic.rs146
-rw-r--r--embassy-time/Cargo.toml24
-rw-r--r--embassy-time/src/driver_mock.rs85
-rw-r--r--embassy-time/src/driver_std.rs121
-rw-r--r--embassy-time/src/driver_wasm.rs80
-rw-r--r--embassy-time/src/lib.rs2
-rw-r--r--embassy-time/src/queue_generic.rs346
-rw-r--r--examples/nrf52840-rtic/Cargo.toml3
32 files changed, 613 insertions, 1184 deletions
diff --git a/.github/ci/test.sh b/.github/ci/test.sh
index 0fe088bfe..285f3f29e 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 generic-queue,mock-driver 20cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver
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/ci-xtensa.sh b/ci-xtensa.sh
index 32d362def..2cac7444c 100755
--- a/ci-xtensa.sh
+++ b/ci-xtensa.sh
@@ -24,7 +24,9 @@ cargo batch \
24 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ 24 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \
25 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ 25 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \
26 --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ 26 --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \
27 --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ 27 --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \
28 --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features integrated-timers \
29 --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \
28 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ 30 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
29 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ 31 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
30 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ 32 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
diff --git a/ci.sh b/ci.sh
index 307e268c4..71b862632 100755
--- a/ci.sh
+++ b/ci.sh
@@ -45,7 +45,9 @@ cargo batch \
45 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ 45 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
46 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ 46 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \
47 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ 47 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
48 --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ 48 --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \
49 --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features integrated-timers \
50 --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \
49 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ 51 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
50 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ 52 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
51 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ 53 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index 0a5360e5d..862d25b59 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -68,7 +68,7 @@ nightly = ["embassy-executor-macros/nightly"]
68turbowakers = [] 68turbowakers = []
69 69
70## Use the executor-integrated `embassy-time` timer queue. 70## Use the executor-integrated `embassy-time` timer queue.
71integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"] 71integrated-timers = ["dep:embassy-time-driver"]
72 72
73#! ### Architecture 73#! ### Architecture
74_arch = [] # some arch was picked 74_arch = [] # some arch was picked
diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs
index 7f9ed4421..70085d04d 100644
--- a/embassy-executor/src/arch/avr.rs
+++ b/embassy-executor/src/arch/avr.rs
@@ -53,10 +53,6 @@ mod thread {
53 /// 53 ///
54 /// This function never returns. 54 /// This function never returns.
55 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 55 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
56 unsafe {
57 self.inner.initialize();
58 }
59
60 init(self.inner.spawner()); 56 init(self.inner.spawner());
61 57
62 loop { 58 loop {
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 0c2af88a6..5c517e0a2 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -98,9 +98,6 @@ mod thread {
98 /// 98 ///
99 /// This function never returns. 99 /// This function never returns.
100 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 100 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
101 unsafe {
102 self.inner.initialize();
103 }
104 init(self.inner.spawner()); 101 init(self.inner.spawner());
105 102
106 loop { 103 loop {
@@ -210,9 +207,6 @@ mod interrupt {
210 } 207 }
211 208
212 let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; 209 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
213 unsafe {
214 executor.initialize();
215 }
216 210
217 unsafe { NVIC::unmask(irq) } 211 unsafe { NVIC::unmask(irq) }
218 212
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index 715e5f3cf..01e63a9fd 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -54,10 +54,6 @@ mod thread {
54 /// 54 ///
55 /// This function never returns. 55 /// This function never returns.
56 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 56 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
57 unsafe {
58 self.inner.initialize();
59 }
60
61 init(self.inner.spawner()); 57 init(self.inner.spawner());
62 58
63 loop { 59 loop {
diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs
index 54c7458b3..340023620 100644
--- a/embassy-executor/src/arch/spin.rs
+++ b/embassy-executor/src/arch/spin.rs
@@ -48,10 +48,6 @@ mod thread {
48 /// 48 ///
49 /// This function never returns. 49 /// This function never returns.
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
51 unsafe {
52 self.inner.initialize();
53 }
54
55 init(self.inner.spawner()); 51 init(self.inner.spawner());
56 52
57 loop { 53 loop {
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index 948c7711b..b02b15988 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -55,10 +55,6 @@ mod thread {
55 /// 55 ///
56 /// This function never returns. 56 /// This function never returns.
57 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 57 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
58 unsafe {
59 self.inner.initialize();
60 }
61
62 init(self.inner.spawner()); 58 init(self.inner.spawner());
63 59
64 loop { 60 loop {
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index 35025f11f..f9d0f935c 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -71,10 +71,6 @@ mod thread {
71 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 71 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
72 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { 72 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
73 unsafe { 73 unsafe {
74 self.inner.initialize();
75 }
76
77 unsafe {
78 let executor = &self.inner; 74 let executor = &self.inner;
79 let future = Closure::new(move |_| { 75 let future = Closure::new(move |_| {
80 executor.poll(); 76 executor.poll();
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 3f93eae6f..80bd49bad 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -17,7 +17,7 @@ mod run_queue;
17mod state; 17mod state;
18 18
19#[cfg(feature = "integrated-timers")] 19#[cfg(feature = "integrated-timers")]
20mod timer_queue; 20pub mod timer_queue;
21#[cfg(feature = "trace")] 21#[cfg(feature = "trace")]
22mod trace; 22mod trace;
23pub(crate) mod util; 23pub(crate) mod util;
@@ -31,9 +31,6 @@ use core::pin::Pin;
31use core::ptr::NonNull; 31use core::ptr::NonNull;
32use core::task::{Context, Poll}; 32use core::task::{Context, Poll};
33 33
34#[cfg(feature = "integrated-timers")]
35use embassy_time_driver::AlarmHandle;
36
37use self::run_queue::{RunQueue, RunQueueItem}; 34use self::run_queue::{RunQueue, RunQueueItem};
38use self::state::State; 35use self::state::State;
39use self::util::{SyncUnsafeCell, UninitCell}; 36use self::util::{SyncUnsafeCell, UninitCell};
@@ -47,8 +44,7 @@ pub(crate) struct TaskHeader {
47 pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>, 44 pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
48 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, 45 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
49 46
50 #[cfg(feature = "integrated-timers")] 47 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
51 pub(crate) expires_at: SyncUnsafeCell<u64>,
52 #[cfg(feature = "integrated-timers")] 48 #[cfg(feature = "integrated-timers")]
53 pub(crate) timer_queue_item: timer_queue::TimerQueueItem, 49 pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
54} 50}
@@ -80,6 +76,12 @@ impl TaskRef {
80 unsafe { self.ptr.as_ref() } 76 unsafe { self.ptr.as_ref() }
81 } 77 }
82 78
79 /// Returns a reference to the executor that the task is currently running on.
80 #[cfg(feature = "integrated-timers")]
81 pub unsafe fn executor(self) -> Option<&'static Executor> {
82 self.header().executor.get().map(|e| Executor::wrap(e))
83 }
84
83 /// The returned pointer is valid for the entire TaskStorage. 85 /// The returned pointer is valid for the entire TaskStorage.
84 pub(crate) fn as_ptr(self) -> *const TaskHeader { 86 pub(crate) fn as_ptr(self) -> *const TaskHeader {
85 self.ptr.as_ptr() 87 self.ptr.as_ptr()
@@ -121,8 +123,6 @@ impl<F: Future + 'static> TaskStorage<F> {
121 poll_fn: SyncUnsafeCell::new(None), 123 poll_fn: SyncUnsafeCell::new(None),
122 124
123 #[cfg(feature = "integrated-timers")] 125 #[cfg(feature = "integrated-timers")]
124 expires_at: SyncUnsafeCell::new(0),
125 #[cfg(feature = "integrated-timers")]
126 timer_queue_item: timer_queue::TimerQueueItem::new(), 126 timer_queue_item: timer_queue::TimerQueueItem::new(),
127 }, 127 },
128 future: UninitCell::uninit(), 128 future: UninitCell::uninit(),
@@ -160,9 +160,6 @@ impl<F: Future + 'static> TaskStorage<F> {
160 Poll::Ready(_) => { 160 Poll::Ready(_) => {
161 this.future.drop_in_place(); 161 this.future.drop_in_place();
162 this.raw.state.despawn(); 162 this.raw.state.despawn();
163
164 #[cfg(feature = "integrated-timers")]
165 this.raw.expires_at.set(u64::MAX);
166 } 163 }
167 Poll::Pending => {} 164 Poll::Pending => {}
168 } 165 }
@@ -316,34 +313,16 @@ impl Pender {
316pub(crate) struct SyncExecutor { 313pub(crate) struct SyncExecutor {
317 run_queue: RunQueue, 314 run_queue: RunQueue,
318 pender: Pender, 315 pender: Pender,
319
320 #[cfg(feature = "integrated-timers")]
321 pub(crate) timer_queue: timer_queue::TimerQueue,
322 #[cfg(feature = "integrated-timers")]
323 alarm: AlarmHandle,
324} 316}
325 317
326impl SyncExecutor { 318impl SyncExecutor {
327 pub(crate) fn new(pender: Pender) -> Self { 319 pub(crate) fn new(pender: Pender) -> Self {
328 #[cfg(feature = "integrated-timers")]
329 let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) };
330
331 Self { 320 Self {
332 run_queue: RunQueue::new(), 321 run_queue: RunQueue::new(),
333 pender, 322 pender,
334
335 #[cfg(feature = "integrated-timers")]
336 timer_queue: timer_queue::TimerQueue::new(),
337 #[cfg(feature = "integrated-timers")]
338 alarm,
339 } 323 }
340 } 324 }
341 325
342 pub(crate) unsafe fn initialize(&'static self) {
343 #[cfg(feature = "integrated-timers")]
344 embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
345 }
346
347 /// Enqueue a task in the task queue 326 /// Enqueue a task in the task queue
348 /// 327 ///
349 /// # Safety 328 /// # Safety
@@ -360,12 +339,6 @@ impl SyncExecutor {
360 } 339 }
361 } 340 }
362 341
363 #[cfg(feature = "integrated-timers")]
364 fn alarm_callback(ctx: *mut ()) {
365 let this: &Self = unsafe { &*(ctx as *const Self) };
366 this.pender.pend();
367 }
368
369 pub(super) unsafe fn spawn(&'static self, task: TaskRef) { 342 pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
370 task.header().executor.set(Some(self)); 343 task.header().executor.set(Some(self));
371 344
@@ -379,56 +352,27 @@ impl SyncExecutor {
379 /// 352 ///
380 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. 353 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
381 pub(crate) unsafe fn poll(&'static self) { 354 pub(crate) unsafe fn poll(&'static self) {
382 #[allow(clippy::never_loop)] 355 self.run_queue.dequeue_all(|p| {
383 loop { 356 let task = p.header();
384 #[cfg(feature = "integrated-timers")] 357
385 self.timer_queue 358 if !task.state.run_dequeue() {
386 .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); 359 // If task is not running, ignore it. This can happen in the following scenario:
387 360 // - Task gets dequeued, poll starts
388 self.run_queue.dequeue_all(|p| { 361 // - While task is being polled, it gets woken. It gets placed in the queue.
389 let task = p.header(); 362 // - Task poll finishes, returning done=true
390 363 // - RUNNING bit is cleared, but the task is already in the queue.
391 #[cfg(feature = "integrated-timers")] 364 return;
392 task.expires_at.set(u64::MAX); 365 }
393
394 if !task.state.run_dequeue() {
395 // If task is not running, ignore it. This can happen in the following scenario:
396 // - Task gets dequeued, poll starts
397 // - While task is being polled, it gets woken. It gets placed in the queue.
398 // - Task poll finishes, returning done=true
399 // - RUNNING bit is cleared, but the task is already in the queue.
400 return;
401 }
402
403 #[cfg(feature = "trace")]
404 trace::task_exec_begin(self, &p);
405 366
406 // Run the task 367 #[cfg(feature = "trace")]
407 task.poll_fn.get().unwrap_unchecked()(p); 368 trace::task_exec_begin(self, &p);
408 369
409 #[cfg(feature = "trace")] 370 // Run the task
410 trace::task_exec_end(self, &p); 371 task.poll_fn.get().unwrap_unchecked()(p);
411 372
412 // Enqueue or update into timer_queue 373 #[cfg(feature = "trace")]
413 #[cfg(feature = "integrated-timers")] 374 trace::task_exec_end(self, &p);
414 self.timer_queue.update(p); 375 });
415 });
416
417 #[cfg(feature = "integrated-timers")]
418 {
419 // If this is already in the past, set_alarm might return false
420 // In that case do another poll loop iteration.
421 let next_expiration = self.timer_queue.next_expiration();
422 if embassy_time_driver::set_alarm(self.alarm, next_expiration) {
423 break;
424 }
425 }
426
427 #[cfg(not(feature = "integrated-timers"))]
428 {
429 break;
430 }
431 }
432 376
433 #[cfg(feature = "trace")] 377 #[cfg(feature = "trace")]
434 trace::executor_idle(self) 378 trace::executor_idle(self)
@@ -494,15 +438,6 @@ impl Executor {
494 } 438 }
495 } 439 }
496 440
497 /// Initializes the executor.
498 ///
499 /// # Safety
500 ///
501 /// This function must be called once before any other method is called.
502 pub unsafe fn initialize(&'static self) {
503 self.inner.initialize();
504 }
505
506 /// Spawn a task in this executor. 441 /// Spawn a task in this executor.
507 /// 442 ///
508 /// # Safety 443 /// # Safety
@@ -575,21 +510,3 @@ pub fn wake_task_no_pend(task: TaskRef) {
575 } 510 }
576 } 511 }
577} 512}
578
579#[cfg(feature = "integrated-timers")]
580struct TimerQueue;
581
582#[cfg(feature = "integrated-timers")]
583impl embassy_time_queue_driver::TimerQueue for TimerQueue {
584 fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) {
585 let task = waker::task_from_waker(waker);
586 let task = task.header();
587 unsafe {
588 let expires_at = task.expires_at.get();
589 task.expires_at.set(expires_at.min(at));
590 }
591 }
592}
593
594#[cfg(feature = "integrated-timers")]
595embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs
index 94a5f340b..953bf014f 100644
--- a/embassy-executor/src/raw/timer_queue.rs
+++ b/embassy-executor/src/raw/timer_queue.rs
@@ -1,75 +1,100 @@
1//! Timer queue operations.
1use core::cmp::min; 2use core::cmp::min;
2 3
4use super::util::SyncUnsafeCell;
3use super::TaskRef; 5use super::TaskRef;
4use crate::raw::util::SyncUnsafeCell;
5 6
6pub(crate) struct TimerQueueItem { 7pub(crate) struct TimerQueueItem {
7 next: SyncUnsafeCell<Option<TaskRef>>, 8 next: SyncUnsafeCell<Option<TaskRef>>,
9 expires_at: SyncUnsafeCell<u64>,
8} 10}
9 11
10impl TimerQueueItem { 12impl TimerQueueItem {
11 pub const fn new() -> Self { 13 pub const fn new() -> Self {
12 Self { 14 Self {
13 next: SyncUnsafeCell::new(None), 15 next: SyncUnsafeCell::new(None),
16 expires_at: SyncUnsafeCell::new(0),
14 } 17 }
15 } 18 }
16} 19}
17 20
18pub(crate) struct TimerQueue { 21/// A timer queue, with items integrated into tasks.
22pub struct TimerQueue {
19 head: SyncUnsafeCell<Option<TaskRef>>, 23 head: SyncUnsafeCell<Option<TaskRef>>,
20} 24}
21 25
22impl TimerQueue { 26impl TimerQueue {
27 /// Creates a new timer queue.
23 pub const fn new() -> Self { 28 pub const fn new() -> Self {
24 Self { 29 Self {
25 head: SyncUnsafeCell::new(None), 30 head: SyncUnsafeCell::new(None),
26 } 31 }
27 } 32 }
28 33
29 pub(crate) unsafe fn update(&self, p: TaskRef) { 34 /// Schedules a task to run at a specific time.
30 let task = p.header(); 35 ///
31 if task.expires_at.get() != u64::MAX { 36 /// If this function returns `true`, the called should find the next expiration time and set
37 /// a new alarm for that time.
38 pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool {
39 unsafe {
40 let task = p.header();
41 let item = &task.timer_queue_item;
32 if task.state.timer_enqueue() { 42 if task.state.timer_enqueue() {
33 task.timer_queue_item.next.set(self.head.get()); 43 // If not in the queue, add it and update.
34 self.head.set(Some(p)); 44 let prev = self.head.replace(Some(p));
45 item.next.set(prev);
46 } else if at <= item.expires_at.get() {
47 // If expiration is sooner than previously set, update.
48 } else {
49 // Task does not need to be updated.
50 return false;
35 } 51 }
52
53 item.expires_at.set(at);
54 true
36 } 55 }
37 } 56 }
38 57
39 pub(crate) unsafe fn next_expiration(&self) -> u64 { 58 /// Dequeues expired timers and returns the next alarm time.
40 let mut res = u64::MAX; 59 ///
41 self.retain(|p| { 60 /// The provided callback will be called for each expired task. Tasks that never expire
42 let task = p.header(); 61 /// will be removed, but the callback will not be called.
43 let expires = task.expires_at.get(); 62 pub fn next_expiration(&mut self, now: u64) -> u64 {
44 res = min(res, expires); 63 let mut next_expiration = u64::MAX;
45 expires != u64::MAX
46 });
47 res
48 }
49 64
50 pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) {
51 self.retain(|p| { 65 self.retain(|p| {
52 let task = p.header(); 66 let task = p.header();
53 if task.expires_at.get() <= now { 67 let item = &task.timer_queue_item;
54 on_task(p); 68 let expires = unsafe { item.expires_at.get() };
69
70 if expires <= now {
71 // Timer expired, process task.
72 super::wake_task(p);
55 false 73 false
56 } else { 74 } else {
57 true 75 // Timer didn't yet expire, or never expires.
76 next_expiration = min(next_expiration, expires);
77 expires != u64::MAX
58 } 78 }
59 }); 79 });
80
81 next_expiration
60 } 82 }
61 83
62 pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { 84 fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) {
63 let mut prev = &self.head; 85 unsafe {
64 while let Some(p) = prev.get() { 86 let mut prev = &self.head;
65 let task = p.header(); 87 while let Some(p) = prev.get() {
66 if f(p) { 88 let task = p.header();
67 // Skip to next 89 let item = &task.timer_queue_item;
68 prev = &task.timer_queue_item.next; 90 if f(p) {
69 } else { 91 // Skip to next
70 // Remove it 92 prev = &item.next;
71 prev.set(task.timer_queue_item.next.get()); 93 } else {
72 task.state.timer_dequeue(); 94 // Remove it
95 prev.set(item.next.get());
96 task.state.timer_dequeue();
97 }
73 } 98 }
74 } 99 }
75 } 100 }
diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs
index c46085e45..e2633658a 100644
--- a/embassy-executor/src/raw/util.rs
+++ b/embassy-executor/src/raw/util.rs
@@ -54,4 +54,9 @@ impl<T> SyncUnsafeCell<T> {
54 { 54 {
55 *self.value.get() 55 *self.value.get()
56 } 56 }
57
58 #[cfg(feature = "integrated-timers")]
59 pub unsafe fn replace(&self, value: T) -> T {
60 core::mem::replace(&mut *self.value.get(), value)
61 }
57} 62}
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs
index 8054bf7eb..0ce1f1891 100644
--- a/embassy-executor/tests/test.rs
+++ b/embassy-executor/tests/test.rs
@@ -40,9 +40,6 @@ fn setup() -> (&'static Executor, Trace) {
40 let trace = Trace::new(); 40 let trace = Trace::new();
41 let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut (); 41 let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
42 let executor = &*Box::leak(Box::new(Executor::new(context))); 42 let executor = &*Box::leak(Box::new(Executor::new(context)));
43 unsafe {
44 executor.initialize();
45 }
46 43
47 (executor, trace) 44 (executor, trace)
48} 45}
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 9da050a22..48f80bb5e 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -119,7 +119,7 @@ _nrf52 = ["_ppi"]
119_nrf51 = ["_ppi"] 119_nrf51 = ["_ppi"]
120_nrf91 = [] 120_nrf91 = []
121 121
122_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"] 122_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-driver"]
123 123
124# trustzone state. 124# trustzone state.
125_s = [] 125_s = []
@@ -135,6 +135,7 @@ _nrf52832_anomaly_109 = []
135 135
136[dependencies] 136[dependencies]
137embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 137embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
138embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
138embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } 139embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
139embassy-sync = { version = "0.6.1", path = "../embassy-sync" } 140embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
140embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } 141embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index 9ba38ec1b..f8b3c4bbc 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;
2use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; 2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
3use core::{mem, ptr};
4 3
5use critical_section::CriticalSection; 4use critical_section::CriticalSection;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; 6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
8use embassy_time_driver::{AlarmHandle, Driver}; 7use embassy_time_driver::Driver;
8use embassy_time_queue_driver::GlobalTimerQueue;
9 9
10use crate::interrupt::InterruptExt; 10use crate::interrupt::InterruptExt;
11use crate::{interrupt, pac}; 11use crate::{interrupt, pac};
@@ -94,11 +94,6 @@ mod test {
94 94
95struct AlarmState { 95struct AlarmState {
96 timestamp: Cell<u64>, 96 timestamp: Cell<u64>,
97
98 // This is really a Option<(fn(*mut ()), *mut ())>
99 // but fn pointers aren't allowed in const yet
100 callback: Cell<*const ()>,
101 ctx: Cell<*mut ()>,
102} 97}
103 98
104unsafe impl Send for AlarmState {} 99unsafe impl Send for AlarmState {}
@@ -107,26 +102,20 @@ impl AlarmState {
107 const fn new() -> Self { 102 const fn new() -> Self {
108 Self { 103 Self {
109 timestamp: Cell::new(u64::MAX), 104 timestamp: Cell::new(u64::MAX),
110 callback: Cell::new(ptr::null()),
111 ctx: Cell::new(ptr::null_mut()),
112 } 105 }
113 } 106 }
114} 107}
115 108
116const ALARM_COUNT: usize = 3;
117
118struct RtcDriver { 109struct RtcDriver {
119 /// Number of 2^23 periods elapsed since boot. 110 /// Number of 2^23 periods elapsed since boot.
120 period: AtomicU32, 111 period: AtomicU32,
121 alarm_count: AtomicU8,
122 /// 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.
123 alarms: Mutex<[AlarmState; ALARM_COUNT]>, 113 alarms: Mutex<AlarmState>,
124} 114}
125 115
126embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 116embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
127 period: AtomicU32::new(0), 117 period: AtomicU32::new(0),
128 alarm_count: AtomicU8::new(0), 118 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
129 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]),
130}); 119});
131 120
132impl RtcDriver { 121impl RtcDriver {
@@ -169,13 +158,12 @@ impl RtcDriver {
169 self.next_period(); 158 self.next_period();
170 } 159 }
171 160
172 for n in 0..ALARM_COUNT { 161 let n = 0;
173 if r.events_compare(n).read() == 1 { 162 if r.events_compare(n).read() == 1 {
174 r.events_compare(n).write_value(0); 163 r.events_compare(n).write_value(0);
175 critical_section::with(|cs| { 164 critical_section::with(|cs| {
176 self.trigger_alarm(n, cs); 165 self.trigger_alarm(cs);
177 }) 166 });
178 }
179 } 167 }
180 } 168 }
181 169
@@ -186,75 +174,33 @@ impl RtcDriver {
186 self.period.store(period, Ordering::Relaxed); 174 self.period.store(period, Ordering::Relaxed);
187 let t = (period as u64) << 23; 175 let t = (period as u64) << 23;
188 176
189 for n in 0..ALARM_COUNT { 177 let n = 0;
190 let alarm = &self.alarms.borrow(cs)[n]; 178 let alarm = &self.alarms.borrow(cs);
191 let at = alarm.timestamp.get(); 179 let at = alarm.timestamp.get();
192 180
193 if at < t + 0xc00000 { 181 if at < t + 0xc00000 {
194 // just enable it. `set_alarm` has already set the correct CC val. 182 // just enable it. `set_alarm` has already set the correct CC val.
195 r.intenset().write(|w| w.0 = compare_n(n)); 183 r.intenset().write(|w| w.0 = compare_n(n));
196 }
197 } 184 }
198 }) 185 })
199 } 186 }
200 187
201 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { 188 fn trigger_alarm(&self, cs: CriticalSection) {
202 // safety: we're allowed to assume the AlarmState is created by us, and 189 let n = 0;
203 // we never create one that's out of bounds.
204 unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
205 }
206
207 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
208 let r = rtc(); 190 let r = rtc();
209 r.intenclr().write(|w| w.0 = compare_n(n)); 191 r.intenclr().write(|w| w.0 = compare_n(n));
210 192
211 let alarm = &self.alarms.borrow(cs)[n]; 193 let alarm = &self.alarms.borrow(cs);
212 alarm.timestamp.set(u64::MAX); 194 alarm.timestamp.set(u64::MAX);
213 195
214 // Call after clearing alarm, so the callback can set another alarm. 196 // Call after clearing alarm, so the callback can set another alarm.
215 197 TIMER_QUEUE_DRIVER.dispatch();
216 // safety:
217 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
218 // - other than that we only store valid function pointers into alarm.callback
219 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
220 f(alarm.ctx.get());
221 }
222}
223
224impl Driver for RtcDriver {
225 fn now(&self) -> u64 {
226 // `period` MUST be read before `counter`, see comment at the top for details.
227 let period = self.period.load(Ordering::Relaxed);
228 compiler_fence(Ordering::Acquire);
229 let counter = rtc().counter().read().0;
230 calc_now(period, counter)
231 }
232
233 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
234 critical_section::with(|_| {
235 let id = self.alarm_count.load(Ordering::Relaxed);
236 if id < ALARM_COUNT as u8 {
237 self.alarm_count.store(id + 1, Ordering::Relaxed);
238 Some(AlarmHandle::new(id))
239 } else {
240 None
241 }
242 })
243 }
244
245 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
246 critical_section::with(|cs| {
247 let alarm = self.get_alarm(cs, alarm);
248
249 alarm.callback.set(callback as *const ());
250 alarm.ctx.set(ctx);
251 })
252 } 198 }
253 199
254 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 200 fn set_alarm(&self, timestamp: u64) -> bool {
255 critical_section::with(|cs| { 201 critical_section::with(|cs| {
256 let n = alarm.id() as _; 202 let n = 0;
257 let alarm = self.get_alarm(cs, alarm); 203 let alarm = &self.alarms.borrow(cs);
258 alarm.timestamp.set(timestamp); 204 alarm.timestamp.set(timestamp);
259 205
260 let r = rtc(); 206 let r = rtc();
@@ -304,6 +250,16 @@ impl Driver for RtcDriver {
304 } 250 }
305} 251}
306 252
253impl Driver for RtcDriver {
254 fn now(&self) -> u64 {
255 // `period` MUST be read before `counter`, see comment at the top for details.
256 let period = self.period.load(Ordering::Relaxed);
257 compiler_fence(Ordering::Acquire);
258 let counter = rtc().counter().read().0;
259 calc_now(period, counter)
260 }
261}
262
307#[cfg(feature = "_nrf54l")] 263#[cfg(feature = "_nrf54l")]
308#[cfg(feature = "rt")] 264#[cfg(feature = "rt")]
309#[interrupt] 265#[interrupt]
@@ -321,3 +277,8 @@ fn RTC1() {
321pub(crate) fn init(irq_prio: crate::interrupt::Priority) { 277pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
322 DRIVER.init(irq_prio) 278 DRIVER.init(irq_prio)
323} 279}
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/Cargo.toml b/embassy-rp/Cargo.toml
index 3809f1894..94de82fa9 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -40,7 +40,7 @@ critical-section-impl = ["critical-section/restore-state-u8"]
40unstable-pac = [] 40unstable-pac = []
41 41
42## Enable the timer for use with `embassy-time` with a 1MHz tick rate. 42## Enable the timer for use with `embassy-time` with a 1MHz tick rate.
43time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000"] 43time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000", "dep:embassy-time-queue-driver"]
44 44
45## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls. 45## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls.
46rom-func-cache = [] 46rom-func-cache = []
@@ -110,6 +110,7 @@ binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"]
110[dependencies] 110[dependencies]
111embassy-sync = { version = "0.6.1", path = "../embassy-sync" } 111embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
112embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 112embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
113embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
113embassy-time = { version = "0.3.2", path = "../embassy-time" } 114embassy-time = { version = "0.3.2", path = "../embassy-time" }
114embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 115embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
115embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 116embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs
index 40fc71bb1..17ae5fff3 100644
--- a/embassy-rp/src/time_driver.rs
+++ b/embassy-rp/src/time_driver.rs
@@ -1,11 +1,10 @@
1//! Timer driver. 1//! Timer driver.
2use core::cell::Cell; 2use core::cell::Cell;
3 3
4use atomic_polyfill::{AtomicU8, Ordering};
5use critical_section::CriticalSection;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 4use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::Mutex; 5use embassy_sync::blocking_mutex::Mutex;
8use embassy_time_driver::{AlarmHandle, Driver}; 6use embassy_time_driver::Driver;
7use embassy_time_queue_driver::GlobalTimerQueue;
9#[cfg(feature = "rp2040")] 8#[cfg(feature = "rp2040")]
10use pac::TIMER; 9use pac::TIMER;
11#[cfg(feature = "_rp235x")] 10#[cfg(feature = "_rp235x")]
@@ -16,23 +15,17 @@ use crate::{interrupt, pac};
16 15
17struct AlarmState { 16struct AlarmState {
18 timestamp: Cell<u64>, 17 timestamp: Cell<u64>,
19 callback: Cell<Option<(fn(*mut ()), *mut ())>>,
20} 18}
21unsafe impl Send for AlarmState {} 19unsafe impl Send for AlarmState {}
22 20
23const ALARM_COUNT: usize = 4;
24
25struct TimerDriver { 21struct TimerDriver {
26 alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, 22 alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
27 next_alarm: AtomicU8,
28} 23}
29 24
30embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ 25embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
31 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState { 26 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
32 timestamp: Cell::new(0), 27 timestamp: Cell::new(0),
33 callback: Cell::new(None), 28 }),
34 }}; ALARM_COUNT]),
35 next_alarm: AtomicU8::new(0),
36}); 29});
37 30
38impl Driver for TimerDriver { 31impl Driver for TimerDriver {
@@ -46,34 +39,13 @@ impl Driver for TimerDriver {
46 } 39 }
47 } 40 }
48 } 41 }
42}
49 43
50 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 44impl TimerDriver {
51 let id = self.next_alarm.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 45 fn set_alarm(&self, timestamp: u64) -> bool {
52 if x < ALARM_COUNT as u8 { 46 let n = 0;
53 Some(x + 1)
54 } else {
55 None
56 }
57 });
58
59 match id {
60 Ok(id) => Some(AlarmHandle::new(id)),
61 Err(_) => None,
62 }
63 }
64
65 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
66 let n = alarm.id() as usize;
67 critical_section::with(|cs| {
68 let alarm = &self.alarms.borrow(cs)[n];
69 alarm.callback.set(Some((callback, ctx)));
70 })
71 }
72
73 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
74 let n = alarm.id() as usize;
75 critical_section::with(|cs| { 47 critical_section::with(|cs| {
76 let alarm = &self.alarms.borrow(cs)[n]; 48 let alarm = &self.alarms.borrow(cs);
77 alarm.timestamp.set(timestamp); 49 alarm.timestamp.set(timestamp);
78 50
79 // Arm it. 51 // Arm it.
@@ -96,15 +68,14 @@ impl Driver for TimerDriver {
96 } 68 }
97 }) 69 })
98 } 70 }
99}
100 71
101impl TimerDriver { 72 fn check_alarm(&self) {
102 fn check_alarm(&self, n: usize) { 73 let n = 0;
103 critical_section::with(|cs| { 74 critical_section::with(|cs| {
104 let alarm = &self.alarms.borrow(cs)[n]; 75 let alarm = &self.alarms.borrow(cs);
105 let timestamp = alarm.timestamp.get(); 76 let timestamp = alarm.timestamp.get();
106 if timestamp <= self.now() { 77 if timestamp <= self.now() {
107 self.trigger_alarm(n, cs) 78 self.trigger_alarm()
108 } else { 79 } else {
109 // Not elapsed, arm it again. 80 // Not elapsed, arm it again.
110 // This can happen if it was set more than 2^32 us in the future. 81 // This can happen if it was set more than 2^32 us in the future.
@@ -116,17 +87,8 @@ impl TimerDriver {
116 TIMER.intr().write(|w| w.set_alarm(n, true)); 87 TIMER.intr().write(|w| w.set_alarm(n, true));
117 } 88 }
118 89
119 fn trigger_alarm(&self, n: usize, cs: CriticalSection) { 90 fn trigger_alarm(&self) {
120 // disarm 91 TIMER_QUEUE_DRIVER.dispatch();
121 TIMER.armed().write(|w| w.set_armed(1 << n));
122
123 let alarm = &self.alarms.borrow(cs)[n];
124 alarm.timestamp.set(u64::MAX);
125
126 // Call after clearing alarm, so the callback can set another alarm.
127 if let Some((f, ctx)) = alarm.callback.get() {
128 f(ctx);
129 }
130 } 92 }
131} 93}
132 94
@@ -134,79 +96,37 @@ impl TimerDriver {
134pub unsafe fn init() { 96pub unsafe fn init() {
135 // init alarms 97 // init alarms
136 critical_section::with(|cs| { 98 critical_section::with(|cs| {
137 let alarms = DRIVER.alarms.borrow(cs); 99 let alarm = DRIVER.alarms.borrow(cs);
138 for a in alarms { 100 alarm.timestamp.set(u64::MAX);
139 a.timestamp.set(u64::MAX);
140 }
141 }); 101 });
142 102
143 // enable all irqs 103 // enable irq
144 TIMER.inte().write(|w| { 104 TIMER.inte().write(|w| {
145 w.set_alarm(0, true); 105 w.set_alarm(0, true);
146 w.set_alarm(1, true);
147 w.set_alarm(2, true);
148 w.set_alarm(3, true);
149 }); 106 });
150 #[cfg(feature = "rp2040")] 107 #[cfg(feature = "rp2040")]
151 { 108 {
152 interrupt::TIMER_IRQ_0.enable(); 109 interrupt::TIMER_IRQ_0.enable();
153 interrupt::TIMER_IRQ_1.enable();
154 interrupt::TIMER_IRQ_2.enable();
155 interrupt::TIMER_IRQ_3.enable();
156 } 110 }
157 #[cfg(feature = "_rp235x")] 111 #[cfg(feature = "_rp235x")]
158 { 112 {
159 interrupt::TIMER0_IRQ_0.enable(); 113 interrupt::TIMER0_IRQ_0.enable();
160 interrupt::TIMER0_IRQ_1.enable();
161 interrupt::TIMER0_IRQ_2.enable();
162 interrupt::TIMER0_IRQ_3.enable();
163 } 114 }
164} 115}
165 116
166#[cfg(all(feature = "rt", feature = "rp2040"))] 117#[cfg(all(feature = "rt", feature = "rp2040"))]
167#[interrupt] 118#[interrupt]
168fn TIMER_IRQ_0() { 119fn TIMER_IRQ_0() {
169 DRIVER.check_alarm(0) 120 DRIVER.check_alarm()
170}
171
172#[cfg(all(feature = "rt", feature = "rp2040"))]
173#[interrupt]
174fn TIMER_IRQ_1() {
175 DRIVER.check_alarm(1)
176}
177
178#[cfg(all(feature = "rt", feature = "rp2040"))]
179#[interrupt]
180fn TIMER_IRQ_2() {
181 DRIVER.check_alarm(2)
182}
183
184#[cfg(all(feature = "rt", feature = "rp2040"))]
185#[interrupt]
186fn TIMER_IRQ_3() {
187 DRIVER.check_alarm(3)
188} 121}
189 122
190#[cfg(all(feature = "rt", feature = "_rp235x"))] 123#[cfg(all(feature = "rt", feature = "_rp235x"))]
191#[interrupt] 124#[interrupt]
192fn TIMER0_IRQ_0() { 125fn TIMER0_IRQ_0() {
193 DRIVER.check_alarm(0) 126 DRIVER.check_alarm()
194} 127}
195 128
196#[cfg(all(feature = "rt", feature = "_rp235x"))] 129embassy_time_queue_driver::timer_queue_impl!(
197#[interrupt] 130 static TIMER_QUEUE_DRIVER: GlobalTimerQueue
198fn TIMER0_IRQ_1() { 131 = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration))
199 DRIVER.check_alarm(1) 132);
200}
201
202#[cfg(all(feature = "rt", feature = "_rp235x"))]
203#[interrupt]
204fn TIMER0_IRQ_2() {
205 DRIVER.check_alarm(2)
206}
207
208#[cfg(all(feature = "rt", feature = "_rp235x"))]
209#[interrupt]
210fn TIMER0_IRQ_3() {
211 DRIVER.check_alarm(3)
212}
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 8b4e7c929..82030f99f 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -45,6 +45,7 @@ rustdoc-args = ["--cfg", "docsrs"]
45embassy-sync = { version = "0.6.1", path = "../embassy-sync" } 45embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
46embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } 46embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
47embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } 47embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
48embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
48embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 49embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
49embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } 50embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
50embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } 51embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false }
@@ -125,6 +126,7 @@ defmt = [
125exti = [] 126exti = []
126low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] 127low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ]
127low-power-debug-with-sleep = [] 128low-power-debug-with-sleep = []
129integrated-timers = ["dep:embassy-executor", "_time-driver"]
128 130
129## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) 131## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
130memory-x = ["stm32-metapac/memory-x"] 132memory-x = ["stm32-metapac/memory-x"]
@@ -149,7 +151,7 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"]
149 151
150# Features starting with `_` are for internal use only. They're not intended 152# Features starting with `_` are for internal use only. They're not intended
151# to be enabled by other crates, and are not covered by semver guarantees. 153# to be enabled by other crates, and are not covered by semver guarantees.
152_time-driver = ["dep:embassy-time-driver", "time"] 154_time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-driver"]
153 155
154## Use any time driver 156## Use any time driver
155time-driver-any = ["_time-driver"] 157time-driver-any = ["_time-driver"]
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index 0b87bd95a..7734365f1 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -256,9 +256,6 @@ impl Executor {
256 /// This function never returns. 256 /// This function never returns.
257 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 257 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
258 let executor = unsafe { EXECUTOR.as_mut().unwrap() }; 258 let executor = unsafe { EXECUTOR.as_mut().unwrap() };
259 unsafe {
260 executor.inner.initialize();
261 }
262 init(executor.inner.spawner()); 259 init(executor.inner.spawner());
263 260
264 loop { 261 loop {
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 00aa3cfa4..290f857ad 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;
4use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; 4use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
5use core::{mem, ptr};
6 5
7use critical_section::CriticalSection; 6use critical_section::CriticalSection;
8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 7use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9use embassy_sync::blocking_mutex::Mutex; 8use embassy_sync::blocking_mutex::Mutex;
10use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ}; 9use embassy_time_driver::{Driver, TICK_HZ};
10use embassy_time_queue_driver::GlobalTimerQueue;
11use stm32_metapac::timer::{regs, TimGp16}; 11use stm32_metapac::timer::{regs, TimGp16};
12 12
13use crate::interrupt::typelevel::Interrupt; 13use crate::interrupt::typelevel::Interrupt;
@@ -24,18 +24,6 @@ use crate::{interrupt, peripherals};
24// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST 24// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST
25// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not 25// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not
26// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) 26// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.)
27//
28// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number
29// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers:
30// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3.
31
32cfg_if::cfg_if! {
33 if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] {
34 const ALARM_COUNT: usize = 1;
35 } else {
36 const ALARM_COUNT: usize = 3;
37 }
38}
39 27
40#[cfg(time_driver_tim1)] 28#[cfg(time_driver_tim1)]
41type T = peripherals::TIM1; 29type T = peripherals::TIM1;
@@ -208,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 {
208 196
209struct AlarmState { 197struct AlarmState {
210 timestamp: Cell<u64>, 198 timestamp: Cell<u64>,
211
212 // This is really a Option<(fn(*mut ()), *mut ())>
213 // but fn pointers aren't allowed in const yet
214 callback: Cell<*const ()>,
215 ctx: Cell<*mut ()>,
216} 199}
217 200
218unsafe impl Send for AlarmState {} 201unsafe impl Send for AlarmState {}
@@ -221,8 +204,6 @@ impl AlarmState {
221 const fn new() -> Self { 204 const fn new() -> Self {
222 Self { 205 Self {
223 timestamp: Cell::new(u64::MAX), 206 timestamp: Cell::new(u64::MAX),
224 callback: Cell::new(ptr::null()),
225 ctx: Cell::new(ptr::null_mut()),
226 } 207 }
227 } 208 }
228} 209}
@@ -230,17 +211,14 @@ impl AlarmState {
230pub(crate) struct RtcDriver { 211pub(crate) struct RtcDriver {
231 /// Number of 2^15 periods elapsed since boot. 212 /// Number of 2^15 periods elapsed since boot.
232 period: AtomicU32, 213 period: AtomicU32,
233 alarm_count: AtomicU8, 214 alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
234 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
235 alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
236 #[cfg(feature = "low-power")] 215 #[cfg(feature = "low-power")]
237 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, 216 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
238} 217}
239 218
240embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 219embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
241 period: AtomicU32::new(0), 220 period: AtomicU32::new(0),
242 alarm_count: AtomicU8::new(0), 221 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
243 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState::new()}; ALARM_COUNT]),
244 #[cfg(feature = "low-power")] 222 #[cfg(feature = "low-power")]
245 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 223 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
246}); 224});
@@ -289,7 +267,7 @@ impl RtcDriver {
289 let r = regs_gp16(); 267 let r = regs_gp16();
290 268
291 // XXX: reduce the size of this critical section ? 269 // XXX: reduce the size of this critical section ?
292 critical_section::with(|cs| { 270 critical_section::with(|_cs| {
293 let sr = r.sr().read(); 271 let sr = r.sr().read();
294 let dier = r.dier().read(); 272 let dier = r.dier().read();
295 273
@@ -308,10 +286,9 @@ impl RtcDriver {
308 self.next_period(); 286 self.next_period();
309 } 287 }
310 288
311 for n in 0..ALARM_COUNT { 289 let n = 0;
312 if sr.ccif(n + 1) && dier.ccie(n + 1) { 290 if sr.ccif(n + 1) && dier.ccie(n + 1) {
313 self.trigger_alarm(n, cs); 291 self.trigger_alarm();
314 }
315 } 292 }
316 }) 293 })
317 } 294 }
@@ -326,36 +303,20 @@ impl RtcDriver {
326 303
327 critical_section::with(move |cs| { 304 critical_section::with(move |cs| {
328 r.dier().modify(move |w| { 305 r.dier().modify(move |w| {
329 for n in 0..ALARM_COUNT { 306 let n = 0;
330 let alarm = &self.alarms.borrow(cs)[n]; 307 let alarm = self.alarm.borrow(cs);
331 let at = alarm.timestamp.get(); 308 let at = alarm.timestamp.get();
332 309
333 if at < t + 0xc000 { 310 if at < t + 0xc000 {
334 // just enable it. `set_alarm` has already set the correct CCR val. 311 // just enable it. `set_alarm` has already set the correct CCR val.
335 w.set_ccie(n + 1, true); 312 w.set_ccie(n + 1, true);
336 }
337 } 313 }
338 }) 314 })
339 }) 315 })
340 } 316 }
341 317
342 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { 318 fn trigger_alarm(&self) {
343 // safety: we're allowed to assume the AlarmState is created by us, and 319 TIMER_QUEUE_DRIVER.dispatch();
344 // we never create one that's out of bounds.
345 unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
346 }
347
348 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
349 let alarm = &self.alarms.borrow(cs)[n];
350 alarm.timestamp.set(u64::MAX);
351
352 // Call after clearing alarm, so the callback can set another alarm.
353
354 // safety:
355 // - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`.
356 // - other than that we only store valid function pointers into alarm.callback
357 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
358 f(alarm.ctx.get());
359 } 320 }
360 321
361 /* 322 /*
@@ -367,14 +328,7 @@ impl RtcDriver {
367 fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { 328 fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration {
368 let now = self.now() + 32; 329 let now = self.now() + 32;
369 330
370 embassy_time::Duration::from_ticks( 331 embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now))
371 self.alarms
372 .borrow(cs)
373 .iter()
374 .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now))
375 .min()
376 .unwrap_or(u64::MAX),
377 )
378 } 332 }
379 333
380 #[cfg(feature = "low-power")] 334 #[cfg(feature = "low-power")]
@@ -409,15 +363,12 @@ impl RtcDriver {
409 self.period.store(period, Ordering::SeqCst); 363 self.period.store(period, Ordering::SeqCst);
410 regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); 364 regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
411 365
412 // Now, recompute all alarms 366 // Now, recompute alarm
413 for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize { 367 let alarm = self.alarm.borrow(cs);
414 let alarm_handle = unsafe { AlarmHandle::new(i as u8) };
415 let alarm = self.get_alarm(cs, alarm_handle);
416 368
417 if !self.set_alarm(alarm_handle, alarm.timestamp.get()) { 369 if !self.set_alarm(alarm.timestamp.get()) {
418 // If the alarm timestamp has passed, we need to trigger it 370 // If the alarm timestamp has passed, we need to trigger it
419 self.trigger_alarm(i, cs); 371 self.trigger_alarm();
420 }
421 } 372 }
422 } 373 }
423 374
@@ -489,46 +440,13 @@ impl RtcDriver {
489 regs_gp16().cr1().modify(|w| w.set_cen(true)); 440 regs_gp16().cr1().modify(|w| w.set_cen(true));
490 }) 441 })
491 } 442 }
492}
493
494impl Driver for RtcDriver {
495 fn now(&self) -> u64 {
496 let r = regs_gp16();
497
498 let period = self.period.load(Ordering::Relaxed);
499 compiler_fence(Ordering::Acquire);
500 let counter = r.cnt().read().cnt();
501 calc_now(period, counter)
502 }
503 443
504 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 444 fn set_alarm(&self, timestamp: u64) -> bool {
505 critical_section::with(|_| {
506 let id = self.alarm_count.load(Ordering::Relaxed);
507 if id < ALARM_COUNT as u8 {
508 self.alarm_count.store(id + 1, Ordering::Relaxed);
509 Some(AlarmHandle::new(id))
510 } else {
511 None
512 }
513 })
514 }
515
516 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
517 critical_section::with(|cs| {
518 let alarm = self.get_alarm(cs, alarm);
519
520 alarm.callback.set(callback as *const ());
521 alarm.ctx.set(ctx);
522 })
523 }
524
525 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
526 critical_section::with(|cs| { 445 critical_section::with(|cs| {
527 let r = regs_gp16(); 446 let r = regs_gp16();
528 447
529 let n = alarm.id() as usize; 448 let n = 0;
530 let alarm = self.get_alarm(cs, alarm); 449 self.alarm.borrow(cs).timestamp.set(timestamp);
531 alarm.timestamp.set(timestamp);
532 450
533 let t = self.now(); 451 let t = self.now();
534 if timestamp <= t { 452 if timestamp <= t {
@@ -536,7 +454,7 @@ impl Driver for RtcDriver {
536 // Disarm the alarm and return `false` to indicate that. 454 // Disarm the alarm and return `false` to indicate that.
537 r.dier().modify(|w| w.set_ccie(n + 1, false)); 455 r.dier().modify(|w| w.set_ccie(n + 1, false));
538 456
539 alarm.timestamp.set(u64::MAX); 457 self.alarm.borrow(cs).timestamp.set(u64::MAX);
540 458
541 return false; 459 return false;
542 } 460 }
@@ -558,7 +476,7 @@ impl Driver for RtcDriver {
558 // It is the caller's responsibility to handle this ambiguity. 476 // It is the caller's responsibility to handle this ambiguity.
559 r.dier().modify(|w| w.set_ccie(n + 1, false)); 477 r.dier().modify(|w| w.set_ccie(n + 1, false));
560 478
561 alarm.timestamp.set(u64::MAX); 479 self.alarm.borrow(cs).timestamp.set(u64::MAX);
562 480
563 return false; 481 return false;
564 } 482 }
@@ -569,6 +487,17 @@ impl Driver for RtcDriver {
569 } 487 }
570} 488}
571 489
490impl Driver for RtcDriver {
491 fn now(&self) -> u64 {
492 let r = regs_gp16();
493
494 let period = self.period.load(Ordering::Relaxed);
495 compiler_fence(Ordering::Acquire);
496 let counter = r.cnt().read().cnt();
497 calc_now(period, counter)
498 }
499}
500
572#[cfg(feature = "low-power")] 501#[cfg(feature = "low-power")]
573pub(crate) fn get_driver() -> &'static RtcDriver { 502pub(crate) fn get_driver() -> &'static RtcDriver {
574 &DRIVER 503 &DRIVER
@@ -577,3 +506,8 @@ pub(crate) fn get_driver() -> &'static RtcDriver {
577pub(crate) fn init(cs: CriticalSection) { 506pub(crate) fn init(cs: CriticalSection) {
578 DRIVER.init(cs) 507 DRIVER.init(cs)
579} 508}
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 12f40b9b9..ffb363cd7 100644
--- a/embassy-time-driver/src/lib.rs
+++ b/embassy-time-driver/src/lib.rs
@@ -21,8 +21,8 @@
21//! 21//!
22//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. 22//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions.
23//! 23//!
24//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. 24//! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it.
25//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the 25//! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the
26//! calls from the `embassy` crate to call into the driver crate. 26//! calls from the `embassy` crate to call into the driver crate.
27//! 27//!
28//! If there is none or multiple drivers in the crate tree, linking will fail. 28//! If there is none or multiple drivers in the crate tree, linking will fail.
@@ -38,7 +38,7 @@
38//! # Example 38//! # Example
39//! 39//!
40//! ``` 40//! ```
41//! use embassy_time_driver::{Driver, AlarmHandle}; 41//! use embassy_time_driver::Driver;
42//! 42//!
43//! struct MyDriver{} // not public! 43//! struct MyDriver{} // not public!
44//! 44//!
@@ -46,15 +46,6 @@
46//! fn now(&self) -> u64 { 46//! fn now(&self) -> u64 {
47//! todo!() 47//! todo!()
48//! } 48//! }
49//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
50//! todo!()
51//! }
52//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
53//! todo!()
54//! }
55//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
56//! todo!()
57//! }
58//! } 49//! }
59//! 50//!
60//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); 51//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
@@ -70,28 +61,6 @@ mod tick;
70/// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) 61/// This value is specified by the [`tick-*` Cargo features](crate#tick-rate)
71pub const TICK_HZ: u64 = tick::TICK_HZ; 62pub const TICK_HZ: u64 = tick::TICK_HZ;
72 63
73/// Alarm handle, assigned by the driver.
74#[derive(Clone, Copy)]
75pub struct AlarmHandle {
76 id: u8,
77}
78
79impl AlarmHandle {
80 /// Create an AlarmHandle
81 ///
82 /// Safety: May only be called by the current global Driver impl.
83 /// The impl is allowed to rely on the fact that all `AlarmHandle` instances
84 /// are created by itself in unsafe code (e.g. indexing operations)
85 pub unsafe fn new(id: u8) -> Self {
86 Self { id }
87 }
88
89 /// Get the ID of the AlarmHandle.
90 pub fn id(&self) -> u8 {
91 self.id
92 }
93}
94
95/// Time driver 64/// Time driver
96pub trait Driver: Send + Sync + 'static { 65pub trait Driver: Send + Sync + 'static {
97 /// Return the current timestamp in ticks. 66 /// Return the current timestamp in ticks.
@@ -105,76 +74,10 @@ pub trait Driver: Send + Sync + 'static {
105 /// you MUST extend them to 64-bit, for example by counting overflows in software, 74 /// you MUST extend them to 64-bit, for example by counting overflows in software,
106 /// or chaining multiple timers together. 75 /// or chaining multiple timers together.
107 fn now(&self) -> u64; 76 fn now(&self) -> u64;
108
109 /// Try allocating an alarm handle. Returns None if no alarms left.
110 /// Initially the alarm has no callback set, and a null `ctx` pointer.
111 ///
112 /// The allocated alarm is a reusable resource and can be used multiple times.
113 /// Once the alarm has fired, it remains allocated and can be set again without needing
114 /// to be reallocated.
115 ///
116 /// # Safety
117 /// It is UB to make the alarm fire before setting a callback.
118 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
119
120 /// Set the callback function to be called when the alarm triggers.
121 /// The callback may be called from any context (interrupt or thread mode).
122 ///
123 /// The callback is maintained after the alarm has fired. Callers do not need
124 /// to set a callback again before setting another alarm, unless they want to
125 /// change the callback function or context.
126 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
127
128 /// Set an alarm at the given timestamp.
129 ///
130 /// ## Behavior
131 ///
132 /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function
133 /// at that time, and returns `true`.
134 ///
135 /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to:
136 ///
137 /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or
138 /// - Not schedule the callback, and return `false`.
139 ///
140 /// Callers must ensure to behave correctly with either behavior.
141 ///
142 /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`.
143 ///
144 /// ## Reentrancy
145 ///
146 /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above,
147 /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed
148 /// to call it synchronously in the context `set_alarm` is running.
149 ///
150 /// The reason for the above is callers are explicitly permitted to do both of:
151 /// - Lock a mutex in the alarm callback.
152 /// - Call `set_alarm` while having that mutex locked.
153 ///
154 /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the
155 /// mutex to be locked twice reentrantly in the same context.
156 ///
157 /// ## Overwriting alarms
158 ///
159 /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any.
160 ///
161 /// ## Unsetting the alarm
162 ///
163 /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`.
164 ///
165 /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and
166 /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set".
167 ///
168 /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp`
169 /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier.
170 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
171} 77}
172 78
173extern "Rust" { 79extern "Rust" {
174 fn _embassy_time_now() -> u64; 80 fn _embassy_time_now() -> u64;
175 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
176 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
177 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool;
178} 81}
179 82
180/// See [`Driver::now`] 83/// See [`Driver::now`]
@@ -182,23 +85,6 @@ pub fn now() -> u64 {
182 unsafe { _embassy_time_now() } 85 unsafe { _embassy_time_now() }
183} 86}
184 87
185/// See [`Driver::allocate_alarm`]
186///
187/// Safety: it is UB to make the alarm fire before setting a callback.
188pub unsafe fn allocate_alarm() -> Option<AlarmHandle> {
189 _embassy_time_allocate_alarm()
190}
191
192/// See [`Driver::set_alarm_callback`]
193pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
194 unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) }
195}
196
197/// See [`Driver::set_alarm`]
198pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool {
199 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
200}
201
202/// Set the time Driver implementation. 88/// Set the time Driver implementation.
203/// 89///
204/// See the module documentation for an example. 90/// See the module documentation for an example.
@@ -211,20 +97,5 @@ macro_rules! time_driver_impl {
211 fn _embassy_time_now() -> u64 { 97 fn _embassy_time_now() -> u64 {
212 <$t as $crate::Driver>::now(&$name) 98 <$t as $crate::Driver>::now(&$name)
213 } 99 }
214
215 #[no_mangle]
216 unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> {
217 <$t as $crate::Driver>::allocate_alarm(&$name)
218 }
219
220 #[no_mangle]
221 fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
222 <$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
223 }
224
225 #[no_mangle]
226 fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool {
227 <$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp)
228 }
229 }; 100 };
230} 101}
diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml
index 9ce9d79bb..599041a3f 100644
--- a/embassy-time-queue-driver/Cargo.toml
+++ b/embassy-time-queue-driver/Cargo.toml
@@ -20,6 +20,39 @@ categories = [
20# This is especially common when mixing crates from crates.io and git. 20# This is especially common when mixing crates from crates.io and git.
21links = "embassy-time-queue" 21links = "embassy-time-queue"
22 22
23[dependencies]
24critical-section = "1.2.0"
25heapless = "0.8"
26embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true }
27embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" }
28
29[features]
30#! ### Generic Queue
31
32## Use the executor-integrated `embassy-time` timer queue. The timer items are stored inside
33## the task headers, so you do not need to set a capacity for the queue.
34## To use this you must have a time driver provided.
35##
36## If this feature is not enabled, a generic queue is available with a configurable capacity.
37integrated-timers = ["embassy-executor/integrated-timers"]
38
39#! The following features set how many timers are used for the generic queue. At most one
40#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used.
41#!
42#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the
43#! end user to pick.
44
45## Generic Queue with 8 timers
46generic-queue-8 = []
47## Generic Queue with 16 timers
48generic-queue-16 = []
49## Generic Queue with 32 timers
50generic-queue-32 = []
51## Generic Queue with 64 timers
52generic-queue-64 = []
53## Generic Queue with 128 timers
54generic-queue-128 = []
55
23[package.metadata.embassy_docs] 56[package.metadata.embassy_docs]
24src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" 57src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/"
25src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/" 58src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/"
diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs
index 50736e8c7..c5e989854 100644
--- a/embassy-time-queue-driver/src/lib.rs
+++ b/embassy-time-queue-driver/src/lib.rs
@@ -6,7 +6,29 @@
6//! 6//!
7//! - Define a struct `MyTimerQueue` 7//! - Define a struct `MyTimerQueue`
8//! - Implement [`TimerQueue`] for it 8//! - Implement [`TimerQueue`] for it
9//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). 9//! - Register it as the global timer queue with [`timer_queue_impl`].
10//! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves
11//! waking expired tasks, finding the next expiration time and setting an alarm.
12//!
13//! If a single global timer queue is sufficient for you, you can use the
14//! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue
15//! protected by a critical section.
16//!
17//! ```
18//! use embassy_time_queue_driver::GlobalTimerQueue;
19//! embassy_time_queue_driver::timer_queue_impl!(
20//! static TIMER_QUEUE_DRIVER: GlobalTimerQueue
21//! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm"))
22//! );
23//! ```
24//!
25//! You can also use the `queue_generic` or the `embassy_executor::raw::timer_queue` modules to
26//! implement your own timer queue. These modules contain queue implementations which you can wrap
27//! and tailor to your needs.
28//!
29//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to
30//! expose the `integrated-timers` feature in your implementation. This feature stores timer items
31//! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation.
10//! 32//!
11//! ## Example 33//! ## Example
12//! 34//!
@@ -14,7 +36,7 @@
14//! use core::task::Waker; 36//! use core::task::Waker;
15//! 37//!
16//! use embassy_time::Instant; 38//! use embassy_time::Instant;
17//! use embassy_time::queue::{TimerQueue}; 39//! use embassy_time::queue::TimerQueue;
18//! 40//!
19//! struct MyTimerQueue{}; // not public! 41//! struct MyTimerQueue{}; // not public!
20//! 42//!
@@ -26,11 +48,18 @@
26//! 48//!
27//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); 49//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{});
28//! ``` 50//! ```
51
52pub mod queue_generic;
53
54use core::cell::RefCell;
29use core::task::Waker; 55use core::task::Waker;
30 56
57use critical_section::Mutex;
58
31/// Timer queue 59/// Timer queue
32pub trait TimerQueue { 60pub trait TimerQueue {
33 /// Schedules a waker in the queue to be awoken at moment `at`. 61 /// Schedules a waker in the queue to be awoken at moment `at`.
62 ///
34 /// If this moment is in the past, the waker might be awoken immediately. 63 /// If this moment is in the past, the waker might be awoken immediately.
35 fn schedule_wake(&'static self, at: u64, waker: &Waker); 64 fn schedule_wake(&'static self, at: u64, waker: &Waker);
36} 65}
@@ -58,3 +87,106 @@ macro_rules! timer_queue_impl {
58 } 87 }
59 }; 88 };
60} 89}
90
91#[cfg(feature = "integrated-timers")]
92type InnerQueue = embassy_executor::raw::timer_queue::TimerQueue;
93
94#[cfg(not(feature = "integrated-timers"))]
95type InnerQueue = queue_generic::Queue;
96
97/// A timer queue implementation that can be used as a global timer queue.
98///
99/// This implementation is not thread-safe, and should be protected by a mutex of some sort.
100pub struct GenericTimerQueue<F: Fn(u64) -> bool> {
101 queue: InnerQueue,
102 set_alarm: F,
103}
104
105impl<F: Fn(u64) -> bool> GenericTimerQueue<F> {
106 /// Creates a new timer queue.
107 ///
108 /// `set_alarm` is a function that should set the next alarm time. The function should
109 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
110 pub const fn new(set_alarm: F) -> Self {
111 Self {
112 queue: InnerQueue::new(),
113 set_alarm,
114 }
115 }
116
117 /// Schedules a task to run at a specific time, and returns whether any changes were made.
118 pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) {
119 #[cfg(feature = "integrated-timers")]
120 let waker = embassy_executor::raw::task_from_waker(waker);
121
122 if self.queue.schedule_wake(at, waker) {
123 self.dispatch()
124 }
125 }
126
127 /// Dequeues expired timers and returns the next alarm time.
128 pub fn next_expiration(&mut self, now: u64) -> u64 {
129 self.queue.next_expiration(now)
130 }
131
132 /// Handle the alarm.
133 ///
134 /// Call this function when the next alarm is due.
135 pub fn dispatch(&mut self) {
136 let mut next_expiration = self.next_expiration(embassy_time_driver::now());
137
138 while !(self.set_alarm)(next_expiration) {
139 // next_expiration is in the past, dequeue and find a new expiration
140 next_expiration = self.next_expiration(next_expiration);
141 }
142 }
143}
144
145/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`].
146pub struct GlobalTimerQueue {
147 inner: Mutex<RefCell<GenericTimerQueue<fn(u64) -> bool>>>,
148}
149
150impl GlobalTimerQueue {
151 /// Creates a new timer queue.
152 ///
153 /// `set_alarm` is a function that should set the next alarm time. The function should
154 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
155 pub const fn new(set_alarm: fn(u64) -> bool) -> Self {
156 Self {
157 inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))),
158 }
159 }
160
161 /// Schedules a task to run at a specific time, and returns whether any changes were made.
162 pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
163 critical_section::with(|cs| {
164 let mut inner = self.inner.borrow_ref_mut(cs);
165 inner.schedule_wake(at, waker);
166 });
167 }
168
169 /// Dequeues expired timers and returns the next alarm time.
170 pub fn next_expiration(&self, now: u64) -> u64 {
171 critical_section::with(|cs| {
172 let mut inner = self.inner.borrow_ref_mut(cs);
173 inner.next_expiration(now)
174 })
175 }
176
177 /// Handle the alarm.
178 ///
179 /// Call this function when the next alarm is due.
180 pub fn dispatch(&self) {
181 critical_section::with(|cs| {
182 let mut inner = self.inner.borrow_ref_mut(cs);
183 inner.dispatch()
184 })
185 }
186}
187
188impl TimerQueue for GlobalTimerQueue {
189 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
190 GlobalTimerQueue::schedule_wake(self, at, waker)
191 }
192}
diff --git a/embassy-time-queue-driver/src/queue_generic.rs b/embassy-time-queue-driver/src/queue_generic.rs
new file mode 100644
index 000000000..232035bc6
--- /dev/null
+++ b/embassy-time-queue-driver/src/queue_generic.rs
@@ -0,0 +1,146 @@
1//! Generic timer queue implementations.
2//!
3//! Time queue drivers may use this to simplify their implementation.
4
5use core::cmp::{min, Ordering};
6use core::task::Waker;
7
8use heapless::Vec;
9
10#[derive(Debug)]
11struct Timer {
12 at: u64,
13 waker: Waker,
14}
15
16impl PartialEq for Timer {
17 fn eq(&self, other: &Self) -> bool {
18 self.at == other.at
19 }
20}
21
22impl Eq for Timer {}
23
24impl PartialOrd for Timer {
25 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
26 self.at.partial_cmp(&other.at)
27 }
28}
29
30impl Ord for Timer {
31 fn cmp(&self, other: &Self) -> Ordering {
32 self.at.cmp(&other.at)
33 }
34}
35
36/// A timer queue with a pre-determined capacity.
37pub struct ConstGenericQueue<const QUEUE_SIZE: usize> {
38 queue: Vec<Timer, QUEUE_SIZE>,
39}
40
41impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {
42 /// Creates a new timer queue.
43 pub const fn new() -> Self {
44 Self { queue: Vec::new() }
45 }
46
47 /// Schedules a task to run at a specific time, and returns whether any changes were made.
48 ///
49 /// If this function returns `true`, the called should find the next expiration time and set
50 /// a new alarm for that time.
51 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
52 self.queue
53 .iter_mut()
54 .find(|timer| timer.waker.will_wake(waker))
55 .map(|timer| {
56 if timer.at > at {
57 timer.at = at;
58 true
59 } else {
60 false
61 }
62 })
63 .unwrap_or_else(|| {
64 let mut timer = Timer {
65 waker: waker.clone(),
66 at,
67 };
68
69 loop {
70 match self.queue.push(timer) {
71 Ok(()) => break,
72 Err(e) => timer = e,
73 }
74
75 self.queue.pop().unwrap().waker.wake();
76 }
77
78 true
79 })
80 }
81
82 /// Dequeues expired timers and returns the next alarm time.
83 pub fn next_expiration(&mut self, now: u64) -> u64 {
84 let mut next_alarm = u64::MAX;
85
86 let mut i = 0;
87 while i < self.queue.len() {
88 let timer = &self.queue[i];
89 if timer.at <= now {
90 let timer = self.queue.swap_remove(i);
91 timer.waker.wake();
92 } else {
93 next_alarm = min(next_alarm, timer.at);
94 i += 1;
95 }
96 }
97
98 next_alarm
99 }
100}
101
102#[cfg(feature = "generic-queue-8")]
103const QUEUE_SIZE: usize = 8;
104#[cfg(feature = "generic-queue-16")]
105const QUEUE_SIZE: usize = 16;
106#[cfg(feature = "generic-queue-32")]
107const QUEUE_SIZE: usize = 32;
108#[cfg(feature = "generic-queue-64")]
109const QUEUE_SIZE: usize = 64;
110#[cfg(feature = "generic-queue-128")]
111const QUEUE_SIZE: usize = 128;
112#[cfg(not(any(
113 feature = "generic-queue-8",
114 feature = "generic-queue-16",
115 feature = "generic-queue-32",
116 feature = "generic-queue-64",
117 feature = "generic-queue-128"
118)))]
119const QUEUE_SIZE: usize = 64;
120
121/// A timer queue with a pre-determined capacity.
122pub struct Queue {
123 queue: ConstGenericQueue<QUEUE_SIZE>,
124}
125
126impl Queue {
127 /// Creates a new timer queue.
128 pub const fn new() -> Self {
129 Self {
130 queue: ConstGenericQueue::new(),
131 }
132 }
133
134 /// Schedules a task to run at a specific time, and returns whether any changes were made.
135 ///
136 /// If this function returns `true`, the called should find the next expiration time and set
137 /// a new alarm for that time.
138 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
139 self.queue.schedule_wake(at, waker)
140 }
141
142 /// Dequeues expired timers and returns the next alarm time.
143 pub fn next_expiration(&mut self, now: u64) -> u64 {
144 self.queue.next_expiration(now)
145 }
146}
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index 8c7de9840..e3074119f 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -42,29 +42,6 @@ defmt-timestamp-uptime-tus = ["defmt"]
42## Create a `MockDriver` that can be manually advanced for testing purposes. 42## Create a `MockDriver` that can be manually advanced for testing purposes.
43mock-driver = ["tick-hz-1_000_000"] 43mock-driver = ["tick-hz-1_000_000"]
44 44
45#! ### Generic Queue
46
47## Create a global, generic queue that can be used with any executor.
48## To use this you must have a time driver provided.
49generic-queue = []
50
51#! The following features set how many timers are used for the generic queue. At most one
52#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used.
53#!
54#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the
55#! end user to pick.
56
57## Generic Queue with 8 timers
58generic-queue-8 = ["generic-queue"]
59## Generic Queue with 16 timers
60generic-queue-16 = ["generic-queue"]
61## Generic Queue with 32 timers
62generic-queue-32 = ["generic-queue"]
63## Generic Queue with 64 timers
64generic-queue-64 = ["generic-queue"]
65## Generic Queue with 128 timers
66generic-queue-128 = ["generic-queue"]
67
68#! ### Tick Rate 45#! ### Tick Rate
69#! 46#!
70#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. 47#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used.
@@ -419,7 +396,6 @@ embedded-hal-async = { version = "1.0" }
419futures-util = { version = "0.3.17", default-features = false } 396futures-util = { version = "0.3.17", default-features = false }
420critical-section = "1.1" 397critical-section = "1.1"
421cfg-if = "1.0.0" 398cfg-if = "1.0.0"
422heapless = "0.8"
423 399
424document-features = "0.2.7" 400document-features = "0.2.7"
425 401
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs
index 8587f9172..829eb0437 100644
--- a/embassy-time/src/driver_mock.rs
+++ b/embassy-time/src/driver_mock.rs
@@ -1,7 +1,7 @@
1use core::cell::RefCell; 1use core::cell::RefCell;
2 2
3use critical_section::Mutex as CsMutex; 3use critical_section::Mutex as CsMutex;
4use embassy_time_driver::{AlarmHandle, Driver}; 4use embassy_time_driver::Driver;
5 5
6use crate::{Duration, Instant}; 6use crate::{Duration, Instant};
7 7
@@ -60,15 +60,13 @@ impl MockDriver {
60 60
61 let now = inner.now.as_ticks(); 61 let now = inner.now.as_ticks();
62 62
63 inner 63 if inner.alarm.timestamp <= now {
64 .alarm 64 inner.alarm.timestamp = u64::MAX;
65 .as_mut()
66 .filter(|alarm| alarm.timestamp <= now)
67 .map(|alarm| {
68 alarm.timestamp = u64::MAX;
69 65
70 (alarm.callback, alarm.ctx) 66 Some((inner.alarm.callback, inner.alarm.ctx))
71 }) 67 } else {
68 None
69 }
72 }) 70 })
73 }; 71 };
74 72
@@ -76,68 +74,48 @@ impl MockDriver {
76 (callback)(ctx); 74 (callback)(ctx);
77 } 75 }
78 } 76 }
79}
80
81impl Driver for MockDriver {
82 fn now(&self) -> u64 {
83 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
84 }
85
86 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
87 critical_section::with(|cs| {
88 let mut inner = self.0.borrow_ref_mut(cs);
89
90 if inner.alarm.is_some() {
91 None
92 } else {
93 inner.alarm.replace(AlarmState::new());
94
95 Some(AlarmHandle::new(0))
96 }
97 })
98 }
99 77
100 fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 78 /// Configures a callback to be called when the alarm fires.
79 pub fn set_alarm_callback(&self, callback: fn(*mut ()), ctx: *mut ()) {
101 critical_section::with(|cs| { 80 critical_section::with(|cs| {
102 let mut inner = self.0.borrow_ref_mut(cs); 81 let mut inner = self.0.borrow_ref_mut(cs);
103 82
104 let Some(alarm) = inner.alarm.as_mut() else { 83 inner.alarm.callback = callback;
105 panic!("Alarm not allocated"); 84 inner.alarm.ctx = ctx;
106 };
107
108 alarm.callback = callback;
109 alarm.ctx = ctx;
110 }); 85 });
111 } 86 }
112 87
113 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { 88 /// Sets the alarm to fire at the specified timestamp.
89 pub fn set_alarm(&self, timestamp: u64) -> bool {
114 critical_section::with(|cs| { 90 critical_section::with(|cs| {
115 let mut inner = self.0.borrow_ref_mut(cs); 91 let mut inner = self.0.borrow_ref_mut(cs);
116 92
117 if timestamp <= inner.now.as_ticks() { 93 if timestamp <= inner.now.as_ticks() {
118 false 94 false
119 } else { 95 } else {
120 let Some(alarm) = inner.alarm.as_mut() else { 96 inner.alarm.timestamp = timestamp;
121 panic!("Alarm not allocated");
122 };
123
124 alarm.timestamp = timestamp;
125 true 97 true
126 } 98 }
127 }) 99 })
128 } 100 }
129} 101}
130 102
103impl Driver for MockDriver {
104 fn now(&self) -> u64 {
105 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
106 }
107}
108
131struct InnerMockDriver { 109struct InnerMockDriver {
132 now: Instant, 110 now: Instant,
133 alarm: Option<AlarmState>, 111 alarm: AlarmState,
134} 112}
135 113
136impl InnerMockDriver { 114impl InnerMockDriver {
137 const fn new() -> Self { 115 const fn new() -> Self {
138 Self { 116 Self {
139 now: Instant::from_ticks(0), 117 now: Instant::from_ticks(0),
140 alarm: None, 118 alarm: AlarmState::new(),
141 } 119 }
142 } 120 }
143} 121}
@@ -189,8 +167,7 @@ mod tests {
189 setup(); 167 setup();
190 168
191 let driver = MockDriver::get(); 169 let driver = MockDriver::get();
192 let alarm = unsafe { AlarmHandle::new(0) }; 170 assert_eq!(false, driver.set_alarm(driver.now()));
193 assert_eq!(false, driver.set_alarm(alarm, driver.now()));
194 } 171 }
195 172
196 #[test] 173 #[test]
@@ -199,23 +176,11 @@ mod tests {
199 setup(); 176 setup();
200 177
201 let driver = MockDriver::get(); 178 let driver = MockDriver::get();
202 let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available");
203 static mut CALLBACK_CALLED: bool = false; 179 static mut CALLBACK_CALLED: bool = false;
204 let ctx = &mut () as *mut (); 180 driver.set_alarm_callback(|_| unsafe { CALLBACK_CALLED = true }, core::ptr::null_mut());
205 driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx); 181 driver.set_alarm(driver.now() + 1);
206 driver.set_alarm(alarm, driver.now() + 1);
207 assert_eq!(false, unsafe { CALLBACK_CALLED }); 182 assert_eq!(false, unsafe { CALLBACK_CALLED });
208 driver.advance(Duration::from_secs(1)); 183 driver.advance(Duration::from_secs(1));
209 assert_eq!(true, unsafe { CALLBACK_CALLED }); 184 assert_eq!(true, unsafe { CALLBACK_CALLED });
210 } 185 }
211
212 #[test]
213 #[serial]
214 fn test_allocate_alarm() {
215 setup();
216
217 let driver = MockDriver::get();
218 assert!(unsafe { driver.allocate_alarm() }.is_some());
219 assert!(unsafe { driver.allocate_alarm() }.is_none());
220 }
221} 186}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index cbef7aae1..45467f09b 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -1,53 +1,38 @@
1use core::sync::atomic::{AtomicU8, Ordering};
2use std::cell::{RefCell, UnsafeCell}; 1use std::cell::{RefCell, UnsafeCell};
3use std::mem::MaybeUninit; 2use std::mem::MaybeUninit;
4use std::sync::{Condvar, Mutex, Once}; 3use std::sync::{Condvar, Mutex, Once};
5use std::time::{Duration as StdDuration, Instant as StdInstant}; 4use std::time::{Duration as StdDuration, Instant as StdInstant};
6use std::{mem, ptr, thread}; 5use std::{ptr, thread};
7 6
8use critical_section::Mutex as CsMutex; 7use critical_section::Mutex as CsMutex;
9use embassy_time_driver::{AlarmHandle, Driver}; 8use embassy_time_driver::Driver;
10 9use embassy_time_queue_driver::GlobalTimerQueue;
11const ALARM_COUNT: usize = 4;
12 10
13struct AlarmState { 11struct AlarmState {
14 timestamp: u64, 12 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} 13}
21 14
22unsafe impl Send for AlarmState {} 15unsafe impl Send for AlarmState {}
23 16
24impl AlarmState { 17impl AlarmState {
25 const fn new() -> Self { 18 const fn new() -> Self {
26 Self { 19 Self { timestamp: u64::MAX }
27 timestamp: u64::MAX,
28 callback: ptr::null(),
29 ctx: ptr::null_mut(),
30 }
31 } 20 }
32} 21}
33 22
34struct TimeDriver { 23struct TimeDriver {
35 alarm_count: AtomicU8,
36
37 once: Once, 24 once: Once,
38 // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't 25 // The STD Driver implementation requires the alarm's 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 26 // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
40 // themselves are reentrant 27 // themselves are reentrant
41 alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>, 28 alarm: UninitCell<CsMutex<RefCell<AlarmState>>>,
42 zero_instant: UninitCell<StdInstant>, 29 zero_instant: UninitCell<StdInstant>,
43 signaler: UninitCell<Signaler>, 30 signaler: UninitCell<Signaler>,
44} 31}
45 32
46embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 33embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
47 alarm_count: AtomicU8::new(0),
48
49 once: Once::new(), 34 once: Once::new(),
50 alarms: UninitCell::uninit(), 35 alarm: UninitCell::uninit(),
51 zero_instant: UninitCell::uninit(), 36 zero_instant: UninitCell::uninit(),
52 signaler: UninitCell::uninit(), 37 signaler: UninitCell::uninit(),
53}); 38});
@@ -55,8 +40,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
55impl TimeDriver { 40impl TimeDriver {
56 fn init(&self) { 41 fn init(&self) {
57 self.once.call_once(|| unsafe { 42 self.once.call_once(|| unsafe {
58 self.alarms 43 self.alarm
59 .write(CsMutex::new(RefCell::new([const { AlarmState::new() }; ALARM_COUNT]))); 44 .write(CsMutex::new(RefCell::new(const { AlarmState::new() })));
60 self.zero_instant.write(StdInstant::now()); 45 self.zero_instant.write(StdInstant::now());
61 self.signaler.write(Signaler::new()); 46 self.signaler.write(Signaler::new());
62 47
@@ -70,36 +55,13 @@ impl TimeDriver {
70 let now = DRIVER.now(); 55 let now = DRIVER.now();
71 56
72 let next_alarm = critical_section::with(|cs| { 57 let next_alarm = critical_section::with(|cs| {
73 let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs); 58 let mut alarm = unsafe { DRIVER.alarm.as_ref() }.borrow_ref_mut(cs);
74 loop { 59 if alarm.timestamp <= now {
75 let pending = alarms 60 alarm.timestamp = u64::MAX;
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 61
97 alarms 62 TIMER_QUEUE_DRIVER.dispatch();
98 .borrow() 63 }
99 .iter() 64 alarm.timestamp
100 .map(|alarm| alarm.timestamp)
101 .min()
102 .unwrap_or(u64::MAX)
103 }); 65 });
104 66
105 // Ensure we don't overflow 67 // Ensure we don't overflow
@@ -110,46 +72,11 @@ impl TimeDriver {
110 unsafe { DRIVER.signaler.as_ref() }.wait_until(until); 72 unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
111 } 73 }
112 } 74 }
113}
114
115impl Driver for TimeDriver {
116 fn now(&self) -> u64 {
117 self.init();
118
119 let zero = unsafe { self.zero_instant.read() };
120 StdInstant::now().duration_since(zero).as_micros() as u64
121 }
122
123 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
124 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
125 if x < ALARM_COUNT as u8 {
126 Some(x + 1)
127 } else {
128 None
129 }
130 });
131
132 match id {
133 Ok(id) => Some(AlarmHandle::new(id)),
134 Err(_) => None,
135 }
136 }
137
138 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
139 self.init();
140 critical_section::with(|cs| {
141 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
142 let alarm = &mut alarms[alarm.id() as usize];
143 alarm.callback = callback as *const ();
144 alarm.ctx = ctx;
145 });
146 }
147 75
148 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 76 fn set_alarm(&self, timestamp: u64) -> bool {
149 self.init(); 77 self.init();
150 critical_section::with(|cs| { 78 critical_section::with(|cs| {
151 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); 79 let mut alarm = unsafe { self.alarm.as_ref() }.borrow_ref_mut(cs);
152 let alarm = &mut alarms[alarm.id() as usize];
153 alarm.timestamp = timestamp; 80 alarm.timestamp = timestamp;
154 unsafe { self.signaler.as_ref() }.signal(); 81 unsafe { self.signaler.as_ref() }.signal();
155 }); 82 });
@@ -158,6 +85,15 @@ impl Driver for TimeDriver {
158 } 85 }
159} 86}
160 87
88impl Driver for TimeDriver {
89 fn now(&self) -> u64 {
90 self.init();
91
92 let zero = unsafe { self.zero_instant.read() };
93 StdInstant::now().duration_since(zero).as_micros() as u64
94 }
95}
96
161struct Signaler { 97struct Signaler {
162 mutex: Mutex<bool>, 98 mutex: Mutex<bool>,
163 condvar: Condvar, 99 condvar: Condvar,
@@ -228,3 +164,8 @@ impl<T: Copy> UninitCell<T> {
228 ptr::read(self.as_mut_ptr()) 164 ptr::read(self.as_mut_ptr())
229 } 165 }
230} 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 d65629e49..dcc935fde 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -1,28 +1,22 @@
1use core::sync::atomic::{AtomicU8, Ordering};
2use std::cell::UnsafeCell; 1use std::cell::UnsafeCell;
3use std::mem::MaybeUninit; 2use std::mem::MaybeUninit;
4use std::ptr; 3use std::ptr;
5use std::sync::{Mutex, Once}; 4use std::sync::{Mutex, Once};
6 5
7use embassy_time_driver::{AlarmHandle, Driver}; 6use embassy_time_driver::Driver;
7use embassy_time_queue_driver::GlobalTimerQueue;
8use wasm_bindgen::prelude::*; 8use wasm_bindgen::prelude::*;
9use wasm_timer::Instant as StdInstant; 9use wasm_timer::Instant as StdInstant;
10 10
11const ALARM_COUNT: usize = 4;
12
13struct AlarmState { 11struct AlarmState {
14 token: Option<f64>, 12 token: Option<f64>,
15 closure: Option<Closure<dyn FnMut() + 'static>>,
16} 13}
17 14
18unsafe impl Send for AlarmState {} 15unsafe impl Send for AlarmState {}
19 16
20impl AlarmState { 17impl AlarmState {
21 const fn new() -> Self { 18 const fn new() -> Self {
22 Self { 19 Self { token: None }
23 token: None,
24 closure: None,
25 }
26 } 20 }
27} 21}
28 22
@@ -33,66 +27,32 @@ extern "C" {
33} 27}
34 28
35struct TimeDriver { 29struct TimeDriver {
36 alarm_count: AtomicU8,
37
38 once: Once, 30 once: Once,
39 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>, 31 alarm: UninitCell<Mutex<AlarmState>>,
40 zero_instant: UninitCell<StdInstant>, 32 zero_instant: UninitCell<StdInstant>,
33 closure: UninitCell<Closure<dyn FnMut()>>,
41} 34}
42 35
43embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 36embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
44 alarm_count: AtomicU8::new(0),
45 once: Once::new(), 37 once: Once::new(),
46 alarms: UninitCell::uninit(), 38 alarm: UninitCell::uninit(),
47 zero_instant: UninitCell::uninit(), 39 zero_instant: UninitCell::uninit(),
40 closure: UninitCell::uninit()
48}); 41});
49 42
50impl TimeDriver { 43impl TimeDriver {
51 fn init(&self) { 44 fn init(&self) {
52 self.once.call_once(|| unsafe { 45 self.once.call_once(|| unsafe {
53 self.alarms 46 self.alarm.write(Mutex::new(const { AlarmState::new() }));
54 .write(Mutex::new([const { AlarmState::new() }; ALARM_COUNT]));
55 self.zero_instant.write(StdInstant::now()); 47 self.zero_instant.write(StdInstant::now());
48 self.closure
49 .write(Closure::new(Box::new(|| TIMER_QUEUE_DRIVER.dispatch())));
56 }); 50 });
57 } 51 }
58}
59
60impl Driver for TimeDriver {
61 fn now(&self) -> u64 {
62 self.init();
63
64 let zero = unsafe { self.zero_instant.read() };
65 StdInstant::now().duration_since(zero).as_micros() as u64
66 }
67
68 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
69 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
70 if x < ALARM_COUNT as u8 {
71 Some(x + 1)
72 } else {
73 None
74 }
75 });
76
77 match id {
78 Ok(id) => Some(AlarmHandle::new(id)),
79 Err(_) => None,
80 }
81 }
82
83 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
84 self.init();
85 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
86 let alarm = &mut alarms[alarm.id() as usize];
87 alarm.closure.replace(Closure::new(move || {
88 callback(ctx);
89 }));
90 }
91 52
92 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 53 fn set_alarm(&self, timestamp: u64) -> bool {
93 self.init(); 54 self.init();
94 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 55 let mut alarm = unsafe { self.alarm.as_ref() }.lock().unwrap();
95 let alarm = &mut alarms[alarm.id() as usize];
96 if let Some(token) = alarm.token { 56 if let Some(token) = alarm.token {
97 clearTimeout(token); 57 clearTimeout(token);
98 } 58 }
@@ -102,13 +62,22 @@ impl Driver for TimeDriver {
102 false 62 false
103 } else { 63 } else {
104 let timeout = (timestamp - now) as u32; 64 let timeout = (timestamp - now) as u32;
105 alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); 65 alarm.token = Some(setTimeout(unsafe { self.closure.as_ref() }, timeout / 1000));
106 66
107 true 67 true
108 } 68 }
109 } 69 }
110} 70}
111 71
72impl Driver for TimeDriver {
73 fn now(&self) -> u64 {
74 self.init();
75
76 let zero = unsafe { self.zero_instant.read() };
77 StdInstant::now().duration_since(zero).as_micros() as u64
78 }
79}
80
112pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); 81pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
113unsafe impl<T> Send for UninitCell<T> {} 82unsafe impl<T> Send for UninitCell<T> {}
114unsafe impl<T> Sync for UninitCell<T> {} 83unsafe impl<T> Sync for UninitCell<T> {}
@@ -139,3 +108,8 @@ impl<T: Copy> UninitCell<T> {
139 ptr::read(self.as_mut_ptr()) 108 ptr::read(self.as_mut_ptr())
140 } 109 }
141} 110}
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);
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
index 8d0648ce5..80a359413 100644
--- a/embassy-time/src/lib.rs
+++ b/embassy-time/src/lib.rs
@@ -25,8 +25,6 @@ pub use driver_mock::MockDriver;
25mod driver_std; 25mod driver_std;
26#[cfg(feature = "wasm")] 26#[cfg(feature = "wasm")]
27mod driver_wasm; 27mod driver_wasm;
28#[cfg(feature = "generic-queue")]
29mod queue_generic;
30 28
31pub use delay::{block_for, Delay}; 29pub use delay::{block_for, Delay};
32pub use duration::Duration; 30pub use duration::Duration;
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
deleted file mode 100644
index 0068edae8..000000000
--- a/embassy-time/src/queue_generic.rs
+++ /dev/null
@@ -1,346 +0,0 @@
1use core::cell::RefCell;
2use core::cmp::{min, Ordering};
3use core::task::Waker;
4
5use critical_section::Mutex;
6use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
7use embassy_time_queue_driver::TimerQueue;
8use heapless::Vec;
9
10use crate::Instant;
11
12#[cfg(feature = "generic-queue-8")]
13const QUEUE_SIZE: usize = 8;
14#[cfg(feature = "generic-queue-16")]
15const QUEUE_SIZE: usize = 16;
16#[cfg(feature = "generic-queue-32")]
17const QUEUE_SIZE: usize = 32;
18#[cfg(feature = "generic-queue-64")]
19const QUEUE_SIZE: usize = 64;
20#[cfg(feature = "generic-queue-128")]
21const QUEUE_SIZE: usize = 128;
22#[cfg(not(any(
23 feature = "generic-queue-8",
24 feature = "generic-queue-16",
25 feature = "generic-queue-32",
26 feature = "generic-queue-64",
27 feature = "generic-queue-128"
28)))]
29const QUEUE_SIZE: usize = 64;
30
31#[derive(Debug)]
32struct Timer {
33 at: Instant,
34 waker: Waker,
35}
36
37impl PartialEq for Timer {
38 fn eq(&self, other: &Self) -> bool {
39 self.at == other.at
40 }
41}
42
43impl Eq for Timer {}
44
45impl PartialOrd for Timer {
46 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47 self.at.partial_cmp(&other.at)
48 }
49}
50
51impl Ord for Timer {
52 fn cmp(&self, other: &Self) -> Ordering {
53 self.at.cmp(&other.at)
54 }
55}
56
57struct InnerQueue {
58 queue: Vec<Timer, QUEUE_SIZE>,
59 alarm: AlarmHandle,
60}
61
62impl InnerQueue {
63 fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
64 self.queue
65 .iter_mut()
66 .find(|timer| timer.waker.will_wake(waker))
67 .map(|timer| {
68 timer.at = min(timer.at, at);
69 })
70 .unwrap_or_else(|| {
71 let mut timer = Timer {
72 waker: waker.clone(),
73 at,
74 };
75
76 loop {
77 match self.queue.push(timer) {
78 Ok(()) => break,
79 Err(e) => timer = e,
80 }
81
82 self.queue.pop().unwrap().waker.wake();
83 }
84 });
85
86 // Don't wait for the alarm callback to trigger and directly
87 // dispatch all timers that are already due
88 //
89 // Then update the alarm if necessary
90 self.dispatch();
91 }
92
93 fn dispatch(&mut self) {
94 loop {
95 let now = Instant::now();
96
97 let mut next_alarm = Instant::MAX;
98
99 let mut i = 0;
100 while i < self.queue.len() {
101 let timer = &self.queue[i];
102 if timer.at <= now {
103 let timer = self.queue.swap_remove(i);
104 timer.waker.wake();
105 } else {
106 next_alarm = min(next_alarm, timer.at);
107 i += 1;
108 }
109 }
110
111 if self.update_alarm(next_alarm) {
112 break;
113 }
114 }
115 }
116
117 fn update_alarm(&mut self, next_alarm: Instant) -> bool {
118 if next_alarm == Instant::MAX {
119 true
120 } else {
121 set_alarm(self.alarm, next_alarm.as_ticks())
122 }
123 }
124
125 fn handle_alarm(&mut self) {
126 self.dispatch();
127 }
128}
129
130struct Queue {
131 inner: Mutex<RefCell<Option<InnerQueue>>>,
132}
133
134impl Queue {
135 const fn new() -> Self {
136 Self {
137 inner: Mutex::new(RefCell::new(None)),
138 }
139 }
140
141 fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
142 critical_section::with(|cs| {
143 let mut inner = self.inner.borrow_ref_mut(cs);
144
145 inner
146 .get_or_insert_with(|| {
147 let handle = unsafe { allocate_alarm() }.unwrap();
148 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
149 InnerQueue {
150 queue: Vec::new(),
151 alarm: handle,
152 }
153 })
154 .schedule_wake(at, waker)
155 });
156 }
157
158 fn handle_alarm(&self) {
159 critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm())
160 }
161
162 fn handle_alarm_callback(ctx: *mut ()) {
163 unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm();
164 }
165}
166
167impl TimerQueue for Queue {
168 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
169 Queue::schedule_wake(self, Instant::from_ticks(at), waker);
170 }
171}
172
173embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new());
174
175#[cfg(test)]
176#[cfg(feature = "mock-driver")]
177mod tests {
178 use core::sync::atomic::{AtomicBool, Ordering};
179 use core::task::Waker;
180 use std::sync::Arc;
181 use std::task::Wake;
182
183 use serial_test::serial;
184
185 use crate::driver_mock::MockDriver;
186 use crate::queue_generic::QUEUE;
187 use crate::{Duration, Instant};
188
189 struct TestWaker {
190 pub awoken: AtomicBool,
191 }
192
193 impl Wake for TestWaker {
194 fn wake(self: Arc<Self>) {
195 self.awoken.store(true, Ordering::Relaxed);
196 }
197
198 fn wake_by_ref(self: &Arc<Self>) {
199 self.awoken.store(true, Ordering::Relaxed);
200 }
201 }
202
203 fn test_waker() -> (Arc<TestWaker>, Waker) {
204 let arc = Arc::new(TestWaker {
205 awoken: AtomicBool::new(false),
206 });
207 let waker = Waker::from(arc.clone());
208
209 (arc, waker)
210 }
211
212 fn setup() {
213 MockDriver::get().reset();
214 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
215 }
216
217 fn queue_len() -> usize {
218 critical_section::with(|cs| {
219 QUEUE
220 .inner
221 .borrow_ref(cs)
222 .as_ref()
223 .map(|inner| inner.queue.iter().count())
224 .unwrap_or(0)
225 })
226 }
227
228 #[test]
229 #[serial]
230 fn test_schedule() {
231 setup();
232
233 assert_eq!(queue_len(), 0);
234
235 let (flag, waker) = test_waker();
236
237 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
238
239 assert!(!flag.awoken.load(Ordering::Relaxed));
240 assert_eq!(queue_len(), 1);
241 }
242
243 #[test]
244 #[serial]
245 fn test_schedule_same() {
246 setup();
247
248 let (_flag, waker) = test_waker();
249
250 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
251
252 assert_eq!(queue_len(), 1);
253
254 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
255
256 assert_eq!(queue_len(), 1);
257
258 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
259
260 assert_eq!(queue_len(), 1);
261
262 let (_flag2, waker2) = test_waker();
263
264 QUEUE.schedule_wake(Instant::from_secs(100), &waker2);
265
266 assert_eq!(queue_len(), 2);
267 }
268
269 #[test]
270 #[serial]
271 fn test_trigger() {
272 setup();
273
274 let (flag, waker) = test_waker();
275
276 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
277
278 assert!(!flag.awoken.load(Ordering::Relaxed));
279
280 MockDriver::get().advance(Duration::from_secs(99));
281
282 assert!(!flag.awoken.load(Ordering::Relaxed));
283
284 assert_eq!(queue_len(), 1);
285
286 MockDriver::get().advance(Duration::from_secs(1));
287
288 assert!(flag.awoken.load(Ordering::Relaxed));
289
290 assert_eq!(queue_len(), 0);
291 }
292
293 #[test]
294 #[serial]
295 fn test_immediate_trigger() {
296 setup();
297
298 let (flag, waker) = test_waker();
299
300 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
301
302 MockDriver::get().advance(Duration::from_secs(50));
303
304 let (flag2, waker2) = test_waker();
305
306 QUEUE.schedule_wake(Instant::from_secs(40), &waker2);
307
308 assert!(!flag.awoken.load(Ordering::Relaxed));
309 assert!(flag2.awoken.load(Ordering::Relaxed));
310 assert_eq!(queue_len(), 1);
311 }
312
313 #[test]
314 #[serial]
315 fn test_queue_overflow() {
316 setup();
317
318 for i in 1..super::QUEUE_SIZE {
319 let (flag, waker) = test_waker();
320
321 QUEUE.schedule_wake(Instant::from_secs(310), &waker);
322
323 assert_eq!(queue_len(), i);
324 assert!(!flag.awoken.load(Ordering::Relaxed));
325 }
326
327 let (flag, waker) = test_waker();
328
329 QUEUE.schedule_wake(Instant::from_secs(300), &waker);
330
331 assert_eq!(queue_len(), super::QUEUE_SIZE);
332 assert!(!flag.awoken.load(Ordering::Relaxed));
333
334 let (flag2, waker2) = test_waker();
335
336 QUEUE.schedule_wake(Instant::from_secs(305), &waker2);
337
338 assert_eq!(queue_len(), super::QUEUE_SIZE);
339 assert!(flag.awoken.load(Ordering::Relaxed));
340
341 let (_flag3, waker3) = test_waker();
342 QUEUE.schedule_wake(Instant::from_secs(320), &waker3);
343 assert_eq!(queue_len(), super::QUEUE_SIZE);
344 assert!(flag2.awoken.load(Ordering::Relaxed));
345 }
346}
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml
index 290b2fdb1..326355dd6 100644
--- a/examples/nrf52840-rtic/Cargo.toml
+++ b/examples/nrf52840-rtic/Cargo.toml
@@ -9,7 +9,8 @@ rtic = { version = "2", features = ["thumbv7-backend"] }
9 9
10embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 10embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] }
13embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver" }
13embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 14embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
14 15
15defmt = "0.3" 16defmt = "0.3"