Skip to main content

seq_runtime/signal/
ops.rs

1//! Seq-callable signal operations: `signal.trap`, `signal.received?`,
2//! `signal.pending?`, `signal.default`, `signal.ignore`, `signal.clear`.
3//!
4//! Unix implementations drive sigaction through `super::handlers`; non-Unix
5//! builds ship stubs that preserve the FFI shape but no-op.
6
7use crate::stack::{Stack, pop, push};
8use crate::value::Value;
9
10#[cfg(unix)]
11use std::sync::atomic::Ordering;
12
13#[cfg(unix)]
14use super::{MAX_SIGNAL, SIGNAL_FLAGS};
15
16/// Trap a signal: install handler that sets flag instead of default behavior
17///
18/// Stack effect: ( signal-num -- )
19///
20/// After trapping, the signal will set an internal flag instead of its default
21/// action (which might be to terminate the process). Use `signal.received?` to
22/// check and clear the flag.
23///
24/// # Safety
25/// Stack must have an Int (signal number) on top
26#[cfg(unix)]
27#[unsafe(no_mangle)]
28pub unsafe extern "C" fn patch_seq_signal_trap(stack: Stack) -> Stack {
29    unsafe {
30        let (stack, sig_val) = pop(stack);
31        let sig_num = match sig_val {
32            Value::Int(n) => {
33                if n < 0 || n as usize >= MAX_SIGNAL {
34                    panic!("signal.trap: invalid signal number {}", n);
35                }
36                n as libc::c_int
37            }
38            _ => panic!(
39                "signal.trap: expected Int (signal number), got {:?}",
40                sig_val
41            ),
42        };
43
44        // Install our flag-setting handler using sigaction
45        if let Err(e) = super::handlers::install_signal_handler(sig_num) {
46            panic!(
47                "signal.trap: failed to install handler for signal {}: {}",
48                sig_num, e
49            );
50        }
51        stack
52    }
53}
54
55/// Check if a signal was received and clear the flag
56///
57/// Stack effect: ( signal-num -- received? )
58///
59/// Returns true if the signal was received since the last check, false otherwise.
60/// This atomically clears the flag, so the signal must be received again to return true.
61///
62/// # Safety
63/// Stack must have an Int (signal number) on top
64#[cfg(unix)]
65#[unsafe(no_mangle)]
66pub unsafe extern "C" fn patch_seq_signal_received(stack: Stack) -> Stack {
67    unsafe {
68        let (stack, sig_val) = pop(stack);
69        let sig_num = match sig_val {
70            Value::Int(n) => {
71                if n < 0 || n as usize >= MAX_SIGNAL {
72                    panic!("signal.received?: invalid signal number {}", n);
73                }
74                n as usize
75            }
76            _ => panic!(
77                "signal.received?: expected Int (signal number), got {:?}",
78                sig_val
79            ),
80        };
81
82        // Atomically swap the flag to false and return the old value
83        let was_set = SIGNAL_FLAGS[sig_num].swap(false, Ordering::Acquire);
84        push(stack, Value::Bool(was_set))
85    }
86}
87
88/// Check if a signal is pending without clearing the flag
89///
90/// Stack effect: ( signal-num -- pending? )
91///
92/// Returns true if the signal was received, false otherwise.
93/// Unlike `signal.received?`, this does NOT clear the flag.
94///
95/// # Safety
96/// Stack must have an Int (signal number) on top
97#[cfg(unix)]
98#[unsafe(no_mangle)]
99pub unsafe extern "C" fn patch_seq_signal_pending(stack: Stack) -> Stack {
100    unsafe {
101        let (stack, sig_val) = pop(stack);
102        let sig_num = match sig_val {
103            Value::Int(n) => {
104                if n < 0 || n as usize >= MAX_SIGNAL {
105                    panic!("signal.pending?: invalid signal number {}", n);
106                }
107                n as usize
108            }
109            _ => panic!(
110                "signal.pending?: expected Int (signal number), got {:?}",
111                sig_val
112            ),
113        };
114
115        let is_set = SIGNAL_FLAGS[sig_num].load(Ordering::Acquire);
116        push(stack, Value::Bool(is_set))
117    }
118}
119
120/// Restore the default handler for a signal
121///
122/// Stack effect: ( signal-num -- )
123///
124/// Restores the system default behavior for the signal.
125///
126/// # Safety
127/// Stack must have an Int (signal number) on top
128#[cfg(unix)]
129#[unsafe(no_mangle)]
130pub unsafe extern "C" fn patch_seq_signal_default(stack: Stack) -> Stack {
131    unsafe {
132        let (stack, sig_val) = pop(stack);
133        let sig_num = match sig_val {
134            Value::Int(n) => {
135                if n < 0 || n as usize >= MAX_SIGNAL {
136                    panic!("signal.default: invalid signal number {}", n);
137                }
138                n as libc::c_int
139            }
140            _ => panic!(
141                "signal.default: expected Int (signal number), got {:?}",
142                sig_val
143            ),
144        };
145
146        if let Err(e) = super::handlers::restore_default_handler(sig_num) {
147            panic!(
148                "signal.default: failed to restore default handler for signal {}: {}",
149                sig_num, e
150            );
151        }
152        stack
153    }
154}
155
156/// Ignore a signal entirely
157///
158/// Stack effect: ( signal-num -- )
159///
160/// The signal will be ignored - it won't terminate the process or set any flag.
161/// Useful for SIGPIPE in network servers.
162///
163/// # Safety
164/// Stack must have an Int (signal number) on top
165#[cfg(unix)]
166#[unsafe(no_mangle)]
167pub unsafe extern "C" fn patch_seq_signal_ignore(stack: Stack) -> Stack {
168    unsafe {
169        let (stack, sig_val) = pop(stack);
170        let sig_num = match sig_val {
171            Value::Int(n) => {
172                if n < 0 || n as usize >= MAX_SIGNAL {
173                    panic!("signal.ignore: invalid signal number {}", n);
174                }
175                n as libc::c_int
176            }
177            _ => panic!(
178                "signal.ignore: expected Int (signal number), got {:?}",
179                sig_val
180            ),
181        };
182
183        if let Err(e) = super::handlers::ignore_signal(sig_num) {
184            panic!("signal.ignore: failed to ignore signal {}: {}", sig_num, e);
185        }
186        stack
187    }
188}
189
190/// Clear the flag for a signal without checking it
191///
192/// Stack effect: ( signal-num -- )
193///
194/// Useful for resetting state without caring about the previous value.
195///
196/// # Safety
197/// Stack must have an Int (signal number) on top
198#[cfg(unix)]
199#[unsafe(no_mangle)]
200pub unsafe extern "C" fn patch_seq_signal_clear(stack: Stack) -> Stack {
201    unsafe {
202        let (stack, sig_val) = pop(stack);
203        let sig_num = match sig_val {
204            Value::Int(n) => {
205                if n < 0 || n as usize >= MAX_SIGNAL {
206                    panic!("signal.clear: invalid signal number {}", n);
207                }
208                n as usize
209            }
210            _ => panic!(
211                "signal.clear: expected Int (signal number), got {:?}",
212                sig_val
213            ),
214        };
215
216        SIGNAL_FLAGS[sig_num].store(false, Ordering::Release);
217        stack
218    }
219}
220
221// Stub implementations for non-Unix platforms
222// Safety: Stack pointer must be valid for all functions below.
223
224/// # Safety
225/// Stack pointer must be valid.
226#[cfg(not(unix))]
227#[unsafe(no_mangle)]
228pub unsafe extern "C" fn patch_seq_signal_trap(stack: Stack) -> Stack {
229    let (stack, _) = unsafe { pop(stack) };
230    // No-op on non-Unix - signals not supported
231    stack
232}
233
234/// # Safety
235/// Stack pointer must be valid.
236#[cfg(not(unix))]
237#[unsafe(no_mangle)]
238pub unsafe extern "C" fn patch_seq_signal_default(stack: Stack) -> Stack {
239    let (stack, _) = unsafe { pop(stack) };
240    stack
241}
242
243/// # Safety
244/// Stack pointer must be valid.
245#[cfg(not(unix))]
246#[unsafe(no_mangle)]
247pub unsafe extern "C" fn patch_seq_signal_ignore(stack: Stack) -> Stack {
248    let (stack, _) = unsafe { pop(stack) };
249    stack
250}
251
252/// # Safety
253/// Stack pointer must be valid.
254#[cfg(not(unix))]
255#[unsafe(no_mangle)]
256pub unsafe extern "C" fn patch_seq_signal_received(stack: Stack) -> Stack {
257    let (stack, _) = unsafe { pop(stack) };
258    // Always return false on non-Unix - signals not supported
259    unsafe { push(stack, Value::Bool(false)) }
260}
261
262/// # Safety
263/// Stack pointer must be valid.
264#[cfg(not(unix))]
265#[unsafe(no_mangle)]
266pub unsafe extern "C" fn patch_seq_signal_pending(stack: Stack) -> Stack {
267    let (stack, _) = unsafe { pop(stack) };
268    // Always return false on non-Unix - signals not supported
269    unsafe { push(stack, Value::Bool(false)) }
270}
271
272/// # Safety
273/// Stack pointer must be valid.
274#[cfg(not(unix))]
275#[unsafe(no_mangle)]
276pub unsafe extern "C" fn patch_seq_signal_clear(stack: Stack) -> Stack {
277    let (stack, _) = unsafe { pop(stack) };
278    // No-op on non-Unix
279    stack
280}