aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/raw/timer_queue.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor/src/raw/timer_queue.rs')
-rw-r--r--embassy-executor/src/raw/timer_queue.rs89
1 files changed, 57 insertions, 32 deletions
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 }