aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/executor/raw/timer_queue.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor/src/executor/raw/timer_queue.rs')
-rw-r--r--embassy-executor/src/executor/raw/timer_queue.rs85
1 files changed, 85 insertions, 0 deletions
diff --git a/embassy-executor/src/executor/raw/timer_queue.rs b/embassy-executor/src/executor/raw/timer_queue.rs
new file mode 100644
index 000000000..62fcfc531
--- /dev/null
+++ b/embassy-executor/src/executor/raw/timer_queue.rs
@@ -0,0 +1,85 @@
1use core::cell::Cell;
2use core::cmp::min;
3use core::ptr;
4use core::ptr::NonNull;
5
6use atomic_polyfill::Ordering;
7
8use super::{TaskHeader, STATE_TIMER_QUEUED};
9use crate::time::Instant;
10
11pub(crate) struct TimerQueueItem {
12 next: Cell<*mut TaskHeader>,
13}
14
15impl TimerQueueItem {
16 pub const fn new() -> Self {
17 Self {
18 next: Cell::new(ptr::null_mut()),
19 }
20 }
21}
22
23pub(crate) struct TimerQueue {
24 head: Cell<*mut TaskHeader>,
25}
26
27impl TimerQueue {
28 pub const fn new() -> Self {
29 Self {
30 head: Cell::new(ptr::null_mut()),
31 }
32 }
33
34 pub(crate) unsafe fn update(&self, p: NonNull<TaskHeader>) {
35 let task = p.as_ref();
36 if task.expires_at.get() != Instant::MAX {
37 let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
38 let is_new = old_state & STATE_TIMER_QUEUED == 0;
39
40 if is_new {
41 task.timer_queue_item.next.set(self.head.get());
42 self.head.set(p.as_ptr());
43 }
44 }
45 }
46
47 pub(crate) unsafe fn next_expiration(&self) -> Instant {
48 let mut res = Instant::MAX;
49 self.retain(|p| {
50 let task = p.as_ref();
51 let expires = task.expires_at.get();
52 res = min(res, expires);
53 expires != Instant::MAX
54 });
55 res
56 }
57
58 pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull<TaskHeader>)) {
59 self.retain(|p| {
60 let task = p.as_ref();
61 if task.expires_at.get() <= now {
62 on_task(p);
63 false
64 } else {
65 true
66 }
67 });
68 }
69
70 pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull<TaskHeader>) -> bool) {
71 let mut prev = &self.head;
72 while !prev.get().is_null() {
73 let p = NonNull::new_unchecked(prev.get());
74 let task = &*p.as_ptr();
75 if f(p) {
76 // Skip to next
77 prev = &task.timer_queue_item.next;
78 } else {
79 // Remove it
80 prev.set(task.timer_queue_item.next.get());
81 task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
82 }
83 }
84 }
85}