Unify alarm implementation

This commit is contained in:
Wildan M
2026-05-05 09:28:53 +07:00
parent 518b597082
commit 90cb143523
7 changed files with 146 additions and 171 deletions
+22 -3
View File
@@ -6,14 +6,14 @@ use core::{mem, ptr};
use cbitset::BitSet;
#[cfg(target_os = "redox")]
use crate::platform::types::pthread_attr_t;
use crate::{
error::{Errno, ResultExt},
header::{bits_sigset_t::sigset_t, errno, setjmp, time::timespec},
platform::{
self, ERRNO, Pal, PalSignal, Sys,
types::{
c_char, c_int, c_ulonglong, c_void, pid_t, pthread_attr_t, pthread_t, size_t, uid_t,
},
types::{c_char, c_int, c_ulonglong, c_void, pid_t, pthread_t, size_t, uid_t},
},
};
@@ -69,6 +69,7 @@ pub struct sigaltstack {
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
#[repr(C)]
#[derive(Clone)]
#[cfg(not(target_os = "linux"))]
pub struct sigevent {
pub sigev_value: sigval,
pub sigev_signo: c_int,
@@ -77,6 +78,24 @@ pub struct sigevent {
pub sigev_notify_attributes: *mut pthread_attr_t,
}
// must match with signature from libc
// https://docs.rs/libc/0.2.186/src/libc/unix/linux_like/mod.rs.html#300-322
#[repr(C)]
#[derive(Clone)]
#[cfg(target_os = "linux")]
pub struct sigevent {
pub sigev_value: sigval,
pub sigev_signo: c_int,
pub sigev_notify: c_int,
// Actually a union. We only expose sigev_notify_thread_id because it's
// the most useful member
pub sigev_notify_thread_id: c_int,
#[cfg(target_pointer_width = "64")]
__unused1: [c_int; 11],
#[cfg(target_pointer_width = "32")]
__unused1: [c_int; 12],
}
// FIXME: This struct is wrong on Linux
#[repr(C)]
#[derive(Clone, Copy)]
+108
View File
@@ -0,0 +1,108 @@
#[cfg(target_os = "redox")]
use crate::header::time::timer_internal_t;
use crate::{
header::{
bits_timespec::timespec,
signal::{SIGALRM, SIGEV_SIGNAL, sigevent},
time::itimerspec,
},
out::Out,
platform::{
Pal, Sys,
types::{c_int, c_uint, timer_t},
},
sync::Mutex,
};
/// Wrapper for timer_t that implements Send (the timer_t pointer is a process-
/// wide mmap'd allocation that outlives any single thread).
struct AlarmTimer(timer_t);
// SAFETY: The timer_t pointer refers to an mmap'd timer_internal_t that is
// only accessed under the ALARM_TIMER mutex lock.
unsafe impl Send for AlarmTimer {}
/// Process-global singleton timer used by alarm(). Protected by a mutex to
/// ensure only one alarm is active at a time (POSIX requirement).
static ALARM_TIMER: Mutex<Option<AlarmTimer>> = Mutex::new(None);
/// Internal helper that arms/disarms the process-global alarm timer.
/// Accepts a full timespec so sub-second timers (ualarm) can reuse this later.
/// Returns the number of seconds remaining on the previous alarm (rounded up),
/// or 0 if there was no previous alarm.
///
/// TODO: This implementation does not survive `exec()`. POSIX requires that a
/// pending alarm be preserved across exec (the timer continues counting down
/// in the new process image as i understand).
pub fn alarm_timespec(duration: timespec) -> c_uint {
let mut guard = ALARM_TIMER.lock();
// Determine remaining time on any existing alarm
let remaining = if let Some(ref alarm) = *guard {
let mut cur = itimerspec::default();
if Sys::timer_gettime(alarm.0, Out::from_mut(&mut cur)).is_ok() {
let secs = cur.it_value.tv_sec as c_uint;
if cur.it_value.tv_nsec > 0 {
secs + 1 // POSIX: round up
} else {
secs
}
} else {
0
}
} else {
0
};
let disarm = duration.tv_sec == 0 && duration.tv_nsec == 0;
if disarm {
// alarm(0): cancel any pending alarm
if let Some(ref alarm) = *guard {
let zero = itimerspec::default();
let _ = Sys::timer_settime(alarm.0, 0, &zero, None);
}
return remaining;
}
// Lazily create the singleton timer if it doesn't exist yet
if guard.is_none() {
let mut evp = unsafe { core::mem::zeroed::<sigevent>() };
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGALRM as c_int;
let mut timer_id: timer_t = core::ptr::null_mut();
if Sys::timer_create(
crate::header::time::CLOCK_REALTIME,
&evp,
Out::from_mut(&mut timer_id),
)
.is_err()
{
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));
}
let timer_id = guard
.as_ref()
.expect("alarm timer must exist after lazy init")
.0;
// Arm the timer as a one-shot (no interval)
let spec = itimerspec {
it_value: duration,
it_interval: timespec::default(),
};
let _ = Sys::timer_settime(timer_id, 0, &spec, None);
remaining
}
+7 -2
View File
@@ -25,10 +25,11 @@ use crate::{
sys_select::timeval,
sys_time, sys_utsname, termios,
time::timespec,
unistd::alarm::alarm_timespec,
},
out::Out,
platform::{
self, ERRNO, Pal, PalSignal, Sys,
self, ERRNO, Pal, Sys,
types::{
c_char, c_int, c_long, c_short, c_uint, c_ulonglong, c_void, gid_t, off_t, pid_t,
size_t, ssize_t, suseconds_t, time_t, uid_t,
@@ -52,6 +53,7 @@ use super::{
stdio::snprintf,
};
mod alarm;
mod brk;
mod getopt;
mod getpass;
@@ -155,7 +157,10 @@ pub unsafe extern "C" fn faccessat(
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html>.
#[unsafe(no_mangle)]
pub extern "C" fn alarm(seconds: c_uint) -> c_uint {
Sys::alarm(seconds)
alarm_timespec(timespec {
tv_sec: seconds.into(),
tv_nsec: 0,
})
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/chdir.html>.