Unify alarm implementation
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>.
|
||||
|
||||
@@ -783,7 +783,7 @@ impl Pal for Sys {
|
||||
syscall!(
|
||||
TIMER_CREATE,
|
||||
clock_id,
|
||||
ptr::addr_of!(evp),
|
||||
evp as *const _,
|
||||
timerid.as_mut_ptr()
|
||||
)
|
||||
})
|
||||
@@ -809,7 +809,7 @@ impl Pal for Sys {
|
||||
TIMER_SETTIME,
|
||||
timerid,
|
||||
flags,
|
||||
ptr::addr_of!(value),
|
||||
value as *const _,
|
||||
match ovalue {
|
||||
None => ptr::null_mut(),
|
||||
Some(mut o) => o.as_mut_ptr(),
|
||||
|
||||
@@ -6,7 +6,7 @@ use core::{
|
||||
use super::{
|
||||
super::{
|
||||
PalSignal,
|
||||
types::{c_int, c_uint, pid_t},
|
||||
types::{c_int, pid_t},
|
||||
},
|
||||
Sys, e_raw,
|
||||
};
|
||||
@@ -16,14 +16,9 @@ use crate::{
|
||||
error::{Errno, Result},
|
||||
header::{
|
||||
bits_sigset_t::sigset_t,
|
||||
signal::{
|
||||
SA_RESTORER, SI_QUEUE, SIGALRM, SIGEV_SIGNAL, sigaction, sigevent, siginfo_t, sigval,
|
||||
stack_t,
|
||||
},
|
||||
time::{self, timespec},
|
||||
bits_timespec::timespec,
|
||||
signal::{SA_RESTORER, SI_QUEUE, sigaction, siginfo_t, sigval, stack_t},
|
||||
},
|
||||
out::Out,
|
||||
platform::{self, Pal, types::timer_t},
|
||||
};
|
||||
|
||||
impl PalSignal for Sys {
|
||||
@@ -77,44 +72,6 @@ impl PalSignal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html>.
|
||||
fn alarm(seconds: c_uint) -> c_uint {
|
||||
let mut signal_event = sigevent {
|
||||
sigev_value: sigval { sival_int: 0 },
|
||||
sigev_signo: SIGALRM as c_int,
|
||||
sigev_notify: SIGEV_SIGNAL,
|
||||
sigev_notify_attributes: ptr::null_mut(),
|
||||
sigev_notify_function: None,
|
||||
};
|
||||
let mut timerid: timer_t = Default::default();
|
||||
let timer = unsafe {
|
||||
time::timer_create(
|
||||
time::CLOCK_REALTIME,
|
||||
ptr::from_mut(&mut signal_event),
|
||||
ptr::from_mut(&mut timerid),
|
||||
)
|
||||
};
|
||||
let value = time::itimerspec {
|
||||
it_value: timespec {
|
||||
tv_sec: i64::from(seconds),
|
||||
tv_nsec: 0,
|
||||
},
|
||||
it_interval: timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
},
|
||||
};
|
||||
let mut ovalue = Default::default();
|
||||
|
||||
let errno_backup = platform::ERRNO.get();
|
||||
if Sys::timer_settime(timerid, 0, &value, Some(Out::from_mut(&mut ovalue))).is_err() {
|
||||
platform::ERRNO.set(errno_backup);
|
||||
0
|
||||
} else {
|
||||
ovalue.it_value.tv_sec as c_uint + if ovalue.it_value.tv_nsec > 0 { 1 } else { 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn sigaction(
|
||||
sig: c_int,
|
||||
act: Option<&sigaction>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::super::{
|
||||
Pal,
|
||||
types::{c_int, c_uint, pid_t},
|
||||
types::{c_int, pid_t},
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use crate::header::sys_time::itimerval;
|
||||
@@ -35,9 +35,6 @@ pub trait PalSignal: Pal {
|
||||
#[allow(deprecated)]
|
||||
fn setitimer(which: c_int, new: &itimerval, old: Option<&mut itimerval>) -> Result<()>;
|
||||
|
||||
/// Platform implementation of [`alarm()`](crate::header::unistd::alarm) from [`unistd.h`](crate::header::unistd).
|
||||
fn alarm(seconds: c_uint) -> c_uint;
|
||||
|
||||
/// Platform implementation of [`sigaction()`](crate::header::signal::sigaction()) from [`signal.h`](crate::header::signal).
|
||||
fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> Result<()>;
|
||||
|
||||
|
||||
@@ -10,13 +10,11 @@ use crate::{
|
||||
bits_sigset_t::sigset_t,
|
||||
errno::{EINVAL, ENOSYS},
|
||||
signal::{
|
||||
SIG_BLOCK, SIG_DFL, SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SIGALRM, SIGEV_SIGNAL,
|
||||
SS_DISABLE, SS_ONSTACK, sigaction, sigevent, siginfo_t, sigval, stack_t, ucontext_t,
|
||||
SIG_BLOCK, SIG_DFL, SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK,
|
||||
sigaction, siginfo_t, sigval, stack_t, ucontext_t,
|
||||
},
|
||||
time::{itimerspec, timer_internal_t, timespec},
|
||||
time::timespec,
|
||||
},
|
||||
out::Out,
|
||||
sync::Mutex,
|
||||
};
|
||||
use core::mem::offset_of;
|
||||
use redox_protocols::protocol::ProcKillTarget;
|
||||
@@ -24,17 +22,6 @@ use redox_rt::signal::{
|
||||
PosixStackt, SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler,
|
||||
};
|
||||
|
||||
/// 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);
|
||||
|
||||
const _: () = {
|
||||
#[track_caller]
|
||||
const fn assert_eq(a: usize, b: usize) {
|
||||
@@ -262,102 +249,4 @@ impl PalSignal for Sys {
|
||||
}
|
||||
Ok(info.si_signo)
|
||||
}
|
||||
|
||||
/// Thread-based alarm() that Recycles the existing POSIX timer machinery
|
||||
/// (timerfd + eventfd + pthread) with process-level signal delivery.
|
||||
///
|
||||
/// Internally works with a timespec to allow sub-second timers in the
|
||||
/// future (e.g. ualarm) as i've been asked, though the public API only exposes whole seconds.
|
||||
fn alarm(seconds: c_uint) -> c_uint {
|
||||
alarm_timespec(timespec {
|
||||
tv_sec: seconds as time_t,
|
||||
tv_nsec: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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).
|
||||
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 evp = sigevent {
|
||||
sigev_value: sigval {
|
||||
sival_ptr: core::ptr::null_mut(),
|
||||
},
|
||||
sigev_signo: SIGALRM as c_int,
|
||||
sigev_notify: SIGEV_SIGNAL,
|
||||
sigev_notify_function: None,
|
||||
sigev_notify_attributes: core::ptr::null_mut(),
|
||||
};
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user