Commit 803f234
Emulate GenerateConsoleCtrlEvent() upon Ctrl+C
This patch is heavily inspired by the Git for Windows' strategy in
handling Ctrl+C.
When a process is terminated via TerminateProcess(), it has no chance to
do anything in the way of cleaning up. This is particularly noticeable
when a lengthy Git for Windows process tries to update Git's index file
and leaves behind an index.lock file. Git's idea is to remove the stale
index.lock file in that case, using the signal and atexit handlers
available in Linux. But those signal handlers never run.
Note: this is not an issue for MSYS2 processes because MSYS2 emulates
Unix' signal system accurately, both for the process sending the kill
signal and the process receiving it. Win32 processes do not have such a
signal handler, though, instead MSYS2 shuts them down via
`TerminateProcess()`.
For a while, Git for Windows tried to use a gentler method, described in
the Dr Dobb's article "A Safer Alternative to TerminateProcess()" by
Andrew Tucker (July 1, 1999),
http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
Essentially, we injected a new thread into the running process that does
nothing else than running the ExitProcess() function.
However, this was still not in line with the way CMD handles Ctrl+C: it
gives processes a chance to do something upon Ctrl+C by calling
SetConsoleCtrlHandler(), and ExitProcess() simply never calls that
handler.
So for a while we tried to handle SIGINT/SIGTERM by attaching to the
console of the command to interrupt, and generating the very same event
as CMD does via GenerateConsoleCtrlEvent().
This method *still* was not correct, though, as it would interrupt
*every* process attached to that Console, not just the process (and its
children) that we wanted to signal. A symptom was that hitting Ctrl+C
while `git log` was shown in the pager would interrupt *the pager*.
The method we settled on is to emulate what GenerateConsoleCtrlEvent()
does, but on a process by process basis: inject a remote thread and call
the (private) function kernel32!CtrlRoutine.
To obtain said function's address, we use the dbghelp API to generate a
stack trace from a handler configured via SetConsoleCtrlHandler() and
triggered via GenerateConsoleCtrlEvent(). To avoid killing each and all
processes attached to the same Console as the MSYS2 runtime, we modify
the cygwin-console-helper to optionally print the address of
kernel32!CtrlRoutine to stdout, and then spawn it with a new Console.
Note that this also opens the door to handling 32-bit process from a
64-bit MSYS2 runtime and vice versa, by letting the MSYS2 runtime look
for the cygwin-console-helper.exe of the "other architecture" in a
specific place (we choose /usr/libexec/, as it seems to be the
convention for helper .exe files that are not intended for public
consumption).
The 32-bit helper implicitly links to libgcc_s_dw2.dll and
libwinpthread-1.dll, so to avoid cluttering /usr/libexec/, we look for
the helped of the "other" architecture in the corresponding mingw32/ or
mingw64/ subdirectory.
Among other bugs, this strategy to handle Ctrl+C fixes the MSYS2 side of
the bug where interrupting `git clone https://...` would send the
spawned-off `git remote-https` process into the background instead of
interrupting it, i.e. the clone would continue and its progress would be
reported mercilessly to the console window without the user being able
to do anything about it (short of firing up the task manager and killing
the appropriate task manually).
Note that this special-handling is only necessary when *MSYS2* handles
the Ctrl+C event, e.g. when interrupting a process started from within
MinTTY or any other non-cmd-based terminal emulator. If the process was
started from within `cmd.exe`'s terminal window, child processes are
already killed appropriately upon Ctrl+C, by `cmd.exe` itself.
Also, we can't trust the processes to end it's subprocesses upon receiving
Ctrl+C. For example, `pip.exe` from `python-pip` doesn't kill the python
it lauches (it tries to but fails), and I noticed that in cmd it kills python
also correctly, which mean we should kill all the process using
`exit_process_tree`.
Co-authored-by: Naveen M K <[email protected]>
Signed-off-by: Johannes Schindelin <[email protected]>1 parent 0e6a5b9 commit 803f234
2 files changed
+384
-4
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
1594 | 1595 | | |
1595 | 1596 | | |
1596 | 1597 | | |
1597 | | - | |
1598 | | - | |
1599 | | - | |
1600 | | - | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
1601 | 1617 | | |
1602 | 1618 | | |
1603 | 1619 | | |
| |||
0 commit comments