Support rt_sigtimedwait syscall

This commit is contained in:
Chen Chengjun 2025-12-09 11:12:16 +00:00 committed by Tate, Hongliang Tian
parent 81da39400c
commit 53575b04cd
7 changed files with 135 additions and 1 deletions

View File

@ -148,7 +148,7 @@ which are summarized in the table below.
| 125 | capget | ✅ | [⚠️](syscall-flag-coverage/namespaces-cgroups-and-security/#capget-and-capset) |
| 126 | capset | ✅ | [⚠️](syscall-flag-coverage/namespaces-cgroups-and-security/#capget-and-capset) |
| 127 | rt_sigpending | ✅ | 💯 |
| 128 | rt_sigtimedwait | ❌ | N/A |
| 128 | rt_sigtimedwait | ✅ | 💯 |
| 129 | rt_sigqueueinfo | ❌ | N/A |
| 130 | rt_sigsuspend | ✅ | 💯 |
| 131 | sigaltstack | ✅ | ❓ |

View File

@ -33,3 +33,6 @@ gettimeofday(tv, tz);
// Get time in seconds
time(tloc);
// Synchronously wait for a signal with a timeout
rt_sigtimedwait(set, info, timeout, sigsetsize);

View File

@ -95,6 +95,7 @@ use super::{
rt_sigprocmask::sys_rt_sigprocmask,
rt_sigreturn::sys_rt_sigreturn,
rt_sigsuspend::sys_rt_sigsuspend,
rt_sigtimedwait::sys_rt_sigtimedwait,
sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity},
sched_get_priority_max::sys_sched_get_priority_max,
sched_get_priority_min::sys_sched_get_priority_min,
@ -271,6 +272,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_RT_SIGACTION = 134 => sys_rt_sigaction(args[..4]);
SYS_RT_SIGPROCMASK = 135 => sys_rt_sigprocmask(args[..4]);
SYS_RT_SIGPENDING = 136 => sys_rt_sigpending(args[..2]);
SYS_RT_SIGTIMEDWAIT = 137 => sys_rt_sigtimedwait(args[..4]);
SYS_RT_SIGRETURN = 139 => sys_rt_sigreturn(args[..0], &mut user_ctx);
SYS_SET_PRIORITY = 140 => sys_set_priority(args[..3]);
SYS_GET_PRIORITY = 141 => sys_get_priority(args[..2]);

View File

@ -95,6 +95,7 @@ use super::{
rt_sigprocmask::sys_rt_sigprocmask,
rt_sigreturn::sys_rt_sigreturn,
rt_sigsuspend::sys_rt_sigsuspend,
rt_sigtimedwait::sys_rt_sigtimedwait,
sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity},
sched_get_priority_max::sys_sched_get_priority_max,
sched_get_priority_min::sys_sched_get_priority_min,
@ -271,6 +272,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_RT_SIGACTION = 134 => sys_rt_sigaction(args[..4]);
SYS_RT_SIGPROCMASK = 135 => sys_rt_sigprocmask(args[..4]);
SYS_RT_SIGPENDING = 136 => sys_rt_sigpending(args[..2]);
SYS_RT_SIGTIMEDWAIT = 137 => sys_rt_sigtimedwait(args[..4]);
SYS_RT_SIGRETURN = 139 => sys_rt_sigreturn(args[..0], &mut user_ctx);
SYS_SET_PRIORITY = 140 => sys_set_priority(args[..3]);
SYS_GET_PRIORITY = 141 => sys_get_priority(args[..2]);

View File

@ -105,6 +105,7 @@ use super::{
rt_sigprocmask::sys_rt_sigprocmask,
rt_sigreturn::sys_rt_sigreturn,
rt_sigsuspend::sys_rt_sigsuspend,
rt_sigtimedwait::sys_rt_sigtimedwait,
sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity},
sched_get_priority_max::sys_sched_get_priority_max,
sched_get_priority_min::sys_sched_get_priority_min,
@ -291,6 +292,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_CAPGET = 125 => sys_capget(args[..2]);
SYS_CAPSET = 126 => sys_capset(args[..2]);
SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]);
SYS_RT_SIGTIMEDWAIT = 128 => sys_rt_sigtimedwait(args[..4]);
SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]);
SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2], &user_ctx);
SYS_UTIME = 132 => sys_utime(args[..2]);

View File

@ -117,6 +117,7 @@ mod rt_sigpending;
mod rt_sigprocmask;
mod rt_sigreturn;
mod rt_sigsuspend;
mod rt_sigtimedwait;
mod sched_affinity;
mod sched_get_priority_max;
mod sched_get_priority_min;

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: MPL-2.0
use core::{sync::atomic::Ordering, time::Duration};
use ostd::{mm::VmIo, sync::Waiter};
use super::SyscallReturn;
use crate::{
prelude::*,
process::signal::{
HandlePendingSignal,
constants::{SIGKILL, SIGSTOP},
sig_mask::{SigMask, SigSet},
signals::Signal,
with_sigmask_changed,
},
time::{timespec_t, wait::ManagedTimeout},
};
pub fn sys_rt_sigtimedwait(
set_ptr: Vaddr,
info_ptr: Vaddr,
timeout_ptr: Vaddr,
sigset_size: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
debug!(
"set_ptr = {:#?}, info_ptr = {:#?}, timeout_ptr = {:#?}, sigset_size = {}",
set_ptr, info_ptr, timeout_ptr, sigset_size
);
// Validate sigset size
if sigset_size != size_of::<SigMask>() {
return_errno_with_message!(Errno::EINVAL, "invalid sigset size");
}
// Read the signal set
let mask = {
let mut set: SigSet = ctx.user_space().read_val(set_ptr)?;
// Remove SIGKILL and SIGSTOP as they cannot be waited for
set -= SIGKILL;
set -= SIGSTOP;
!set
};
// Read timeout if provided
let timeout = if timeout_ptr != 0 {
let timespec: timespec_t = ctx.user_space().read_val(timeout_ptr)?;
Some(Duration::try_from(timespec)?)
} else {
None
};
debug!(
"pid = {}, sig_mask = {:?}, timeout = {:?}",
ctx.process.pid(),
mask,
timeout
);
let block_list = ctx.posix_thread.sig_mask().load(Ordering::Relaxed);
// Fast path: If a signal is already pending, dequeue and return it immediately.
if let Some(signal) = dequeue_signal_with_checking_ignore(ctx, mask, block_list) {
if info_ptr != 0 {
let siginfo = signal.to_info();
ctx.user_space().write_val(info_ptr, &siginfo)?;
}
return Ok(SyscallReturn::Return(signal.num().as_u8() as _));
}
with_sigmask_changed(
ctx,
|sig_mask| sig_mask & mask,
|| {
// Wait for a signal to arrive or timeout.
let waiter = Waiter::new_pair().0;
let signal = waiter
.pause_until_or_timeout(
|| dequeue_signal_with_checking_ignore(ctx, mask, block_list),
timeout.map(ManagedTimeout::new),
)
.map_err(|e| {
if e.error() == Errno::ETIME {
Error::new(Errno::EAGAIN)
} else {
e
}
})?;
if info_ptr != 0 {
let siginfo = signal.to_info();
ctx.user_space().write_val(info_ptr, &siginfo)?;
}
Ok(SyscallReturn::Return(signal.num().as_u8() as _))
},
)
}
/// Dequeue a signal from the thread's pending signal queue.
///
/// If the signal is ignored and not blocked, it will be dropped and
/// the next signal will be checked.
fn dequeue_signal_with_checking_ignore(
ctx: &Context,
mask: SigMask,
block_list: SigMask,
) -> Option<Box<dyn Signal>> {
let sig_dispositions = ctx.process.sig_dispositions().lock();
let sig_disposition = sig_dispositions.lock();
while let Some(signal) = ctx.posix_thread.dequeue_signal(&mask) {
// If the signal is ignored and not blocked, the signal will be directly dropped.
if !block_list.contains(signal.num()) && sig_disposition.will_ignore(signal.as_ref()) {
continue;
}
return Some(signal);
}
None
}