#![warn(missing_docs)]
#[macro_use]
mod error;
mod platform;
pub use platform::Signal;
mod signal;
pub use signal::*;
pub use error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::thread::{self, JoinHandle};
static INIT: AtomicBool = AtomicBool::new(false);
static INIT_LOCK: Mutex<()> = Mutex::new(());
pub fn set_handler<F>(user_handler: F) -> Result<JoinHandle<()>, Error>
where
F: FnMut() -> bool + 'static + Send,
{
init_and_set_handler(user_handler, true)
}
pub fn try_set_handler<F>(user_handler: F) -> Result<JoinHandle<()>, Error>
where
F: FnMut() -> bool + 'static + Send,
{
init_and_set_handler(user_handler, false)
}
fn init_and_set_handler<F>(user_handler: F, overwrite: bool) -> Result<JoinHandle<()>, Error>
where
F: FnMut() -> bool + 'static + Send,
{
if !INIT.load(Ordering::Acquire) {
let _guard = INIT_LOCK.lock().unwrap();
if !INIT.load(Ordering::Relaxed) {
let handle = set_handler_inner(user_handler, overwrite)?;
INIT.store(true, Ordering::Release);
return Ok(handle);
}
}
Err(Error::MultipleHandlers)
}
fn set_handler_inner<F>(mut user_handler: F, overwrite: bool) -> Result<JoinHandle<()>, Error>
where
F: FnMut() -> bool + 'static + Send,
{
unsafe { platform::init_os_handler(overwrite)? };
let builder = thread::Builder::new()
.name("ctrl-c".into())
.spawn(move || loop {
unsafe {
platform::block_ctrl_c().expect("Critical system error while waiting for Ctrl-C");
}
if user_handler() {
break;
}
})
.map_err(Error::System)?;
Ok(builder)
}
#[cfg(feature = "tokio")]
pub async fn set_async_handler<F>(user_handler: F) -> tokio::task::JoinHandle<()>
where
F: std::future::Future<Output = ()> + 'static + Send,
{
tokio::spawn(async move {
let block = async move {
#[cfg(unix)]
{
#[cfg(not(feature = "termination"))]
tokio::signal::ctrl_c().await?;
#[cfg(feature = "termination")]
{
use tokio::signal::unix::{signal, SignalKind};
let mut kill_signal = signal(SignalKind::terminate())?;
let mut int_signal = signal(SignalKind::interrupt())?;
let mut hup_signal = signal(SignalKind::hangup())?;
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = kill_signal.recv() => {},
_ = int_signal.recv() => {},
_ = hup_signal.recv() => {}
}
}
}
#[cfg(windows)]
tokio::signal::ctrl_c().await?;
user_handler.await;
Ok::<(), std::io::Error>(())
};
if let Err(err) = block.await {
eprintln!("Critical system error while waiting for Ctrl-C: {}", err);
}
})
}