1use std::{
2 cell::{Cell, RefCell},
3 cmp::Ordering,
4 collections::BinaryHeap,
5 pin::Pin,
6 time::Duration,
7};
8
9use slotmap::{SlotMap, new_key_type};
10
11thread_local! {
16 pub(crate) static TIMER_COUNTER: Cell<u128> = const { Cell::new(0) };
17 pub(crate) static TASKS: RefCell<SlotMap<TaskId, Task>> = RefCell::default();
18 pub(crate) static TIMERS: RefCell<BinaryHeap<Timer>> = RefCell::default();
19 static MOST_RECENT: Cell<Option<u64>> = const { Cell::new(None) };
20 pub(crate) static ALL_CALLS: Cell<usize> = const { Cell::new(0) };
21}
22
23pub(crate) enum Task {
24 Once(Pin<Box<dyn Future<Output = ()>>>),
25 Repeated {
26 func: Box<dyn FnMut() -> Pin<Box<dyn Future<Output = ()>>>>,
27 interval: Duration,
28 concurrent_calls: usize,
29 },
30 RepeatedSerial {
31 func: Box<dyn SerialClosure>,
32 interval: Duration,
33 },
34 RepeatedSerialBusy {
35 interval: Duration,
36 },
37 Invalid,
38}
39
40pub(crate) trait SerialClosure {
41 fn call<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + 'a>>;
42}
43
44impl<F: AsyncFnMut()> SerialClosure for F {
45 fn call<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
46 Box::pin(self())
47 }
48}
49
50new_key_type! {
51 #[expect(missing_docs)] pub struct TaskId;
53}
54
55#[derive(Debug)]
56pub(crate) struct Timer {
57 pub(crate) task: TaskId,
58 pub(crate) time: u64,
59 pub(crate) counter: u128,
60}
61
62impl Ord for Timer {
66 fn cmp(&self, other: &Self) -> Ordering {
67 self.time
68 .cmp(&other.time)
69 .then_with(|| self.counter.cmp(&other.counter))
70 .reverse()
71 }
72}
73
74impl PartialOrd for Timer {
75 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76 Some(self.cmp(other))
77 }
78}
79
80impl PartialEq for Timer {
81 fn eq(&self, other: &Self) -> bool {
82 self.time == other.time
83 }
84}
85
86impl Eq for Timer {}
87
88pub(crate) fn next_counter() -> u128 {
89 TIMER_COUNTER.with(|c| {
90 let v = c.get();
91 c.set(v + 1);
92 v
93 })
94}
95
96pub(crate) fn update_ic0_timer() {
98 TIMERS.with_borrow(|timers| {
99 let soonest_timer = timers.peek().map(|timer| timer.time);
100 let should_change = match (soonest_timer, MOST_RECENT.get()) {
101 (Some(timer), Some(recent)) => timer < recent,
102 (Some(_), None) => true,
103 _ => false,
104 };
105 if should_change {
106 ic0::global_timer_set(soonest_timer.unwrap());
107 MOST_RECENT.set(soonest_timer);
108 }
109 });
110}
111
112pub(crate) fn update_ic0_timer_clean() {
114 MOST_RECENT.set(None);
115 update_ic0_timer();
116}
117
118impl Task {
119 pub(crate) fn increment_concurrent(&mut self) {
120 if let Task::Repeated {
121 concurrent_calls, ..
122 } = self
123 {
124 *concurrent_calls += 1;
125 }
126 }
127 pub(crate) fn decrement_concurrent(&mut self) {
128 if let Task::Repeated {
129 concurrent_calls, ..
130 } = self
131 {
132 if *concurrent_calls > 0 {
133 *concurrent_calls -= 1;
134 }
135 }
136 }
137}
138
139pub(crate) fn increment_all_calls() {
140 ALL_CALLS.set(ALL_CALLS.get() + 1);
141}
142
143pub(crate) fn decrement_all_calls() {
144 let current = ALL_CALLS.get();
145 if current > 0 {
146 ALL_CALLS.set(current - 1);
147 }
148}