fix: remove PR_SET_PDEATHSIG that kills Chrome after ~10s idle (#1157) (#1173)

v0.24.1 introduced `prctl(PR_SET_PDEATHSIG, SIGKILL)` in #1137 to kill Chrome
when the daemon dies. However, `PR_SET_PDEATHSIG` tracks the **thread** that
called `fork()`, not the process (`prctl(2)` documents this). Chrome is spawned
via `tokio::task::spawn_blocking`, whose threads are reaped after ~10 seconds of
idle time. When the blocking thread exits, the kernel sends SIGKILL to Chrome
even though the daemon is still alive.

Symptoms reported in #1157:
- `tab list` shows `about:blank` after a few seconds
- `snapshot` returns an empty page
- All Chrome processes exit ~9 seconds after launch
- Any workflow involving navigation or waiting breaks

The fix removes `PR_SET_PDEATHSIG` from the Chrome `pre_exec` hook. Orphan
cleanup is already handled by the process-group kill (`kill(-pgid, SIGKILL)`) in
`ChromeProcess::kill()`, which runs via daemon signal handlers, `close_notify`,
idle timeout, and `Drop`.

Fixes #1157
This commit is contained in:
Chris Tate
2026-04-06 18:44:56 -05:00
committed by GitHub
parent 7b3f826cbb
commit eb15cc0894
+6 -11
View File
@@ -351,23 +351,18 @@ fn try_launch_chrome(chrome_path: &Path, options: &LaunchOptions) -> Result<Chro
// Place Chrome in its own process group so we can kill the entire tree // Place Chrome in its own process group so we can kill the entire tree
// (main process + GPU/renderer/utility/crashpad helpers) with a single // (main process + GPU/renderer/utility/crashpad helpers) with a single
// killpg(), preventing orphaned processes (issue #1113). // killpg(), preventing orphaned processes (issue #1113).
//
// NOTE: Do NOT use PR_SET_PDEATHSIG here. Chrome is spawned via
// tokio::task::spawn_blocking, and PR_SET_PDEATHSIG fires when the
// *thread* that forked the child exits, not the process. Tokio reaps
// idle blocking threads after ~10s, which kills Chrome (issue #1157).
#[cfg(unix)] #[cfg(unix)]
{ {
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
// SAFETY: pre_exec runs between fork() and exec() in the child. // SAFETY: pre_exec runs between fork() and exec() in the child.
// Both prctl and setpgid are async-signal-safe. // setpgid is async-signal-safe.
unsafe { unsafe {
cmd.pre_exec(|| { cmd.pre_exec(|| {
// On Linux, ask the kernel to send SIGKILL to this process
// when the parent (daemon) dies for any reason, including
// SIGKILL. This is the most robust orphan prevention
// available and has no macOS equivalent.
#[cfg(target_os = "linux")]
{
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL);
}
// Create a new process group (PGID = own PID) so the
// daemon can kill the entire Chrome tree in one call.
libc::setpgid(0, 0); libc::setpgid(0, 0);
Ok(()) Ok(())
}); });