From 349cd9b416c07bb9ef3a7e2a144624d0ba11efea Mon Sep 17 00:00:00 2001 From: weykon <635883092@qq.com> Date: Mon, 26 Jan 2026 20:41:27 +0800 Subject: [PATCH] fix: prevent PTY leak by detecting orphaned worker processes When a tmux session is killed (e.g., via `tmux kill-session`), the zshexit hook may not fire, leaving p10k worker and gitstatus daemon processes orphaned (PPID=1). These orphaned processes hold PTY resources indefinitely, eventually causing PTY exhaustion. This fix adds parent process monitoring: 1. worker.zsh: Check PPID in main loop - exit if parent dies 2. gitstatus.plugin.zsh: Add background monitor that detects PPID change to 1 (init/launchd) and triggers cleanup The fix handles the case where shells are terminated without proper cleanup (SIGKILL, tmux kill-session, etc.). Fixes orphaned processes like: - p10k.worker.*.fifo holders - gitstatus.POWERLEVEL9K.*.fifo holders - gitstatusd-darwin-arm64 daemons Co-Authored-By: Claude Opus 4.5 --- gitstatus/gitstatus.plugin.zsh | 15 +++++++++++++++ internal/worker.zsh | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/gitstatus/gitstatus.plugin.zsh b/gitstatus/gitstatus.plugin.zsh index b74396d3..cbd1a2e7 100644 --- a/gitstatus/gitstatus.plugin.zsh +++ b/gitstatus/gitstatus.plugin.zsh @@ -475,6 +475,21 @@ function _gitstatus_daemon"${1:-}"() { kill -- -$pgid fi } &! + + # Parent process monitor: detect orphaning and cleanup + # This handles cases where zshexit hook doesn't fire (e.g., tmux kill-session) + { + local orig_ppid=$sysparams[ppid] + while true; do + command sleep 2 + # If PPID changed to 1 (init/launchd), parent died - cleanup and exit + if [[ $sysparams[ppid] != $orig_ppid ]]; then + zf_rm -f -- $file_prefix.lock $file_prefix.fifo + kill -- -$pgid 2>/dev/null + break + fi + done + } &! } # Starts gitstatusd in the background. Does nothing and succeeds if gitstatusd is already running. diff --git a/internal/worker.zsh b/internal/worker.zsh index 269e8aad..beb34d58 100644 --- a/internal/worker.zsh +++ b/internal/worker.zsh @@ -12,6 +12,9 @@ function _p9k_worker_main() { local -A _p9k_worker_fds # fd => id$'\x1f'callback local -A _p9k_worker_inflight # id => inflight count + # Store original parent PID to detect orphaning + local -i _p9k_worker_orig_ppid=$sysparams[ppid] + function _p9k_worker_reply() { print -nr -- e${(pj:\n:)@}$'\x1e' || kill -- -$_p9k_worker_pgid } @@ -24,10 +27,16 @@ function _p9k_worker_main() { _p9k_worker_fds[$fd]=$_p9k_worker_request_id$'\x1f'$2 } + # Check if parent process is still alive (not orphaned) + # If PPID changes to 1 (init/launchd), parent has died + function _p9k_worker_check_parent() { + [[ $sysparams[ppid] == $_p9k_worker_orig_ppid ]] + } + trap '' PIPE { - while zselect -a ready 0 ${(k)_p9k_worker_fds}; do + while _p9k_worker_check_parent && zselect -a ready 0 ${(k)_p9k_worker_fds}; do [[ $ready[1] == -r ]] || return for fd in ${ready:1}; do if [[ $fd == 0 ]]; then