mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
fix: signal.SIGINT and signal() raise NotImplementedError on wasi (#8986)
* fix: signal.SIGINT and signal() raise NotImplementedError on wasi - Problem: signal.SIGINT and friends don't exist on wasi, and signal.signal() is hard-stubbed to raise NotImplementedError there - Cause: wasi-libc emulates signal()/raise() in a separate opt-in library that Rust's own wasm32-wasip1 target distribution doesn't ship - Fix: vendor wasi-sdk's prebuilt libwasi-emulated-signal.a, link it for wasm32-wasip1, and wire SIGINT/SIGABRT/SIGFPE/SIGILL/SIGSEGV/SIGTERM, signal()/getsignal()/raise_signal() through it. CPython's own WASI build links the same library Assisted-by: Claude Code:claude-sonnet-5 Signed-off-by: Jiwoo Ahn <ikwydls1314@gmail.com> * Group WASI signal definitions in mod wasm Move WASI-only types, constants, FFI, and handlers into `mod wasm` and re-export them with `#[cfg(target_os = "wasi")]`. Assisted-by: Grok 4.6 --------- Signed-off-by: Jiwoo Ahn <ikwydls1314@gmail.com> Co-authored-by: Jeong YunWon <jeong@youknowone.org>
This commit is contained in:
1 parent
a403341791
commit
ef5c651958
8 files changed
+114
-20
No files matched your search
@@ -10,9 +10,12 @@ rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]
|
||||
# Enforce a 1 MB stack limit for WASI targets.
|
||||
# (Runaway heap growth is fixed by the smaller DataStack chunk
|
||||
# and the size‑optimized wasm‑release profile.)
|
||||
|
||||
[target.wasm32-wasip1]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-zstack-size=1048576",
|
||||
"-L", "crates/host_env/vendor/wasm32-wasip1",
|
||||
"-l", "wasi-emulated-signal",
|
||||
]
|
||||
|
||||
[target.wasm32-wasip2]
|
||||
|
||||
@@ -74,7 +74,7 @@ pub mod pwd;
|
||||
pub mod resource;
|
||||
#[cfg(all(unix, not(target_os = "redox"), not(target_os = "android")))]
|
||||
pub mod shm;
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
pub mod signal;
|
||||
pub mod time;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[cfg(any(unix, windows))]
|
||||
use std::io;
|
||||
#[cfg(windows)]
|
||||
use std::sync::Once;
|
||||
@@ -13,13 +14,83 @@ pub use libc::sighandler_t;
|
||||
#[cfg(unix)]
|
||||
pub use libc::{SIG_DFL, SIG_ERR, SIG_IGN};
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(not(any(unix, target_os = "wasi")))]
|
||||
pub const SIG_DFL: libc::sighandler_t = 0;
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(not(any(unix, target_os = "wasi")))]
|
||||
pub const SIG_IGN: libc::sighandler_t = 1;
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(not(any(unix, target_os = "wasi")))]
|
||||
pub const SIG_ERR: libc::sighandler_t = -1 as _;
|
||||
|
||||
/// wasi-libc's userspace emulation of `signal()`/`raise()`, linked from
|
||||
/// `libwasi-emulated-signal` (see `crates/host_env/vendor/wasm32-wasip1`).
|
||||
/// WebAssembly has no asynchronous signal delivery, so this is a
|
||||
/// synchronous, in-process handler table: a handler only runs when this
|
||||
/// process's own code calls `raise()`, never from an outside interrupt.
|
||||
/// Numbering matches wasi-libc's `bits/signal.h`, the same values CPython's
|
||||
/// WASI build gets by linking the same library.
|
||||
#[cfg(target_os = "wasi")]
|
||||
mod wasm {
|
||||
use std::io;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type sighandler_t = usize;
|
||||
|
||||
pub const SIG_DFL: sighandler_t = 0;
|
||||
pub const SIG_IGN: sighandler_t = 1;
|
||||
pub const SIG_ERR: sighandler_t = -1isize as usize;
|
||||
|
||||
pub const SIGINT: i32 = 2;
|
||||
pub const SIGILL: i32 = 4;
|
||||
pub const SIGABRT: i32 = 6;
|
||||
pub const SIGFPE: i32 = 8;
|
||||
pub const SIGSEGV: i32 = 11;
|
||||
pub const SIGTERM: i32 = 15;
|
||||
|
||||
unsafe extern "C" {
|
||||
fn signal(signum: i32, handler: sighandler_t) -> sighandler_t;
|
||||
fn raise(signum: i32) -> i32;
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `signalnum` is a valid platform signal number.
|
||||
pub unsafe fn probe_handler(signalnum: i32) -> Option<sighandler_t> {
|
||||
let handler = unsafe { signal(signalnum, SIG_IGN) };
|
||||
if handler == SIG_ERR {
|
||||
None
|
||||
} else {
|
||||
unsafe { signal(signalnum, handler) };
|
||||
Some(handler)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `signalnum` is a valid platform signal number and
|
||||
/// `handler` is accepted by the platform signal ABI.
|
||||
pub unsafe fn install_handler(
|
||||
signalnum: i32,
|
||||
handler: sighandler_t,
|
||||
) -> io::Result<sighandler_t> {
|
||||
let old = unsafe { signal(signalnum, handler) };
|
||||
if old == SIG_ERR {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(old)
|
||||
}
|
||||
|
||||
pub fn raise_signal(signalnum: i32) -> io::Result<()> {
|
||||
if unsafe { raise(signalnum) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
pub use wasm::*;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};
|
||||
|
||||
@@ -320,7 +391,7 @@ pub fn notify_signal(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
pub fn notify_signal(signum: i32, wakeup_fd: i32) {
|
||||
if wakeup_fd == -1 {
|
||||
return;
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Vendored wasi-libc emulation library
|
||||
|
||||
`libwasi-emulated-signal.a` is a prebuilt static library from
|
||||
[wasi-sdk](https://github.com/WebAssembly/wasi-sdk), providing wasi-libc's
|
||||
userspace emulation of `signal()`/`raise()` for `wasm32-wasip1` (WebAssembly
|
||||
has no asynchronous signal delivery, so this is a synchronous, in-process
|
||||
handler table, not a real OS signal mechanism).
|
||||
|
||||
CPython's own WASI build links the same library
|
||||
(`configure.ac`'s `_WASI_EMULATED_SIGNAL` case).
|
||||
|
||||
- Source: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sysroot-34.0.tar.gz
|
||||
(path: `lib/wasm32-wasip1/libwasi-emulated-signal.a`)
|
||||
- License: Apache-2.0 WITH LLVM-exception (same as wasi-libc)
|
||||
- sha256: see below
|
||||
becc35b856608fb9282d94c70166796e4d7b21e4e1057e625725306e765eb035 crates/host_env/vendor/wasm32-wasip1/libwasi-emulated-signal.a
|
||||
Binary file not shown.
@@ -233,6 +233,10 @@ impl SignalNum {
|
||||
#[allow(dead_code, reason = "Not used on all platforms")]
|
||||
pub(crate) const SIGINT: Self = Self(libc::SIGINT);
|
||||
|
||||
#[cfg(all(target_os = "wasi", feature = "host_env"))]
|
||||
#[allow(dead_code, reason = "Not used on all platforms")]
|
||||
pub(crate) const SIGINT: Self = Self(rustpython_host_env::signal::SIGINT);
|
||||
|
||||
/// Construct [`Self`] without any validation on the signalnum value.
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
@@ -13,7 +13,7 @@ pub(crate) mod _signal {
|
||||
use core::sync::atomic::{self, Ordering};
|
||||
|
||||
cfg_select! {
|
||||
any(unix, windows) => {
|
||||
any(unix, windows, target_os = "wasi") => {
|
||||
use crate::convert::{IntoPyException, TryFromBorrowedObject};
|
||||
use rustpython_host_env::signal as host_signal;
|
||||
}
|
||||
@@ -73,11 +73,11 @@ pub(crate) mod _signal {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
#[allow(unused_imports)]
|
||||
pub use host_signal::SIG_ERR;
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
#[pyattr]
|
||||
pub use host_signal::{SIG_DFL, SIG_IGN};
|
||||
|
||||
@@ -86,22 +86,22 @@ pub(crate) mod _signal {
|
||||
#[pyattr]
|
||||
use host_signal::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
#[cfg(not(any(unix, windows, target_os = "wasi")))]
|
||||
#[pyattr]
|
||||
pub const SIG_DFL: sighandler_t = 0;
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
#[cfg(not(any(unix, windows, target_os = "wasi")))]
|
||||
#[pyattr]
|
||||
pub const SIG_IGN: sighandler_t = 1;
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
#[cfg(not(any(unix, windows, target_os = "wasi")))]
|
||||
#[allow(dead_code)]
|
||||
pub const SIG_ERR: sighandler_t = -1 as _;
|
||||
|
||||
#[pyattr]
|
||||
use crate::signal::NSIG;
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
#[pyattr]
|
||||
pub use host_signal::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
|
||||
|
||||
@@ -160,7 +160,7 @@ pub(crate) mod _signal {
|
||||
const _: () = assert!(SignalNum::VALID_RANGE.start.is_positive());
|
||||
const _: () = assert!(SignalNum::VALID_RANGE.end.is_positive());
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
pub(super) fn init_signal_handlers(
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
vm: &VirtualMachine,
|
||||
@@ -199,7 +199,7 @@ pub(crate) mod _signal {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
#[cfg(not(any(unix, windows, target_os = "wasi")))]
|
||||
#[pyfunction]
|
||||
pub fn signal(
|
||||
_signalnum: i32,
|
||||
@@ -209,7 +209,7 @@ pub(crate) mod _signal {
|
||||
Err(vm.new_not_implemented_error("signal is not implemented on this platform"))
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
#[pyfunction]
|
||||
pub fn signal(
|
||||
signalnum: SignalNum,
|
||||
@@ -401,7 +401,7 @@ pub(crate) mod _signal {
|
||||
host_signal::siginterrupt(signalnum.into(), flag).map_err(|_| vm.new_last_errno_error())
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
#[pyfunction]
|
||||
fn raise_signal(signalnum: i32, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let signalnum = SignalNum::try_from(signalnum).map_err(cfg_select! {
|
||||
@@ -506,7 +506,7 @@ pub(crate) mod _signal {
|
||||
sigset_to_pyset(old_mask, vm)
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
pub extern "C" fn run_signal(signum: i32) {
|
||||
signal::TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
|
||||
signal::set_triggered();
|
||||
@@ -535,7 +535,7 @@ pub(crate) mod _signal {
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[cfg(any(unix, windows, target_os = "wasi"))]
|
||||
init_signal_handlers(module, vm);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -21,8 +21,8 @@ assert old_signal is signal.SIG_IGN
|
||||
assert signal.getsignal(signal.SIGILL) is signal.SIG_DFL
|
||||
|
||||
|
||||
# unix
|
||||
if "win" not in sys.platform:
|
||||
# unix, and not WASI
|
||||
if "win" not in sys.platform and sys.platform != "wasi":
|
||||
signal.signal(signal.SIGALRM, handler)
|
||||
assert signal.getsignal(signal.SIGALRM) is handler
|
||||
|
||||
|
||||
Reference in new issue
Block a user