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}