use core::{cell::RefCell, default};
use portable_atomic::{AtomicUsize, Ordering};
#[allow(clippy::declare_interior_mutable_const)]
const SIG_DFL_ATOMIC: AtomicUsize = AtomicUsize::new(SIG_DFL);
static SIGNAL_HANDLERS: [AtomicUsize; 16] = [SIG_DFL_ATOMIC; 16];
pub type SignalHandler = usize;
const SIG_DFL: usize = 0;
const SIG_IGN: usize = 1;
const SIG_ERR: usize = usize::MAX;
const SIGTERM: i32 = 15;
const SIGSEGV: i32 = 11;
const SIGINT: i32 = 2;
const SIGILL: i32 = 4;
const SIGABRT: i32 = 6;
const SIGFPE: i32 = 8;
const SIGNALS: [i32; 6] = [SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE];
fn ignore_handler(_sig: i32) {}
fn default_handler(_sig: i32) {
panic!("Aborted");
}
#[cfg_attr(all(not(test), feature = "signal"), no_mangle)]
pub unsafe extern "C" fn signal(sig: i32, handler: SignalHandler) -> SignalHandler {
if !SIGNALS.contains(&sig) {
return SIG_ERR;
}
SIGNAL_HANDLERS[sig as usize].swap(handler, Ordering::Relaxed)
}
#[cfg_attr(all(not(test), feature = "signal"), no_mangle)]
pub extern "C" fn raise(sig: i32) -> i32 {
if !SIGNALS.contains(&sig) {
return -1;
}
let handler = SIGNAL_HANDLERS[sig as usize].load(Ordering::Relaxed);
match handler {
SIG_DFL => {
default_handler(sig);
}
SIG_IGN => {
ignore_handler(sig);
}
_ => unsafe {
let handler_fn: unsafe extern "C" fn(core::ffi::c_int) = core::mem::transmute(handler);
handler_fn(sig);
},
}
0
}
#[cfg_attr(all(not(test), feature = "signal"), no_mangle)]
pub extern "C" fn abort() {
raise(SIGABRT);
}
#[cfg(test)]
mod tests {
use super::*;
struct State {
inner: std::sync::Mutex<()>,
}
impl State {
fn lock(&self) -> std::sync::MutexGuard<()> {
let guard = self.inner.lock().unwrap();
for sig in SIGNAL_HANDLERS.iter() {
sig.store(SIG_DFL, Ordering::SeqCst);
}
guard
}
}
static TEST_LOCK: State = State {
inner: std::sync::Mutex::new(()),
};
#[test]
fn test_signal() {
let _guard = TEST_LOCK.lock();
static COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn count_handler(_sig: i32) {
COUNT.fetch_add(1, Ordering::Relaxed);
}
let count_handler_ptr = count_handler as *const fn(i32) as usize;
let old_handler = unsafe { signal(SIGTERM, count_handler_ptr) };
assert_eq!(old_handler, SIG_DFL);
(0..10).for_each(|_| {
raise(SIGTERM);
});
let old_handler = unsafe { signal(SIGTERM, SIG_DFL) };
assert_eq!(COUNT.load(Ordering::Relaxed), 10);
assert_eq!(old_handler, count_handler_ptr);
}
#[test]
fn test_abort() {
let _guard = TEST_LOCK.lock();
let result = std::panic::catch_unwind(|| {
abort();
});
assert!(result.is_err());
}
#[test]
fn test_signal_error() {
let _guard = TEST_LOCK.lock();
let err = unsafe { signal(1000, SIG_DFL) };
assert_eq!(err, SIG_ERR);
}
#[test]
fn test_raise() {
let result = std::panic::catch_unwind(|| raise(SIGTERM));
assert!(result.is_err());
}
#[test]
fn test_ignore() {
let _guard = TEST_LOCK.lock();
let old_handler = unsafe { signal(SIGTERM, SIG_IGN) };
assert_eq!(old_handler, SIG_DFL);
raise(SIGTERM);
let old_handler = unsafe { signal(SIGTERM, SIG_DFL) };
assert_eq!(old_handler, SIG_IGN);
}
#[test]
fn test_raise_error() {
assert!(raise(1000) == -1);
}
}