use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use anyhow::{Context, Result};
const SIGINT_EXIT_CODE: i32 = 130;
pub(crate) fn handle_sigint(
cancel: &AtomicBool,
intercepting: &AtomicBool,
sigint_fired: &AtomicBool,
) {
if intercepting.load(Ordering::Relaxed) {
cancel.store(true, Ordering::Relaxed);
sigint_fired.store(true, Ordering::Relaxed);
} else {
std::process::exit(SIGINT_EXIT_CODE);
}
}
pub(crate) fn install_line_repl_handler(
cancel: Arc<AtomicBool>,
intercepting: Arc<AtomicBool>,
sigint_fired: Arc<AtomicBool>,
) -> Result<()> {
ctrlc::set_handler(move || handle_sigint(&cancel, &intercepting, &sigint_fired))
.context("install SIGINT handler for line REPL")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intercepting_true_sets_cancel_and_fired() {
let cancel = AtomicBool::new(false);
let intercepting = AtomicBool::new(true);
let fired = AtomicBool::new(false);
handle_sigint(&cancel, &intercepting, &fired);
assert!(cancel.load(Ordering::Relaxed));
assert!(fired.load(Ordering::Relaxed));
assert!(intercepting.load(Ordering::Relaxed));
}
#[test]
fn intercepting_true_idempotent() {
let cancel = AtomicBool::new(false);
let intercepting = AtomicBool::new(true);
let fired = AtomicBool::new(false);
handle_sigint(&cancel, &intercepting, &fired);
handle_sigint(&cancel, &intercepting, &fired);
handle_sigint(&cancel, &intercepting, &fired);
assert!(cancel.load(Ordering::Relaxed));
assert!(fired.load(Ordering::Relaxed));
}
}