-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
Ctrl + C Doesn't Kill Gracefully | Node v6.11.3 - Git Bash #16103
Comments
Hi @dark-swordsman, I can't repro. Is there a specific way that you start the process? |
I start the process like any other. The $ npm start
> express-test@1.0.0 start E:\github\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337! I usually do $ npm start
> express-test@1.0.0 start E:\github\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337!
DarkSwordsman@FUJIWARA_TOFU_SHOP MINGW64 /e/github/uaction/express-test (phonegap-testing)
$ Doing Then from there, doing
$ netstat -aon
Active Connections
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1100
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:1337 0.0.0.0:0 LISTENING 8012 then I did find this in the documentation, which may be relevant, but I am not sure: " According to that, I am not sure what I may have installed... unless it may be firebase tools? I will uninstall them temporarily and see if that fixes it, since I also changed that recently. UPDATE |
By my guess it wouldn't be a general CLI tool but rather something that traps keystrokes like ComEnu or clink.
I'll try to reproduce so I can investigate, but the simplest answer I can give you, don't launch node from |
I just tried using Node.js command prompt (A CMD with Node: " E:\GitHub\uaction\express-test>npm start
> express-test@1.0.0 start E:\GitHub\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337!
Terminate batch job (Y/N)? Y
E:\GitHub\uaction\express-test>npm start
> express-test@1.0.0 start E:\GitHub\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337!
Terminate batch job (Y/N)? Y
E:\GitHub\uaction\express-test> So I can confirm that this is an issue with (at least) MinTTY, hopefully not with Git Bash completely. I will continue to find anything that would interfere with Bash (I may start with just a clean re-install; perhaps different settings) since I would prefer not to work in CMD... Lol EDIT: I want to mention I had Git version UPDATE: Updating to Git version
I will come back and reinstall changing from MinTTY to CMD. |
The issue was MinTTY. I don't know why, but changing to the Default Console Window fixed the issue. EDIT: I referenced this issue in git-for-windows/git/issues/1219 which seems to be the most relevant issue. @dscho said something about MSYS2 being modified since v2.13.1 (which is what MinTTY uses) that causes this. Here are the settings: - Use Git from Git Bash only
- Use the OpenSSL Library
- Checkout Windows-style, commit Unix-style line endings
- Use Default Console Window (CMD)
- Enable file system caching
- Enable Git Credential Manager
- Disable symbolic links Here's it gracefully closing and restarting: DarkSwordsman@FUJIWARA_TOFU_SHOP MINGW64 /e/github/uaction/express-test (phonegap-testing)
$ npm start
> express-test@1.0.0 start E:\github\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337!
DarkSwordsman@FUJIWARA_TOFU_SHOP MINGW64 /e/github/uaction/express-test (phonegap-testing)
$ npm start
> express-test@1.0.0 start E:\github\uaction\express-test
> node index.js
UAction Test Server Running on Port 1337!
DarkSwordsman@FUJIWARA_TOFU_SHOP MINGW64 /e/github/uaction/express-test (phonegap-testing)
$ Everyone tells me Mac is better, and I'm starting to believe that more and more everyday, but I really don't want to fork over thousands when I already have a great working computer. Might try ElementaryOS though. 🤔 Sorry for cluttering Node with another issue, but hopefully this will help being here since many people may think it's Node's fault like I did. |
There are a few of us who develop node on Windows, so it's not a completely uncharted territory. Personally I use |
@refack doing that, I get an error:
I have never used or heard of |
Hi all, so I experienced the same issue lately and as indicated here zombie processes were present when launching node in the git bash terminal MinTTY - launched from the default Git Bash shortcut and from the context menu on a folder - located in C:\Program Files\Git Hope this helps. |
I also met the same problem, this is the latest git bash bug, if back to the previous version can kill the process node |
chiming in here to say I'm having the same issue. Switching away from MinTTY fixed the issue |
i has same problem ,but my colleague use old git-bash version not the problem , so I use the git-bash version 2.8.1 now :) |
@KiritoCheng @martinmckenna @hottredpen I suggest you guys post that in the actual issue with a little more information. git-for-windows/git#1219 I'm sure it will help dscho pinpoint the issue a little more. |
I am pretty certain that I know what the culprit is. It is time that is lacking. Essentially, this code needs to be replaced with code that injects the thread in the entire process tree, i.e. reuses the enumeration in But it also needs to send a MSYS2/Cygwin signal instead if any of the encountered PIDs is actually referring to an MSYS2/Cygwin process. But only for that arm of the process tree. Not an easy project. And as long as I am the only one to answer questions on the bug tracker like "why does my terminal not show any input anymore after I forget the Of course, if one (or more) of you gentle people could give me a hand with those more mundane tickets. Then. Then I could... |
I also have this problem on windows. It starts occuring when i have an npm script with && in it then the CTRL + C command is never send to the b script. $ npm -v $ node --version |
I was able to get all my node processes to die directly from the Git Bash shell on Windows 10 by typing |
We're just running it in phpstorm now instead of git bash. phpstorm does propperly kill the process for it kills the terminal. But thnx though! |
Thanks to an excellent contribution by the excellent @afsmith92, the next Git for Windows version will come with a proper fix. You can even get it early by downloading https://bintray.com/git-for-windows/pacman/download_file?file_path=x86_64%2Fmsys2-runtime-2.9.0-6-x86_64.pkg.tar.xz and unpacking its |
After further testing, it turns out my change didn't end up fixing the issue, but @dscho generously stepped in and took the time to resolve it. |
@dscho and @afsmith92 thank you for your effort! I can confirm that it works for my setup :
|
I am facing the same issue for Gradle instead of nodejs: Git for windows version 2.16.2 Fixed when applying the patch from @dscho . |
Then it should be fixed in Git for Windows v2.17.0, which was released yesterday... |
I can confirm that v2.17.0 resolved the issue after installing over the top of my existing installation. Thanks @dscho |
@dscho I collegue of mine and me has the same result: It does not work for us on 2.17.0. It only works again when copying in the dll from here afterwars: https://bintray.com/git-for-windows/pacman/download_file?file_path=x86_64%2Fmsys2-runtime-2.9.0-6-x86_64.pkg.tar.xz Git for windows version 2.17.0 |
There is exactly one way how it can work. But there are gazillions of ways for it not to work. Would you care to impart some details? |
@dscho what kind of information would help?
This task takes a few seconds to download depdenencies the first time; so just any example where you cannot kill the process. Now i apply your msys-2.0.dll from this thread and try it again.
|
I guess the culprit is that I had to change this again because it caused more problems. Ctrl+C is really a vexing problem, and whatever way we do it, it seems to break somebody's scenario. So currently we call Damn if you do, damn if you don't. |
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>
Okay, so I spent way too many hours on trying to fix this, but I have something to show: The newest Git for Windows snapshot at https://wingit.blob.core.windows.net/files/index.html should fix this. Please test. |
@dscho Thanks for your hard work on this, it's been plaguing me forever. I actually had a script just to call taskkill on node. I've tested the snapshot (commit e6ee2f786d) on Windows 10 and Node v9.8 - it's working like a charm! 😁 |
@dscho For the gradle-scenario described above it does not work. the process does not terminate on ctrl-c. |
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>
For the record, this happens with cygwin as well.
I searched and found this command helps me know what process to kill:
Found from https://superuser.com/questions/1003921/how-to-show-full-command-line-of-all-processes-in-windows |
That looks more like MSYS than Cygwin. (And a pretty old one at that.) |
Does anyone know of a workaround for the Mintty shell that ships with MSYS2? I don't want to give up MSYS2 or use git bash, because I love tmux and pacman too much. |
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>
So what about those of us who want to use mintty? We're just SOL? |
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>
Referencing #4182 which seems similar (but at the end, it was stated that a new issue should be opened).
Ctrl + C does not kill the server. The resolution to the issue was using:
however, when I use it, my server exits immediately. It completely skips
app.listen()
and exits completely, even when I previously kill the process on the specific port. Without theprocess.exit()
function, it starts up and I canCtrl + C
to exit, but the process still runs in the background. I opened an issue on StackOverflow which hasn't gone anywhere really. This happens across multiple projects, so it's not just one project. I'm pretty sure it started happening when I updated node.I am able to use
netstat -aon
to list it, thentskill <pid>
to kill it afterwards, but this isn't really a solution.Here's my code with my express server:
UPDATE
So the server is able to start now with:
I added
app.use('/', express.static('./src'));
, but removing it didn't change anything.When I do
Ctrl + C
, theconsole.log("\nGracefully shutting down from SIGINT (Ctrl-C)" );
doesn't appear, and trying to restart causes the same issue. Just for reference, here's the log:The text was updated successfully, but these errors were encountered: