1use core::{
2 cell::{Cell, RefCell},
3 fmt,
4 ops::{Deref, DerefMut, Index, IndexMut, Range},
5 sync::atomic::{AtomicBool, AtomicU8, Ordering},
6};
7use std::sync::mpsc;
8
9#[cfg(windows)]
10use core::sync::atomic::AtomicIsize;
11
12use crate::{PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine};
13
14pub(crate) const NSIG: usize = 64;
15
16#[cfg(not(feature = "threading"))]
17bitflagset::bitflag! {
18 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
20 #[repr(u8)]
21 enum EvalBreakerFlag {
22 Signal = 0,
23 }
24}
25
26#[cfg(feature = "threading")]
27bitflagset::bitflag! {
28 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
30 #[repr(u8)]
31 enum EvalBreakerFlag {
32 Signal = 0,
33 Qsbr = 1,
34 Stop = 3,
35 Finalizing = 4,
36 }
37}
38
39bitflagset::bitflagset! {
40 #[derive(Copy, Clone, PartialEq, Eq)]
41 struct EvalBreakerBits(u8): EvalBreakerFlag
42}
43
44bitflagset::atomic_bitflagset!(struct EvalBreaker(AtomicU8) on EvalBreakerBits);
45
46static EVAL_BREAKER: EvalBreaker = EvalBreaker::new();
49
50#[expect(
51 clippy::declare_interior_mutable_const,
52 reason = "workaround for const array repeat limitation (rust issue #79270)"
53)]
54const ATOMIC_FALSE: AtomicBool = AtomicBool::new(false);
55
56pub(crate) static TRIGGERS: [AtomicBool; NSIG] = [ATOMIC_FALSE; NSIG];
57
58#[cfg(windows)]
59static SIGINT_EVENT: AtomicIsize = AtomicIsize::new(0);
60
61thread_local! {
62 static IN_SIGNAL_HANDLER: Cell<bool> = const { Cell::new(false) };
65}
66
67struct SignalHandlerGuard;
68
69impl Drop for SignalHandlerGuard {
70 fn drop(&mut self) {
71 IN_SIGNAL_HANDLER.with(|h| h.set(false));
72 }
73}
74
75#[cfg_attr(feature = "flame-it", flame)]
76#[inline(always)]
77pub fn check_signals(vm: &VirtualMachine) -> PyResult<()> {
78 if vm.signal_handlers.get().is_none() {
79 return Ok(());
80 }
81
82 if !EVAL_BREAKER.contains(&EvalBreakerFlag::Signal) {
85 return Ok(());
86 }
87
88 if !EVAL_BREAKER.remove(EvalBreakerFlag::Signal) {
90 return Ok(());
91 }
92
93 trigger_signals(vm)
94}
95
96#[inline(never)]
97#[cold]
98fn trigger_signals(vm: &VirtualMachine) -> PyResult<()> {
99 if IN_SIGNAL_HANDLER.with(|h| h.replace(true)) {
100 set_triggered();
102 return Ok(());
103 }
104 let _guard = SignalHandlerGuard;
105
106 let signal_handlers = vm
107 .signal_handlers
108 .get()
109 .expect("should never fail since we check above");
110
111 for (signum, trigger) in TRIGGERS.iter().enumerate().skip(1) {
112 let triggered = trigger.swap(false, Ordering::Relaxed);
113 if !triggered {
114 continue;
115 }
116
117 let signum = unsafe { SignalNum::new_unchecked(signum as i32) };
119
120 let handler = signal_handlers.borrow()[signum].clone();
124
125 if let Some(handler) = handler
126 && let Some(callable) = handler.to_callable()
127 {
128 callable.invoke((signum.as_i32(), vm.ctx.none()), vm)?;
129 }
130 }
131
132 if let Some(signal_rx) = &vm.signal_rx {
133 for f in signal_rx.rx.try_iter() {
134 f(vm)?;
135 }
136 }
137
138 Ok(())
139}
140
141pub(crate) fn set_triggered() {
142 EVAL_BREAKER.insert(EvalBreakerFlag::Signal);
145}
146
147#[inline(always)]
149pub(crate) fn eval_breaker_pending() -> bool {
150 !EVAL_BREAKER.is_empty()
151}
152
153#[cfg(feature = "threading")]
155mod mt {
156 use super::{EVAL_BREAKER, EvalBreakerFlag};
157
158 pub(crate) fn set_qsbr_bit() {
160 EVAL_BREAKER.insert(EvalBreakerFlag::Qsbr);
161 }
162
163 pub(crate) fn clear_qsbr_bit() {
164 EVAL_BREAKER.remove(EvalBreakerFlag::Qsbr);
165 }
166
167 pub(crate) fn qsbr_bit_set() -> bool {
168 EVAL_BREAKER.contains(&EvalBreakerFlag::Qsbr)
169 }
170
171 pub(crate) fn set_stop_bit() {
176 EVAL_BREAKER.insert(EvalBreakerFlag::Stop);
177 }
178
179 pub(crate) fn clear_stop_bit() {
182 EVAL_BREAKER.remove(EvalBreakerFlag::Stop);
183 }
184
185 pub(crate) fn set_finalizing_bit() {
188 EVAL_BREAKER.insert(EvalBreakerFlag::Finalizing);
189 }
190
191 #[cfg(test)]
196 pub(crate) fn clear_eval_breaker_for_test() {
197 EVAL_BREAKER.clear();
198 }
199}
200
201#[cfg(feature = "threading")]
202pub(crate) use mt::{
203 clear_qsbr_bit, clear_stop_bit, qsbr_bit_set, set_finalizing_bit, set_qsbr_bit, set_stop_bit,
204};
205
206#[cfg(all(test, feature = "threading"))]
207pub(crate) use mt::clear_eval_breaker_for_test;
208
209#[cfg(all(unix, feature = "host_env"))]
212pub(crate) fn clear_after_fork() {
213 EVAL_BREAKER.remove(EvalBreakerFlag::Signal);
214 for trigger in &TRIGGERS {
215 trigger.store(false, Ordering::Relaxed);
216 }
217}
218
219#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
221pub struct SignalNum(i32);
222
223impl SignalNum {
224 pub(crate) const VALID_RANGE: Range<i32> = 1..NSIG as i32;
225
226 #[cfg(any(unix, windows))]
233 #[allow(dead_code, reason = "Not used on all platforms")]
234 pub(crate) const SIGINT: Self = Self(libc::SIGINT);
235
236 #[must_use]
242 pub const unsafe fn new_unchecked(value: i32) -> Self {
243 Self(value)
244 }
245
246 #[must_use]
248 pub const fn as_i32(&self) -> i32 {
249 self.0
250 }
251
252 #[must_use]
254 pub const fn as_usize(&self) -> usize {
255 self.0 as usize
256 }
257}
258
259impl fmt::Display for SignalNum {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 fmt::Display::fmt(&self.0, f)
262 }
263}
264
265impl From<SignalNum> for i32 {
266 fn from(signalnum: SignalNum) -> Self {
267 signalnum.as_i32()
268 }
269}
270
271impl TryFrom<i32> for SignalNum {
272 type Error = String;
273
274 fn try_from(value: i32) -> Result<Self, Self::Error> {
275 let bounds = cfg_select! {
276 all(windows, feature = "host_env") => rustpython_host_env::signal::VALID_SIGNALS,
277 _ => Self::VALID_RANGE,
278 };
279
280 if bounds.contains(&value) {
281 Ok(Self(value))
282 } else {
283 Err("signal number out of range".into())
284 }
285 }
286}
287
288impl TryFromObject for SignalNum {
289 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
290 Self::try_from(i32::try_from_borrowed_object(vm, &obj)?)
291 .map_err(|msg| vm.new_value_error(msg))
292 }
293}
294
295#[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))]
299pub fn set_interrupt_ex(signum: SignalNum) -> PyResult<()> {
300 use crate::stdlib::_signal::_signal::{SIG_DFL, SIG_IGN, run_signal};
301
302 match signum.as_usize() {
303 SIG_DFL | SIG_IGN => Ok(()),
304 _ => {
305 run_signal(signum.into());
307 Ok(())
308 }
309 }
310}
311
312pub type UserSignal = Box<dyn FnOnce(&VirtualMachine) -> PyResult<()> + Send>;
313
314#[derive(Clone, Debug)]
315pub struct UserSignalSender {
316 tx: mpsc::Sender<UserSignal>,
317}
318
319#[derive(Debug)]
320pub struct UserSignalReceiver {
321 rx: mpsc::Receiver<UserSignal>,
322}
323
324impl UserSignalSender {
325 pub fn send(&self, sig: UserSignal) -> Result<(), UserSignalSendError> {
326 self.tx
327 .send(sig)
328 .map_err(|mpsc::SendError(sig)| UserSignalSendError(sig))?;
329 set_triggered();
330 Ok(())
331 }
332}
333
334pub struct UserSignalSendError(pub UserSignal);
335
336impl fmt::Debug for UserSignalSendError {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 f.debug_struct("UserSignalSendError")
339 .finish_non_exhaustive()
340 }
341}
342
343impl fmt::Display for UserSignalSendError {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 f.write_str("sending a signal to a exited vm")
346 }
347}
348
349#[must_use]
350pub fn user_signal_channel() -> (UserSignalSender, UserSignalReceiver) {
351 let (tx, rx) = mpsc::channel();
352 (UserSignalSender { tx }, UserSignalReceiver { rx })
353}
354
355#[cfg(windows)]
356pub fn set_sigint_event(handle: isize) {
357 SIGINT_EVENT.store(handle, Ordering::Release);
358}
359
360#[cfg(windows)]
361pub fn get_sigint_event() -> Option<isize> {
362 let handle = SIGINT_EVENT.load(Ordering::Acquire);
363 if handle == 0 { None } else { Some(handle) }
364}
365
366pub struct SignalHandlersInner([Option<PyObjectRef>; NSIG]);
367
368impl Default for SignalHandlersInner {
369 fn default() -> Self {
370 Self([const { None }; NSIG])
371 }
372}
373
374impl Index<SignalNum> for SignalHandlersInner {
375 type Output = Option<PyObjectRef>;
376
377 fn index(&self, index: SignalNum) -> &Self::Output {
378 &self.0[index.as_usize()]
379 }
380}
381
382impl IndexMut<SignalNum> for SignalHandlersInner {
383 fn index_mut(&mut self, index: SignalNum) -> &mut Self::Output {
384 &mut self.0[index.as_usize()]
385 }
386}
387
388pub struct SignalHandlers(Box<RefCell<SignalHandlersInner>>);
389
390impl Default for SignalHandlers {
391 fn default() -> Self {
392 Self(Box::new(RefCell::new(SignalHandlersInner::default())))
393 }
394}
395
396impl Deref for SignalHandlers {
397 type Target = Box<RefCell<SignalHandlersInner>>;
398
399 fn deref(&self) -> &Self::Target {
400 &self.0
401 }
402}
403
404impl DerefMut for SignalHandlers {
405 fn deref_mut(&mut self) -> &mut Self::Target {
406 &mut self.0
407 }
408}