#![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)
}