use alloc::{borrow::ToOwned, collections::binary_heap::BinaryHeap, sync::Arc};
use core::{mem, time::Duration};
use ax_hal::time::{NANOS_PER_SEC, TimeValue, monotonic_time_nanos, wall_time};
use ax_task::{
WeakAxTaskRef, current,
future::{block_on, timeout_at},
};
use event_listener::{Event, listener};
use lazy_static::lazy_static;
use spin::Mutex;
use starry_process::Pid;
use starry_signal::Signo;
use strum::FromRepr;
use crate::task::{poll_process_timer, poll_timer};
fn time_value_from_nanos(nanos: usize) -> TimeValue {
let secs = nanos as u64 / NANOS_PER_SEC;
let nsecs = nanos as u64 - secs * NANOS_PER_SEC;
TimeValue::new(secs, nsecs as u32)
}
#[derive(Debug, Clone)]
pub enum AlarmTarget {
Thread(WeakAxTaskRef),
Process(Pid),
}
struct Entry {
deadline: Duration,
target: AlarmTarget,
}
impl PartialEq for Entry {
fn eq(&self, other: &Self) -> bool {
self.deadline == other.deadline
}
}
impl Eq for Entry {}
impl PartialOrd for Entry {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Entry {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
other.deadline.cmp(&self.deadline)
}
}
lazy_static! {
static ref ALARM_LIST: Mutex<BinaryHeap<Entry>> = Mutex::new(BinaryHeap::new());
static ref EVENT_NEW_TIMER: Event = Event::new();
}
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Eq, PartialEq, Debug, Clone, Copy, FromRepr)]
pub enum ITimerType {
Real = 0,
Virtual = 1,
Prof = 2,
}
impl ITimerType {
pub fn signo(&self) -> Signo {
match self {
ITimerType::Real => Signo::SIGALRM,
ITimerType::Virtual => Signo::SIGVTALRM,
ITimerType::Prof => Signo::SIGPROF,
}
}
}
#[derive(Default)]
struct ITimer {
interval_ns: usize,
remained_ns: usize,
}
impl ITimer {
pub fn new(interval_ns: usize, remained_ns: usize) -> Self {
let result = Self {
interval_ns,
remained_ns,
};
result.renew_timer();
result
}
pub fn update(&mut self, delta: usize) -> bool {
if self.remained_ns == 0 {
return false;
}
if self.remained_ns > delta {
self.remained_ns -= delta;
false
} else {
self.remained_ns = self.interval_ns;
self.renew_timer();
true
}
}
pub fn renew_timer(&self) {
if self.remained_ns > 0 {
let deadline = wall_time() + Duration::from_nanos(self.remained_ns as u64);
register_alarm(deadline);
}
}
}
pub fn register_alarm(deadline: Duration) {
register_alarm_for(deadline, AlarmTarget::Thread(Arc::downgrade(¤t())));
}
pub fn register_alarm_for(deadline: Duration, target: AlarmTarget) {
let mut guard = ALARM_LIST.lock();
let should_wake = guard.peek().is_none_or(|it| it.deadline > deadline);
guard.push(Entry { deadline, target });
drop(guard);
if should_wake {
EVENT_NEW_TIMER.notify(1);
}
}
#[derive(Debug)]
pub enum TimerState {
None,
User,
Kernel,
}
pub struct TimeManager {
utime_ns: usize,
stime_ns: usize,
last_wall_ns: usize,
last_tick_ns: usize,
state: TimerState,
itimers: [ITimer; 3],
}
impl Default for TimeManager {
fn default() -> Self {
Self::new()
}
}
impl TimeManager {
pub(crate) fn new() -> Self {
Self {
utime_ns: 0,
stime_ns: 0,
last_wall_ns: 0,
last_tick_ns: 0,
state: TimerState::None,
itimers: Default::default(),
}
}
pub fn output(&self) -> (TimeValue, TimeValue) {
let utime = time_value_from_nanos(self.utime_ns);
let stime = time_value_from_nanos(self.stime_ns);
(utime, stime)
}
pub fn tick(&mut self) {
let now_ns = monotonic_time_nanos() as usize;
let delta = now_ns.saturating_sub(self.last_tick_ns);
match self.state {
TimerState::User => self.utime_ns += delta,
TimerState::Kernel => self.stime_ns += delta,
TimerState::None => {}
}
self.last_tick_ns = now_ns;
}
pub fn poll(&mut self, emitter: impl Fn(Signo)) {
let now_ns = monotonic_time_nanos() as usize;
let itimer_delta = now_ns.saturating_sub(self.last_wall_ns);
let remaining = now_ns.saturating_sub(self.last_tick_ns);
match self.state {
TimerState::User => {
self.utime_ns += remaining;
self.update_itimer(ITimerType::Virtual, itimer_delta, &emitter);
self.update_itimer(ITimerType::Prof, itimer_delta, &emitter);
}
TimerState::Kernel => {
self.stime_ns += remaining;
self.update_itimer(ITimerType::Prof, itimer_delta, &emitter);
}
TimerState::None => {}
}
self.update_itimer(ITimerType::Real, itimer_delta, &emitter);
self.last_wall_ns = now_ns;
self.last_tick_ns = now_ns;
}
pub fn set_state(&mut self, state: TimerState) {
self.state = state;
}
pub fn set_itimer(
&mut self,
ty: ITimerType,
interval_ns: usize,
remained_ns: usize,
) -> (TimeValue, TimeValue) {
let old = mem::replace(
&mut self.itimers[ty as usize],
ITimer::new(interval_ns, remained_ns),
);
(
time_value_from_nanos(old.interval_ns),
time_value_from_nanos(old.remained_ns),
)
}
pub fn get_itimer(&self, ty: ITimerType) -> (TimeValue, TimeValue) {
let itimer = &self.itimers[ty as usize];
(
time_value_from_nanos(itimer.interval_ns),
time_value_from_nanos(itimer.remained_ns),
)
}
fn update_itimer(&mut self, ty: ITimerType, delta: usize, emitter: impl Fn(Signo)) {
if self.itimers[ty as usize].update(delta) {
emitter(ty.signo());
}
}
}
async fn alarm_task() {
loop {
let mut guard = ALARM_LIST.lock();
let Some(entry) = guard.peek() else {
drop(guard);
listener!(EVENT_NEW_TIMER => listener);
if !ALARM_LIST.lock().is_empty() {
continue;
}
listener.await;
continue;
};
let now = wall_time();
if entry.deadline <= now {
let entry_deadline = entry.deadline;
let target = entry.target.clone();
assert!(guard.pop().is_some_and(|it| it.deadline == entry_deadline));
drop(guard);
match target {
AlarmTarget::Thread(weak_task) => {
if let Some(task) = weak_task.upgrade() {
poll_timer(&task);
}
}
AlarmTarget::Process(pid) => {
poll_process_timer(pid);
}
}
} else {
let deadline = entry.deadline;
drop(guard);
listener!(EVENT_NEW_TIMER => listener);
if ALARM_LIST
.lock()
.peek()
.is_none_or(|it| it.deadline != deadline)
{
continue;
}
let _ = timeout_at(Some(deadline), listener).await;
}
}
}
pub fn spawn_alarm_task() {
info!("Initialize alarm...");
ax_task::spawn_raw(
|| block_on(alarm_task()),
"alarm_task".to_owned(),
ax_config::TASK_STACK_SIZE,
);
}