#![doc = include_str!("../README.md")]
mod error;
mod platform;
pub use platform::Signal;
mod signal;
pub use signal::*;
#[cfg(feature = "async")]
mod r#async;
#[cfg(feature = "async")]
pub use r#async::AsyncCtrlC;
pub use error::Error;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
static INIT: AtomicBool = AtomicBool::new(false);
static INIT_LOCK: Mutex<()> = Mutex::new(());
pub fn set_handler<F>(user_handler: F) -> Result<std::thread::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<std::thread::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<std::thread::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<std::thread::JoinHandle<()>, Error>
where
F: FnMut() -> bool + 'static + Send,
{
unsafe { platform::init_os_handler(overwrite)? };
let builder = std::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")]
#[deprecated(
since = "3.6.4",
note = "Use 'async' feature instead. The 'tokio' feature is deprecated and will be removed in the future."
)]
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::{SignalKind, signal};
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}");
}
})
}