jesseduffield.lazygit/pkg
Stefan Haller 6048fee49d Kill the pty child's process tree on Windows to avoid orphans (#5879)
Stopping a pty task on Windows relies on ClosePseudoConsole, which
delivers CTRL_CLOSE_EVENT to the console's attached clients. But only
to those attached at that moment: when the user flicks quickly through
commits, a task is often stopped within the first few milliseconds of
its life, before the child has attached to the pseudoconsole. Such a
child misses the event and survives, running the entire diff to
completion in the background (spawning one external differ per changed
file) and keeping its conhost.exe alive; rapid navigation accumulates
these git/difft/conhost trees, and they outlive lazygit. Grandchildren
are affected too: git for Windows runs commands through a two-level
git.exe wrapper, so a single task has several attach windows, and a
grandchild spawned while the console is going down is orphaned even
when its parent got the event and exited.

Fix this by putting the child into a job object before it runs its
first instruction (created suspended, assigned, then resumed), so that
every descendant is in the job from the start; the teardown in Close
terminates the job right after initiating the pseudoconsole close.
There is no point in a grace period between the two: the close event
is not a graceful signal -- git and the common diff tools leave it to
the default handler, which calls ExitProcess at an arbitrary point --
so clients that received it are already dying, and the kill exists for
those that missed it. Killing at an arbitrary point cannot leak a
stale index.lock, because pty-rendered commands no longer take that
lock (see withPtyGitConfig in pkg/gui/pty.go).

The pseudoconsole close runs on its own goroutine because the kill
must not wait for it: on builds where ClosePseudoConsole blocks until
the console host exits (pre-24H2), the host keeps running as long as a
surviving client does, and that client only goes away through the job
kill; sequencing the kill after a blocking close would deadlock in
exactly the case the kill exists for.

KILL_ON_JOB_CLOSE doubles as a safety net: if lazygit exits without
running the teardown, the OS closes the job handle and reaps the tree.

In a harness that mimicked the stop path with randomized 0-120ms stop
delays, 3 of 30 process trees survived as orphans before this change;
none survive with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 07:03:27 +02:00
..
app Move dragged commits in one rebase 2026-07-31 08:37:28 +02:00
cheatsheet Render every key for a binding in the cheatsheet 2026-05-25 15:18:18 +02:00
commands Kill the pty child's process tree on Windows to avoid orphans (#5879) 2026-08-04 07:03:27 +02:00
common Move NewDummyCommon to pkg/common 2025-05-06 09:43:26 +02:00
config Move Github PR cache out of state.yml into a separate file 2026-08-02 19:09:22 +02:00
constants Adapt links 2026-07-31 08:42:51 +02:00
env Support bare worktrees where worktree does not have its own .git file 2023-08-07 22:40:53 +10:00
fakes Modernize all codes 2025-11-15 10:46:23 +01:00
gocui Exclude more commit trailers from auto-wrapping 2026-08-02 19:22:34 +02:00
gui Give task commands a Terminate method instead of exposing their process 2026-08-04 07:03:27 +02:00
i18n Show a Github PR's combined checks state in branches list and main view 2026-08-02 13:06:21 +02:00
integration Rework the custom pager config (rename to diff renderer) 2026-07-31 08:42:51 +02:00
jsonschema Fold legacy quit-alt1 into the multi-key quit binding 2026-05-25 15:32:47 +02:00
logs Fix Windows linter errors 2026-06-16 16:21:13 +02:00
snake Enable intrange linter, and fix warnings 2025-06-30 18:30:11 +02:00
tasks Give task commands a Terminate method instead of exposing their process 2026-08-04 07:03:27 +02:00
theme Copy gocui files into lazygit repo under pkg/gocui 2026-04-30 14:29:08 +02:00
updates Change direct access to Common.UserConfig to a getter 2024-08-18 10:24:52 +02:00
utils Rework the custom pager config (rename to diff renderer) 2026-07-31 08:42:51 +02:00