@@ -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