Skip to main content

sparreal_kernel/os/time/
timer.rs

1use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
2use core::{
3    sync::atomic::{AtomicBool, Ordering},
4    time::Duration,
5};
6
7use super::{duration_to_ticks, ticks};
8use crate::os::sync::IrqSpinlock;
9
10type TimerCallback = Box<dyn FnMut() + Send + 'static>;
11
12static TIMER_MANAGER: IrqSpinlock<Option<TimerManager>> = IrqSpinlock::new(None);
13static TIMER_READY: AtomicBool = AtomicBool::new(false);
14
15pub type TimerResult<T> = core::result::Result<T, TimerError>;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum TimerError {
19    NotReady,
20    Overflow,
21}
22
23#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
24pub struct TimerHandle(TimerId);
25
26impl TimerHandle {
27    pub fn cancel(self) -> bool {
28        cancel(self)
29    }
30}
31
32#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
33struct TimerId(u64);
34
35#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
36struct TimerKey {
37    // deadline in ticks
38    deadline: usize,
39    id: TimerId,
40}
41
42#[derive(Debug, Clone, Copy)]
43pub struct TimeListEntry {
44    pub handle: TimerHandle,
45    pub deadline: usize,
46    pub remaining: usize,
47}
48
49/// Software timer core that keeps a sorted list of one-shot callbacks.
50/// Uses on-demand hardware timer interrupts instead of periodic ticks.
51struct TimerManager {
52    next_id: u64,
53    timers: BTreeMap<TimerKey, TimerCallback>,
54    index: BTreeMap<TimerId, usize>,
55}
56
57impl TimerManager {
58    fn new() -> Self {
59        Self {
60            next_id: 1,
61            timers: BTreeMap::new(),
62            index: BTreeMap::new(),
63        }
64    }
65
66    fn schedule_after<F>(&mut self, delay: usize, callback: F) -> TimerResult<TimerHandle>
67    where
68        F: FnOnce() + Send + 'static,
69    {
70        let now = ticks();
71        let deadline = now.checked_add(delay).ok_or(TimerError::Overflow)?;
72        Ok(self.schedule_at_internal(deadline, callback))
73    }
74
75    fn schedule_at<F>(&mut self, deadline: usize, callback: F) -> TimerHandle
76    where
77        F: FnOnce() + Send + 'static,
78    {
79        self.schedule_at_internal(deadline, callback)
80    }
81
82    fn schedule_at_internal<F>(&mut self, deadline: usize, callback: F) -> TimerHandle
83    where
84        F: FnOnce() + Send + 'static,
85    {
86        let id = self.next_timer_id();
87        let key = TimerKey { deadline, id };
88
89        // Check if this is the new earliest deadline
90        let is_earliest = self
91            .timers
92            .keys()
93            .next()
94            .is_none_or(|k| deadline < k.deadline);
95
96        self.timers.insert(key, into_callback(callback));
97
98        self.index.insert(id, deadline);
99        // If this is the earliest timer, reprogram hardware timer
100
101        if is_earliest {
102            self.arm_hardware_timer();
103        }
104
105        TimerHandle(id)
106    }
107
108    fn cancel(&mut self, handle: TimerHandle) -> bool {
109        if let Some(deadline) = self.index.remove(&handle.0) {
110            let key = TimerKey {
111                deadline,
112                id: handle.0,
113            };
114            let was_first = self.timers.keys().next().is_some_and(|k| *k == key);
115            self.timers.remove(&key);
116
117            // If we removed the earliest timer, reprogram for the next one
118            if was_first {
119                self.arm_hardware_timer();
120            }
121            return true;
122        }
123        false
124    }
125
126    fn handle_irq(&mut self) -> Vec<TimerCallback> {
127        let now = ticks();
128        let mut expired = Vec::new();
129
130        // Collect all expired timers
131        while let Some(key) = self.timers.keys().next().cloned() {
132            if key.deadline > now {
133                break;
134            }
135            if let Some(cb) = self.timers.remove(&key) {
136                expired.push(cb);
137            }
138            self.index.remove(&key.id);
139        }
140
141        // Arm hardware timer for next deadline (if any)
142        self.arm_hardware_timer();
143
144        expired
145    }
146
147    /// Program the hardware timer for the next deadline
148    fn arm_hardware_timer(&self) {
149        if let Some(key) = self.timers.keys().next() {
150            let now = ticks();
151            let delay = key.deadline.saturating_sub(now);
152
153            // Ensure minimum delay to avoid missing the interrupt
154            // Use a larger minimum to handle edge cases with zero/near-zero delays
155            let delay = delay.max(1);
156
157            crate::hal::al::cpu::systick_set_interval(delay);
158            crate::hal::al::cpu::systick_irq_enable();
159        } else {
160            // No pending timers, disable hardware timer
161            crate::hal::al::cpu::systick_set_interval(usize::MAX);
162            crate::hal::al::cpu::systick_irq_disable();
163        }
164    }
165
166    fn snapshot(&self) -> Vec<TimeListEntry> {
167        let now = ticks();
168        let mut list = Vec::with_capacity(self.timers.len());
169        for key in self.timers.keys() {
170            let remaining = key.deadline.saturating_sub(now);
171            list.push(TimeListEntry {
172                handle: TimerHandle(key.id),
173                deadline: key.deadline,
174                remaining,
175            });
176        }
177        list
178    }
179
180    // fn next_deadline(&self) -> Option<usize> {
181    //     self.timers.keys().next().map(|k| k.deadline)
182    // }
183
184    fn next_timer_id(&mut self) -> TimerId {
185        loop {
186            let id = TimerId(self.next_id);
187            self.next_id = self.next_id.wrapping_add(1);
188            if !self.index.contains_key(&id) {
189                return id;
190            }
191        }
192    }
193}
194
195pub(crate) fn init() {
196    {
197        let mut guard = TIMER_MANAGER.lock();
198        if guard.is_some() {
199            return;
200        }
201        *guard = Some(TimerManager::new());
202    }
203
204    TIMER_READY.store(true, Ordering::Release);
205
206    let timer_irq = crate::hal::al::cpu::systick_irq_id();
207    crate::os::irq::register_handler(timer_irq, systimer_irq_handler);
208}
209
210/// Schedule a one-shot timer after the provided delay.
211pub fn one_shot_after<F>(delay: Duration, callback: F) -> Result<TimerHandle, TimerError>
212where
213    F: FnOnce() + Send + 'static,
214{
215    if !is_ready() {
216        return Err(TimerError::NotReady);
217    }
218    let delay = duration_to_ticks(delay);
219    with_manager_mut(move |mgr| mgr.schedule_after(delay, callback)).ok_or(TimerError::NotReady)?
220}
221
222/// Schedule a one-shot timer that fires at the absolute deadline.
223pub fn one_shot_at<F>(deadline: Duration, callback: F) -> Result<TimerHandle, TimerError>
224where
225    F: FnOnce() + Send + 'static,
226{
227    if !is_ready() {
228        return Err(TimerError::NotReady);
229    }
230    let mut cb = Some(callback);
231    let deadline = duration_to_ticks(deadline);
232    with_manager_mut(|mgr| mgr.schedule_at(deadline, cb.take().unwrap()))
233        .ok_or(TimerError::NotReady)
234}
235
236/// Cancel a scheduled timer.
237pub fn cancel(handle: TimerHandle) -> bool {
238    with_manager_mut(|mgr| mgr.cancel(handle)).unwrap_or(false)
239}
240
241/// Snapshot the current pending timers for diagnostics.
242pub fn time_list() -> Vec<TimeListEntry> {
243    with_manager(|mgr| mgr.snapshot()).unwrap_or_default()
244}
245
246/// Check if timer subsystem is ready.
247pub fn is_ready() -> bool {
248    TIMER_READY.load(Ordering::Acquire)
249}
250
251fn systimer_irq_handler() {
252    // Acknowledge the timer interrupt first to prevent interrupt storm
253    crate::hal::al::cpu::systick_ack();
254    let callbacks = with_manager_mut(|mgr| mgr.handle_irq()).unwrap_or_default();
255    run_callbacks(callbacks);
256}
257
258fn run_callbacks(callbacks: Vec<TimerCallback>) {
259    for mut cb in callbacks {
260        (cb)();
261    }
262}
263
264fn into_callback<F>(f: F) -> TimerCallback
265where
266    F: FnOnce() + Send + 'static,
267{
268    let mut opt = Some(f);
269    Box::new(move || {
270        if let Some(inner) = opt.take() {
271            inner();
272        }
273    })
274}
275
276fn with_manager<F, R>(f: F) -> Option<R>
277where
278    F: FnOnce(&TimerManager) -> R,
279{
280    let guard = TIMER_MANAGER.lock();
281    guard.as_ref().map(f)
282}
283
284fn with_manager_mut<F, R>(f: F) -> Option<R>
285where
286    F: FnOnce(&mut TimerManager) -> R,
287{
288    let mut guard = TIMER_MANAGER.lock();
289    guard.as_mut().map(f)
290}