Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 83 additions & 6 deletions crates/vm/src/stdlib/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ pub(crate) mod _signal {
#[pyattr]
pub use libc::{SIG_DFL, SIG_IGN};

// pthread_sigmask 'how' constants
#[cfg(unix)]
#[pyattr]
use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};

#[cfg(not(unix))]
#[pyattr]
pub const SIG_DFL: sighandler_t = 0;
Expand Down Expand Up @@ -400,14 +405,17 @@ pub(crate) mod _signal {
let set = PySet::default().into_ref(&vm.ctx);
#[cfg(unix)]
{
// On Unix, most signals 1..NSIG are valid
// Use sigfillset to get all valid signals
let mut mask: libc::sigset_t = unsafe { std::mem::zeroed() };
// SAFETY: mask is a valid pointer
if unsafe { libc::sigfillset(&mut mask) } != 0 {
return Err(vm.new_os_error("sigfillset failed".to_owned()));
}
// Convert the filled mask to a Python set
for signum in 1..signal::NSIG {
// Skip signals that cannot be caught
#[cfg(not(target_os = "wasi"))]
if signum == libc::SIGKILL as usize || signum == libc::SIGSTOP as usize {
continue;
if unsafe { libc::sigismember(&mask, signum as i32) } == 1 {
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
}
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
}
}
#[cfg(windows)]
Expand All @@ -432,6 +440,75 @@ pub(crate) mod _signal {
Ok(set.into())
}

#[cfg(unix)]
fn sigset_to_pyset(mask: &libc::sigset_t, vm: &VirtualMachine) -> PyResult {
use crate::PyPayload;
use crate::builtins::PySet;
let set = PySet::default().into_ref(&vm.ctx);
for signum in 1..signal::NSIG {
// SAFETY: mask is a valid sigset_t
if unsafe { libc::sigismember(mask, signum as i32) } == 1 {
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
}
}
Ok(set.into())
}

#[cfg(unix)]
#[pyfunction]
fn pthread_sigmask(
how: i32,
mask: crate::function::ArgIterable,
vm: &VirtualMachine,
) -> PyResult {
use crate::convert::IntoPyException;

// Initialize sigset
let mut sigset: libc::sigset_t = unsafe { std::mem::zeroed() };
// SAFETY: sigset is a valid pointer
if unsafe { libc::sigemptyset(&mut sigset) } != 0 {
return Err(std::io::Error::last_os_error().into_pyexception(vm));
}

// Add signals to the set
for sig in mask.iter(vm)? {
let sig = sig?;
// Convert to i32, handling overflow by returning ValueError
let signum: i32 = sig.try_to_value(vm).map_err(|_| {
vm.new_value_error(format!(
"signal number out of range [1, {}]",
signal::NSIG - 1
))
})?;
// Validate signal number is in range [1, NSIG)
if signum < 1 || signum >= signal::NSIG as i32 {
return Err(vm.new_value_error(format!(
"signal number {} out of range [1, {}]",
signum,
signal::NSIG - 1
)));
}
// SAFETY: sigset is a valid pointer and signum is validated
if unsafe { libc::sigaddset(&mut sigset, signum) } != 0 {
return Err(std::io::Error::last_os_error().into_pyexception(vm));
}
}

// Call pthread_sigmask
let mut old_mask: libc::sigset_t = unsafe { std::mem::zeroed() };
// SAFETY: all pointers are valid
let err = unsafe { libc::pthread_sigmask(how, &sigset, &mut old_mask) };
if err != 0 {
return Err(std::io::Error::from_raw_os_error(err).into_pyexception(vm));
}

// Check for pending signals
signal::check_signals(vm)?;

// Convert old mask to Python set
sigset_to_pyset(&old_mask, vm)
}

#[cfg(any(unix, windows))]
pub extern "C" fn run_signal(signum: i32) {
signal::TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
Expand Down
Loading