Skip to main content

seq_runtime/
signal.rs

1//! Signal handling API for Seq
2//!
3//! Provides Unix signal handling with a safe, flag-based approach:
4//! - Signals are trapped and set atomic flags (no code runs in signal context)
5//! - User code polls for signals at safe points
6//! - Fits Seq's explicit, predictable style
7//!
8//! # Example
9//! ```seq
10//! signal.SIGINT signal.trap
11//! signal.SIGTERM signal.trap
12//!
13//! : main-loop ( -- )
14//!   signal.SIGINT signal.received? if
15//!     "Shutting down..." io.write-line
16//!     return
17//!   then
18//!   do-work
19//!   main-loop
20//! ;
21//! ```
22//!
23//! # Safety
24//!
25//! Signal handlers execute in an interrupt context with severe restrictions.
26//! This module uses only async-signal-safe operations (atomic flag setting).
27//! All Seq code execution happens outside the signal handler, when the user
28//! explicitly checks for received signals.
29//!
30//! # Thread Safety and Concurrent Access
31//!
32//! This module is designed to be safe for concurrent use from multiple strands:
33//!
34//! - **Handler installation** (`signal.trap`, `signal.default`, `signal.ignore`):
35//!   Protected by a mutex to ensure only one strand modifies handlers at a time.
36//!   Concurrent calls will serialize safely.
37//!
38//! - **Flag operations** (`signal.received?`, `signal.pending?`, `signal.clear`):
39//!   Use lock-free atomic operations with appropriate memory ordering:
40//!   - `signal.received?`: Atomic swap with Acquire ordering (read-modify-write)
41//!   - `signal.pending?`: Atomic load with Acquire ordering (read-only)
42//!   - `signal.clear`: Atomic store with Release ordering (write-only)
43//!
44//!   Multiple strands can safely check the same signal. However, `signal.received?`
45//!   clears the flag atomically, so if two strands both call it, only one will
46//!   observe `true`. Use `signal.pending?` if you need non-destructive reads.
47//!
48//! - **Signal handler**: Executes outside the strand context (in OS interrupt
49//!   context) and only performs a single atomic store. This is async-signal-safe.
50//!
51//! This module uses `sigaction()` instead of the deprecated `signal()` function
52//! for well-defined behavior in multithreaded environments.
53//!
54//! # Platform Support
55//!
56//! - Unix: Full signal support using sigaction()
57//! - Windows: Stub implementations (signals not supported, all operations no-op)
58//!
59//! # Module Layout
60//!
61//! Per-concern sub-modules:
62//! - `constants` — 9 `SIG*` constant getters (unix + non-unix stubs)
63//! - `handlers` — unix-only sigaction wrappers (`install`/`restore`/`ignore`)
64//!   plus the async-signal-safe `flag_signal_handler`
65//! - `ops` — user-facing FFI ops (`trap`/`received?`/`pending?`/`default`/
66//!   `ignore`/`clear`), both unix and non-unix stubs
67//!
68//! Shared state (`SIGNAL_FLAGS` and `MAX_SIGNAL`) stays on this aggregator so
69//! every sub-module points at the same flag table.
70
71use std::sync::atomic::AtomicBool;
72
73/// Maximum signal number we support (covers all standard Unix signals)
74pub(super) const MAX_SIGNAL: usize = 32;
75
76/// Atomic flags for each signal - set by signal handler, cleared by user code
77pub(super) static SIGNAL_FLAGS: [AtomicBool; MAX_SIGNAL] = [
78    AtomicBool::new(false),
79    AtomicBool::new(false),
80    AtomicBool::new(false),
81    AtomicBool::new(false),
82    AtomicBool::new(false),
83    AtomicBool::new(false),
84    AtomicBool::new(false),
85    AtomicBool::new(false),
86    AtomicBool::new(false),
87    AtomicBool::new(false),
88    AtomicBool::new(false),
89    AtomicBool::new(false),
90    AtomicBool::new(false),
91    AtomicBool::new(false),
92    AtomicBool::new(false),
93    AtomicBool::new(false),
94    AtomicBool::new(false),
95    AtomicBool::new(false),
96    AtomicBool::new(false),
97    AtomicBool::new(false),
98    AtomicBool::new(false),
99    AtomicBool::new(false),
100    AtomicBool::new(false),
101    AtomicBool::new(false),
102    AtomicBool::new(false),
103    AtomicBool::new(false),
104    AtomicBool::new(false),
105    AtomicBool::new(false),
106    AtomicBool::new(false),
107    AtomicBool::new(false),
108    AtomicBool::new(false),
109    AtomicBool::new(false),
110];
111
112mod constants;
113mod ops;
114
115#[cfg(unix)]
116mod handlers;
117
118pub use constants::*;
119pub use ops::*;
120
121#[cfg(test)]
122mod tests;