implement housekeeping for posix timers

This commit is contained in:
sourceturner
2026-05-13 08:28:15 +02:00
parent d16183732c
commit 4b4dc2ce94
4 changed files with 99 additions and 69 deletions
+2 -3
View File
@@ -60,7 +60,6 @@ pub(crate) struct timer_internal_t {
pub eventfd: usize,
pub evp: sigevent,
pub thread: platform::types::pthread_t,
pub caller_thread: crate::pthread::OsTid,
/// relibc handles it_interval, not the kernel
pub next_wake_time: itimerspec,
/// kernel does not support unregistering timer
@@ -72,8 +71,8 @@ pub(crate) struct timer_internal_t {
#[cfg(target_os = "redox")]
impl timer_internal_t {
pub unsafe fn from_raw(timerid: timer_t) -> &'static Mutex<Self> {
unsafe { &*(timerid as *const Mutex<Self>) }
pub unsafe fn from_raw(timerid: timer_t) -> &'static mut Self {
unsafe { &mut *(timerid as *mut Self) }
}
}
-11
View File
@@ -1,5 +1,3 @@
#[cfg(target_os = "redox")]
use crate::header::time::timer_internal_t;
use crate::{
header::{
bits_timespec::timespec,
@@ -80,15 +78,6 @@ pub fn alarm_timespec(duration: timespec) -> c_uint {
return remaining;
}
// Enable process-wide signal delivery instead of thread-specific
#[cfg(target_os = "redox")]
{
let timer_ptr = unsafe { timer_internal_t::from_raw(timer_id) };
let mut timer_st = timer_ptr.lock();
timer_st.process_pid = Sys::getpid();
drop(timer_st);
}
*guard = Some(AlarmTimer(timer_id));
}
+64 -46
View File
@@ -2,7 +2,6 @@ use core::{
convert::TryFrom,
mem::{self, size_of},
num::NonZeroU64,
ops::DerefMut,
ptr, slice, str,
};
use object::bytes_of_slice_mut;
@@ -39,6 +38,7 @@ use crate::{
pthread::{pthread_cancel, pthread_create},
signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent},
stdio::RENAME_NOREPLACE,
stdlib::posix_memalign,
sys_file,
sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
sys_random,
@@ -56,8 +56,11 @@ use crate::{
io::{self, BufReader, prelude::*},
ld_so::tcb::OsSpecific,
out::Out,
platform::sys::timer::{timer_routine, timer_update_wake_time},
sync::{Mutex, rwlock::RwLock},
platform::{
free,
sys::timer::{TIMERS, timer_routine, timer_update_wake_time},
},
sync::rwlock::RwLock,
};
pub use redox_rt::proc::FdGuard;
@@ -1247,65 +1250,77 @@ impl Pal for Sys {
event::redox_event_queue_create_v1(0)
})?)
.to_upper()?;
let caller_thread = Self::current_os_tid();
let timer_buf = unsafe {
let timer_buf = Self::mmap(
ptr::null_mut(),
size_of::<Mutex<timer_internal_t>>(),
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS,
0,
0,
)?;
let timer_ptr = timer_internal_t::from_raw(timer_buf);
let mut timer_st = timer_ptr.lock();
timer_st.clockid = clock_id;
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_st.process_pid = 0;
timer_st.next_wake_version = 0;
drop(timer_st);
timer_buf
let timer_st = timer_internal_t {
clockid: clock_id,
timerfd: timerfd.take(),
eventfd: eventfd.take(),
evp: (*evp).clone(),
thread: ptr::null_mut(),
next_wake_time: itimerspec::default(),
next_wake_version: 0,
process_pid: Sys::getpid(),
};
let timers = &mut TIMERS.lock().0;
// allocate enough memory on the heap to store one timer_internal_t
let mut memory_pointer: *mut timer_internal_t = ptr::null_mut();
unsafe {
let result = posix_memalign(
(&mut memory_pointer as *mut *mut timer_internal_t).cast(),
align_of::<timer_internal_t>(),
size_of::<timer_internal_t>(),
);
assert_eq!(result, 0, "Failed to allocate or invalid alignment");
};
timerid.write(timer_buf);
let pointer = {
ptr::NonNull::new(memory_pointer)
.expect("Pointer is guaranteed to not be null if posix_memalign returns 0")
};
// move value from the stack to the location we allocated on the heap
unsafe {
// Safety: If non-null, posix_memalign gives us a pointer that is valid
// for writes and properly aligned.
pointer.as_ptr().write(timer_st);
}
let timer_ptr = pointer.as_ptr() as timer_t;
timers.insert(timer_ptr);
timerid.write(timer_ptr);
Ok(())
}
fn timer_delete(timerid: timer_t) -> Result<()> {
unsafe {
let timer_ptr = timer_internal_t::from_raw(timerid);
let timer_st = timer_ptr.lock();
let _ = syscall::close(timer_st.timerfd);
let _ = syscall::close(timer_st.eventfd);
if !timer_st.thread.is_null() {
let _ = pthread_cancel(timer_st.thread);
}
drop(timer_st);
Self::munmap(timerid, size_of::<Mutex<timer_internal_t>>())?;
let timers = &mut TIMERS.lock().0;
let removed = timers.remove(&timerid);
if !removed {
return Err(Errno(EINVAL));
}
let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
let _ = syscall::close(timer_st.timerfd);
let _ = syscall::close(timer_st.eventfd);
if !timer_st.thread.is_null() {
let _ = unsafe { pthread_cancel(timer_st.thread) };
}
unsafe { free(timerid) };
Ok(())
}
fn timer_gettime(timerid: timer_t, mut value: Out<itimerspec>) -> Result<()> {
let timer_ptr = unsafe { timer_internal_t::from_raw(timerid) };
let mut timer_st = timer_ptr.lock();
let timers = &mut TIMERS.lock().0;
if !timers.contains(&timerid) {
return Err(Errno(EINVAL));
}
let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
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.deref_mut());
let _ = timer_update_wake_time(timer_st);
}
}
let remaining = &timer_st.next_wake_time.it_value;
@@ -1328,13 +1343,16 @@ impl Pal for Sys {
value: &itimerspec,
ovalue: Option<Out<itimerspec>>,
) -> Result<()> {
let timer_ptr = unsafe { timer_internal_t::from_raw(timerid) };
let mut timer_st = timer_ptr.lock();
if let Some(ovalue) = ovalue {
Self::timer_gettime(timerid, ovalue)?;
}
let timers = &mut TIMERS.lock().0;
if !timers.contains(&timerid) {
return Err(Errno(EINVAL));
}
let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
if value.it_value.is_zero() {
timer_st.next_wake_version += 1;
return Ok(());
+33 -9
View File
@@ -8,18 +8,37 @@ use crate::{
time::{timer_internal_t, timespec},
},
out::Out,
platform::{Pal, Sys, sys::event, types::c_void},
platform::{
Pal, PalSignal, Sys,
sys::event,
types::{c_void, timer_t},
},
sync::Mutex,
};
use alloc::collections::BTreeSet;
use core::{
mem::{MaybeUninit, size_of},
ops::DerefMut,
ptr,
};
pub static TIMERS: Mutex<ForceSendSync<BTreeSet<timer_t>>> =
Mutex::new(ForceSendSync(BTreeSet::new()));
unsafe impl Send for timer_internal_t {}
unsafe impl Sync for timer_internal_t {}
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct ForceSendSync<T>(pub(crate) T);
unsafe impl<T> Send for ForceSendSync<T> {}
unsafe impl<T> Sync for ForceSendSync<T> {}
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();
let timers = &mut TIMERS.lock().0;
if !timers.contains(&arg) {
return ptr::null_mut();
}
let timer_st = unsafe { timer_internal_t::from_raw(arg) };
(timer_st.next_wake_version, timer_st.eventfd)
};
loop {
@@ -41,18 +60,23 @@ pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void {
break;
}
let mut timer_st = timer_ptr.lock();
let timers = &mut TIMERS.lock().0;
if !timers.contains(&arg) {
return ptr::null_mut();
}
let mut timer_st = unsafe { timer_internal_t::from_raw(arg) };
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()
if Sys::sigqueue(
timer_st.process_pid,
timer_st.evp.sigev_signo as _,
timer_st.evp.sigev_value,
)
.is_err()
{
break;
}