use alloc::collections::BTreeMap;
use core::{
sync::atomic::{AtomicI32, Ordering},
time::Duration,
};
use ax_errno::{AxError, AxResult};
use ax_kspin::SpinNoIrq as Mutex;
use ax_runtime::hal::time::{NANOS_PER_SEC, monotonic_time_nanos, wall_time};
use linux_raw_sys::general::{
CLOCK_BOOTTIME, CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_RAW,
CLOCK_PROCESS_CPUTIME_ID, CLOCK_REALTIME, CLOCK_REALTIME_COARSE, CLOCK_THREAD_CPUTIME_ID,
SIGEV_NONE, SIGEV_SIGNAL,
};
use starry_process::Pid;
use starry_signal::{SignalInfo, Signo};
use super::timer::{AlarmTarget, register_alarm_for};
struct PosixTimer {
clock_id: u32,
signo: Option<Signo>,
sigev_value: i64,
interval_ns: u64,
deadline_ns: u64,
}
pub struct TimerSpec {
pub value_sec: i64,
pub value_nsec: i64,
pub interval_sec: i64,
pub interval_nsec: i64,
}
pub struct PosixTimerTable {
next_id: AtomicI32,
timers: Mutex<BTreeMap<i32, PosixTimer>>,
}
impl Default for PosixTimerTable {
fn default() -> Self {
Self {
next_id: AtomicI32::new(0),
timers: Mutex::new(BTreeMap::new()),
}
}
}
fn is_supported_timer_clock(clock_id: u32) -> bool {
matches!(clock_id, CLOCK_REALTIME | CLOCK_MONOTONIC | CLOCK_BOOTTIME)
}
fn is_valid_clock(clock_id: u32) -> bool {
matches!(
clock_id,
CLOCK_REALTIME
| CLOCK_REALTIME_COARSE
| CLOCK_MONOTONIC
| CLOCK_MONOTONIC_RAW
| CLOCK_MONOTONIC_COARSE
| CLOCK_BOOTTIME
| CLOCK_PROCESS_CPUTIME_ID
| CLOCK_THREAD_CPUTIME_ID
)
}
fn clock_now_ns(clock_id: u32) -> u64 {
match clock_id {
CLOCK_REALTIME | CLOCK_REALTIME_COARSE => {
let t = wall_time();
t.as_secs() * NANOS_PER_SEC + t.subsec_nanos() as u64
}
_ => monotonic_time_nanos() as u64,
}
}
impl PosixTimerTable {
pub fn create(
&self,
clock_id: u32,
sigev_notify: u32,
sigev_signo: i32,
sigev_value: i64,
) -> AxResult<i32> {
if !is_supported_timer_clock(clock_id) {
if is_valid_clock(clock_id) {
return Err(AxError::OperationNotSupported);
} else {
return Err(AxError::InvalidInput);
}
}
let signo = match sigev_notify {
SIGEV_NONE => None,
SIGEV_SIGNAL => {
if sigev_signo <= 0 || sigev_signo > 64 {
return Err(AxError::InvalidInput);
}
Signo::from_repr(sigev_signo as u8)
}
_ => return Err(AxError::InvalidInput),
};
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
let timer = PosixTimer {
clock_id,
signo,
sigev_value,
interval_ns: 0,
deadline_ns: 0,
};
self.timers.lock().insert(id, timer);
Ok(id)
}
pub fn delete(&self, id: i32) -> bool {
self.timers.lock().remove(&id).is_some()
}
pub fn clear(&self) {
self.timers.lock().clear();
}
pub fn settime(
&self,
pid: Pid,
id: i32,
flags: i32,
spec: TimerSpec,
) -> Result<(u64, u64), ()> {
let TimerSpec {
value_sec,
value_nsec,
interval_sec,
interval_nsec,
} = spec;
if value_nsec < 0 || value_nsec >= NANOS_PER_SEC as i64 {
return Err(());
}
if interval_nsec < 0 || interval_nsec >= NANOS_PER_SEC as i64 {
return Err(());
}
if value_sec < 0 {
return Err(());
}
if interval_sec < 0 {
return Err(());
}
let mut timers = self.timers.lock();
let timer = timers.get_mut(&id).ok_or(())?;
let old_interval = timer.interval_ns;
let old_remaining = if timer.deadline_ns > 0 {
let now = clock_now_ns(timer.clock_id);
timer.deadline_ns.saturating_sub(now)
} else {
0
};
let new_value_ns = value_sec as u64 * NANOS_PER_SEC + value_nsec as u64;
let new_interval_ns = interval_sec as u64 * NANOS_PER_SEC + interval_nsec as u64;
timer.interval_ns = new_interval_ns;
if new_value_ns == 0 {
timer.deadline_ns = 0;
} else {
let now = clock_now_ns(timer.clock_id);
let abs_flag = flags & 1; if abs_flag != 0 {
timer.deadline_ns = new_value_ns;
} else {
timer.deadline_ns = now + new_value_ns;
}
if timer.deadline_ns > 0 {
let remaining = timer
.deadline_ns
.saturating_sub(clock_now_ns(timer.clock_id));
register_alarm_for(
wall_time() + Duration::from_nanos(remaining),
AlarmTarget::Process(pid),
);
}
}
Ok((old_interval, old_remaining))
}
pub fn gettime(&self, id: i32) -> Result<(u64, u64), ()> {
let timers = self.timers.lock();
let timer = timers.get(&id).ok_or(())?;
let remaining = if timer.deadline_ns > 0 {
let now = clock_now_ns(timer.clock_id);
timer.deadline_ns.saturating_sub(now)
} else {
0
};
Ok((timer.interval_ns, remaining))
}
pub fn poll_expired(&self, pid: Pid, mut emitter: impl FnMut(SignalInfo)) {
let mut timers = self.timers.lock();
for timer in timers.values_mut() {
if timer.deadline_ns == 0 {
continue;
}
let now = clock_now_ns(timer.clock_id);
if now >= timer.deadline_ns {
if let Some(signo) = timer.signo {
emitter(SignalInfo::new_timer(signo, timer.sigev_value));
}
if timer.interval_ns > 0 {
timer.deadline_ns += timer.interval_ns;
let remaining = timer
.deadline_ns
.saturating_sub(clock_now_ns(timer.clock_id));
register_alarm_for(
wall_time() + Duration::from_nanos(remaining),
AlarmTarget::Process(pid),
);
} else {
timer.deadline_ns = 0;
}
}
}
}
}