Subject: [PATCH 0/2] mingw: terminate child processes in a gentler way

From: "Johannes Schindelin via GitGitGadget" <[email-masked]>
Date: Thu, 04 Jun 2026 16:24:18 +0000

This patch series consists of two patches that have been carried in Git for
Windows since 2017 an 2018, respectively.

The problem they work around is a fundamental mismatch between Git's
understanding how processes can be terminated and Windows' multi-threading
centric world view (where multi-process architectures are quite, quite
rare), where processes do not tell other processes to terminate gently
(meaning: giving them a chance to run their atexit() handlers).

As such, Git thinks that it can send processes signals to terminate or
force-stop ("kill") them. There are no signals in the Unix sense on Windows,
though. So we try to emulate them. At present, in vanilla Git that means
that we use the TerminateProcess() Win32 API functions, which is most
similar to Unix' SIGKILL and is typically frowned upon because it does not
allow an orderly shutdown of multi-threaded applications. That's definitely
not what Git wants to do: If it wants to terminate a child process, it wants
that child process to clean up any .lock files, for example. And therefore
it wants to send a SIGTERM.

But the SIGTERM signal does not really have any equivalent on Windows. The
closest is to somehow get the target process to call the ExitProcess() Win32
API function. There is a trick that we employ here to do precisely that: we
create a remote thread in the target process, and specify the ExitProcess()
function as the callee. This works because that function matches the
function signature of thread functions enough that we can get away with it,
and because the address of that function is identical between processes
matching the same CPU architecture. Read: This approach does not work when
trying to terminate i686 processes from an x86_64 git.exe. But since it is
rare to mix and match processes of different CPU architectures on Windows
(certainly in Git scenarios), we kind of resort to this best effort that
works often enough to make it worthwhile.

It's a different story for SIGINT: That signal matches most closely what
Windows calls a ConsoleCtrlEvent. It is different, though, in that a
ConsoleCtrlEvent is not sent to a process, but to a Console, and is handled
by all processes that are attached to said Console. In the MSYS2 runtime
that provides the POSIX emulation layer required by the Bash distributed
with Git for Windows, we work around that by using a similar trick as the
SIGTERM/ExitProcess() injection: a thread is injected into the remote
process, passing the address of the (undocumented) kernel32!CtrlRoutine.
This is quite hacky and requires spawning a separate process to just to
figure out the address of said function, which only works in the MSYS2
runtime because it acquires that address once, and then remembers it for the
rest of its lifetime. Git also simply has no business emulating a Ctrl+C and
instead sends child processes SIGTERM. Therefore, there is no support for
sending SIGINT in this patch series. But patch number 2 implements reacting
to the emulated SIGINT "sent" by the MSYS2 runtime.

Johannes Schindelin (2):
  mingw: kill child processes in a gentler way
  mingw: really handle SIGINT

 compat/mingw.c              |  38 +++++++--
 compat/win32/exit-process.h | 165 ++++++++++++++++++++++++++++++++++++
 2 files changed, 195 insertions(+), 8 deletions(-)
 create mode 100644 compat/win32/exit-process.h

base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2130%2Fdscho%2Fmingw-kill-gentle-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2130/dscho/mingw-kill-gentle-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2130
-- 
gitgitgadget