From e5c875389c62d0a61ca0c243832be1166aeab33d Mon Sep 17 00:00:00 2001 From: "J. Champagne" Date: Sat, 14 Mar 2026 11:45:08 -0400 Subject: [PATCH] feat: Add graceful shutdown of active hooks. --- crates/pikl/src/handler.rs | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/crates/pikl/src/handler.rs b/crates/pikl/src/handler.rs index 4c912e3..8b45ebf 100644 --- a/crates/pikl/src/handler.rs +++ b/crates/pikl/src/handler.rs @@ -156,15 +156,49 @@ async fn run_handler_process( // Close stdin to signal the child drop(stdin_writer); - // Wait briefly for the reader to finish + // Wait up to 1s for child to exit after EOF + let exited = tokio::time::timeout( + std::time::Duration::from_secs(1), + child.wait(), + ) + .await + .is_ok(); + + if !exited { + // Send SIGTERM on Unix, then wait another second + #[cfg(unix)] + { + if let Some(pid) = child.id() { + // SAFETY: pid is valid (child is still running), SIGTERM is + // a standard signal. kill() returns 0 on success or -1 on + // error, which we ignore (child may have exited between the + // check and the signal). + unsafe { + libc::kill(pid as libc::pid_t, libc::SIGTERM); + } + } + let termed = tokio::time::timeout( + std::time::Duration::from_secs(1), + child.wait(), + ) + .await + .is_ok(); + if !termed { + let _ = child.kill().await; + } + } + #[cfg(not(unix))] + { + let _ = child.kill().await; + } + } + + // Wait for the reader to finish let _ = tokio::time::timeout( std::time::Duration::from_secs(2), reader_handle, ) .await; - // Kill child if still running - let _ = child.kill().await; - Ok(()) }