1#![cfg_attr(target_os = "wasi", allow(dead_code))]
2use crate::{PyResult, VirtualMachine};
3use std::{
4 fmt,
5 sync::{
6 atomic::{AtomicBool, Ordering},
7 mpsc,
8 },
9};
10
11pub(crate) const NSIG: usize = 64;
12static ANY_TRIGGERED: AtomicBool = AtomicBool::new(false);
13#[allow(clippy::declare_interior_mutable_const)]
15const ATOMIC_FALSE: AtomicBool = AtomicBool::new(false);
16pub(crate) static TRIGGERS: [AtomicBool; NSIG] = [ATOMIC_FALSE; NSIG];
17
18#[cfg_attr(feature = "flame-it", flame)]
19#[inline(always)]
20pub fn check_signals(vm: &VirtualMachine) -> PyResult<()> {
21 if vm.signal_handlers.is_none() {
22 return Ok(());
23 }
24
25 if !ANY_TRIGGERED.swap(false, Ordering::Acquire) {
26 return Ok(());
27 }
28
29 trigger_signals(vm)
30}
31#[inline(never)]
32#[cold]
33fn trigger_signals(vm: &VirtualMachine) -> PyResult<()> {
34 let signal_handlers = vm.signal_handlers.as_ref().unwrap().borrow();
36 for (signum, trigger) in TRIGGERS.iter().enumerate().skip(1) {
37 let triggered = trigger.swap(false, Ordering::Relaxed);
38 if triggered {
39 if let Some(handler) = &signal_handlers[signum] {
40 if let Some(callable) = handler.to_callable() {
41 callable.invoke((signum, vm.ctx.none()), vm)?;
42 }
43 }
44 }
45 }
46 if let Some(signal_rx) = &vm.signal_rx {
47 for f in signal_rx.rx.try_iter() {
48 f(vm)?;
49 }
50 }
51 Ok(())
52}
53
54pub(crate) fn set_triggered() {
55 ANY_TRIGGERED.store(true, Ordering::Release);
56}
57
58pub fn assert_in_range(signum: i32, vm: &VirtualMachine) -> PyResult<()> {
59 if (1..NSIG as i32).contains(&signum) {
60 Ok(())
61 } else {
62 Err(vm.new_value_error("signal number out of range".to_owned()))
63 }
64}
65
66#[allow(dead_code)]
70#[cfg(not(target_arch = "wasm32"))]
71pub fn set_interrupt_ex(signum: i32, vm: &VirtualMachine) -> PyResult<()> {
72 use crate::stdlib::signal::_signal::{run_signal, SIG_DFL, SIG_IGN};
73 assert_in_range(signum, vm)?;
74
75 match signum as usize {
76 SIG_DFL | SIG_IGN => Ok(()),
77 _ => {
78 run_signal(signum);
80 Ok(())
81 }
82 }
83}
84
85pub type UserSignal = Box<dyn FnOnce(&VirtualMachine) -> PyResult<()> + Send>;
86
87#[derive(Clone, Debug)]
88pub struct UserSignalSender {
89 tx: mpsc::Sender<UserSignal>,
90}
91
92#[derive(Debug)]
93pub struct UserSignalReceiver {
94 rx: mpsc::Receiver<UserSignal>,
95}
96
97impl UserSignalSender {
98 pub fn send(&self, sig: UserSignal) -> Result<(), UserSignalSendError> {
99 self.tx
100 .send(sig)
101 .map_err(|mpsc::SendError(sig)| UserSignalSendError(sig))?;
102 set_triggered();
103 Ok(())
104 }
105}
106
107pub struct UserSignalSendError(pub UserSignal);
108
109impl fmt::Debug for UserSignalSendError {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 f.debug_struct("UserSignalSendError")
112 .finish_non_exhaustive()
113 }
114}
115
116impl fmt::Display for UserSignalSendError {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 f.write_str("sending a signal to a exited vm")
119 }
120}
121
122pub fn user_signal_channel() -> (UserSignalSender, UserSignalReceiver) {
123 let (tx, rx) = mpsc::channel();
124 (UserSignalSender { tx }, UserSignalReceiver { rx })
125}