diff --git a/crates/nu-protocol/src/pipeline/handlers.rs b/crates/nu-protocol/src/pipeline/handlers.rs index 202eb0a12e..75df29c488 100644 --- a/crates/nu-protocol/src/pipeline/handlers.rs +++ b/crates/nu-protocol/src/pipeline/handlers.rs @@ -68,6 +68,19 @@ impl Handlers { }) } + /// Registers a new handler which persists for the entire process lifetime. + /// + /// Only use this for handlers which should exist for the lifetime of the program. + /// You should prefer to use `register` with a `HandlerGuard` when possible. + pub fn register_unguarded(&self, handler: Handler) -> Result<(), ShellError> { + let id = self.next_id.next()?; + if let Ok(mut handlers) = self.handlers.lock() { + handlers.push((id, handler)); + } + + Ok(()) + } + /// Runs all registered handlers. pub fn run(&self, action: SignalAction) { if let Ok(handlers) = self.handlers.lock() { diff --git a/src/signals.rs b/src/signals.rs index 323231e14e..c6b8c8190a 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -9,6 +9,21 @@ pub(crate) fn ctrlc_protection(engine_state: &mut EngineState) { engine_state.set_signals(Signals::new(interrupt.clone())); let signal_handlers = Handlers::new(); + + // Register a handler to kill all background jobs on interrupt. + signal_handlers + .register_unguarded({ + let jobs = engine_state.jobs.clone(); + Box::new(move |action| { + if action == SignalAction::Interrupt { + if let Ok(mut jobs) = jobs.lock() { + let _ = jobs.kill_all(); + } + } + }) + }) + .expect("Failed to register interrupt signal handler"); + engine_state.signal_handlers = Some(signal_handlers.clone()); ctrlc::set_handler(move || {