Zsh prompt latency and background job notification limits
0 reputation · 02 Aug 2024, 23:43 UTC
0 reputation · 02 Aug 2024, 23:43 UTC
Managing asynchronous process notifications in zsh often involves using the precmd hook to check the status of background jobs before rendering the prompt. This ensures the user is alerted to command completion without requiring constant polling or intrusive noise.
However, as the number of tracked background PIDs increases, the overhead of executing these checks within the precmd cycle can introduce perceptible latency to the shell prompt. There is a design tension between providing immediate feedback on process exit and maintaining the responsiveness of the Zsh Line Editor (ZLE).
35275 reputation · 03 Aug 2024, 01:20 UTC
Yes. Zsh has a built-in, non-blocking mechanism for job completion alerts, and precmd job polling can be throttled to avoid prompt lag.
setopt notify is a documented Zsh option. With it enabled the shell prints a job completion message as soon as the job terminates, driven by Zsh's internal SIGCHLD handling. No user code runs in the main thread to poll.precmd runs synchronously before each primary prompt is drawn. Any work done there, e.g. jobs, jobs -p, or looping over $jobstates, adds directly to ZLE prompt latency.Polling background PIDs on every precmd is O(number of jobs) per prompt. With many jobs the jobs builtin and any custom status formatting become perceptible. This is a common cause of prompt slowdown, not a Zsh bug.
setopt notify
With notify a message like [1] Done sleep 2 is printed immediately on exit, without waiting for the next prompt. This removes the need for a precmd check to provide timely feedback.
LAST_JOB_CHECK=0
JOB_CACHE=""
precmd_job_guard() {
local now=$EPOCHREALTIME
# throttle to ~1s
(( ${now%.*} - ${LAST_JOB_CHECK%.*} < 1 )) && return
LAST_JOB_CHECK=$now
local current=$(jobs -p)
[[ $current == $JOB_CACHE ]] && return
JOB_CACHE=$current
# your lightweight summary here, avoid heavy formatting
}
add-zsh-hook precmd precmd_job_guard
The guard skips work when the prompt arrives quickly and skips work when the PID set has not changed.
jobs with formatting options on every prompt.$jobstates which is maintained by Zsh, rather than spawning external commands.preexec or run it asynchronously with (... &!) if you must keep it.Combined approach: setopt notify for instant, SIGCHLD-driven alerts, and a throttled, cache-guarded precmd for any periodic UI. This keeps the prompt responsive while still surfacing job completion.
Verification you can do locally: start a new Zsh with setopt notify, run sleep 2 &, and confirm the Done message appears at ~2s without pressing Enter. Then add the timestamp guard and run several background jobs to confirm prompt return is immediate.
Note on behavior: notify interleaves messages with terminal output, which some users find noisy. Throttling interval trade-off: intervals longer than ~1s can miss very short jobs in the precmd summary, though notify will still print them.
One missing diagnostic that would change the recommendation: which prompt framework and exact precmd implementation you are using. If it already uses jobs with heavy formatting or external commands, the specific commands to replace will differ.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.