1#![doc = include_str!("../README.md")]
11
12mod error;
13mod platform;
14pub use platform::Signal;
15mod signal;
16pub use signal::*;
17#[cfg(feature = "async")]
18mod r#async;
19#[cfg(feature = "async")]
20pub use r#async::AsyncCtrlC;
21
22pub use error::Error;
23use std::sync::Mutex;
24use std::sync::atomic::{AtomicBool, Ordering};
25
26static INIT: AtomicBool = AtomicBool::new(false);
27static INIT_LOCK: Mutex<()> = Mutex::new(());
28
29pub fn set_handler<F>(user_handler: F) -> Result<std::thread::JoinHandle<()>, Error>
61where
62 F: FnMut() -> bool + 'static + Send,
63{
64 init_and_set_handler(user_handler, true)
65}
66
67pub fn try_set_handler<F>(user_handler: F) -> Result<std::thread::JoinHandle<()>, Error>
76where
77 F: FnMut() -> bool + 'static + Send,
78{
79 init_and_set_handler(user_handler, false)
80}
81
82fn init_and_set_handler<F>(user_handler: F, overwrite: bool) -> Result<std::thread::JoinHandle<()>, Error>
83where
84 F: FnMut() -> bool + 'static + Send,
85{
86 if !INIT.load(Ordering::Acquire) {
87 let _guard = INIT_LOCK.lock().unwrap();
88
89 if !INIT.load(Ordering::Relaxed) {
90 let handle = set_handler_inner(user_handler, overwrite)?;
91 INIT.store(true, Ordering::Release);
92 return Ok(handle);
93 }
94 }
95
96 Err(Error::MultipleHandlers)
97}
98
99fn set_handler_inner<F>(mut user_handler: F, overwrite: bool) -> Result<std::thread::JoinHandle<()>, Error>
100where
101 F: FnMut() -> bool + 'static + Send,
102{
103 unsafe { platform::init_os_handler(overwrite)? };
104
105 let builder = std::thread::Builder::new()
106 .name("ctrl-c".into())
107 .spawn(move || {
108 loop {
109 unsafe { platform::block_ctrl_c() }.expect("Critical system error while waiting for Ctrl-C");
110
111 if user_handler() {
112 break;
113 }
114 }
115 })
116 .map_err(Error::System)?;
117
118 Ok(builder)
119}
120
121#[cfg(feature = "tokio")]
123#[deprecated(
124 since = "3.6.4",
125 note = "Use 'async' feature instead. The 'tokio' feature is deprecated and will be removed in the future."
126)]
127pub async fn set_async_handler<F>(user_handler: F) -> tokio::task::JoinHandle<()>
128where
129 F: std::future::Future<Output = ()> + 'static + Send,
130{
131 tokio::spawn(async move {
132 let block = async move {
133 #[cfg(unix)]
134 {
135 #[cfg(not(feature = "termination"))]
136 tokio::signal::ctrl_c().await?;
137
138 #[cfg(feature = "termination")]
139 {
140 use tokio::signal::unix::{SignalKind, signal};
141 let mut kill_signal = signal(SignalKind::terminate())?;
142 let mut int_signal = signal(SignalKind::interrupt())?;
143 let mut hup_signal = signal(SignalKind::hangup())?;
144 tokio::select! {
145 _ = tokio::signal::ctrl_c() => {},
146 _ = kill_signal.recv() => {},
147 _ = int_signal.recv() => {},
148 _ = hup_signal.recv() => {}
149 }
150 }
151 }
152
153 #[cfg(windows)]
154 tokio::signal::ctrl_c().await?;
155
156 user_handler.await;
157
158 Ok::<(), std::io::Error>(())
159 };
160 if let Err(err) = block.await {
161 eprintln!("Critical system error while waiting for Ctrl-C: {err}");
162 }
163 })
164}