Skip to content

Dired refresh can prompt to kill a running process; diff-hl-dired-extra-indicators nil avoids it #295

Description

@marcuslannister

Summary

On Emacs 31 I get a Buffer ... has a running process; kill it? style
confirmation while refreshing a Dired buffer with diff-hl-dired-mode on.
Setting diff-hl-dired-extra-indicators to nil makes it stop.

I want to be upfront: I do not have a reliable recipe. I tried hard to
build one and failed (details below). I am filing this because the code path
that I believe is involved looks genuinely unguarded, and you will judge that
much faster than I can.

Environment

  • Emacs 31.0.50
  • diff-hl 20260723.238 (MELPA)
  • git 2.50.1
  • Backend: Git. The same config runs on macOS and on Windows, and I am not
    certain which of the two first showed the prompt.

Workaround that works

(with-eval-after-load 'diff-hl-dired
  (setq diff-hl-dired-extra-indicators nil))

That skips diff-hl-dir-status-ignored-files, so only one Git process per
refresh instead of two. The prompt has not come back since.

The code path I suspect

diff-hl-dired-update keeps one reusable buffer per Dired buffer in
diff-hl-dired-process-buffer, and the final callback kills it:

https://github.com/dgutov/diff-hl/blob/master/diff-hl-dired.el#L139

Two things combine there:

  1. VC's async processes keep process-query-on-exit-flag at t.
    vc-dispatcher.el never clears it, so process-kill-buffer-query-function
    will ask if kill-buffer meets a live process.
  2. The buffer is shared across runs. When a process exits normally but its
    queued continuation has not yet reached the kill-buffer call, a new
    refresh can find the buffer still live, erase it, and start a second
    process in it. The older continuation then kills a buffer that owns a
    younger, live process.

With diff-hl-dired-extra-indicators non-nil there are two chained Git
processes per refresh instead of one, which widens that window. That fits the
fact that nil makes the symptom go away.

What I could not reproduce

I built a repo with ~2,300 directory entries and ~6,000 ignored files, put a
sleep 0.4 wrapper in front of git via vc-git-program, and drove 8
overlapping revert-buffer calls in batch, recording every case where
kill-buffer met a live process with the query flag set. The overlap
definitely happened (each refresh found the previous process in run state),
but the recorded count stayed at 0.

The reason looks like vc-exec-after (vc-dispatcher.el:233): given an
okstatus, it skips the continuation for any process that died from a signal.
So the process that diff-hl-dired-update explicitly kills never reaches
kill-buffer. That rules out the obvious race and leaves only the narrower
"exited normally, continuation not yet run" window above, which I could not
hit on demand.

Possible fixes

Both look cheap, if you agree the window is real:

  • (set-process-query-on-exit-flag proc nil) on the status process, since this
    buffer is internal and the user has no reason to be asked about it; or
  • have the continuation kill the buffer only when the process it is holding is
    still the buffer's current process, so a stale run cannot kill a live one.

For context, the kill-buffer call came from #89.

Happy to test any patch, or to run further instrumentation if you tell me what
would be useful to capture.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions