From 97cfa4f1d781e8c12f7b3b1271a0aeb93af3785f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 8 Dec 2024 16:55:18 +0100 Subject: [PATCH] Provide a common implementation for global queues --- ci-xtensa.sh | 1 - ci.sh | 1 - embassy-executor/Cargo.toml | 2 +- embassy-nrf/src/time_driver.rs | 59 +------- embassy-rp/src/time_driver.rs | 59 +------- embassy-stm32/src/time_driver.rs | 71 +--------- embassy-time-queue-driver/Cargo.toml | 20 ++- embassy-time-queue-driver/src/lib.rs | 131 ++++++++++++++++-- .../src/queue_generic.rs | 29 +--- embassy-time/Cargo.toml | 4 +- embassy-time/src/driver_std.rs | 44 +----- embassy-time/src/driver_wasm.rs | 44 +----- examples/nrf52840-rtic/Cargo.toml | 2 +- 13 files changed, 168 insertions(+), 299 deletions(-) diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 2ab533f450..9811cabd17 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh @@ -25,7 +25,6 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-const-generic \ --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ diff --git a/ci.sh b/ci.sh index 716eb5e477..bd7b256b8e 100755 --- a/ci.sh +++ b/ci.sh @@ -46,7 +46,6 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-const-generic \ --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 7c930b658a..348ab5a259 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -68,7 +68,7 @@ nightly = ["embassy-executor-macros/nightly"] turbowakers = [] ## Use the executor-integrated `embassy-time` timer queue. -integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"] +integrated-timers = ["dep:embassy-time-driver"] #! ### Architecture _arch = [] # some arch was picked diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 0ea985a0e9..f8b3c4bbcf 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -5,6 +5,7 @@ use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; @@ -277,57 +278,7 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { DRIVER.init(irq_prio) } -#[cfg(feature = "integrated-timers")] -type RawQueue = embassy_executor::raw::timer_queue::TimerQueue; - -#[cfg(not(feature = "integrated-timers"))] -type RawQueue = embassy_time_queue_driver::queue_generic::RefCellQueue; - -struct TimerQueueDriver { - inner: Mutex, -} - -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE_DRIVER: TimerQueueDriver = TimerQueueDriver::new()); - -impl embassy_time_queue_driver::TimerQueue for TimerQueueDriver { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - #[cfg(feature = "integrated-timers")] - let waker = embassy_executor::raw::task_from_waker(waker); - self.inner.lock(|q| { - if q.schedule_wake(at, waker) { - self.dispatch(); - } - }); - } -} - -impl TimerQueueDriver { - const fn new() -> Self { - Self { - inner: Mutex::new(RawQueue::new()), - } - } - - pub fn dispatch(&self) { - let now = DRIVER.now(); - let next_expiration = self.inner.lock(|q| dequeue(q, now)); - self.arm_alarm(next_expiration); - } - - fn arm_alarm(&self, mut next_expiration: u64) { - while !DRIVER.set_alarm(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - next_expiration = self.inner.lock(|q| dequeue(q, next_expiration)); - } - } -} - -fn dequeue(q: &RawQueue, now: u64) -> u64 { - #[cfg(feature = "integrated-timers")] - let next = unsafe { q.next_expiration(now, embassy_executor::raw::wake_task) }; - - #[cfg(not(feature = "integrated-timers"))] - let next = q.next_expiration(now); - - next -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index 2890d4dffa..17ae5fff3b 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs @@ -4,6 +4,7 @@ use core::cell::Cell; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; #[cfg(feature = "rp2040")] use pac::TIMER; #[cfg(feature = "_rp235x")] @@ -125,57 +126,7 @@ fn TIMER0_IRQ_0() { DRIVER.check_alarm() } -#[cfg(feature = "integrated-timers")] -type RawQueue = embassy_executor::raw::timer_queue::TimerQueue; - -#[cfg(not(feature = "integrated-timers"))] -type RawQueue = embassy_time_queue_driver::queue_generic::RefCellQueue; - -struct TimerQueueDriver { - inner: Mutex, -} - -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE_DRIVER: TimerQueueDriver = TimerQueueDriver::new()); - -impl embassy_time_queue_driver::TimerQueue for TimerQueueDriver { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - #[cfg(feature = "integrated-timers")] - let waker = embassy_executor::raw::task_from_waker(waker); - self.inner.lock(|q| { - if q.schedule_wake(at, waker) { - self.dispatch(); - } - }); - } -} - -impl TimerQueueDriver { - const fn new() -> Self { - Self { - inner: Mutex::new(RawQueue::new()), - } - } - - pub fn dispatch(&self) { - let now = DRIVER.now(); - let next_expiration = self.inner.lock(|q| dequeue(q, now)); - self.arm_alarm(next_expiration); - } - - fn arm_alarm(&self, mut next_expiration: u64) { - while !DRIVER.set_alarm(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - next_expiration = self.inner.lock(|q| dequeue(q, next_expiration)); - } - } -} - -fn dequeue(q: &RawQueue, now: u64) -> u64 { - #[cfg(feature = "integrated-timers")] - let next = unsafe { q.next_expiration(now, embassy_executor::raw::wake_task) }; - - #[cfg(not(feature = "integrated-timers"))] - let next = q.next_expiration(now); - - next -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 1015eefb35..290f857ade 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -7,6 +7,7 @@ use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::{Driver, TICK_HZ}; +use embassy_time_queue_driver::GlobalTimerQueue; use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; @@ -23,18 +24,6 @@ use crate::{interrupt, peripherals}; // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) -// -// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number -// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: -// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. - -//cfg_if::cfg_if! { -// if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] { -// const ALARM_COUNT: usize = 1; -// } else { -// const ALARM_COUNT: usize = 3; -// } -//} #[cfg(time_driver_tim1)] type T = peripherals::TIM1; @@ -518,57 +507,7 @@ pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) } -#[cfg(feature = "integrated-timers")] -type RawQueue = embassy_executor::raw::timer_queue::TimerQueue; - -#[cfg(not(feature = "integrated-timers"))] -type RawQueue = embassy_time_queue_driver::queue_generic::RefCellQueue; - -struct TimerQueueDriver { - inner: Mutex, -} - -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE_DRIVER: TimerQueueDriver = TimerQueueDriver::new()); - -impl embassy_time_queue_driver::TimerQueue for TimerQueueDriver { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - #[cfg(feature = "integrated-timers")] - let waker = embassy_executor::raw::task_from_waker(waker); - self.inner.lock(|q| { - if q.schedule_wake(at, waker) { - self.dispatch(); - } - }); - } -} - -impl TimerQueueDriver { - const fn new() -> Self { - Self { - inner: Mutex::new(RawQueue::new()), - } - } - - pub fn dispatch(&self) { - let now = DRIVER.now(); - let next_expiration = self.inner.lock(|q| dequeue(q, now)); - self.arm_alarm(next_expiration); - } - - fn arm_alarm(&self, mut next_expiration: u64) { - while !DRIVER.set_alarm(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - next_expiration = self.inner.lock(|q| dequeue(q, next_expiration)); - } - } -} - -fn dequeue(q: &RawQueue, now: u64) -> u64 { - #[cfg(feature = "integrated-timers")] - let next = unsafe { q.next_expiration(now, embassy_executor::raw::wake_task) }; - - #[cfg(not(feature = "integrated-timers"))] - let next = q.next_expiration(now); - - next -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 94a223581e..599041a3f5 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml @@ -21,14 +21,20 @@ categories = [ links = "embassy-time-queue" [dependencies] +critical-section = "1.2.0" heapless = "0.8" +embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } +embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } [features] #! ### Generic Queue -## Create a global, generic queue that can be used with any executor. +## Use the executor-integrated `embassy-time` timer queue. The timer items are stored inside +## the task headers, so you do not need to set a capacity for the queue. ## To use this you must have a time driver provided. -generic-queue = [] +## +## If this feature is not enabled, a generic queue is available with a configurable capacity. +integrated-timers = ["embassy-executor/integrated-timers"] #! The following features set how many timers are used for the generic queue. At most one #! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. @@ -37,15 +43,15 @@ generic-queue = [] #! end user to pick. ## Generic Queue with 8 timers -generic-queue-8 = ["generic-queue"] +generic-queue-8 = [] ## Generic Queue with 16 timers -generic-queue-16 = ["generic-queue"] +generic-queue-16 = [] ## Generic Queue with 32 timers -generic-queue-32 = ["generic-queue"] +generic-queue-32 = [] ## Generic Queue with 64 timers -generic-queue-64 = ["generic-queue"] +generic-queue-64 = [] ## Generic Queue with 128 timers -generic-queue-128 = ["generic-queue"] +generic-queue-128 = [] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index f09fa52acf..4993c01aea 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -7,17 +7,27 @@ //! - Define a struct `MyTimerQueue` //! - Implement [`TimerQueue`] for it //! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). +//! - Ensure that [`TimerQueue::dispatch`] is called when `schedule_wake` is due. //! -//! ### Design choices +//! If a single global timer queue is sufficient for you, you can use the +//! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue +//! protected by a critical section. //! -//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to -//! expose the `embassy-executor/integrated-timers` feature in your implementation. This feature -//! stores timer items in the tasks themselves, so you don't need a fixed-size queue or dynamic -//! memory allocation. +//! ``` +//! use embassy_time_queue_driver::GlobalTimerQueue; +//! embassy_time_queue_driver::timer_queue_impl!( +//! static TIMER_QUEUE_DRIVER: GlobalTimerQueue +//! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) +//! ); +//! ``` +//! +//! You can also use the `queue_generic` or the `embassy_executor::raw::timer_queue` modules to +//! implement your own timer queue. These modules contain queue implementations which you can wrap +//! and tailor to your needs. //! -//! To implement the `TimerQueue` trait, you can use the `queue_generic` module from the -//! `embassy-time` crate, or the `raw::timer_queue` module from the `embassy-executor` crate. -//! These modules contain queue implementations which you can wrap and tailor to your needs. +//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to +//! expose the `integrated-timers` feature in your implementation. This feature stores timer items +//! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. //! //! ## Example //! @@ -40,11 +50,15 @@ pub mod queue_generic; +use core::cell::RefCell; use core::task::Waker; +use critical_section::Mutex; + /// Timer queue pub trait TimerQueue { /// Schedules a waker in the queue to be awoken at moment `at`. + /// /// If this moment is in the past, the waker might be awoken immediately. fn schedule_wake(&'static self, at: u64, waker: &Waker); } @@ -72,3 +86,104 @@ macro_rules! timer_queue_impl { } }; } + +#[cfg(feature = "integrated-timers")] +type InnerQueue = embassy_executor::raw::timer_queue::TimerQueue; + +#[cfg(not(feature = "integrated-timers"))] +type InnerQueue = queue_generic::Queue; + +/// A timer queue implementation that can be used as a global timer queue. +/// +/// This implementation is not thread-safe, and should be protected by a mutex of some sort. +pub struct GenericTimerQueue bool> { + queue: InnerQueue, + set_alarm: F, +} + +impl bool> GenericTimerQueue { + /// Creates a new timer queue. + /// + /// `set_alarm` is a function that should set the next alarm time. The function should + /// return `true` if the alarm was set, and `false` if the alarm was in the past. + pub const fn new(set_alarm: F) -> Self { + Self { + queue: InnerQueue::new(), + set_alarm, + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) { + #[cfg(feature = "integrated-timers")] + let waker = embassy_executor::raw::task_from_waker(waker); + + if self.queue.schedule_wake(at, waker) { + self.dispatch() + } + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&mut self, now: u64) -> u64 { + self.queue.next_expiration(now) + } + + /// Handle the alarm. + pub fn dispatch(&mut self) { + let mut next_expiration = self.next_expiration(embassy_time_driver::now()); + + while !(self.set_alarm)(next_expiration) { + // next_expiration is in the past, dequeue and find a new expiration + next_expiration = self.next_expiration(next_expiration); + } + } +} + +/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`]. +pub struct GlobalTimerQueue { + inner: Mutex bool>>>, +} + +impl GlobalTimerQueue { + /// Creates a new timer queue. + /// + /// `set_alarm` is a function that should set the next alarm time. The function should + /// return `true` if the alarm was set, and `false` if the alarm was in the past. + pub const fn new(set_alarm: fn(u64) -> bool) -> Self { + Self { + inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))), + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.schedule_wake(at, waker); + }); + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&self, now: u64) -> u64 { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.next_expiration(now) + }) + } + + /// Handle the alarm. + /// + /// Call this function when the next alarm is due. + pub fn dispatch(&self) { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.dispatch() + }) + } +} + +impl TimerQueue for GlobalTimerQueue { + fn schedule_wake(&'static self, at: u64, waker: &Waker) { + GlobalTimerQueue::schedule_wake(self, at, waker) + } +} diff --git a/embassy-time-queue-driver/src/queue_generic.rs b/embassy-time-queue-driver/src/queue_generic.rs index da76bf4bae..62778ca6fa 100644 --- a/embassy-time-queue-driver/src/queue_generic.rs +++ b/embassy-time-queue-driver/src/queue_generic.rs @@ -1,6 +1,7 @@ -//! A generic timer queue. Time queue drivers may use this to simplify their implementation. +//! Generic timer queue implementations. +//! +//! Time queue drivers may use this to simplify their implementation. -use core::cell::RefCell; use core::cmp::{min, Ordering}; use core::task::Waker; @@ -137,27 +138,3 @@ impl Queue { self.queue.next_expiration(now) } } - -/// A simple wrapper around a `Queue` to provide interior mutability. -pub struct RefCellQueue { - inner: RefCell, -} - -impl RefCellQueue { - /// Creates a new timer queue. - pub const fn new() -> Self { - Self { - inner: RefCell::new(Queue::new()), - } - } - - /// Schedules a task to run at a specific time, and returns whether any changes were made. - pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) -> bool { - self.inner.borrow_mut().schedule_wake(at, waker) - } - - /// Dequeues expired timers and returns the next alarm time. - pub fn next_expiration(&self, now: u64) -> u64 { - self.inner.borrow_mut().next_expiration(now) - } -} diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index cf98334e68..e3074119f3 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -24,8 +24,8 @@ target = "x86_64-unknown-linux-gnu" features = ["defmt", "std"] [features] -std = ["tick-hz-1_000_000", "critical-section/std", "embassy-time-queue-driver/generic-queue"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000", "embassy-time-queue-driver/generic-queue"] +std = ["tick-hz-1_000_000", "critical-section/std"] +wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] ## Display the time since startup next to defmt log messages. ## At most 1 `defmt-timestamp-uptime-*` feature can be used. diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 63f5c92d9c..45467f09bd 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -6,6 +6,7 @@ use std::{ptr, thread}; use critical_section::Mutex as CsMutex; use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; struct AlarmState { timestamp: u64, @@ -164,42 +165,7 @@ impl UninitCell { } } -type RawQueue = embassy_time_queue_driver::queue_generic::RefCellQueue; - -struct TimerQueueDriver { - inner: Mutex, -} - -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE_DRIVER: TimerQueueDriver = TimerQueueDriver::new()); - -impl embassy_time_queue_driver::TimerQueue for TimerQueueDriver { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - let q = self.inner.lock().unwrap(); - if q.schedule_wake(at, waker) { - self.dispatch(); - } - } -} - -impl TimerQueueDriver { - const fn new() -> Self { - Self { - inner: Mutex::new(RawQueue::new()), - } - } - - pub fn dispatch(&self) { - let now = DRIVER.now(); - let q = self.inner.lock().unwrap(); - let next_expiration = q.next_expiration(now); - self.arm_alarm(next_expiration); - } - - fn arm_alarm(&self, mut next_expiration: u64) { - while !DRIVER.set_alarm(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - let q = self.inner.lock().unwrap(); - next_expiration = q.next_expiration(next_expiration); - } - } -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index 003947de24..dcc935fdef 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -4,6 +4,7 @@ use std::ptr; use std::sync::{Mutex, Once}; use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; @@ -108,42 +109,7 @@ impl UninitCell { } } -type RawQueue = embassy_time_queue_driver::queue_generic::RefCellQueue; - -struct TimerQueueDriver { - inner: Mutex, -} - -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE_DRIVER: TimerQueueDriver = TimerQueueDriver::new()); - -impl embassy_time_queue_driver::TimerQueue for TimerQueueDriver { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - let q = self.inner.lock().unwrap(); - if q.schedule_wake(at, waker) { - self.dispatch(); - } - } -} - -impl TimerQueueDriver { - const fn new() -> Self { - Self { - inner: Mutex::new(RawQueue::new()), - } - } - - pub fn dispatch(&self) { - let now = DRIVER.now(); - let q = self.inner.lock().unwrap(); - let next_expiration = q.next_expiration(now); - self.arm_alarm(next_expiration); - } - - fn arm_alarm(&self, mut next_expiration: u64) { - while !DRIVER.set_alarm(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - let q = self.inner.lock().unwrap(); - next_expiration = q.next_expiration(next_expiration); - } - } -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 200b29a550..326355dd6c 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -10,7 +10,7 @@ rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } -embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver", features = [ "generic-queue" ] } +embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3"