Implement POSIX timer functions

This commit is contained in:
Wildan Mubarok
2025-10-15 14:10:06 +00:00
committed by Jeremy Soller
parent 6acf185685
commit 2efcd20fbc
8 changed files with 405 additions and 22 deletions
+23 -1
View File
@@ -7,11 +7,12 @@ use crate::{
dirent::dirent,
errno::{EINVAL, EIO, EOPNOTSUPP},
fcntl::{AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW},
signal::SIGCHLD,
signal::{SIGCHLD, sigevent},
sys_resource::{rlimit, rusage},
sys_stat::{S_IFIFO, stat},
sys_statvfs::statvfs,
sys_time::{timeval, timezone},
time::itimerspec,
unistd::{SEEK_CUR, SEEK_SET},
},
io::Write,
@@ -725,6 +726,27 @@ impl Pal for Sys {
e_raw(unsafe { syscall!(SYNC) }).map(|_| ())
}
fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()> {
e_raw(unsafe { syscall!(TIMER_CREATE, clock_id, evp, timerid) }).map(|_| ())
}
fn timer_delete(timerid: timer_t) -> Result<()> {
e_raw(unsafe { syscall!(TIMER_DELETE, timerid) }).map(|_| ())
}
fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()> {
e_raw(unsafe { syscall!(TIMER_GETTIME, timerid, value) }).map(|_| ())
}
fn timer_settime(
timerid: timer_t,
flags: c_int,
value: *const itimerspec,
ovalue: *mut itimerspec,
) -> Result<()> {
e_raw(unsafe { syscall!(TIMER_SETTIME, timerid, flags, value, ovalue) }).map(|_| ())
}
fn umask(mask: mode_t) -> mode_t {
unsafe { syscall!(UMASK, mask) as mode_t }
}
+15 -1
View File
@@ -3,12 +3,13 @@ use crate::{
c_str::CStr,
error::{Errno, Result},
header::{
signal::sigevent,
sys_resource::{rlimit, rusage},
sys_stat::stat,
sys_statvfs::statvfs,
sys_time::{timeval, timezone},
sys_utsname::utsname,
time::timespec,
time::{itimerspec, timespec},
},
out::Out,
pthread,
@@ -252,6 +253,19 @@ pub trait Pal {
fn sync() -> Result<()>;
fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()>;
fn timer_delete(timerid: timer_t) -> Result<()>;
fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()>;
fn timer_settime(
timerid: timer_t,
flags: c_int,
value: *const itimerspec,
ovalue: *mut itimerspec,
) -> Result<()>;
// Always successful
fn umask(mask: mode_t) -> mode_t;
+183 -3
View File
@@ -21,11 +21,13 @@ use crate::{
header::{
dirent::dirent,
errno::{
EBADF, EBADFD, EBADR, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS,
EBADF, EBADFD, EBADR, EFAULT, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS,
EOPNOTSUPP, EPERM, ERANGE,
},
fcntl::{self, AT_FDCWD, O_RDONLY},
fcntl::{self, AT_FDCWD, O_CREAT, O_RDONLY, O_RDWR},
limits,
pthread::{pthread_cancel, pthread_create},
signal::{SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, sigevent},
sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE},
sys_random,
sys_resource::{RLIM_INFINITY, rlimit, rusage},
@@ -34,11 +36,15 @@ use crate::{
sys_time::{timeval, timezone},
sys_utsname::{UTSLENGTH, utsname},
sys_wait,
time::timespec,
time::{itimerspec, timer_internal_t, timespec},
unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK},
},
io::{self, BufReader, prelude::*},
out::Out,
platform::sys::{
libredox::RawResult,
timer::{timer_routine, timer_update_wake_time},
},
sync::rwlock::RwLock,
};
@@ -73,6 +79,7 @@ pub(crate) mod path;
mod ptrace;
pub(crate) mod signal;
mod socket;
mod timer;
macro_rules! path_from_c_str {
($c_str:expr) => {{
@@ -863,6 +870,7 @@ impl Pal for Sys {
let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?;
Ok(redox_rt::sys::posix_read(fd, buf)?)
}
fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result<usize> {
unsafe {
Ok(syscall::syscall5(
@@ -1008,6 +1016,178 @@ impl Pal for Sys {
Ok(())
}
fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()> {
if timerid.is_null() || evp.is_null() {
return Err(Errno(EFAULT));
}
let ev = unsafe { &*evp };
if ev.sigev_notify == SIGEV_THREAD && ev.sigev_notify_function.is_none() {
return Err(Errno(EINVAL));
}
let path = format!("/scheme/time/{clock_id}");
let timerfd = libredox::open(&path, O_RDWR, 0)?;
unsafe {
let eventfd = Error::demux(event::redox_event_queue_create_v1(0)).map_err(|e| {
let _ = syscall::close(timerfd);
e
})?;
let timer_buf = Self::mmap(
ptr::null_mut(),
size_of::<timer_internal_t>(),
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS,
0,
0,
)
.map_err(|e| {
let _ = syscall::close(timerfd);
let _ = syscall::close(eventfd);
e
})?;
*timerid = timer_buf;
let timer_ptr = timer_buf as *mut timer_internal_t;
let timer_st = (&mut *timer_ptr);
timer_st.clockid = clock_id;
timer_st.timerfd = timerfd;
timer_st.eventfd = eventfd;
timer_st.evp = (*evp).clone();
timer_st.next_wake_time = itimerspec::default();
timer_st.thread = ptr::null_mut();
}
Ok(())
}
fn timer_delete(timerid: timer_t) -> Result<()> {
if timerid.is_null() {
return Err(Errno(EFAULT));
}
unsafe {
let timer_ptr = timerid as *mut timer_internal_t;
let timer_st = (&mut *timer_ptr);
let _ = syscall::close(timer_st.timerfd);
let _ = syscall::close(timer_st.eventfd);
if !timer_st.thread.is_null() {
let _ = pthread_cancel(timer_st.thread);
}
Self::munmap(timer_ptr as *mut c_void, size_of::<timer_internal_t>())?;
}
Ok(())
}
fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()> {
if value.is_null() {
return Err(Errno(EFAULT));
}
let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) };
let mut now = timespec::default();
Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
if timer_st.evp.sigev_notify == SIGEV_NONE {
if timespec::subtract(timer_st.next_wake_time.it_value, now).is_none() {
// error here means the timer is disarmed
let _ = timer_update_wake_time(timer_st);
}
}
unsafe {
*value = if timer_st.next_wake_time.it_value.is_default() {
// disarmed
itimerspec::default()
} else {
itimerspec {
it_interval: timer_st.next_wake_time.it_interval,
it_value: timespec::subtract(timer_st.next_wake_time.it_value, now)
.unwrap_or_default(),
}
};
}
Ok(())
}
fn timer_settime(
timerid: timer_t,
_flags: c_int,
value: *const itimerspec,
ovalue: *mut itimerspec,
) -> Result<()> {
if timerid.is_null() || value.is_null() {
return Err(Errno(EFAULT));
}
let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) };
if !ovalue.is_null() {
Self::timer_gettime(timerid, ovalue)?;
}
let mut now = timespec::default();
Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
//FIXME: make these atomic?
timer_st.next_wake_time = unsafe {
let mut val = (*value).clone();
val.it_value = timespec::add(now, val.it_value).ok_or((Errno(EINVAL)))?;
val
};
Error::demux(unsafe {
event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0)
})?;
let buf_to_write = unsafe {
slice::from_raw_parts(
&timer_st.next_wake_time.it_value as *const _ as *const u8,
mem::size_of::<timespec>(),
)
};
let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?;
if bytes_written < mem::size_of::<timespec>() {
return Err(Errno(EIO));
}
if timer_st.thread.is_null() {
timer_st.thread = match timer_st.evp.sigev_notify {
SIGEV_THREAD => {
let mut tid = pthread_t::default();
let result = unsafe {
pthread_create(
&mut tid as *mut _,
ptr::null(),
timer_routine,
timerid as *mut c_void,
)
};
if result != 0 {
return Err(Errno(result));
}
tid
}
//TODO
SIGEV_SIGNAL => {
return Err(Errno(ENOSYS));
}
SIGEV_NONE => ptr::null_mut(),
_ => {
return Err(Errno(EINVAL));
}
};
}
Ok(())
}
fn umask(mask: mode_t) -> mode_t {
let new_effective_mask = mask & mode_t::from(MODE_PERM) & !S_ISVTX;
(redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX
+100
View File
@@ -0,0 +1,100 @@
use ::event::raw::RawEventV1;
use syscall::Error;
use crate::{
error::{Errno, Result},
header::{
errno::EIO,
signal::{sigevent, sigval},
time::{timer_internal_t, timespec},
},
out::Out,
platform::{
Pal, Sys,
sys::{event, libredox},
types::c_void,
},
};
use core::{
mem::{MaybeUninit, size_of, transmute},
ops::ControlFlow,
ptr, slice,
};
pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void {
unsafe {
let timer_st = &mut *(arg as *mut timer_internal_t);
loop {
let mut buf = MaybeUninit::uninit();
unsafe {
let res = Error::demux(event::redox_event_queue_get_events_v1(
timer_st.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 {
timer_st.thread = ptr::null_mut();
break;
}
if let Some(fun) = timer_st.evp.sigev_notify_function {
fun(timer_st.evp.sigev_value);
}
if timer_next_event(timer_st).is_err() {
timer_st.thread = ptr::null_mut();
break;
}
}
}
}
ptr::null_mut()
}
fn timer_next_event(timer_st: &mut timer_internal_t) -> Result<()> {
timer_update_wake_time(timer_st)?;
Error::demux(unsafe {
event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0)
})?;
let buf_to_write = unsafe {
slice::from_raw_parts(
&timer_st.next_wake_time.it_value as *const _ as *const u8,
size_of::<timespec>(),
)
};
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(())
}
pub(crate) fn timer_update_wake_time(timer_st: &mut timer_internal_t) -> Result<()> {
timer_st.next_wake_time.it_value = if timer_st.next_wake_time.it_interval.is_default() {
timespec::default()
} else {
let mut now = timespec::default();
Sys::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
let next_time = match timespec::add(now, timer_st.next_wake_time.it_interval) {
Some(a) => a,
None => timespec::default(),
};
next_time
};
if timer_st.next_wake_time.it_value.is_default() {
return Err(Errno(0));
}
Ok(())
}