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 <noreply@anthropic.com>
This commit is contained in:
weykon 2026-01-26 20:41:27 +08:00
parent b97926675a
commit 349cd9b416
2 changed files with 25 additions and 1 deletions

View File

@ -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.

View File

@ -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