use std::sync::atomic::{AtomicBool, Ordering};
const SUPPORTED: bool = cfg!(all(unix, not(target_arch = "wasm32")));
static SUSPEND_REQUESTED: AtomicBool = AtomicBool::new(false);
pub(crate) fn request_suspend() {
if SUPPORTED {
SUSPEND_REQUESTED.store(true, Ordering::SeqCst);
}
}
pub(crate) fn take_suspend_request() -> bool {
SUSPEND_REQUESTED.swap(false, Ordering::SeqCst)
}
pub(crate) struct StopSignalGuard {
installed: bool,
}
impl Drop for StopSignalGuard {
fn drop(&mut self) {
#[cfg(all(unix, not(target_arch = "wasm32")))]
if self.installed {
set_stop_disposition(libc::SIG_DFL);
}
SUSPEND_REQUESTED.store(false, Ordering::SeqCst);
}
}
pub(crate) fn install_stop_handler() -> StopSignalGuard {
#[cfg(all(unix, not(target_arch = "wasm32")))]
{
StopSignalGuard {
installed: set_stop_disposition(stop_request_handler()),
}
}
#[cfg(not(all(unix, not(target_arch = "wasm32"))))]
{
StopSignalGuard { installed: false }
}
}
pub(crate) fn stop_until_continued() {
#[cfg(all(unix, not(target_arch = "wasm32")))]
{
set_stop_disposition(libc::SIG_DFL);
#[allow(unsafe_code)]
unsafe {
libc::killpg(0, libc::SIGTSTP)
};
set_stop_disposition(stop_request_handler());
}
}
#[cfg(all(unix, not(target_arch = "wasm32")))]
extern "C" fn note_stop_request(_signal: libc::c_int) {
SUSPEND_REQUESTED.store(true, Ordering::SeqCst);
}
#[cfg(all(unix, not(target_arch = "wasm32")))]
fn stop_request_handler() -> libc::sighandler_t {
note_stop_request as *const () as libc::sighandler_t
}
#[cfg(all(unix, not(target_arch = "wasm32")))]
fn set_stop_disposition(handler: libc::sighandler_t) -> bool {
#[allow(unsafe_code)]
unsafe {
let mut action: libc::sigaction = std::mem::zeroed();
action.sa_sigaction = handler;
libc::sigemptyset(&mut action.sa_mask);
action.sa_flags = libc::SA_RESTART;
libc::sigaction(libc::SIGTSTP, &action, std::ptr::null_mut()) == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_is_taken_once_and_cleared_by_the_guard() {
assert!(!take_suspend_request(), "no request is pending initially");
let guard = install_stop_handler();
assert_eq!(
guard.installed, SUPPORTED,
"the handler installs exactly where job control exists"
);
request_suspend();
assert_eq!(take_suspend_request(), SUPPORTED);
assert!(!take_suspend_request(), "a request is delivered once");
request_suspend();
drop(guard);
assert!(!take_suspend_request());
}
}