use core::cell::UnsafeCell;
pub const MAX_TIMERS: usize = crate::config::MAX_TIMERS;
struct TimerSlot {
deadline: UnsafeCell<u64>,
task: UnsafeCell<crate::task::TaskId>,
}
unsafe impl Sync for TimerSlot {}
static TIMER_SLOTS: [TimerSlot; MAX_TIMERS] = [const {
TimerSlot {
deadline: UnsafeCell::new(0),
task: UnsafeCell::new(crate::task::TaskId::new(0, 0)),
}
}; MAX_TIMERS];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimerHandle {
slot: u8,
deadline: u64,
}
#[cfg(not(feature = "test-support"))]
pub(crate) fn register_deadline(
deadline_us: u64,
task: crate::task::TaskId,
) -> Result<TimerHandle, TimerQueueFull> {
register_deadline_impl(deadline_us, task)
}
#[cfg(feature = "test-support")]
pub fn register_deadline(
deadline_us: u64,
task: crate::task::TaskId,
) -> Result<TimerHandle, TimerQueueFull> {
register_deadline_impl(deadline_us, task)
}
fn register_deadline_impl(
deadline_us: u64,
task: crate::task::TaskId,
) -> Result<TimerHandle, TimerQueueFull> {
crate::critical::enter(|| {
for (i, slot) in TIMER_SLOTS.iter().enumerate() {
unsafe {
if *slot.deadline.get() == 0 {
*slot.task.get() = task;
*slot.deadline.get() = deadline_us;
return Ok(TimerHandle {
slot: i as u8,
deadline: deadline_us,
});
}
}
}
Err(TimerQueueFull)
})
}
pub(crate) fn cancel_deadline(handle: TimerHandle) {
if let Some(slot) = TIMER_SLOTS.get(handle.slot as usize) {
crate::critical::enter(|| {
unsafe {
if *slot.deadline.get() == handle.deadline {
*slot.deadline.get() = 0;
}
}
});
}
}
pub fn poll_timers(now_us: u64) {
let mut woke_any = false;
crate::critical::enter(|| {
for slot in &TIMER_SLOTS {
unsafe {
let d = *slot.deadline.get();
if d != 0 && now_us >= d {
*slot.deadline.get() = 0;
crate::waker::mark_ready(*slot.task.get());
woke_any = true;
}
}
}
});
if woke_any {
crate::waker::broadcast_reschedule();
}
poll_ptask_deadlines(now_us);
}
struct PtaskDeadline {
deadline: UnsafeCell<u64>,
}
unsafe impl Sync for PtaskDeadline {}
static PTASK_DEADLINES: [PtaskDeadline; crate::preempt::tcb::MAX_PTASKS] = [const {
PtaskDeadline {
deadline: UnsafeCell::new(0),
}
};
crate::preempt::tcb::MAX_PTASKS];
pub(crate) fn register_ptask_deadline(deadline_us: u64, task: usize) -> Result<(), TimerQueueFull> {
let Some(slot) = PTASK_DEADLINES.get(task) else {
return Err(TimerQueueFull);
};
crate::critical::enter(|| {
unsafe {
*slot.deadline.get() = deadline_us;
}
});
Ok(())
}
pub(crate) fn cancel_ptask_deadline(task: usize) {
if let Some(slot) = PTASK_DEADLINES.get(task) {
crate::critical::enter(|| {
unsafe {
*slot.deadline.get() = 0;
}
});
}
}
fn poll_ptask_deadlines(now_us: u64) {
let mut woke_any = false;
crate::critical::enter(|| {
for (task, slot) in PTASK_DEADLINES.iter().enumerate() {
unsafe {
let d = *slot.deadline.get();
if d != 0 && now_us >= d {
*slot.deadline.get() = 0;
crate::preempt::sched::unblock(task);
woke_any = true;
}
}
}
});
if woke_any {
let hart = crate::port::arch::hart_id();
for other in 0..crate::config::MAX_HARTS {
if other != hart {
crate::port::arch::request_reschedule_on(other);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimerQueueFull;
#[cfg(feature = "test-support")]
pub(crate) fn reset_for_test() {
crate::critical::enter(|| {
for slot in &TIMER_SLOTS {
unsafe {
*slot.deadline.get() = 0;
}
}
for slot in &PTASK_DEADLINES {
unsafe {
*slot.deadline.get() = 0;
}
}
});
}
pub fn slots_in_use() -> usize {
crate::critical::enter(|| {
TIMER_SLOTS
.iter()
.filter(|slot| unsafe { *slot.deadline.get() != 0 })
.count()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_and_expire() {
crate::kernel_test! {
register_deadline(1000, crate::task::TaskId::new(3, 2)).unwrap();
poll_timers(500); assert_eq!(crate::waker::next_ready(), None);
poll_timers(1000); assert_eq!(crate::waker::next_ready(), Some(crate::task::TaskId::new(3, 2)));
}
}
#[test]
fn multiple_timers_independent() {
crate::kernel_test! {
register_deadline(100, crate::task::TaskId::new(1, 0)).unwrap();
register_deadline(200, crate::task::TaskId::new(2, 0)).unwrap();
poll_timers(150);
assert_eq!(crate::waker::next_ready(), Some(crate::task::TaskId::new(1, 0)));
assert_eq!(crate::waker::next_ready(), None);
poll_timers(250);
assert_eq!(crate::waker::next_ready(), Some(crate::task::TaskId::new(2, 0)));
}
}
}