From c8fce6787363683bf6b680b413055522651fa228 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Thu, 16 Oct 2025 22:37:24 +0700 Subject: [PATCH] Support SIGEV_SIGNAL and TIMER_ABSTIME --- src/header/time/constants.rs | 2 + src/header/time/mod.rs | 37 +++++++--- src/platform/linux/mod.rs | 34 ++++++++-- src/platform/pal/mod.rs | 8 +-- src/platform/redox/mod.rs | 127 ++++++++++++++++------------------- src/platform/redox/timer.rs | 75 +++++++++++---------- 6 files changed, 160 insertions(+), 123 deletions(-) diff --git a/src/header/time/constants.rs b/src/header/time/constants.rs index 303a38936f..2a89e49e54 100644 --- a/src/header/time/constants.rs +++ b/src/header/time/constants.rs @@ -20,3 +20,5 @@ pub(crate) const MON_NAMES: [&str; 12] = [ pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; // Can't be time_t because cbindgen UGH pub const CLOCKS_PER_SEC: c_long = 1_000_000; + +pub const TIMER_ABSTIME: c_int = 1; diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs index 93dd9aaa96..1420509669 100644 --- a/src/header/time/mod.rs +++ b/src/header/time/mod.rs @@ -4,16 +4,21 @@ use crate::{ c_str::{CStr, CString}, - error::ResultExt, + error::{Errno, ResultExt}, fs::File, header::{ - errno::EOVERFLOW, fcntl::O_RDONLY, signal::sigevent, stdlib::getenv, unistd::readlink, + errno::{EFAULT, EOVERFLOW}, + fcntl::O_RDONLY, + signal::sigevent, + stdlib::getenv, + unistd::readlink, }, io::Read, out::Out, platform::{self, Pal, Sys, types::*}, sync::{Mutex, MutexGuard}, }; +use __libc_only_for_layout_checks::EINVAL; use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec}; use chrono::{ DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, Offset, ParseError, TimeZone, @@ -115,6 +120,7 @@ pub(crate) struct timer_internal_t { pub eventfd: usize, pub evp: sigevent, pub thread: pthread_t, + pub caller_thread: crate::pthread::OsTid, // relibc handles it_interval, not the kernel pub next_wake_time: itimerspec, } @@ -535,19 +541,26 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t { /// See . #[unsafe(no_mangle)] -pub extern "C" fn timer_create( +pub unsafe extern "C" fn timer_create( clock_id: clockid_t, - evp: *mut sigevent, + evp: *const sigevent, timerid: *mut timer_t, ) -> c_int { - Sys::timer_create(clock_id, evp, timerid) + if evp.is_null() || timerid.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let (evp, timerid) = unsafe { (&*evp, Out::nonnull(timerid)) }; + Sys::timer_create(clock_id, &evp, timerid) .map(|()| 0) .or_minus_one_errno() } /// See . #[unsafe(no_mangle)] -pub extern "C" fn timer_delete(timerid: timer_t) -> c_int { +pub unsafe extern "C" fn timer_delete(timerid: timer_t) -> c_int { + if timerid.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } Sys::timer_delete(timerid).map(|()| 0).or_minus_one_errno() } @@ -559,7 +572,11 @@ pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int { /// See . #[unsafe(no_mangle)] -pub extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int { +pub unsafe extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int { + if timerid.is_null() || value.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let value = unsafe { Out::nonnull(value) }; Sys::timer_gettime(timerid, value) .map(|()| 0) .or_minus_one_errno() @@ -567,12 +584,16 @@ pub extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_i /// See . #[unsafe(no_mangle)] -pub extern "C" fn timer_settime( +pub unsafe extern "C" fn timer_settime( timerid: timer_t, flags: c_int, value: *const itimerspec, ovalue: *mut itimerspec, ) -> c_int { + if timerid.is_null() || value.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let (value, ovalue) = unsafe { (&*value, Out::nullable(ovalue)) }; Sys::timer_settime(timerid, flags, value, ovalue) .map(|()| 0) .or_minus_one_errno() diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index bc53294ae8..88bacb52e1 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -726,25 +726,45 @@ 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_create(clock_id: clockid_t, evp: &sigevent, mut timerid: Out) -> Result<()> { + e_raw(unsafe { + syscall!( + TIMER_CREATE, + clock_id, + ptr::addr_of!(evp), + timerid.as_mut_ptr() + ) + }) + .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_gettime(timerid: timer_t, mut value: Out) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_GETTIME, timerid, value.as_mut_ptr()) }).map(|_| ()) } fn timer_settime( timerid: timer_t, flags: c_int, - value: *const itimerspec, - ovalue: *mut itimerspec, + value: &itimerspec, + mut ovalue: Option>, ) -> Result<()> { - e_raw(unsafe { syscall!(TIMER_SETTIME, timerid, flags, value, ovalue) }).map(|_| ()) + e_raw(unsafe { + syscall!( + TIMER_SETTIME, + timerid, + flags, + ptr::addr_of!(value), + match ovalue { + None => ptr::null_mut(), + Some(mut o) => o.as_mut_ptr(), + } + ) + }) + .map(|_| ()) } fn umask(mask: mode_t) -> mode_t { diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 38c114e355..ca2e67fbdf 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -253,17 +253,17 @@ pub trait Pal { fn sync() -> Result<()>; - fn timer_create(clock_id: clockid_t, evp: *mut sigevent, timerid: *mut timer_t) -> Result<()>; + fn timer_create(clock_id: clockid_t, evp: &sigevent, timerid: Out) -> Result<()>; fn timer_delete(timerid: timer_t) -> Result<()>; - fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()>; + fn timer_gettime(timerid: timer_t, value: Out) -> Result<()>; fn timer_settime( timerid: timer_t, flags: c_int, - value: *const itimerspec, - ovalue: *mut itimerspec, + value: &itimerspec, + ovalue: Option>, ) -> Result<()>; // Always successful diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 60c75b26ab..8021ddf4b7 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1,6 +1,6 @@ use core::{ convert::TryFrom, - mem::{self, size_of}, + mem::{self, MaybeUninit, size_of}, ptr, slice, str, }; use redox_rt::{ @@ -26,8 +26,8 @@ use crate::{ }, fcntl::{self, AT_FDCWD, O_CREAT, O_RDONLY, O_RDWR}, limits, - pthread::{pthread_cancel, pthread_create}, - signal::{SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, sigevent}, + pthread::{pthread_cancel, pthread_create, pthread_self}, + signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent}, sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE}, sys_random, sys_resource::{RLIM_INFINITY, rlimit, rusage}, @@ -36,7 +36,7 @@ use crate::{ sys_time::{timeval, timezone}, sys_utsname::{UTSLENGTH, utsname}, sys_wait, - time::{itimerspec, timer_internal_t, timespec}, + time::{TIMER_ABSTIME, itimerspec, timer_internal_t, timespec}, unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK}, }, io::{self, BufReader, prelude::*}, @@ -1016,24 +1016,32 @@ 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)); + fn timer_create(clock_id: clockid_t, evp: &sigevent, mut timerid: Out) -> Result<()> { + if evp.sigev_notify == SIGEV_THREAD { + if evp.sigev_notify_function.is_none() { + return Err(Errno(EINVAL)); + } + } else if evp.sigev_notify == SIGEV_SIGNAL { + const n_sig: i32 = NSIG as i32; + const rt_min: i32 = SIGRTMIN as i32; + const rt_max: i32 = SIGRTMIN as i32; + match evp.sigev_signo { + 0..n_sig => {} + rt_min..=rt_max => {} + _ => { + 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 timerfd = FdGuard::new(libredox::open(&path, O_RDWR, 0)?); + let eventfd = FdGuard::new(Error::demux(unsafe { + event::redox_event_queue_create_v1(0) + })?); + let caller_thread = Self::current_os_tid(); + let timer_buf = unsafe { let timer_buf = Self::mmap( ptr::null_mut(), size_of::(), @@ -1041,51 +1049,41 @@ impl Pal for Sys { 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.timerfd = timerfd.take(); + timer_st.eventfd = eventfd.take(); timer_st.evp = (*evp).clone(); timer_st.next_wake_time = itimerspec::default(); timer_st.thread = ptr::null_mut(); - } + timer_st.caller_thread = caller_thread; + timer_buf + }; + + timerid.write(timer_buf); + 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 timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) }; 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::())?; + Self::munmap(timerid, size_of::())?; } Ok(()) } - fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> Result<()> { - if value.is_null() { - return Err(Errno(EFAULT)); - } - + fn timer_gettime(timerid: timer_t, mut value: Out) -> Result<()> { 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))?; @@ -1097,35 +1095,29 @@ impl Pal for Sys { } } - 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(), - } - }; - } + value.write(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, + flags: c_int, + value: &itimerspec, + ovalue: Option>, ) -> 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() { + if let Some(ovalue) = ovalue { Self::timer_gettime(timerid, ovalue)?; } @@ -1133,9 +1125,11 @@ impl Pal for Sys { 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)))?; + timer_st.next_wake_time = { + let mut val = value.clone(); + if flags & TIMER_ABSTIME == 0 { + val.it_value = timespec::add(now, val.it_value).ok_or((Errno(EINVAL)))?; + } val }; @@ -1158,9 +1152,8 @@ impl Pal for Sys { if timer_st.thread.is_null() { timer_st.thread = match timer_st.evp.sigev_notify { - SIGEV_THREAD => { + SIGEV_THREAD | SIGEV_SIGNAL => { let mut tid = pthread_t::default(); - let result = unsafe { pthread_create( &mut tid as *mut _, @@ -1174,10 +1167,6 @@ impl Pal for Sys { } tid } - //TODO - SIGEV_SIGNAL => { - return Err(Errno(ENOSYS)); - } SIGEV_NONE => ptr::null_mut(), _ => { return Err(Errno(EINVAL)); diff --git a/src/platform/redox/timer.rs b/src/platform/redox/timer.rs index 59cdb578b1..2dce62bcfb 100644 --- a/src/platform/redox/timer.rs +++ b/src/platform/redox/timer.rs @@ -5,7 +5,7 @@ use crate::{ error::{Errno, Result}, header::{ errno::EIO, - signal::{sigevent, sigval}, + signal::{SIGEV_SIGNAL, SIGEV_THREAD, raise, sigevent, sigval}, time::{timer_internal_t, timespec}, }, out::Out, @@ -22,57 +22,62 @@ use core::{ }; pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void { - unsafe { - let timer_st = &mut *(arg as *mut timer_internal_t); + let timer_st = unsafe { &mut *(arg as *mut timer_internal_t) }; - loop { - let mut buf = MaybeUninit::uninit(); + loop { + let mut buf = MaybeUninit::uninit(); + let res = Error::demux(unsafe { + 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 { + break; + } - 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; - } + 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 { + if unsafe { Sys::rlct_kill(timer_st.caller_thread, timer_st.evp.sigev_signo as _) } + .is_err() + { + break; + } + } + + if timer_next_event(timer_st).is_err() { + break; } } + timer_st.thread = ptr::null_mut(); 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 { + Error::demux(event::redox_event_queue_ctl_v1( + timer_st.eventfd, + timer_st.timerfd, + 1, + 0, + ))?; + slice::from_raw_parts( &timer_st.next_wake_time.it_value as *const _ as *const u8, size_of::(), ) }; - let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?; if bytes_written < size_of::() { return Err(Errno(EIO));