From db1b58452a8a074bfd28d70313c6909c68f664d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 26 Nov 2024 23:54:21 +0100 Subject: [PATCH 01/13] Dequeue in alarm --- embassy-executor/src/raw/mod.rs | 40 +++++++++++----- embassy-executor/src/raw/timer_queue.rs | 61 +++++++++++++------------ 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index ebabee1ba2..e439415595 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -29,6 +29,10 @@ use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; +#[cfg(feature = "integrated-timers")] +use core::cell::Cell; +#[cfg(feature = "integrated-timers")] +use critical_section::Mutex; #[cfg(feature = "integrated-timers")] use embassy_time_driver::AlarmHandle; #[cfg(feature = "rtos-trace")] @@ -48,7 +52,7 @@ pub(crate) struct TaskHeader { poll_fn: SyncUnsafeCell>, #[cfg(feature = "integrated-timers")] - pub(crate) expires_at: SyncUnsafeCell, + pub(crate) expires_at: Mutex>, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -121,7 +125,7 @@ impl TaskStorage { poll_fn: SyncUnsafeCell::new(None), #[cfg(feature = "integrated-timers")] - expires_at: SyncUnsafeCell::new(0), + expires_at: Mutex::new(Cell::new(0)), #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, @@ -162,7 +166,9 @@ impl TaskStorage { this.raw.state.despawn(); #[cfg(feature = "integrated-timers")] - this.raw.expires_at.set(u64::MAX); + critical_section::with(|cs| { + this.raw.expires_at.borrow(cs).set(u64::MAX); + }); } Poll::Pending => {} } @@ -363,6 +369,12 @@ impl SyncExecutor { #[cfg(feature = "integrated-timers")] fn alarm_callback(ctx: *mut ()) { let this: &Self = unsafe { &*(ctx as *const Self) }; + + unsafe { + this.timer_queue + .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); + } + this.pender.pend(); } @@ -379,17 +391,16 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { + //trace!("poll"); #[allow(clippy::never_loop)] loop { - #[cfg(feature = "integrated-timers")] - self.timer_queue - .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); - self.run_queue.dequeue_all(|p| { let task = p.header(); #[cfg(feature = "integrated-timers")] - task.expires_at.set(u64::MAX); + critical_section::with(|cs| { + task.expires_at.borrow(cs).set(u64::MAX); + }); if !task.state.run_dequeue() { // If task is not running, ignore it. This can happen in the following scenario: @@ -421,6 +432,11 @@ impl SyncExecutor { let next_expiration = self.timer_queue.next_expiration(); if embassy_time_driver::set_alarm(self.alarm, next_expiration) { break; + } else { + // Time driver did not schedule the alarm, + // so we need to dequeue expired timers manually. + self.timer_queue + .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); } } @@ -584,10 +600,10 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); let task = task.header(); - unsafe { - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); - } + critical_section::with(|cs| { + let expires_at = task.expires_at.borrow(cs).get(); + task.expires_at.borrow(cs).set(expires_at.min(at)); + }); } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 94a5f340be..0be2379207 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,74 +1,79 @@ +use core::cell::Cell; use core::cmp::min; use super::TaskRef; -use crate::raw::util::SyncUnsafeCell; +use critical_section::{CriticalSection, Mutex}; pub(crate) struct TimerQueueItem { - next: SyncUnsafeCell>, + pub(super) next: Mutex>>, } impl TimerQueueItem { pub const fn new() -> Self { Self { - next: SyncUnsafeCell::new(None), + next: Mutex::new(Cell::new(None)), } } } pub(crate) struct TimerQueue { - head: SyncUnsafeCell>, + pub(super) head: Mutex>>, } impl TimerQueue { pub const fn new() -> Self { Self { - head: SyncUnsafeCell::new(None), + head: Mutex::new(Cell::new(None)), } } pub(crate) unsafe fn update(&self, p: TaskRef) { let task = p.header(); - if task.expires_at.get() != u64::MAX { - if task.state.timer_enqueue() { - task.timer_queue_item.next.set(self.head.get()); - self.head.set(Some(p)); + critical_section::with(|cs| { + if task.expires_at.borrow(cs).get() != u64::MAX { + if task.state.timer_enqueue() { + let prev = self.head.borrow(cs).replace(Some(p)); + task.timer_queue_item.next.borrow(cs).set(prev); + } } - } + }); } pub(crate) unsafe fn next_expiration(&self) -> u64 { let mut res = u64::MAX; - self.retain(|p| { - let task = p.header(); - let expires = task.expires_at.get(); - res = min(res, expires); - expires != u64::MAX + critical_section::with(|cs| { + self.retain(cs, |p| { + let task = p.header(); + let expires = task.expires_at.borrow(cs).get(); + res = min(res, expires); + expires != u64::MAX + }); }); res } pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { - self.retain(|p| { - let task = p.header(); - if task.expires_at.get() <= now { - on_task(p); - false - } else { - true - } + critical_section::with(|cs| { + self.retain(cs, |p| { + let task = p.header(); + if task.expires_at.borrow(cs).get() <= now { + on_task(p); + false + } else { + true + } + }); }); } - pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { + unsafe fn retain(&self, cs: CriticalSection<'_>, mut f: impl FnMut(TaskRef) -> bool) { let mut prev = &self.head; - while let Some(p) = prev.get() { + while let Some(p) = prev.borrow(cs).get() { let task = p.header(); if f(p) { - // Skip to next prev = &task.timer_queue_item.next; } else { - // Remove it - prev.set(task.timer_queue_item.next.get()); + prev.borrow(cs).set(task.timer_queue_item.next.borrow(cs).get()); task.state.timer_dequeue(); } } From 457e708f1c527c915597efaa2dbfb0b01d759fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 11:31:20 +0100 Subject: [PATCH 02/13] Only take one critical section to track next expiration --- embassy-executor/src/raw/mod.rs | 25 ++++++++++++------------- embassy-executor/src/raw/timer_queue.rs | 10 ++++++---- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e439415595..c6c0f9dc4a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -22,6 +22,8 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; +#[cfg(feature = "integrated-timers")] +use core::cell::Cell; use core::future::Future; use core::marker::PhantomData; use core::mem; @@ -29,8 +31,6 @@ use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; -#[cfg(feature = "integrated-timers")] -use core::cell::Cell; #[cfg(feature = "integrated-timers")] use critical_section::Mutex; #[cfg(feature = "integrated-timers")] @@ -54,6 +54,8 @@ pub(crate) struct TaskHeader { #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Mutex>, #[cfg(feature = "integrated-timers")] + pub(crate) next_expiration: SyncUnsafeCell, + #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -127,6 +129,8 @@ impl TaskStorage { #[cfg(feature = "integrated-timers")] expires_at: Mutex::new(Cell::new(0)), #[cfg(feature = "integrated-timers")] + next_expiration: SyncUnsafeCell::new(0), + #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, future: UninitCell::uninit(), @@ -166,9 +170,7 @@ impl TaskStorage { this.raw.state.despawn(); #[cfg(feature = "integrated-timers")] - critical_section::with(|cs| { - this.raw.expires_at.borrow(cs).set(u64::MAX); - }); + this.raw.next_expiration.set(u64::MAX); } Poll::Pending => {} } @@ -391,16 +393,13 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { - //trace!("poll"); #[allow(clippy::never_loop)] loop { self.run_queue.dequeue_all(|p| { let task = p.header(); #[cfg(feature = "integrated-timers")] - critical_section::with(|cs| { - task.expires_at.borrow(cs).set(u64::MAX); - }); + task.next_expiration.set(u64::MAX); if !task.state.run_dequeue() { // If task is not running, ignore it. This can happen in the following scenario: @@ -600,10 +599,10 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); let task = task.header(); - critical_section::with(|cs| { - let expires_at = task.expires_at.borrow(cs).get(); - task.expires_at.borrow(cs).set(expires_at.min(at)); - }); + unsafe { + let expires_at = task.next_expiration.get(); + task.next_expiration.set(expires_at.min(at)); + } } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 0be2379207..1716b8d9f4 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,11 +1,12 @@ use core::cell::Cell; use core::cmp::min; -use super::TaskRef; use critical_section::{CriticalSection, Mutex}; +use super::TaskRef; + pub(crate) struct TimerQueueItem { - pub(super) next: Mutex>>, + next: Mutex>>, } impl TimerQueueItem { @@ -17,7 +18,7 @@ impl TimerQueueItem { } pub(crate) struct TimerQueue { - pub(super) head: Mutex>>, + head: Mutex>>, } impl TimerQueue { @@ -30,7 +31,8 @@ impl TimerQueue { pub(crate) unsafe fn update(&self, p: TaskRef) { let task = p.header(); critical_section::with(|cs| { - if task.expires_at.borrow(cs).get() != u64::MAX { + task.expires_at.borrow(cs).set(task.next_expiration.get()); + if task.next_expiration.get() != u64::MAX { if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); task.timer_queue_item.next.borrow(cs).set(prev); From dcc2c29627a1dbad2a511b0f5d298f266d8113e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 12:11:37 +0100 Subject: [PATCH 03/13] Don't take CS if the task didn't re-schedule itself --- embassy-executor/src/raw/timer_queue.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 1716b8d9f4..d0ff49e8ca 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -30,15 +30,14 @@ impl TimerQueue { pub(crate) unsafe fn update(&self, p: TaskRef) { let task = p.header(); - critical_section::with(|cs| { - task.expires_at.borrow(cs).set(task.next_expiration.get()); - if task.next_expiration.get() != u64::MAX { + if task.next_expiration.get() != u64::MAX { + critical_section::with(|cs| { if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); task.timer_queue_item.next.borrow(cs).set(prev); } - } - }); + }); + } } pub(crate) unsafe fn next_expiration(&self) -> u64 { @@ -46,7 +45,8 @@ impl TimerQueue { critical_section::with(|cs| { self.retain(cs, |p| { let task = p.header(); - let expires = task.expires_at.borrow(cs).get(); + let expires = task.next_expiration.get(); + task.expires_at.borrow(cs).set(expires); res = min(res, expires); expires != u64::MAX }); From 91e1b3f09d83baae79c9557f6e7a3348ff4673f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 13:01:32 +0100 Subject: [PATCH 04/13] Only update on schedul --- embassy-executor/src/raw/mod.rs | 9 ++------- embassy-executor/src/raw/timer_queue.rs | 5 +++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index c6c0f9dc4a..f65dedfa98 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -418,10 +418,6 @@ impl SyncExecutor { #[cfg(feature = "rtos-trace")] trace::task_exec_end(); - - // Enqueue or update into timer_queue - #[cfg(feature = "integrated-timers")] - self.timer_queue.update(p); }); #[cfg(feature = "integrated-timers")] @@ -598,10 +594,9 @@ struct TimerQueue; impl embassy_time_queue_driver::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); - let task = task.header(); unsafe { - let expires_at = task.next_expiration.get(); - task.next_expiration.set(expires_at.min(at)); + let executor = task.header().executor.get().unwrap_unchecked(); + executor.timer_queue.update(task, at); } } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index d0ff49e8ca..c0c5b6ce64 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -28,9 +28,10 @@ impl TimerQueue { } } - pub(crate) unsafe fn update(&self, p: TaskRef) { + pub(crate) unsafe fn update(&self, p: TaskRef, at: u64) { let task = p.header(); - if task.next_expiration.get() != u64::MAX { + if at < task.next_expiration.get() { + task.next_expiration.set(at); critical_section::with(|cs| { if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); From 04c14190d622cd1b137920309567dc1c990073f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 13:25:33 +0100 Subject: [PATCH 05/13] Do not scan for new expiration if nothing changes --- embassy-executor/src/raw/mod.rs | 9 ++++++++- embassy-executor/src/raw/timer_queue.rs | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f65dedfa98..27ab9a7d42 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -170,7 +170,12 @@ impl TaskStorage { this.raw.state.despawn(); #[cfg(feature = "integrated-timers")] - this.raw.next_expiration.set(u64::MAX); + this.raw + .executor + .get() + .unwrap_unchecked() + .timer_queue + .notify_task_exited(p); } Poll::Pending => {} } @@ -407,6 +412,8 @@ impl SyncExecutor { // - While task is being polled, it gets woken. It gets placed in the queue. // - Task poll finishes, returning done=true // - RUNNING bit is cleared, but the task is already in the queue. + #[cfg(feature = "integrated-timers")] + self.timer_queue.notify_task_exited(p); return; } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index c0c5b6ce64..0bf7321ea3 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -19,20 +19,31 @@ impl TimerQueueItem { pub(crate) struct TimerQueue { head: Mutex>>, + rescan: Mutex>, } impl TimerQueue { pub const fn new() -> Self { Self { head: Mutex::new(Cell::new(None)), + rescan: Mutex::new(Cell::new(true)), } } + pub(crate) unsafe fn notify_task_exited(&self, p: TaskRef) { + let task = p.header(); + task.next_expiration.set(u64::MAX); + critical_section::with(|cs| { + self.rescan.borrow(cs).set(true); + }); + } + pub(crate) unsafe fn update(&self, p: TaskRef, at: u64) { let task = p.header(); if at < task.next_expiration.get() { task.next_expiration.set(at); critical_section::with(|cs| { + self.rescan.borrow(cs).set(true); if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); task.timer_queue_item.next.borrow(cs).set(prev); @@ -44,6 +55,10 @@ impl TimerQueue { pub(crate) unsafe fn next_expiration(&self) -> u64 { let mut res = u64::MAX; critical_section::with(|cs| { + let rescan = self.rescan.borrow(cs).replace(false); + if !rescan { + return; + } self.retain(cs, |p| { let task = p.header(); let expires = task.next_expiration.get(); @@ -57,15 +72,20 @@ impl TimerQueue { pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { critical_section::with(|cs| { + let mut changed = false; self.retain(cs, |p| { let task = p.header(); if task.expires_at.borrow(cs).get() <= now { on_task(p); + changed = true; false } else { true } }); + if changed { + self.rescan.borrow(cs).set(true); + } }); } From 2645e52e5c8b37929530fabd0d360858c7cbaf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 14:10:25 +0100 Subject: [PATCH 06/13] Do less work per poll --- embassy-executor/src/raw/mod.rs | 67 +++++++-------------- embassy-executor/src/raw/timer_queue.rs | 79 ++++++++++++++----------- 2 files changed, 66 insertions(+), 80 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 27ab9a7d42..f0ad04769f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -346,7 +346,7 @@ impl SyncExecutor { pender, #[cfg(feature = "integrated-timers")] - timer_queue: timer_queue::TimerQueue::new(), + timer_queue: timer_queue::TimerQueue::new(alarm), #[cfg(feature = "integrated-timers")] alarm, } @@ -398,55 +398,30 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { - #[allow(clippy::never_loop)] - loop { - self.run_queue.dequeue_all(|p| { - let task = p.header(); - - #[cfg(feature = "integrated-timers")] - task.next_expiration.set(u64::MAX); - - if !task.state.run_dequeue() { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - #[cfg(feature = "integrated-timers")] - self.timer_queue.notify_task_exited(p); - return; - } - - #[cfg(feature = "rtos-trace")] - trace::task_exec_begin(p.as_ptr() as u32); - - // Run the task - task.poll_fn.get().unwrap_unchecked()(p); - - #[cfg(feature = "rtos-trace")] - trace::task_exec_end(); - }); + self.run_queue.dequeue_all(|p| { + let task = p.header(); #[cfg(feature = "integrated-timers")] - { - // If this is already in the past, set_alarm might return false - // In that case do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - if embassy_time_driver::set_alarm(self.alarm, next_expiration) { - break; - } else { - // Time driver did not schedule the alarm, - // so we need to dequeue expired timers manually. - self.timer_queue - .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); - } + task.next_expiration.set(u64::MAX); + + if !task.state.run_dequeue() { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; } - #[cfg(not(feature = "integrated-timers"))] - { - break; - } - } + #[cfg(feature = "rtos-trace")] + trace::task_exec_begin(p.as_ptr() as u32); + + // Run the task + task.poll_fn.get().unwrap_unchecked()(p); + + #[cfg(feature = "rtos-trace")] + trace::task_exec_end(); + }); #[cfg(feature = "rtos-trace")] trace::system_idle(); diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 0bf7321ea3..7b954d86c9 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -3,7 +3,7 @@ use core::cmp::min; use critical_section::{CriticalSection, Mutex}; -use super::TaskRef; +use super::{AlarmHandle, TaskRef}; pub(crate) struct TimerQueueItem { next: Mutex>>, @@ -19,14 +19,14 @@ impl TimerQueueItem { pub(crate) struct TimerQueue { head: Mutex>>, - rescan: Mutex>, + alarm: AlarmHandle, } impl TimerQueue { - pub const fn new() -> Self { + pub const fn new(alarm: AlarmHandle) -> Self { Self { head: Mutex::new(Cell::new(None)), - rescan: Mutex::new(Cell::new(true)), + alarm, } } @@ -34,7 +34,7 @@ impl TimerQueue { let task = p.header(); task.next_expiration.set(u64::MAX); critical_section::with(|cs| { - self.rescan.borrow(cs).set(true); + self.dispatch(cs, super::wake_task); }); } @@ -43,48 +43,34 @@ impl TimerQueue { if at < task.next_expiration.get() { task.next_expiration.set(at); critical_section::with(|cs| { - self.rescan.borrow(cs).set(true); if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); task.timer_queue_item.next.borrow(cs).set(prev); } + self.dispatch(cs, super::wake_task); }); } } - pub(crate) unsafe fn next_expiration(&self) -> u64 { - let mut res = u64::MAX; - critical_section::with(|cs| { - let rescan = self.rescan.borrow(cs).replace(false); - if !rescan { - return; + unsafe fn dequeue_expired_internal(&self, now: u64, cs: CriticalSection<'_>, on_task: fn(TaskRef)) -> bool { + let mut changed = false; + self.retain(cs, |p| { + let task = p.header(); + if task.expires_at.borrow(cs).get() <= now { + on_task(p); + changed = true; + false + } else { + true } - self.retain(cs, |p| { - let task = p.header(); - let expires = task.next_expiration.get(); - task.expires_at.borrow(cs).set(expires); - res = min(res, expires); - expires != u64::MAX - }); }); - res + changed } - pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { + pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: fn(TaskRef)) { critical_section::with(|cs| { - let mut changed = false; - self.retain(cs, |p| { - let task = p.header(); - if task.expires_at.borrow(cs).get() <= now { - on_task(p); - changed = true; - false - } else { - true - } - }); - if changed { - self.rescan.borrow(cs).set(true); + if self.dequeue_expired_internal(now, cs, on_task) { + self.dispatch(cs, on_task); } }); } @@ -101,4 +87,29 @@ impl TimerQueue { } } } + + unsafe fn next_expiration(&self, cs: CriticalSection<'_>) -> u64 { + let mut res = u64::MAX; + + self.retain(cs, |p| { + let task = p.header(); + let expires = task.next_expiration.get(); + task.expires_at.borrow(cs).set(expires); + res = min(res, expires); + expires != u64::MAX + }); + + res + } + + unsafe fn dispatch(&self, cs: CriticalSection<'_>, cb: fn(TaskRef)) { + // If this is already in the past, set_alarm might return false + // In that case do another poll loop iteration. + let next_expiration = self.next_expiration(cs); + if !embassy_time_driver::set_alarm(self.alarm, next_expiration) { + // Time driver did not schedule the alarm, + // so we need to dequeue expired timers manually. + self.dequeue_expired_internal(embassy_time_driver::now(), cs, cb); + } + } } From fdbb055f0b1cce0822aa1d6c7661611e6881c1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 14:22:31 +0100 Subject: [PATCH 07/13] Fix racy expiration reset --- embassy-executor/src/raw/mod.rs | 6 +----- embassy-executor/src/raw/timer_queue.rs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f0ad04769f..09ae3a7ff9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -54,8 +54,6 @@ pub(crate) struct TaskHeader { #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Mutex>, #[cfg(feature = "integrated-timers")] - pub(crate) next_expiration: SyncUnsafeCell, - #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -129,8 +127,6 @@ impl TaskStorage { #[cfg(feature = "integrated-timers")] expires_at: Mutex::new(Cell::new(0)), #[cfg(feature = "integrated-timers")] - next_expiration: SyncUnsafeCell::new(0), - #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, future: UninitCell::uninit(), @@ -402,7 +398,7 @@ impl SyncExecutor { let task = p.header(); #[cfg(feature = "integrated-timers")] - task.next_expiration.set(u64::MAX); + self.timer_queue.notify_task_started(p); if !task.state.run_dequeue() { // If task is not running, ignore it. This can happen in the following scenario: diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 7b954d86c9..6872b5ce67 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -32,24 +32,31 @@ impl TimerQueue { pub(crate) unsafe fn notify_task_exited(&self, p: TaskRef) { let task = p.header(); - task.next_expiration.set(u64::MAX); critical_section::with(|cs| { + task.expires_at.borrow(cs).set(u64::MAX); self.dispatch(cs, super::wake_task); }); } + pub(crate) unsafe fn notify_task_started(&self, p: TaskRef) { + let task = p.header(); + critical_section::with(|cs| { + task.expires_at.borrow(cs).set(u64::MAX); + }); + } + pub(crate) unsafe fn update(&self, p: TaskRef, at: u64) { let task = p.header(); - if at < task.next_expiration.get() { - task.next_expiration.set(at); - critical_section::with(|cs| { + critical_section::with(|cs| { + if at < task.expires_at.borrow(cs).get() { + task.expires_at.borrow(cs).set(at); if task.state.timer_enqueue() { let prev = self.head.borrow(cs).replace(Some(p)); task.timer_queue_item.next.borrow(cs).set(prev); } self.dispatch(cs, super::wake_task); - }); - } + } + }); } unsafe fn dequeue_expired_internal(&self, now: u64, cs: CriticalSection<'_>, on_task: fn(TaskRef)) -> bool { @@ -93,8 +100,7 @@ impl TimerQueue { self.retain(cs, |p| { let task = p.header(); - let expires = task.next_expiration.get(); - task.expires_at.borrow(cs).set(expires); + let expires = task.expires_at.borrow(cs).get(); res = min(res, expires); expires != u64::MAX }); From 0ba15194d0dc817c67edbfc8d848da5092aeefb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 14:23:25 +0100 Subject: [PATCH 08/13] Fix comment --- embassy-executor/src/raw/timer_queue.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 6872b5ce67..4e8e19eb12 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -109,8 +109,7 @@ impl TimerQueue { } unsafe fn dispatch(&self, cs: CriticalSection<'_>, cb: fn(TaskRef)) { - // If this is already in the past, set_alarm might return false - // In that case do another poll loop iteration. + // If this is already in the past, set_alarm might return false. let next_expiration = self.next_expiration(cs); if !embassy_time_driver::set_alarm(self.alarm, next_expiration) { // Time driver did not schedule the alarm, From 0a54911afd24420783d064c30da12585257c9a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 14:34:59 +0100 Subject: [PATCH 09/13] Don't reset expiration time of exited task --- embassy-executor/src/raw/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 09ae3a7ff9..3cc2c0d2a2 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -397,9 +397,6 @@ impl SyncExecutor { self.run_queue.dequeue_all(|p| { let task = p.header(); - #[cfg(feature = "integrated-timers")] - self.timer_queue.notify_task_started(p); - if !task.state.run_dequeue() { // If task is not running, ignore it. This can happen in the following scenario: // - Task gets dequeued, poll starts @@ -409,6 +406,9 @@ impl SyncExecutor { return; } + #[cfg(feature = "integrated-timers")] + self.timer_queue.notify_task_started(p); + #[cfg(feature = "rtos-trace")] trace::task_exec_begin(p.as_ptr() as u32); From 536373a60386de593c025d2b0f5658a5c75ff49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 00:07:15 +0100 Subject: [PATCH 10/13] Move Mutex out of timer queue --- embassy-executor/src/raw/mod.rs | 40 ++++++++------ embassy-executor/src/raw/timer_queue.rs | 70 +++++++++++-------------- embassy-executor/src/raw/util.rs | 4 ++ 3 files changed, 58 insertions(+), 56 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 3cc2c0d2a2..cd0a0c2f3a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -22,8 +22,6 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; -#[cfg(feature = "integrated-timers")] -use core::cell::Cell; use core::future::Future; use core::marker::PhantomData; use core::mem; @@ -51,8 +49,10 @@ pub(crate) struct TaskHeader { pub(crate) executor: SyncUnsafeCell>, poll_fn: SyncUnsafeCell>, + // The following fields are conceptually owned by the executor's timer queue and should not + // be accessed outside of that. #[cfg(feature = "integrated-timers")] - pub(crate) expires_at: Mutex>, + pub(crate) expires_at: SyncUnsafeCell, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -125,7 +125,7 @@ impl TaskStorage { poll_fn: SyncUnsafeCell::new(None), #[cfg(feature = "integrated-timers")] - expires_at: Mutex::new(Cell::new(0)), + expires_at: SyncUnsafeCell::new(0), #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, @@ -166,12 +166,15 @@ impl TaskStorage { this.raw.state.despawn(); #[cfg(feature = "integrated-timers")] - this.raw - .executor - .get() - .unwrap_unchecked() - .timer_queue - .notify_task_exited(p); + critical_section::with(|cs| { + this.raw + .executor + .get() + .unwrap_unchecked() + .timer_queue + .borrow(cs) + .notify_task_exited(p); + }); } Poll::Pending => {} } @@ -327,7 +330,7 @@ pub(crate) struct SyncExecutor { pender: Pender, #[cfg(feature = "integrated-timers")] - pub(crate) timer_queue: timer_queue::TimerQueue, + pub(crate) timer_queue: Mutex, #[cfg(feature = "integrated-timers")] alarm: AlarmHandle, } @@ -342,7 +345,7 @@ impl SyncExecutor { pender, #[cfg(feature = "integrated-timers")] - timer_queue: timer_queue::TimerQueue::new(alarm), + timer_queue: Mutex::new(timer_queue::TimerQueue::new(alarm)), #[cfg(feature = "integrated-timers")] alarm, } @@ -373,10 +376,11 @@ impl SyncExecutor { fn alarm_callback(ctx: *mut ()) { let this: &Self = unsafe { &*(ctx as *const Self) }; - unsafe { + critical_section::with(|cs| unsafe { this.timer_queue + .borrow(cs) .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); - } + }); this.pender.pend(); } @@ -407,7 +411,9 @@ impl SyncExecutor { } #[cfg(feature = "integrated-timers")] - self.timer_queue.notify_task_started(p); + critical_section::with(|cs| { + self.timer_queue.borrow(cs).notify_task_started(p); + }); #[cfg(feature = "rtos-trace")] trace::task_exec_begin(p.as_ptr() as u32); @@ -574,7 +580,9 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { let task = waker::task_from_waker(waker); unsafe { let executor = task.header().executor.get().unwrap_unchecked(); - executor.timer_queue.update(task, at); + critical_section::with(|cs| { + executor.timer_queue.borrow(cs).update(task, at); + }); } } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 4e8e19eb12..abccc418cd 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,69 +1,61 @@ -use core::cell::Cell; use core::cmp::min; -use critical_section::{CriticalSection, Mutex}; - +use super::util::SyncUnsafeCell; use super::{AlarmHandle, TaskRef}; pub(crate) struct TimerQueueItem { - next: Mutex>>, + next: SyncUnsafeCell>, } impl TimerQueueItem { pub const fn new() -> Self { Self { - next: Mutex::new(Cell::new(None)), + next: SyncUnsafeCell::new(None), } } } pub(crate) struct TimerQueue { - head: Mutex>>, + head: SyncUnsafeCell>, alarm: AlarmHandle, } impl TimerQueue { pub const fn new(alarm: AlarmHandle) -> Self { Self { - head: Mutex::new(Cell::new(None)), + head: SyncUnsafeCell::new(None), alarm, } } pub(crate) unsafe fn notify_task_exited(&self, p: TaskRef) { let task = p.header(); - critical_section::with(|cs| { - task.expires_at.borrow(cs).set(u64::MAX); - self.dispatch(cs, super::wake_task); - }); + task.expires_at.set(u64::MAX); + self.dispatch(super::wake_task); } pub(crate) unsafe fn notify_task_started(&self, p: TaskRef) { let task = p.header(); - critical_section::with(|cs| { - task.expires_at.borrow(cs).set(u64::MAX); - }); + task.expires_at.set(u64::MAX); } pub(crate) unsafe fn update(&self, p: TaskRef, at: u64) { let task = p.header(); - critical_section::with(|cs| { - if at < task.expires_at.borrow(cs).get() { - task.expires_at.borrow(cs).set(at); - if task.state.timer_enqueue() { - let prev = self.head.borrow(cs).replace(Some(p)); - task.timer_queue_item.next.borrow(cs).set(prev); - } - self.dispatch(cs, super::wake_task); + if at < task.expires_at.get() { + task.expires_at.set(at); + if task.state.timer_enqueue() { + let prev = self.head.replace(Some(p)); + task.timer_queue_item.next.set(prev); } - }); + self.dispatch(super::wake_task); + } } - unsafe fn dequeue_expired_internal(&self, now: u64, cs: CriticalSection<'_>, on_task: fn(TaskRef)) -> bool { + unsafe fn dequeue_expired_internal(&self, now: u64, on_task: fn(TaskRef)) -> bool { let mut changed = false; - self.retain(cs, |p| { + self.retain(|p| { let task = p.header(); - if task.expires_at.borrow(cs).get() <= now { + if task.expires_at.get() <= now { on_task(p); changed = true; false @@ -75,32 +67,30 @@ impl TimerQueue { } pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: fn(TaskRef)) { - critical_section::with(|cs| { - if self.dequeue_expired_internal(now, cs, on_task) { - self.dispatch(cs, on_task); - } - }); + if self.dequeue_expired_internal(now, on_task) { + self.dispatch(on_task); + } } - unsafe fn retain(&self, cs: CriticalSection<'_>, mut f: impl FnMut(TaskRef) -> bool) { + unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { let mut prev = &self.head; - while let Some(p) = prev.borrow(cs).get() { + while let Some(p) = prev.get() { let task = p.header(); if f(p) { prev = &task.timer_queue_item.next; } else { - prev.borrow(cs).set(task.timer_queue_item.next.borrow(cs).get()); + prev.set(task.timer_queue_item.next.get()); task.state.timer_dequeue(); } } } - unsafe fn next_expiration(&self, cs: CriticalSection<'_>) -> u64 { + unsafe fn next_expiration(&self) -> u64 { let mut res = u64::MAX; - self.retain(cs, |p| { + self.retain(|p| { let task = p.header(); - let expires = task.expires_at.borrow(cs).get(); + let expires = task.expires_at.get(); res = min(res, expires); expires != u64::MAX }); @@ -108,13 +98,13 @@ impl TimerQueue { res } - unsafe fn dispatch(&self, cs: CriticalSection<'_>, cb: fn(TaskRef)) { + unsafe fn dispatch(&self, cb: fn(TaskRef)) { // If this is already in the past, set_alarm might return false. - let next_expiration = self.next_expiration(cs); + let next_expiration = self.next_expiration(); if !embassy_time_driver::set_alarm(self.alarm, next_expiration) { // Time driver did not schedule the alarm, // so we need to dequeue expired timers manually. - self.dequeue_expired_internal(embassy_time_driver::now(), cs, cb); + self.dequeue_expired_internal(embassy_time_driver::now(), cb); } } } diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index c46085e450..5fb15b4f46 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -54,4 +54,8 @@ impl SyncUnsafeCell { { *self.value.get() } + + pub unsafe fn replace(&self, value: T) -> T { + core::mem::replace(&mut *self.value.get(), value) + } } From f6802ee28c169234f0af1a7d6373237a42b3ddce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 00:46:53 +0100 Subject: [PATCH 11/13] Remove need to reset expiration in every poll --- embassy-executor/src/raw/mod.rs | 22 ++---- embassy-executor/src/raw/timer_queue.rs | 91 ++++++++++++------------- embassy-executor/src/raw/util.rs | 1 + 3 files changed, 49 insertions(+), 65 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index cd0a0c2f3a..01a6f2a62d 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -167,13 +167,8 @@ impl TaskStorage { #[cfg(feature = "integrated-timers")] critical_section::with(|cs| { - this.raw - .executor - .get() - .unwrap_unchecked() - .timer_queue - .borrow(cs) - .notify_task_exited(p); + let executor = this.raw.executor.get().unwrap_unchecked(); + executor.timer_queue.borrow(cs).notify_task_exited(p); }); } Poll::Pending => {} @@ -377,9 +372,7 @@ impl SyncExecutor { let this: &Self = unsafe { &*(ctx as *const Self) }; critical_section::with(|cs| unsafe { - this.timer_queue - .borrow(cs) - .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); + this.timer_queue.borrow(cs).dispatch(wake_task_no_pend); }); this.pender.pend(); @@ -410,11 +403,6 @@ impl SyncExecutor { return; } - #[cfg(feature = "integrated-timers")] - critical_section::with(|cs| { - self.timer_queue.borrow(cs).notify_task_started(p); - }); - #[cfg(feature = "rtos-trace")] trace::task_exec_begin(p.as_ptr() as u32); @@ -579,9 +567,9 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); unsafe { - let executor = task.header().executor.get().unwrap_unchecked(); critical_section::with(|cs| { - executor.timer_queue.borrow(cs).update(task, at); + let executor = task.header().executor.get().unwrap_unchecked(); + executor.timer_queue.borrow(cs).schedule(task, at); }); } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index abccc418cd..fdff7bb84c 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -30,45 +30,63 @@ impl TimerQueue { pub(crate) unsafe fn notify_task_exited(&self, p: TaskRef) { let task = p.header(); + + // Trigger removal from the timer queue. task.expires_at.set(u64::MAX); self.dispatch(super::wake_task); } - pub(crate) unsafe fn notify_task_started(&self, p: TaskRef) { + pub(crate) unsafe fn schedule(&self, p: TaskRef, at: u64) { let task = p.header(); - task.expires_at.set(u64::MAX); - } + let update = if task.state.timer_enqueue() { + // Not in the queue, add it and update. + let prev = self.head.replace(Some(p)); + task.timer_queue_item.next.set(prev); - pub(crate) unsafe fn update(&self, p: TaskRef, at: u64) { - let task = p.header(); - if at < task.expires_at.get() { + true + } else { + // Expiration is sooner than previously set, update. + at < task.expires_at.get() + }; + + if update { task.expires_at.set(at); - if task.state.timer_enqueue() { - let prev = self.head.replace(Some(p)); - task.timer_queue_item.next.set(prev); - } self.dispatch(super::wake_task); } } - unsafe fn dequeue_expired_internal(&self, now: u64, on_task: fn(TaskRef)) -> bool { - let mut changed = false; - self.retain(|p| { - let task = p.header(); - if task.expires_at.get() <= now { - on_task(p); - changed = true; - false - } else { - true + pub(crate) unsafe fn dispatch(&self, on_task: fn(TaskRef)) { + loop { + let now = embassy_time_driver::now(); + + let mut next_expiration = u64::MAX; + + self.retain(|p| { + let task = p.header(); + let expires = task.expires_at.get(); + + if expires <= now { + // Timer expired, process task. + on_task(p); + false + } else { + // Timer didn't yet expire, or never expires. + next_expiration = min(next_expiration, expires); + expires != u64::MAX + } + }); + + if self.update_alarm(next_expiration) { + break; } - }); - changed + } } - pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: fn(TaskRef)) { - if self.dequeue_expired_internal(now, on_task) { - self.dispatch(on_task); + fn update_alarm(&self, next_alarm: u64) -> bool { + if next_alarm == u64::MAX { + true + } else { + embassy_time_driver::set_alarm(self.alarm, next_alarm) } } @@ -84,27 +102,4 @@ impl TimerQueue { } } } - - unsafe fn next_expiration(&self) -> u64 { - let mut res = u64::MAX; - - self.retain(|p| { - let task = p.header(); - let expires = task.expires_at.get(); - res = min(res, expires); - expires != u64::MAX - }); - - res - } - - unsafe fn dispatch(&self, cb: fn(TaskRef)) { - // If this is already in the past, set_alarm might return false. - let next_expiration = self.next_expiration(); - if !embassy_time_driver::set_alarm(self.alarm, next_expiration) { - // Time driver did not schedule the alarm, - // so we need to dequeue expired timers manually. - self.dequeue_expired_internal(embassy_time_driver::now(), cb); - } - } } diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index 5fb15b4f46..e2633658a9 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -55,6 +55,7 @@ impl SyncUnsafeCell { *self.value.get() } + #[cfg(feature = "integrated-timers")] pub unsafe fn replace(&self, value: T) -> T { core::mem::replace(&mut *self.value.get(), value) } From 28771bc3a46717add3592ec27bb197273a96df76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 10:50:57 +0100 Subject: [PATCH 12/13] RTC: Trigger expired alarms --- embassy-stm32/src/time_driver.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 74e4d05753..41b69a7608 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -438,7 +438,10 @@ impl RtcDriver { let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; let alarm = self.get_alarm(cs, alarm_handle); - self.set_alarm(alarm_handle, alarm.timestamp.get()); + if !self.set_alarm(alarm_handle, alarm.timestamp.get()) { + // If the alarm timestamp has passed, we need to trigger it + self.trigger_alarm(i, cs); + } } } From 368c68e6aded5adfc31f0c6be1998d4053824f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 11:07:20 +0100 Subject: [PATCH 13/13] Only recompute allocated alarms --- embassy-stm32/src/time_driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 41b69a7608..deb5bde99e 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -434,7 +434,7 @@ impl RtcDriver { regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); // Now, recompute all alarms - for i in 0..ALARM_COUNT { + for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize { let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; let alarm = self.get_alarm(cs, alarm_handle);