use crossbeam_channel::Sender;
use std::{io, thread};
pub fn install_shutdown_listener(tx: Sender<()>) -> io::Result<thread::JoinHandle<()>> {
#[cfg(unix)]
{
unix::install(tx)
}
#[cfg(windows)]
{
windows::install(tx)
}
}
#[cfg(unix)]
mod unix {
use super::*;
use signal_hook::{consts::signal::*, iterator::Signals};
pub fn install(tx: Sender<()>) -> io::Result<thread::JoinHandle<()>> {
let mut signals = Signals::new([SIGINT, SIGTERM, SIGQUIT, SIGHUP])?;
Ok(thread::spawn(move || {
for _sig in signals.forever() {
let _ = tx.try_send(());
}
}))
}
}
#[cfg(windows)]
mod windows {
use super::*;
use once_cell::sync::OnceCell;
use windows_sys::Win32::System::Console::{
CTRL_BREAK_EVENT,
CTRL_C_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT,
CTRL_SHUTDOWN_EVENT,
SetConsoleCtrlHandler,
};
static TX_SLOT: OnceCell<Sender<()>> = OnceCell::new();
pub fn install(tx: Sender<()>) -> io::Result<thread::JoinHandle<()>> {
ctrlc::set_handler({
let tx = tx.clone();
move || {
let _ = tx.try_send(());
}
})
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
enable_console_close_handler(tx)?;
Ok(std::thread::spawn(|| {
loop {
std::thread::park();
}
}))
}
fn enable_console_close_handler(tx: Sender<()>) -> io::Result<()> {
let _ = TX_SLOT.set(tx);
#[allow(unsafe_code)]
unsafe extern "system" fn handler(ctrl: u32) -> i32 {
match ctrl {
CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_LOGOFF_EVENT | CTRL_SHUTDOWN_EVENT => {
if let Some(tx) = TX_SLOT.get() {
let _ = tx.try_send(());
}
1 }
_ => 0, }
}
#[allow(unsafe_code)]
unsafe {
if SetConsoleCtrlHandler(Some(handler), 1) == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "SetConsoleCtrlHandler failed"));
}
}
Ok(())
}
}