Merge branch 'timer-signal' into 'master'

Support SIGEV_SIGNAL and TIMER_ABSTIME

See merge request redox-os/relibc!750
This commit is contained in:
Jeremy Soller
2025-10-16 11:00:39 -06:00
6 changed files with 160 additions and 123 deletions
+2
View File
@@ -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;
+29 -8
View File
@@ -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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_create.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_delete.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_getoverrun.html>.
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_getoverrun.html>.
#[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()
+27 -7
View File
@@ -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<timer_t>) -> 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<itimerspec>) -> 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<Out<itimerspec>>,
) -> 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 {
+4 -4
View File
@@ -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<timer_t>) -> 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<itimerspec>) -> Result<()>;
fn timer_settime(
timerid: timer_t,
flags: c_int,
value: *const itimerspec,
ovalue: *mut itimerspec,
value: &itimerspec,
ovalue: Option<Out<itimerspec>>,
) -> Result<()>;
// Always successful
+58 -69
View File
@@ -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<timer_t>) -> 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::<timer_internal_t>(),
@@ -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::<timer_internal_t>())?;
Self::munmap(timerid, size_of::<timer_internal_t>())?;
}
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<itimerspec>) -> 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<Out<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() {
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));
+40 -35
View File
@@ -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::<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));