Skip to content
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

Handle Ctrl+C in Git Bash nicely #1170

Merged
merged 2 commits into from
May 19, 2017

Conversation

dscho
Copy link
Member

@dscho dscho commented May 18, 2017

This PR depends on git-for-windows/msys2-runtime#15.

The idea is that the MSYS2 runtime will no longer kill Git's processes using TerminateProcess(), but by injecting a thread calling ExitProcess(), which in turn allows Git's atexit() handlers to run.

NOTE: this does not mean that we all of a sudden have POSIX semantics. Far from it: there is still no real signal handling (think SIGSTOP and SIGCONT), and the signal handlers are not run.

Git's source code amazingly seems to be prepared for that case, as most (or all?) signal handlers simply enter code paths that are also entered by atexit handlers.

For example, if a long-running git clone is interrupted by Ctrl+C, or by a fatal error, does not matter: in both cases do we want to clean up after us, meaning: the same cleanup functions are called.

This is excellent news, as we now can use the same method as the MSYS2 runtime just learned to kill our child processes when we were "exited" by a signal. And those child processes can do the same in return.

@dscho dscho requested a review from whoisj May 18, 2017 13:54
Copy link

@whoisj whoisj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this looks great. I'll assume you've tested it and it works. 😉

I've left a couple of comments. The assumption in kill_child_processes_on_signal that as long as the exit code is greater than 128, it is OK to exit the process might be invalid though - given that GetExitCodeProcess can return on a still running process and an exit code of 259 on Windows means the process is still running.

Approving the changes, as to not be a blocker - but please review the logic in kill_child_processes_on_signal; also please forgive any stupidity I've displayed as I'm still sipping for first cup of coffee ☕️ .

if (!thread)
return terminate_process_tree(process, exit_code);
CloseHandle(thread);
if (WaitForSingleObject(process, 10000) == WAIT_OBJECT_0)

This comment was marked as off-topic.

This comment was marked as off-topic.

break;
}

for (i = len - 1; i >= 0; i--) {

This comment was marked as off-topic.

This comment was marked as off-topic.

compat/mingw.c Outdated
* Only continue if the process was terminated by a signal, as
* indicated by the exit status (128 + sig_no).
*/
if (!GetExitCodeProcess(GetCurrentProcess(), &status) || status < 128)

This comment was marked as off-topic.

This comment was marked as off-topic.

@dscho
Copy link
Member Author

dscho commented May 18, 2017

In general this looks great. I'll assume you've tested it and it works.

Yes, I did.

The assumption in kill_child_processes_on_signal that as long as the exit code is greater than 128, it is OK to exit the process might be invalid though - given that GetExitCodeProcess can return on a still running process and an exit code of 259 on Windows means the process is still running.

Well, no, the atexit handler is not started unless the process was exited with an exit status ;-)

But you are correct, I should be a lot more careful here. I changed the code to look for SIGRTMAX (which is not #defined in MINGW, so I fall back to the hard-coded constant 63, which should be "good enough").

@whoisj
Copy link

whoisj commented May 18, 2017

But you are correct, I should be a lot more careful here. I changed the code to look for SIGRTMAX (which is not #defined in MINGW, so I fall back to the hard-coded constant 63, which should be "good enough").

I guess my concern here is that you're asking "what is my exit code" which in theory, because you (the current process) is asking, the value is 259. Which makes the conditional kind of pointless.

Does the call to ExitProcess set the exit code before kill_child_processes_on_signal gets called?

compat/mingw.c Outdated
* Only continue if the process was terminated by a signal, as
* indicated by the exit status (128 + sig_no).
*/
if (!GetExitCodeProcess(GetCurrentProcess(), &status) ||

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

compat/mingw.c Outdated
}

errno = err_win_to_posix(GetLastError());
ret = exit_process(h, 3);

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

@jeffhostetler
Copy link

Many of my comments in https://github.com/git-for-windows/msys2-runtime/pull/15/files also apply to this PR. I didn't bother repeating them here.

@dscho
Copy link
Member Author

dscho commented May 18, 2017

Does the call to ExitProcess set the exit code before kill_child_processes_on_signal gets called?

Yep.

@dscho
Copy link
Member Author

dscho commented May 18, 2017

This revision should be good.

if (!initialized) {
HINSTANCE kernel32 = GetModuleHandle("kernel32");
if (!kernel32)
die("BUG: cannot find kernel32");

This comment was marked as off-topic.

This comment was marked as off-topic.

@whoisj
Copy link

whoisj commented May 19, 2017

@dscho please seem my comment on https://github.com/git-for-windows/msys2-runtime/pull/15/files#r117399303 about making this work when msys is 64-bit and the target is 32-bit.

@dscho
Copy link
Member Author

dscho commented May 19, 2017

I just had another fresh look over the overall diff, and I realized that the exit code 128 does not signify exit-by-signal (signal numbers start at 1, for SIGHUP). Instead, it is the exit code Git uses for fatal errors (die()). So the test status < 128 was incorrect, and I fixed it by replacing it with status <= 128.

@dscho
Copy link
Member Author

dscho commented May 19, 2017

I also realized a problem with the IsWoW64Process() test: what we really want is to ensure that the current process' architecture matches the one of the process to be killed. But what we tested was that that the process to be killed matches the OS' architecture. Therefore, we would have tried to kill a 64-bit process on a 64-bit OS when running as 32-bit git.exe.

I adjusted the test to verify that IsWow64Process() returns the same value for the current process and the one to be killed. The code is less elegant than I want it to be, but I could not find a way to make it better...

The TerminateProcess() function does not actually leave the child
processes any chance to perform any cleanup operations. This is bad
insofar as Git itself expects its signal handlers to run.

A symptom is e.g. a left-behind .lock file that would not be left behind
if the same operation was run, say, on Linux.

To remedy this situation, we use an obscure trick: we inject a thread
into the process that needs to be killed and to let that thread run the
ExitProcess() function with the desired exit status. Thanks J Wyman for
describing this trick.

The advantage is that the ExitProcess() function lets the atexit
handlers run. While this is still different from what Git expects (i.e.
running a signal handler), in practice Git sets up signal handlers and
atexit handlers that call the same code to clean up after itself.

In case that the gentle method to terminate the process failed, we still
fall back to calling TerminateProcess(), but in that case we now also
make sure that processes spawned by the spawned process are terminated;
TerminateProcess() does not give the spawned process a chance to do so
itself.

Please note that this change only affects how Git for Windows tries to
terminate processes spawned by Git's own executables. Third-party
software that *calls* Git and wants to terminate it *still* need to make
sure to imitate this gentle method, otherwise this patch will not have
any effect.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Git for Windows' MSYS2 runtime was just adjusted to kill processes
gently, by injecting a thread that calls ExitProcess(). In case of
signals (such as when handling Ctrl+C in a MinTTY window), the exit code
is 128 + sign_no, as expected by Git's source code.

However, as there is no POSIX signal handling on Windows, no signal
handlers are called. Instead, functions registered via atexit() are
called. We work around that by testing the exit code explicitly.

This fixes the Git for Windows side of the bug where  interrupting `git
clone https://...` would send the spawned-off `git remote-https` process
into the background instead of interrupting it, i.e. the clone would
continue and its progress would be reported mercilessly to the console
window without the user being able to do anything about it (short of
firing up the task manager and killing the appropriate task manually).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho dscho merged commit 80a6209 into git-for-windows:master May 19, 2017
@dscho dscho deleted the mingw-kill-process branch May 19, 2017 13:12
dscho added a commit to git-for-windows/build-extra that referenced this pull request May 19, 2017
When interrupting Git processes in Git Bash by
pressing Ctrl+C, [Git now removes `.lock` files as
designed](git-for-windows/msys2-runtime#15)
([accompanying Git
PR](git-for-windows/git#1170); this should also
fix [issue #338](git-for-windows/git#338)).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request May 23, 2017
Handle Ctrl+C in Git Bash nicely
git-for-windows-ci pushed a commit that referenced this pull request May 23, 2017
Handle Ctrl+C in Git Bash nicely
dscho added a commit that referenced this pull request May 29, 2017
Handle Ctrl+C in Git Bash nicely
dscho added a commit that referenced this pull request May 29, 2017
Handle Ctrl+C in Git Bash nicely
dscho added a commit that referenced this pull request May 29, 2017
Handle Ctrl+C in Git Bash nicely
dscho added a commit that referenced this pull request May 30, 2017
Handle Ctrl+C in Git Bash nicely
git-for-windows-ci pushed a commit that referenced this pull request Jul 16, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 16, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 16, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho added a commit that referenced this pull request Jul 17, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 17, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 18, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
mjcheetham pushed a commit to mjcheetham/git that referenced this pull request Jul 18, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 18, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 18, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 18, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 19, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 19, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 20, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho added a commit that referenced this pull request Jul 22, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 22, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 22, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 22, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 23, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 23, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 23, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 23, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 23, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
mjcheetham pushed a commit to mjcheetham/git that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
mjcheetham pushed a commit to mjcheetham/git that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this pull request Jul 24, 2024
Handle Ctrl+C in Git Bash nicely

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants