use std::sync::atomic::{AtomicI32, Ordering};
use crate::ported::signals::{queue_front, queue_rear, signal_queue, signal_mask_queue};
#[cfg(target_os = "linux")]
pub const SIGCOUNT: i32 = 64;
#[cfg(target_os = "macos")]
pub const SIGCOUNT: i32 = 31;
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
pub const SIGCOUNT: i32 = 31;
pub const SIGZERR: i32 = SIGCOUNT + 1;
pub const SIGDEBUG: i32 = SIGCOUNT + 2;
pub const VSIGCOUNT: i32 = SIGCOUNT + 3;
#[cfg(target_os = "linux")]
pub const TRAPCOUNT: i32 = VSIGCOUNT + 32;
#[cfg(not(target_os = "linux"))]
pub const TRAPCOUNT: i32 = VSIGCOUNT;
pub const SIGEXIT: i32 = 0;
pub static SIGS: &[(&str, i32)] = &[ ("HUP", libc::SIGHUP), ("INT", libc::SIGINT),
("QUIT", libc::SIGQUIT), ("ILL", libc::SIGILL),
("TRAP", libc::SIGTRAP), ("ABRT", libc::SIGABRT),
("BUS", libc::SIGBUS), ("FPE", libc::SIGFPE),
("KILL", libc::SIGKILL), ("USR1", libc::SIGUSR1),
("SEGV", libc::SIGSEGV), ("USR2", libc::SIGUSR2),
("PIPE", libc::SIGPIPE), ("ALRM", libc::SIGALRM),
("TERM", libc::SIGTERM), ("CHLD", libc::SIGCHLD),
("CONT", libc::SIGCONT), ("STOP", libc::SIGSTOP),
("TSTP", libc::SIGTSTP), ("TTIN", libc::SIGTTIN),
("TTOU", libc::SIGTTOU), ("URG", libc::SIGURG),
("XCPU", libc::SIGXCPU), ("XFSZ", libc::SIGXFSZ),
("VTALRM", libc::SIGVTALRM), ("PROF", libc::SIGPROF),
("WINCH", libc::SIGWINCH), ("IO", libc::SIGIO),
("SYS", libc::SIGSYS),
];
pub static ALT_SIGS: &[(&str, i32)] = &[ ("CLD", libc::SIGCHLD), ("IOT", libc::SIGABRT), ("ERR", SIGZERR), ];
pub static SIG_MSG: &[(i32, &str)] = &[ (libc::SIGHUP, "hangup"),
(libc::SIGINT, "interrupt"),
(libc::SIGQUIT, "quit"),
(libc::SIGILL, "illegal hardware instruction"),
(libc::SIGTRAP, "trace trap"),
(libc::SIGABRT, "abort"),
(libc::SIGBUS, "bus error"),
(libc::SIGFPE, "floating point exception"),
(libc::SIGKILL, "killed"),
(libc::SIGUSR1, "user-defined signal 1"),
(libc::SIGSEGV, "segmentation fault"),
(libc::SIGUSR2, "user-defined signal 2"),
(libc::SIGPIPE, "broken pipe"),
(libc::SIGALRM, "alarm"),
(libc::SIGTERM, "terminated"),
(libc::SIGCHLD, "death of child"),
(libc::SIGCONT, "continued"),
(libc::SIGSTOP, "stopped (signal)"),
(libc::SIGTSTP, "stopped"),
(libc::SIGTTIN, "stopped (tty input)"),
(libc::SIGTTOU, "stopped (tty output)"),
(libc::SIGURG, "urgent condition"),
(libc::SIGXCPU, "cpu limit exceeded"),
(libc::SIGXFSZ, "file size limit exceeded"),
(libc::SIGVTALRM, "virtual time alarm"),
(libc::SIGPROF, "profile signal"),
(libc::SIGWINCH, "window size changed"),
(libc::SIGIO, "i/o ready"),
(libc::SIGSYS, "invalid system call"),
];
pub fn sigs_name(idx: i32) -> Option<&'static str> { if idx == 0 { return Some("EXIT"); } if idx == SIGZERR { return Some("ZERR"); } if idx == SIGDEBUG { return Some("DEBUG"); } SIGS.iter().find(|(_, n)| *n == idx).map(|(name, _)| *name)
}
pub fn sigs_number(name: &str) -> Option<i32> { let bare = name.strip_prefix("SIG").unwrap_or(name);
SIGS.iter().find(|(n, _)| *n == bare).map(|(_, num)| *num)
.or_else(|| ALT_SIGS.iter().find(|(n, _)| *n == bare).map(|(_, num)| *num))
}
#[inline]
#[allow(non_snake_case)]
pub fn SIGNUM(x: i32) -> i32 { #[cfg(target_os = "linux")]
{
if x >= VSIGCOUNT {
x - VSIGCOUNT + libc::SIGRTMIN()
} else {
x
}
}
#[cfg(not(target_os = "linux"))]
{ x }
}
#[inline]
#[allow(non_snake_case)]
pub fn SIGIDX(x: i32) -> i32 { #[cfg(target_os = "linux")]
{
let rtmin = libc::SIGRTMIN();
let rtmax = libc::SIGRTMAX();
if x >= rtmin && x <= rtmax {
x - rtmin + VSIGCOUNT
} else {
x
}
}
#[cfg(not(target_os = "linux"))]
{ x }
}
pub const MAX_QUEUE_SIZE: usize = 128;
#[inline]
#[allow(non_snake_case)]
pub fn queue_signals() { crate::ported::signals::queue_in.fetch_add(1, Ordering::SeqCst);
crate::ported::signals::queueing_enabled.fetch_add(1, Ordering::SeqCst);
}
#[inline]
#[allow(non_snake_case)]
pub fn unqueue_signals() { crate::ported::signals::queue_in.fetch_sub(1, Ordering::SeqCst);
let prev = crate::ported::signals::queueing_enabled.fetch_sub(1, Ordering::SeqCst);
if prev == 1 {
run_queued_signals();
}
}
#[inline]
#[allow(non_snake_case)]
pub fn dont_queue_signals() { let level = crate::ported::signals::queueing_enabled.swap(0, Ordering::SeqCst);
crate::ported::signals::queue_in.store(level, Ordering::SeqCst);
run_queued_signals();
}
#[inline]
#[allow(non_snake_case)]
pub fn restore_queue_signals(q: i32) { crate::ported::signals::queueing_enabled.store(q, Ordering::SeqCst);
}
#[inline]
#[allow(non_snake_case)]
pub fn queue_signal_level() -> i32 { crate::ported::signals::queueing_enabled.load(Ordering::SeqCst)
}
#[inline]
#[allow(non_snake_case)]
pub fn run_queued_signals() { loop {
let f = queue_front.load(Ordering::SeqCst);
let r = queue_rear.load(Ordering::SeqCst);
if f == r { break; }
let nf = (f + 1) % MAX_QUEUE_SIZE;
let sig = signal_queue[nf].load(Ordering::SeqCst);
let mask = signal_mask_queue.lock().ok().and_then(|g| g.get(nf).copied());
queue_front.store(nf, Ordering::SeqCst);
if let Some(m) = mask {
let _ = crate::ported::signals::signal_setmask(&m);
}
unsafe { libc::raise(sig); }
}
}
#[inline]
#[allow(non_snake_case)]
pub fn child_block() { }
#[inline]
#[allow(non_snake_case)]
pub fn child_unblock() { }
#[inline]
#[allow(non_snake_case)]
pub fn winch_block() { }
#[inline]
#[allow(non_snake_case)]
pub fn winch_unblock() { }
#[inline]
#[allow(non_snake_case)]
pub fn signal_ignore(s: i32) { #[cfg(unix)]
unsafe {
libc::signal(s, libc::SIG_IGN);
}
#[cfg(not(unix))]
{ let _ = s; }
}
#[inline]
#[allow(non_snake_case)]
pub fn signal_default(s: i32) { #[cfg(unix)]
unsafe {
libc::signal(s, libc::SIG_DFL);
}
#[cfg(not(unix))]
{ let _ = s; }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pseudo_signal_indexes_correct() {
assert_eq!(SIGEXIT, 0);
assert_eq!(SIGZERR, SIGCOUNT + 1);
assert_eq!(SIGDEBUG, SIGCOUNT + 2);
assert_eq!(VSIGCOUNT, SIGCOUNT + 3);
assert!(VSIGCOUNT > 0);
}
#[test]
fn trapcount_at_least_vsigcount() {
assert!(TRAPCOUNT >= VSIGCOUNT);
}
#[test]
fn signum_sigidx_round_trip_for_normal_signal() {
assert_eq!(SIGNUM(libc::SIGINT), libc::SIGINT);
assert_eq!(SIGIDX(libc::SIGINT), libc::SIGINT);
}
#[test]
fn max_queue_size_is_128() {
assert_eq!(MAX_QUEUE_SIZE, 128);
}
#[test]
fn queue_unqueue_balance() {
let initial = queue_signal_level();
queue_signals();
assert_eq!(queue_signal_level(), initial + 1);
queue_signals();
assert_eq!(queue_signal_level(), initial + 2);
unqueue_signals();
assert_eq!(queue_signal_level(), initial + 1);
unqueue_signals();
assert_eq!(queue_signal_level(), initial);
}
#[test]
fn dont_queue_signals_zeros_counter() {
queue_signals();
queue_signals();
queue_signals();
dont_queue_signals();
assert_eq!(queue_signal_level(), 0);
}
#[test]
fn restore_queue_signals_sets_counter() {
restore_queue_signals(0);
restore_queue_signals(5);
assert_eq!(queue_signal_level(), 5);
restore_queue_signals(0);
}
}