Files
RedBear-OS/src/platform/redox/timer.rs
T
2026-05-03 05:02:29 +07:00

110 lines
3.5 KiB
Rust

use syscall::Error;
use crate::{
error::{Errno, Result},
header::{
bits_timespec::timespec,
errno::EIO,
signal::{SIGEV_SIGNAL, SIGEV_THREAD},
time::timer_internal_t,
},
out::Out,
platform::{Pal, Sys, sys::event, types::c_void},
};
use core::{
mem::{MaybeUninit, size_of},
ops::DerefMut,
ptr,
};
pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void {
let timer_ptr = unsafe { timer_internal_t::from_raw(arg) };
let (mut timer_version, eventfd) = {
let timer_st = timer_ptr.lock();
(timer_st.next_wake_version, timer_st.eventfd)
};
loop {
let mut buf = MaybeUninit::uninit();
let res = Error::demux(unsafe {
// this blocks the thread
event::redox_event_queue_get_events_v1(
eventfd,
buf.as_mut_ptr(),
1,
0,
core::ptr::null(),
core::ptr::null(),
)
});
if let Ok(res) = res {
assert_eq!(res, 1, "EOF is not yet well defined for event queues");
} else {
break;
}
let mut timer_st = timer_ptr.lock();
if timer_version == timer_st.next_wake_version {
if timer_st.evp.sigev_notify == SIGEV_THREAD {
if let Some(fun) = timer_st.evp.sigev_notify_function {
fun(timer_st.evp.sigev_value);
}
} else if timer_st.evp.sigev_notify == SIGEV_SIGNAL {
// TODO: This will deliver signal to process, which is required for alarm()
// Until it can bypass the exec() boundary, do not uncomment this code
// if timer_st.process_pid != 0 && Sys::kill(timer_st.process_pid, timer_st.evp.sigev_signo).is_err() { break; } else
if unsafe { Sys::rlct_kill(timer_st.caller_thread, timer_st.evp.sigev_signo as _) }
.is_err()
{
break;
}
}
}
if timer_next_event(timer_st.deref_mut()).is_err() {
break;
}
timer_version = timer_st.next_wake_version;
}
ptr::null_mut()
}
// Internal function only valid for inside timer_routine
fn timer_next_event(timer_st: &mut timer_internal_t) -> Result<()> {
if let Err(e) = timer_update_wake_time(timer_st) {
timer_st.thread = ptr::null_mut();
return Err(e);
}
let buf_to_write = unsafe {
Error::demux(event::redox_event_queue_ctl_v1(
timer_st.eventfd,
timer_st.timerfd,
1,
0,
))?;
syscall::TimeSpec::from(&timer_st.next_wake_time.it_value)
};
let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, &*buf_to_write)?;
if bytes_written < size_of::<timespec>() {
return Err(Errno(EIO));
}
Ok(())
}
/// Update next_wake_time.it_value from next_wake_time.it_interval
pub(crate) fn timer_update_wake_time(timer_st: &mut timer_internal_t) -> Result<()> {
let interval = &timer_st.next_wake_time.it_interval;
timer_st.next_wake_time.it_value = if interval.is_zero() {
timespec::default()
} else {
let mut now = timespec::default();
Sys::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
timespec::add(&now, interval).unwrap_or_default()
};
if timer_st.next_wake_time.it_value.is_zero() {
return Err(Errno(0));
}
timer_st.next_wake_version += 1;
Ok(())
}