Skip to content

Commit 688cd43

Browse files
committed
signal.pthread_sigmask
1 parent c35cb89 commit 688cd43

File tree

1 file changed

+83
-6
lines changed

1 file changed

+83
-6
lines changed

crates/vm/src/stdlib/signal.rs

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ pub(crate) mod _signal {
6969
#[pyattr]
7070
pub use libc::{SIG_DFL, SIG_IGN};
7171

72+
// pthread_sigmask 'how' constants
73+
#[cfg(unix)]
74+
#[pyattr]
75+
use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};
76+
7277
#[cfg(not(unix))]
7378
#[pyattr]
7479
pub const SIG_DFL: sighandler_t = 0;
@@ -400,14 +405,17 @@ pub(crate) mod _signal {
400405
let set = PySet::default().into_ref(&vm.ctx);
401406
#[cfg(unix)]
402407
{
403-
// On Unix, most signals 1..NSIG are valid
408+
// Use sigfillset to get all valid signals
409+
let mut mask: libc::sigset_t = unsafe { std::mem::zeroed() };
410+
// SAFETY: mask is a valid pointer
411+
if unsafe { libc::sigfillset(&mut mask) } != 0 {
412+
return Err(vm.new_os_error("sigfillset failed".to_owned()));
413+
}
414+
// Convert the filled mask to a Python set
404415
for signum in 1..signal::NSIG {
405-
// Skip signals that cannot be caught
406-
#[cfg(not(target_os = "wasi"))]
407-
if signum == libc::SIGKILL as usize || signum == libc::SIGSTOP as usize {
408-
continue;
416+
if unsafe { libc::sigismember(&mask, signum as i32) } == 1 {
417+
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
409418
}
410-
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
411419
}
412420
}
413421
#[cfg(windows)]
@@ -432,6 +440,75 @@ pub(crate) mod _signal {
432440
Ok(set.into())
433441
}
434442

443+
#[cfg(unix)]
444+
fn sigset_to_pyset(mask: &libc::sigset_t, vm: &VirtualMachine) -> PyResult {
445+
use crate::PyPayload;
446+
use crate::builtins::PySet;
447+
let set = PySet::default().into_ref(&vm.ctx);
448+
for signum in 1..signal::NSIG {
449+
// SAFETY: mask is a valid sigset_t
450+
if unsafe { libc::sigismember(mask, signum as i32) } == 1 {
451+
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
452+
}
453+
}
454+
Ok(set.into())
455+
}
456+
457+
#[cfg(unix)]
458+
#[pyfunction]
459+
fn pthread_sigmask(
460+
how: i32,
461+
mask: crate::function::ArgIterable,
462+
vm: &VirtualMachine,
463+
) -> PyResult {
464+
use crate::convert::IntoPyException;
465+
466+
// Initialize sigset
467+
let mut sigset: libc::sigset_t = unsafe { std::mem::zeroed() };
468+
// SAFETY: sigset is a valid pointer
469+
if unsafe { libc::sigemptyset(&mut sigset) } != 0 {
470+
return Err(std::io::Error::last_os_error().into_pyexception(vm));
471+
}
472+
473+
// Add signals to the set
474+
for sig in mask.iter(vm)? {
475+
let sig = sig?;
476+
// Convert to i32, handling overflow by returning ValueError
477+
let signum: i32 = sig.try_to_value(vm).map_err(|_| {
478+
vm.new_value_error(format!(
479+
"signal number out of range [1, {}]",
480+
signal::NSIG - 1
481+
))
482+
})?;
483+
// Validate signal number is in range [1, NSIG)
484+
if signum < 1 || signum >= signal::NSIG as i32 {
485+
return Err(vm.new_value_error(format!(
486+
"signal number {} out of range [1, {}]",
487+
signum,
488+
signal::NSIG - 1
489+
)));
490+
}
491+
// SAFETY: sigset is a valid pointer and signum is validated
492+
if unsafe { libc::sigaddset(&mut sigset, signum) } != 0 {
493+
return Err(std::io::Error::last_os_error().into_pyexception(vm));
494+
}
495+
}
496+
497+
// Call pthread_sigmask
498+
let mut old_mask: libc::sigset_t = unsafe { std::mem::zeroed() };
499+
// SAFETY: all pointers are valid
500+
let err = unsafe { libc::pthread_sigmask(how, &sigset, &mut old_mask) };
501+
if err != 0 {
502+
return Err(std::io::Error::from_raw_os_error(err).into_pyexception(vm));
503+
}
504+
505+
// Check for pending signals
506+
signal::check_signals(vm)?;
507+
508+
// Convert old mask to Python set
509+
sigset_to_pyset(&old_mask, vm)
510+
}
511+
435512
#[cfg(any(unix, windows))]
436513
pub extern "C" fn run_signal(signum: i32) {
437514
signal::TRIGGERS[signum as usize].store(true, Ordering::Relaxed);

0 commit comments

Comments
 (0)