Newtype SigalNum (#8096)

* Newtype SigalNum

* impl Index<SigalNum> for SignalHandlersInner
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-06-15 11:12:39 +09:00
1 parent c3e1f0fa37
commit d758d60eb1
5 files changed
+210 -113

No files matched your search

+8
View File
@@ -93,6 +93,7 @@ class PosixTests(unittest.TestCase):
self.assertRaises(TypeError, signal.signal,
signal.SIGUSR1, None)
@unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON; AssertionError: None is not an instance of <enum 'Handlers'>")
def test_getsignal(self):
hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler)
self.assertIsInstance(hup, signal.Handlers)
@@ -101,6 +102,7 @@ class PosixTests(unittest.TestCase):
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 @@ class SiginterruptTest(unittest.TestCase):
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 @@ class StressTest(unittest.TestCase):
"(> 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 @@ class StressTest(unittest.TestCase):
# 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 @@ class StressTest(unittest.TestCase):
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 @@ class RaiseSignalTest(unittest.TestCase):
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 @@ class RaiseSignalTest(unittest.TestCase):
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",
+3 -8
View File
@@ -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<sighandler_t> {
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<sighandler_t> {
}
}
#[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<sighandler_t> {
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<libc::sigse
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn pidfd_send_signal(pidfd: i32, sig: i32, flags: u32) -> 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();
+118 -19
View File
@@ -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<i32> = 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<SignalNum> for i32 {
fn from(signalnum: SignalNum) -> Self {
signalnum.as_i32()
}
}
impl TryFrom<i32> for SignalNum {
type Error = String;
fn try_from(value: i32) -> Result<Self, Self::Error> {
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> {
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<isize> {
if handle == 0 { None } else { Some(handle) }
}
pub struct SignalHandlers(Box<RefCell<[Option<PyObjectRef>; NSIG]>>);
pub struct SignalHandlersInner([Option<PyObjectRef>; NSIG]);
impl Default for SignalHandlersInner {
fn default() -> Self {
Self([const { None }; NSIG])
}
}
impl Index<SignalNum> for SignalHandlersInner {
type Output = Option<PyObjectRef>;
fn index(&self, index: SignalNum) -> &Self::Output {
&self.0[index.as_usize()]
}
}
impl IndexMut<SignalNum> for SignalHandlersInner {
fn index_mut(&mut self, index: SignalNum) -> &mut Self::Output {
&mut self.0[index.as_usize()]
}
}
pub struct SignalHandlers(Box<RefCell<SignalHandlersInner>>);
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<RefCell<[Option<PyObjectRef>; NSIG]>>;
type Target = Box<RefCell<SignalHandlersInner>>;
fn deref(&self) -> &Self::Target {
&self.0
+60 -68
View File
@@ -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<i32> = 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<Option<PyObjectRef>> {
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<PyObjectRef>,
flags: OptionalArg<u32>,
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)?;
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)
}
})?;
// 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());
}
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<Option<String>> {
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<String> {
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::<i32>(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
))
})?;
+21 -18
View File
@@ -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<i32>, vm: &VirtualMachine) -> PyResult<()> {
crate::signal::set_interrupt_ex(signum.unwrap_or(libc::SIGINT), vm)
fn interrupt_main(signum: OptionalArg<SignalNum>) -> PyResult<()> {
let sig = signum.unwrap_or(SignalNum::SIGINT);
crate::signal::set_interrupt_ex(sig)
}
#[pyfunction]