ctrlc_tiny/lib.rs
1mod bindings;
2
3use std::{io, sync::Once};
4
5static INIT: Once = Once::new();
6
7/// Initializes the SIGINT (Ctrl-C) signal handler.
8///
9/// This function installs a minimal, signal-safe handler for `SIGINT`.
10/// Once installed, any incoming Ctrl-C will set an internal flag,
11/// which can later be queried via [`is_ctrlc_received()`].
12///
13/// This function may be called multiple times;
14/// the signal handler will only be installed once.
15/// Repeated calls are safe and have no additional effect.
16///
17/// # Errors
18///
19/// Returns an `Err` if the underlying system call (`sigaction`)
20/// fails during handler installation. This typically indicates a
21/// low-level OS error or permission issue.
22///
23/// # Examples
24///
25/// ```rust,no_run
26/// ctrlc_tiny::init_ctrlc()?;
27/// loop {
28/// if ctrlc_tiny::is_ctrlc_received() {
29/// println!("Ctrl-C detected!");
30/// break;
31/// }
32/// }
33/// # Ok::<_, std::io::Error>(())
34/// ```
35pub fn init_ctrlc() -> io::Result<()> {
36 let mut result = Ok(());
37 INIT.call_once(|| unsafe {
38 if bindings::init_sigint_handler() != 0 {
39 result = Err(io::Error::last_os_error());
40 }
41 });
42 result
43}
44
45/// Checks whether Ctrl-C (SIGINT) has been received.
46///
47/// Returns `true` if a `SIGINT` signal (typically from Ctrl-C)
48/// has been delivered since [`init_ctrlc()`] was called.
49///
50/// Once set, the flag remains `true` for the lifetime of the process.
51///
52/// This function is safe to call from any thread at any time
53/// after initialization.
54///
55/// # Examples
56///
57/// ```rust,no_run
58/// ctrlc_tiny::init_ctrlc()?;
59/// loop {
60/// if ctrlc_tiny::is_ctrlc_received() {
61/// println!("Received Ctrl-C");
62/// break;
63/// }
64/// }
65/// # Ok::<_, std::io::Error>(())
66/// ```
67pub fn is_ctrlc_received() -> bool {
68 unsafe { bindings::get_is_sigint_received() != 0 }
69}
70
71/// Resets the internal Ctrl-C received flag to `false`.
72///
73/// This can be useful if you want to detect multiple Ctrl-C presses
74/// independently (e.g. "exit on second Ctrl-C").
75///
76/// # Safety
77///
78/// Internally, this clears a `sig_atomic_t` flag that may be concurrently
79/// modified by the signal handler. This is safe but may cause a signal
80/// received during the reset to be missed.
81///
82/// # Examples
83///
84/// ```rust,no_run
85/// ctrlc_tiny::init_ctrlc()?;
86/// let mut count = 0;
87/// loop {
88/// if ctrlc_tiny::is_ctrlc_received() {
89/// ctrlc_tiny::reset_ctrlc_received();
90/// count += 1;
91/// if count == 2 {
92/// break;
93/// }
94/// }
95/// }
96/// # Ok::<_, std::io::Error>(())
97/// ```
98pub fn reset_ctrlc_received() {
99 unsafe {
100 bindings::reset_is_sigint_received();
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn init_ctrlc_should_succeed_and_be_idempotent() {
110 assert!(init_ctrlc().is_ok());
111 assert!(init_ctrlc().is_ok());
112 }
113
114 #[test]
115 fn is_ctrlc_received_initially_false() {
116 assert!(!is_ctrlc_received());
117 reset_ctrlc_received();
118 assert!(!is_ctrlc_received());
119 }
120}