Rust Handling Signals
A signal is a message the system sends to a running program. Ctrl-C sends the signal named SIGINT.
Install a tiny handler
The ctrlc crate installs the handler. The handler does one thing only. It sets a flag.
ctrlc::set_handler(move || {
flag.store(true, Ordering::SeqCst);
})
.expect("handler should install");Share the flag
The handler and the work loop are separate code paths. An Arc<AtomicBool> is the switch both sides can touch.
let stop = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&stop);The loop watches the flag
Each round checks the switch. A set flag ends the loop. Normal code then does the tidying, never the handler.
while !stop.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(200));
}Leave through the front door
After the loop, print the tidy-up lines. Return success. The shell sees a normal, clean exit:
println!("Ctrl-C seen; tidying up");
println!("done");
ExitCode::SUCCESSExample
// Cargo.toml: ctrlc = "3"
use std::process::ExitCode;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() -> ExitCode {
let stop = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&stop);
ctrlc::set_handler(move || {
flag.store(true, Ordering::SeqCst);
})
.expect("handler should install");
println!("working; press Ctrl-C to stop");
while !stop.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(200));
}
println!("Ctrl-C seen; tidying up");
println!("done");
ExitCode::SUCCESS
}Key points
- Handling a signal means catching it and answering in normal code.
- SIGINT arrives on Ctrl-C.
- SIGTERM arrives on a plain kill or a system shutdown.
- SIGTERM has no key.
- A handler must be tiny.
- It can interrupt at any moment, even mid-print.
- Set a flag and get out.
- With no handler, Ctrl-C kills the program at once.
- The shell reports exit code 130.
- Some tools need a second pattern.
- The handler sends on a channel, and the main code waits on that channel.
- The system uses signals to message a running program.
In this example, the program prints its working line and waits in a sleep loop. The handler holds one clone of the flag. The loop holds the other. Send the program SIGINT, by Ctrl-C or by kill. The handler sets the flag. The loop notices within one sleep round. The program prints its two tidy-up lines and returns success. The exit code is 0, verified under a real terminal and by direct signal.
Note
Rust ignores SIGPIPE by default, as the Broken Pipes lesson showed.
SIGINT keeps its killing default until you install a handler.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why should a signal handler only set a flag?