-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interrupting git with "Ctrl+C" renders Git Bash unusable (input not printed anymore) #1491
Comments
Thank you for your bug report. Linux behavior in this case would not actually be to quit the pager, but to kill just git and keep the pager running until This should be the behavior we want to imitate (but I guess we kill the pager, too...) As a workaround for you, to get your terminal functional again: call |
I'm seeing this issue in Windows 10 as well. Git-2.16.2-64-bit This kills my Git Bash productivity when diff'ing files before checkin. Hope ya'll can fix this soon. Thank you |
@jefhai to get you unblocked, just reinstall Git for Windows and select "ConHost" instead of "MinTTY" as Git Bash's terminal. |
Thank you @dscho ! |
Hi all, As mentioned on stackoverflow, a A Linux friend gave me another command, Again, in this context, the difference in using the two commands may be little or none, but psychologically the latter feels more comforting to me. Cheers, |
FWIW I did work three full days this week on fixing this, and I got stuck on the issue where something in the MSYS2 runtime decides to kill a I will have to figure out a minimal reproducer and then ask on the Cygwin mailing list whether anybody can help me figure this out. This is the current state of affairs: git-for-windows/msys2-runtime@master...dscho:ctrl-c-with-CtrlRoutine @dieselVtwin Feel free to build the MSYS2 runtime based on this branch and test. |
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was git-for-windows/msys2-runtime@c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: git-for-windows/msys2-runtime@e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: git-for-windows/msys2-runtime@53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: git-for-windows/msys2-runtime@ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The newest snapshot at https://wingit.blob.core.windows.net/files/index.html should fix this. Please test. |
I am still experiencing this in VS Code in Windows. Got here from this issue: microsoft/node-pty#7 Wiped Git for Windows completely (like even manually went through the registry and removed it. Wiped all the caches for VS Code and disabled all extensions, removed setting for Git Bash to be default terminal. Restarted. Reinstalled this build: Thu, 3 May 2018 12:46:36 +0200 No change. I am able to reproduce this consistently by attempting a Git connection that requires password input, then hitting Ctrl C when prompted for the password. I usually have key authentication setup so when I get prompted for a password, I know I haven't setup a key yet for that user (I admin a bunch of private repos within our organization). Ctrl C will kick you back to the bash prompt (or so it seems) but if you type anything, it continues on like you provided the wrong password and goes back into the password prompt loop - again, this is AFTER already having displaying the bash prompt again. If you just enter through, this you get to the "real" bash prompt again, but whatever you type is invisible - that is, it's registered (like if you type exit, it exits) but you can't see it. This doesn't happen directly in Git Bash itself, only from within VS Code. According to the issue I linked to above, this is a Git Bash issue. Not sure, but it doesn't happen with the same actions when using PowerShell as the terminal. |
@methodbox when you call this with Lines 113 to 165 in e7621d8
In that case, I could imagine that prefixing the string in line 121 with something like Why not give it a try? |
Sorry late reply - just saw this. I’ll give it a shot and report back. Actually I just realized I’m not quite sure how to do what you’re asking. I don’t know C well - more of a JS guy. Can you give me some guideance here? |
@methodbox first, install the Git for Windows SDK. Then, run |
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: 53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
I am experiencing the same problem, when I press CTRL+C, the editor unexpectedly quits, appears 3221225786, my solution is to upgrade the git tool, the following is my git version git --version
// git version 2.23.0.windows.1 The issue has been resolved after the upgrade |
This thing again... Background: when you hit Ctrl+C on Linux or macOS, a signal (SIGINT) is sent to the foreground process and its child processes. This signal can be intercepted by installing a signal handler for this specific signal. On Windows, there is no precise equivalent for this system. Instead, the Ctrl+C is translated by the current ConHost (i.e. the container running the Console processes) to a ConsoleCtrl event that is sent to all processes attached to that Console. If any of these processes installed a handler via SetConsoleCtrlHandler(), they can intercept that event (and avoid exiting or doing some cleanup work). On Linux and macOS (and every Unix flavor, really), processes can also be killed via the `kill` executable, which really just sends a signal to the process, typically SIGTERM. Processes can intercept that signal, too. To force processes to terminate, without giving them any chance to prevent that, SIGKILL can be sent. There is no equivalent for SIGTERM on Windows. To emulate SIGKILL on Windows, TerminateProcess() can be used, but it only kills one process (unlike SIGKILL, which is sent also to the child processes). In Git for Windows, we struggled with emulating SIGINT, SIGTERM and SIGKILL handling essentially since the beginning of the efforts to port Git to Windows. At least the SIGINT part of the problem becomes a lot worse when using a terminal window other than cmd.exe's: as long as using cmd.exe (AKA "ConHost"), Ctrl+C is handled entirely outside of our code. But with the big jump from v1.x to v2.x, Git for Windows not only switched from MSys to MSYS2, but also started using MinTTY as the default terminal window, which uses the MSYS2 runtime-provided pseudo terminals (inherited from Cygwin thanks to the MSYS2 runtime being a "friendly fork" of Cygwin). When Ctrl+C is pressed in MinTTY, all of the signaling has to be done by our code. The original code to handle Ctrl+C comes straight from Cygwin. It simply ignores the entire conundrum for non-Cygwin processes and simply calls TerminateProcess() on them, leaving spawned child processes running. The first attempt at fixing "the Ctrl+C problem" (with the symptom that interrupting `git clone ...` would not stop the actual download of the Git objects that was still running in a child process) was git-for-windows/msys2-runtime@c4ba4e3357f. It simply enumerated all the processes' process IDs and parent process IDs and extracted the tree of (possibly transitive) child processes of the process to kill, then called TerminateProcess() on them. This solved the problem with interrupting `git clone`, but it did not address the problem that Git typically wants to "clean up" when being interrupted. In particular, Git installs atexit() and signal handlers to remove .lock files. The most common symptom was that a stale .git/index.lock file was still present after interrupting a Git process. Based on the idea presented in Dr Dobb's Journal in the article "A Safer Alternative to TerminateProcess()" by Andrew Tucker (July 1, 1999) http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 we changed our handling to inject a remote thread calling ExitProcess() first, and fall back to TerminateProcess() the process tree instead: git-for-windows/msys2-runtime@e9cb332976c This change was a little misguided in hindsight, as it only called TerminateProcess() on the process tree, but expected the atexit() handler of Git to take care of the child processes when killing the process via the remote ExitProcess() method. Therefore, we changed the strategy once again, to inject ExitProcess() threads into the child processes of the process to kill, too: git-for-windows/msys2-runtime@53e5c0313e1 (That commit also tries to handle Cygwin process among the child processes by sending Cygwin signals, but unfortunately that part of the commit was buggy.) This worked well for Git processes. However, Git Bash is used in all kinds of circumstances, including launching Maven, or node.js scripts that want to intercept SIGINT. Naturally, these callees have no idea that Git for Windows injects an ExitProcess() with exit code 130 (corresponding to 0x100 + SIGINT). Therefore, they never "got" the signal. So what is it that happens when ConHost generates a ConsoleCtrl event? This question was asked and answered in the excellent blog post at: http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/#comment-2880 Essentially, the same happens as what we did with ExitProcess(): a remote thread gets injected, with the event type as parameter. Of course it is not ExitProcess() that is called, but CtrlRoutine(). This function lives in kernel32.dll, too, but it is not exported, i.e. GetProcAddress() won't find it. The trick proposed in the blog post (to send a test ConsoleCtrl event to the process itself, using a special handler that then inspects the stack trace to figure out the address of the caller) does not work for us, however: it would send a CTRL_BREAK_EVENT to *all* processes attached to the same Console, essentially killing MinTTY. But could we make this still work somehow? Yes, we could. We came up with yet another trick up our sleeves: instead of determining the address of kernel32!CtrlRoutine in our own process, we spawn a new one, with a new Console, to avoid killing MinTTY. To do that, we need a helper .exe, of course, which we put into /usr/libexec/. If this helper is not found, we fall back to the previous methods of injecting ExitProcess() or calling TerminateProcess(). This method (to spawn a helper .exe) has a further incidental benefit: by compiling 32-bit *and* 64-bit helpers and providing them as getprocaddr32.exe and getprocaddr64.exe, we can now also handle 32-bit processes in a 64-bit Git for Windows. Sadly not vice versa: calling CreateRemoteThread() on a 64-bit process from a 32-bit process seems to fail all the time (and require a lot of assembly hackery to fix that I am not really willing to include in Git for Windows' MSYS2 runtime). The current method was implemented in this commit: git-for-windows/msys2-runtime@ca6188a7520 This is the hopeful final fix for git-for-windows/git#1491, git-for-windows/git#1470, git-for-windows/git#1248, git-for-windows/git#1239, git-for-windows/git#227, git-for-windows/git#1553, nodejs/node#16103, and plenty other tickets that petered out mostly due to a willingness of community members to leave all the hard work to a single, already overworked person. This fix also partially helps git-for-windows/git#1629 (only partially because the user wanted to quit the pager using Ctrl+C, which is not the intended consequence of a Ctrl+C: it should stop the Git process, but not the pager). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Setup
defaults?
to the issue you're seeing?
MSYS2/mingw-w64 installed on system (problem persists when it's excluded from PATH, though)
Details
Git Bash
Minimal, Complete, and Verifiable example
this will help us understand the issue.
(more than one page of output so it pauses)
Exit with "Ctrl+C" instead of "Q"
Command interrupted, normal operation of shell restored.
Command interrupted, shell in an unusable state:
Upstream MSYS2-git is not affected:
This shortcut has no function in Git Bash, though.
The text was updated successfully, but these errors were encountered: