diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 07fc97cb6a1..a4af92d52eb 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -93,6 +93,7 @@ def test_setting_signal_handler_to_none_raises_error(self): self.assertRaises(TypeError, signal.signal, signal.SIGUSR1, None) + @unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON; AssertionError: None is not an instance of ") def test_getsignal(self): hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler) self.assertIsInstance(hup, signal.Handlers) @@ -101,6 +102,7 @@ def test_getsignal(self): signal.signal(signal.SIGHUP, hup) self.assertEqual(signal.getsignal(signal.SIGHUP), hup) + @unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON") def test_no_repr_is_called_on_signal_handler(self): # See https://github.com/python/cpython/issues/112559. @@ -778,6 +780,7 @@ def test_siginterrupt_off(self): self.assertFalse(interrupted) +@unittest.skipIf(sys.platform == "android", "TODO: RUSTPYTHON; Error during teardown") @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @unittest.skipUnless(hasattr(signal, 'getitimer') and hasattr(signal, 'setitimer'), "needs signal.getitimer() and signal.setitimer()") @@ -1264,6 +1267,7 @@ def decide_itimer_count(self): "(> 10 ms.) on this platform (or system too busy)" % (reso,)) + @unittest.skipIf(sys.platform == "android", "TODO: RUSTPYTHON; TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object") @unittest.skipUnless(hasattr(signal, "setitimer"), "test needs setitimer()") def test_stress_delivery_dependent(self): @@ -1310,6 +1314,7 @@ def second_handler(signum=None, frame=None): # Python handler self.assertEqual(len(sigs), N, "Some signals were lost") + @unittest.skipIf(sys.platform == "android", "TODO: RUSTPYTHON; TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object") @unittest.skipUnless(hasattr(signal, "setitimer"), "test needs setitimer()") def test_stress_delivery_simultaneous(self): @@ -1409,6 +1414,7 @@ def cycle_handlers(): class RaiseSignalTest(unittest.TestCase): + @unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON; AssertionError: KeyboardInterrupt not raised") def test_sigint(self): with self.assertRaises(KeyboardInterrupt): signal.raise_signal(signal.SIGINT) @@ -1425,6 +1431,7 @@ def test_invalid_argument(self): else: raise + @unittest.skipIf(sys.platform == "android", "TODO: RUSTPYTHON; TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object") def test_handler(self): is_ok = False def handler(a, b): @@ -1455,6 +1462,7 @@ def __del__(self): class PidfdSignalTest(unittest.TestCase): + @unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON; AssertionError: KeyboardInterrupt not raised") @unittest.skipUnless( hasattr(signal, "pidfd_send_signal"), "pidfd support not built in", diff --git a/crates/host_env/src/signal.rs b/crates/host_env/src/signal.rs index f9f2a8206b9..ab1974f37df 100644 --- a/crates/host_env/src/signal.rs +++ b/crates/host_env/src/signal.rs @@ -55,10 +55,10 @@ mod ffi { } } -#[cfg(any(unix, windows))] /// # Safety /// /// The caller must ensure `signalnum` is a valid platform signal number. +#[cfg(any(unix, windows))] pub unsafe fn probe_handler(signalnum: i32) -> Option { let handler = unsafe { libc::signal(signalnum, libc::SIG_IGN) }; if handler == libc::SIG_ERR as sighandler_t { @@ -69,11 +69,11 @@ pub unsafe fn probe_handler(signalnum: i32) -> Option { } } -#[cfg(any(unix, windows))] /// # Safety /// /// The caller must ensure `signalnum` is a valid platform signal number and /// `handler` is accepted by the platform signal ABI. +#[cfg(any(unix, windows))] pub unsafe fn install_handler(signalnum: i32, handler: sighandler_t) -> io::Result { let old = unsafe { libc::signal(signalnum, handler) }; if old == libc::SIG_ERR as sighandler_t { @@ -158,7 +158,7 @@ pub fn pthread_sigmask(how: i32, set: &libc::sigset_t) -> io::Result io::Result<()> { let ret = unsafe { libc::syscall( @@ -199,11 +199,6 @@ pub const CTRL_BREAK_EVENT: u32 = 1; #[cfg(windows)] pub const INVALID_SOCKET: libc::SOCKET = windows_sys::Win32::Networking::WinSock::INVALID_SOCKET; -#[cfg(windows)] -pub fn is_valid_signal(signalnum: i32) -> bool { - VALID_SIGNALS.contains(&signalnum) -} - #[cfg(windows)] fn init_winsock() { static WSA_INIT: Once = Once::new(); diff --git a/crates/vm/src/signal.rs b/crates/vm/src/signal.rs index 11fe4f21271..eea42f4a87e 100644 --- a/crates/vm/src/signal.rs +++ b/crates/vm/src/signal.rs @@ -1,8 +1,7 @@ -use crate::{PyObjectRef, PyResult, VirtualMachine}; use core::{ cell::{Cell, RefCell}, fmt, - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, Index, IndexMut, Range}, sync::atomic::{AtomicBool, Ordering}, }; use std::sync::mpsc; @@ -10,10 +9,12 @@ use std::sync::mpsc; #[cfg(windows)] use core::sync::atomic::AtomicIsize; -static ANY_TRIGGERED: AtomicBool = AtomicBool::new(false); +use crate::{PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine}; pub(crate) const NSIG: usize = 64; +static ANY_TRIGGERED: AtomicBool = AtomicBool::new(false); + #[expect( clippy::declare_interior_mutable_const, reason = "workaround for const array repeat limitation (rust issue #79270)" @@ -70,22 +71,32 @@ fn trigger_signals(vm: &VirtualMachine) -> PyResult<()> { } let _guard = SignalHandlerGuard; - // unwrap should never fail since we check above - let signal_handlers = vm.signal_handlers.get().unwrap().borrow(); + let signal_handlers = vm + .signal_handlers + .get() + .expect("should never fail since we check above") + .borrow(); + for (signum, trigger) in TRIGGERS.iter().enumerate().skip(1) { let triggered = trigger.swap(false, Ordering::Relaxed); + + // SAFETY: TRIGGERS has the same length as the signal_handlers + let signum = unsafe { SignalNum::new_unchecked(signum as i32) }; + if triggered && let Some(handler) = &signal_handlers[signum] && let Some(callable) = handler.to_callable() { - callable.invoke((signum, vm.ctx.none()), vm)?; + callable.invoke((signum.as_i32(), vm.ctx.none()), vm)?; } } + if let Some(signal_rx) = &vm.signal_rx { for f in signal_rx.rx.try_iter() { f(vm)?; } } + Ok(()) } @@ -109,28 +120,94 @@ pub(crate) fn clear_after_fork() { } } -pub fn assert_in_range(signum: i32, vm: &VirtualMachine) -> PyResult<()> { - if (1..NSIG as i32).contains(&signum) { - Ok(()) - } else { - Err(vm.new_value_error("signal number out of range")) +/// A valid signal number. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct SignalNum(i32); + +impl SignalNum { + pub(crate) const VALID_RANGE: Range = 1..NSIG as i32; + + /// Alias for: + /// ```rust + /// # use rustpython_vm::signal::SignalNum; + /// + /// unsafe { SignalNum::new_unchecked(libc::SIGINT) }; + /// ``` + #[cfg(any(unix, windows))] + #[allow(dead_code, reason = "Not used on all platforms")] + pub(crate) const SIGINT: Self = Self(libc::SIGINT); + + /// Construct [`Self`] without any validation on the signalnum value. + /// + /// # Safety + /// + /// Caller's responsibility to ensure the signal num is valid. + #[must_use] + pub const unsafe fn new_unchecked(value: i32) -> Self { + Self(value) + } + + /// Get the self as an [`i32`]. + #[must_use] + pub const fn as_i32(&self) -> i32 { + self.0 + } + + /// Get the self as an [`usize`]. + #[must_use] + pub const fn as_usize(&self) -> usize { + self.0 as usize + } +} + +impl fmt::Display for SignalNum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl From for i32 { + fn from(signalnum: SignalNum) -> Self { + signalnum.as_i32() + } +} + +impl TryFrom for SignalNum { + type Error = String; + + fn try_from(value: i32) -> Result { + let bounds = cfg_select! { + all(windows, feature = "host_env") => rustpython_host_env::signal::VALID_SIGNALS, + _ => Self::VALID_RANGE, + }; + + if bounds.contains(&value) { + Ok(Self(value)) + } else { + Err("signal number out of range".into()) + } + } +} + +impl TryFromObject for SignalNum { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + Self::try_from(i32::try_from_borrowed_object(vm, &obj)?) + .map_err(|msg| vm.new_value_error(msg)) } } /// Similar to `PyErr_SetInterruptEx` in CPython /// /// Missing signal handler for the given signal number is silently ignored. -#[allow(dead_code)] #[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))] -pub fn set_interrupt_ex(signum: i32, vm: &VirtualMachine) -> PyResult<()> { +pub fn set_interrupt_ex(signum: SignalNum) -> PyResult<()> { use crate::stdlib::_signal::_signal::{SIG_DFL, SIG_IGN, run_signal}; - assert_in_range(signum, vm)?; - match signum as usize { + match signum.as_usize() { SIG_DFL | SIG_IGN => Ok(()), _ => { // interrupt the main thread with given signal number - run_signal(signum); + run_signal(signum.into()); Ok(()) } } @@ -190,16 +267,38 @@ pub fn get_sigint_event() -> Option { if handle == 0 { None } else { Some(handle) } } -pub struct SignalHandlers(Box; NSIG]>>); +pub struct SignalHandlersInner([Option; NSIG]); + +impl Default for SignalHandlersInner { + fn default() -> Self { + Self([const { None }; NSIG]) + } +} + +impl Index for SignalHandlersInner { + type Output = Option; + + fn index(&self, index: SignalNum) -> &Self::Output { + &self.0[index.as_usize()] + } +} + +impl IndexMut for SignalHandlersInner { + fn index_mut(&mut self, index: SignalNum) -> &mut Self::Output { + &mut self.0[index.as_usize()] + } +} + +pub struct SignalHandlers(Box>); impl Default for SignalHandlers { fn default() -> Self { - Self(Box::new(const { RefCell::new([const { None }; NSIG]) })) + Self(Box::new(RefCell::new(SignalHandlersInner::default()))) } } impl Deref for SignalHandlers { - type Target = Box; NSIG]>>; + type Target = Box>; fn deref(&self) -> &Self::Target { &self.0 diff --git a/crates/vm/src/stdlib/_signal.rs b/crates/vm/src/stdlib/_signal.rs index 2d9eaa53a28..e3d12568d26 100644 --- a/crates/vm/src/stdlib/_signal.rs +++ b/crates/vm/src/stdlib/_signal.rs @@ -8,12 +8,9 @@ pub(crate) mod _signal { use crate::{ Py, PyObjectRef, PyResult, VirtualMachine, - signal::{self, SignalHandlers}, - }; - use core::{ - ops::Range, - sync::atomic::{self, Ordering}, + signal::{self, SignalHandlers, SignalNum}, }; + use core::sync::atomic::{self, Ordering}; cfg_select! { any(unix, windows) => { @@ -42,8 +39,6 @@ pub(crate) mod _signal { _ => usize, }; - const SIGNUM_RANGE: Range = 1..signal::NSIG as i32; - cfg_select! { windows => { type WakeupFdRaw = libc::SOCKET; @@ -94,9 +89,11 @@ pub(crate) mod _signal { #[cfg(not(unix))] #[pyattr] pub const SIG_DFL: sighandler_t = 0; + #[cfg(not(unix))] #[pyattr] pub const SIG_IGN: sighandler_t = 1; + #[cfg(not(unix))] #[allow(dead_code)] pub const SIG_ERR: sighandler_t = -1 as _; @@ -168,11 +165,12 @@ pub(crate) mod _signal { #[cfg(unix)] fn new_itimer_error(msg: &str, vm: &VirtualMachine) -> PyBaseExceptionRef { - vm.new_exception_msg(itimer_error(vm), msg.into()) + vm.new_os_subtype_error(itimer_error(vm), None, msg) + .upcast() } - const _: () = assert!(SIGNUM_RANGE.start.is_positive()); - const _: () = assert!(SIGNUM_RANGE.end.is_positive()); + const _: () = assert!(SignalNum::VALID_RANGE.start.is_positive()); + const _: () = assert!(SignalNum::VALID_RANGE.end.is_positive()); #[cfg(any(unix, windows))] pub(super) fn init_signal_handlers( @@ -183,7 +181,7 @@ pub(crate) mod _signal { let sig_dfl = vm.new_pyobj(SIG_DFL as u8); let sig_ign = vm.new_pyobj(SIG_IGN as u8); - for signum in SIGNUM_RANGE { + for signum in SignalNum::VALID_RANGE { let Some(handler) = (unsafe { host_signal::probe_handler(signum) }) else { continue; }; @@ -195,15 +193,19 @@ pub(crate) mod _signal { None }; + // SAFETY: Trust `SignalNum::VALID_RANGE` + let signum = unsafe { SignalNum::new_unchecked(signum) }; + vm.signal_handlers .get_or_init(SignalHandlers::default) - .borrow_mut()[signum as usize] = py_handler; + .borrow_mut()[signum] = py_handler; } let int_handler = module .get_attr("default_int_handler", vm) .expect("_signal does not have this attr?"); - signal(libc::SIGINT, int_handler, vm).expect("Failed to set sigint handler"); + + signal(SignalNum::SIGINT, int_handler, vm).expect("Failed to set sigint handler"); } } @@ -220,49 +222,44 @@ pub(crate) mod _signal { #[cfg(any(unix, windows))] #[pyfunction] pub fn signal( - signalnum: i32, + signalnum: SignalNum, handler: PyObjectRef, vm: &VirtualMachine, ) -> PyResult> { - signal::assert_in_range(signalnum, vm)?; - - #[cfg(windows)] - if !host_signal::is_valid_signal(signalnum) { - return Err(vm.new_value_error(format!("signal number {signalnum} out of range"))); - } - if !vm.is_main_thread() { - return Err(vm.new_value_error("signal only works in main thread")); + return Err( + vm.new_value_error("signal only works in main thread of the main interpreter") + ); } - let sig_handler = - match usize::try_from_borrowed_object(vm, &handler).ok() { - Some(SIG_DFL) => SIG_DFL, - Some(SIG_IGN) => SIG_IGN, - None if handler.is_callable() => run_signal as *const () as sighandler_t, - _ => return Err(vm.new_type_error( - "signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object", - )), - }; + let sig_handler = if handler.is_callable() { + run_signal as *const () as sighandler_t + } else { + const MSG: &str = + "signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"; + + usize::try_from_borrowed_object(vm, &handler) + .ok() + .filter(|&v| matches!(v, SIG_DFL | SIG_IGN)) + .ok_or_else(|| vm.new_type_error(MSG))? + }; signal::check_signals(vm)?; - unsafe { host_signal::install_handler(signalnum, sig_handler) } + unsafe { host_signal::install_handler(signalnum.into(), sig_handler) } .map_err(|_| vm.new_os_error("Failed to set signal"))?; let signal_handlers = vm.signal_handlers.get_or_init(SignalHandlers::default); - let old_handler = signal_handlers.borrow_mut()[signalnum as usize].replace(handler); + let old_handler = signal_handlers.borrow_mut()[signalnum].replace(handler); Ok(old_handler) } #[pyfunction] - fn getsignal(signalnum: i32, vm: &VirtualMachine) -> PyResult { - signal::assert_in_range(signalnum, vm)?; + fn getsignal(signalnum: SignalNum, vm: &VirtualMachine) -> PyObjectRef { let signal_handlers = vm.signal_handlers.get_or_init(SignalHandlers::default); - let handler = signal_handlers.borrow()[signalnum as usize] + signal_handlers.borrow()[signalnum] .clone() - .unwrap_or_else(|| vm.ctx.none()); - Ok(handler) + .unwrap_or_else(|| vm.ctx.none()) } #[cfg(unix)] @@ -274,7 +271,7 @@ pub(crate) mod _signal { #[cfg(unix)] #[pyfunction] fn pause(vm: &VirtualMachine) -> PyResult<()> { - host_signal::pause(); + vm.allow_threads(host_signal::pause); signal::check_signals(vm)?; Ok(()) } @@ -332,7 +329,9 @@ pub(crate) mod _signal { }; if !vm.is_main_thread() { - return Err(vm.new_value_error("set_wakeup_fd only works in main thread")); + return Err(vm.new_value_error( + "set_wakeup_fd only works in main thread of the main interpreter", + )); } #[cfg(windows)] @@ -371,16 +370,15 @@ pub(crate) mod _signal { Ok(old_fd as i64) } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "android", target_os = "linux"))] #[pyfunction] fn pidfd_send_signal( pidfd: i32, - sig: i32, + sig: SignalNum, siginfo: OptionalArg, flags: OptionalArg, vm: &VirtualMachine, ) -> PyResult<()> { - signal::assert_in_range(sig, vm)?; if let OptionalArg::Present(obj) = siginfo && !vm.is_none(&obj) { @@ -388,33 +386,33 @@ pub(crate) mod _signal { } let flags = flags.unwrap_or(0); - host_signal::pidfd_send_signal(pidfd, sig, flags).map_err(|_| vm.new_last_errno_error()) + host_signal::pidfd_send_signal(pidfd, sig.into(), flags) + .map_err(|_| vm.new_last_errno_error()) } #[cfg(all(unix, not(target_os = "redox")))] #[pyfunction(name = "siginterrupt")] - fn py_siginterrupt(signum: i32, flag: i32, vm: &VirtualMachine) -> PyResult<()> { - signal::assert_in_range(signum, vm)?; - host_signal::siginterrupt(signum, flag).map_err(|_| vm.new_last_errno_error()) + fn py_siginterrupt(signum: SignalNum, flag: i32, vm: &VirtualMachine) -> PyResult<()> { + host_signal::siginterrupt(signum.into(), flag).map_err(|_| vm.new_last_errno_error()) } #[cfg(any(unix, windows))] #[pyfunction] fn raise_signal(signalnum: i32, vm: &VirtualMachine) -> PyResult<()> { - signal::assert_in_range(signalnum, vm)?; - - // On Windows, only certain signals are supported - #[cfg(windows)] - if !host_signal::is_valid_signal(signalnum) { - return Err(vm - .new_errno_error(libc::EINVAL, "Invalid argument") - .upcast()); - } + let signalnum = SignalNum::try_from(signalnum).map_err(cfg_select! { + windows => { + |_| vm.new_errno_error(libc::EINVAL, "Invalid argument").upcast() + }, + _ => { + |msg| vm.new_value_error(msg) + } + })?; - host_signal::raise_signal(signalnum) + vm.allow_threads(|| host_signal::raise_signal(signalnum.into())) .map_err(|_| vm.new_os_error(format!("raise_signal failed for signal {signalnum}")))?; // Check if a signal was triggered and handle it + signal::check_signals(vm)?; Ok(()) @@ -422,12 +420,8 @@ pub(crate) mod _signal { #[cfg(any(unix, windows))] #[pyfunction] - fn strsignal(signalnum: i32, vm: &VirtualMachine) -> PyResult> { - if !SIGNUM_RANGE.contains(&signalnum) { - return Err(vm.new_value_error(format!("signal number {signalnum} out of range"))); - } - - Ok(host_signal::strsignal(signalnum)) + fn strsignal(signalnum: SignalNum) -> Option { + host_signal::strsignal(signalnum.into()) } #[pyfunction] @@ -452,7 +446,7 @@ pub(crate) mod _signal { use crate::PyPayload; use crate::builtins::PySet; let set = PySet::default().into_ref(&vm.ctx); - for signum in SIGNUM_RANGE { + for signum in SignalNum::VALID_RANGE { if host_signal::sigset_contains(mask, signum) { set.add(vm.ctx.new_int(signum).into(), vm)?; } @@ -467,8 +461,6 @@ pub(crate) mod _signal { mask: crate::function::ArgIterable, vm: &VirtualMachine, ) -> PyResult { - use crate::convert::IntoPyException; - // Initialize sigset let mut sigset = host_signal::sigemptyset().map_err(|e| e.into_pyexception(vm))?; @@ -481,11 +473,11 @@ pub(crate) mod _signal { let signum = sig .try_to_value::(vm) .ok() - .filter(|v| SIGNUM_RANGE.contains(v)) + .filter(|v| SignalNum::VALID_RANGE.contains(v)) .ok_or_else(|| { vm.new_value_error(format!( "signal number out of range [1, {}]", - SIGNUM_RANGE.end - 1 + SignalNum::VALID_RANGE.end - 1 )) })?; diff --git a/crates/vm/src/stdlib/_thread.rs b/crates/vm/src/stdlib/_thread.rs index 93d42676b4e..ae612e4d539 100644 --- a/crates/vm/src/stdlib/_thread.rs +++ b/crates/vm/src/stdlib/_thread.rs @@ -13,6 +13,11 @@ pub(crate) use _thread::{ #[pymodule] pub(crate) mod _thread { + use parking_lot::{ + RawMutex, RawThreadId, + lock_api::{RawMutex as RawMutexT, RawMutexTimed, RawReentrantMutex}, + }; + use crate::{ AsObject, Py, PyPayload, PyRef, PyResult, VirtualMachine, builtins::{PyDictRef, PyIntRef, PyStr, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef}, @@ -21,19 +26,17 @@ pub(crate) mod _thread { function::{ArgCallable, FuncArgs, KwArgs, OptionalArg, PySetterValue, TimeoutSeconds}, types::{Constructor, GetAttr, Representable, SetAttr}, }; - use alloc::{ - fmt, - sync::{Arc, Weak}, - }; - use core::{cell::RefCell, time::Duration}; - use parking_lot::{ - RawMutex, RawThreadId, - lock_api::{RawMutex as RawMutexT, RawMutexTimed, RawReentrantMutex}, - }; + + use alloc::sync::{Arc, Weak}; + use core::{cell::RefCell, fmt, time::Duration}; + use std::thread; + use rustpython_common::str::levenshtein::{MOVE_COST, levenshtein_distance}; #[cfg(any(unix, windows))] use rustpython_host_env::thread as host_thread; - use std::thread; + + #[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))] + use crate::signal::SignalNum; // PYTHREAD_NAME: show current thread name pub(crate) const PYTHREAD_NAME: Option<&str> = cfg_select! { @@ -43,12 +46,11 @@ pub(crate) mod _thread { _ => None, }; - // TIMEOUT_MAX_IN_MICROSECONDS is a value in microseconds - #[cfg(not(target_os = "windows"))] - const TIMEOUT_MAX_IN_MICROSECONDS: i64 = i64::MAX / 1_000; - - #[cfg(target_os = "windows")] - const TIMEOUT_MAX_IN_MICROSECONDS: i64 = 0xffffffff * 1_000; + const TIMEOUT_MAX_IN_MICROSECONDS: i64 = if cfg!(target_os = "windows") { + 0xffffffff * 1_000 + } else { + i64::MAX / 1_000 + }; /// [CPython `SYSTEM_PAGE_SIZE`](https://github.com/python/cpython/blob/v3.14.5/Include/internal/pycore_obmalloc.h#L170) const SYSTEM_PAGE_SIZE: usize = 4 * 1024; @@ -614,8 +616,9 @@ pub(crate) mod _thread { #[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))] #[pyfunction] - fn interrupt_main(signum: OptionalArg, vm: &VirtualMachine) -> PyResult<()> { - crate::signal::set_interrupt_ex(signum.unwrap_or(libc::SIGINT), vm) + fn interrupt_main(signum: OptionalArg) -> PyResult<()> { + let sig = signum.unwrap_or(SignalNum::SIGINT); + crate::signal::set_interrupt_ex(sig) } #[pyfunction]