-
-
Notifications
You must be signed in to change notification settings - Fork 170
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
Fix child processes not being killed properly #613
Fix child processes not being killed properly #613
Conversation
On Linux, child processes are allowed to persist when their parent is terminated. Currently, headless LSP processes are spawned with `shell: true` option, which means a shell process is launched first, and its PID is returned. Calling `process.kill()` on that does kill the shell process, but the Godot Engine instance launched by the shell keeps running. Instead, kill the process group of which the shell is the leader (guaranteed by the `detached: true` option). In Linux, process group IDs are equal to PIDs of their leader processes, and negative PIDs are used to send a signal to a group. Thus, negating the PID in `process.kill()` call is enough.
I had that minus sign at one point and I vaguely recall that it caused a problem on either Windows or MacOS. I'll have to look into this to remember why I removed it. |
Sure. If I'm reading this correctly, the |
This was a slight dig, but after finding the originating pull request on the change (#452) DaelonSuzuka@557fb29 There appeared to be an issue with using the group PID - perhaps it didn't work at all? If I get to it, I'll also try investigating that in Linux. |
I see. That seems to be debugger-related? Both the debugger and the headless LSP use the same There's quite a lot going on in the linked PR, and I don't seem to find any discussion related to the linked commits. However, there's in fact a similar ongoing issue with the debugger where the process under debug is not being terminated when the debugging session closes. It reproduces very reliably on my machine. It might be that the commits above were an attempt to fix this. I wasn't able to locate the cause a week ago when I prepared this PR, but I took a second look just now, and this line seems to be the cause:
This call makes the debugged process unkillable, because I tested on my machine, and can confirm that adding I'm not in love with the idea of adding the fix to this PR, or other PR for that matter, because I'm not sure what other things I should test. What do you think? |
In that case, since this PR doesn't break the debugger's ability to properly kill its own process (as it is already broken), it seems fine to merge with an additional PR (tested by @DaelonSuzuka) that adds the detached header. |
See discussion near godotengine#613 (comment) . Currently, the debugger process is spawned with `shell: true` option, which means a shell process is launched first, and its PID is returned. When a debugging session terminates, `godot-tools` attempts to kill the process. However, on Linux, calling `process.kill()` on that kills the shell process, while allowing the main process to keep running. To fix, enable `detached: true` flag when launching the debugger to force both processes to share the same process group; then rely on changes introduced in godotengine#613 to kill the entire group.
OK, I've sent a separate PR for this. Thank you! |
@vpyatnitskiy Thanks for catching this problem and submitting a fix. |
Currently, headless LSP processes continue running when VS Code exits on Linux.
Simple steps to reproduce:
Launch VS Code.
Check the headless processes ― for example, with
ps ajx | grep headless
.Here's the output on my machine:
Close VS Code.
Check the processes again. Here's my output:
As you can see, the shell process (
/bin/sh
) is gone, but the engine itself continues running. Each of those is half a gig of RAM ― relaunch a few times and it adds up.What happens here is:
shell: true
causeschild_process.spawn()
to start a shell process which, in turn, starts Godot Engine. The returned PID is the shell's PID.Child processes are allowed to persist even when detached (see Node.js documentation on
options.detached
) ― soprocess.kill()
ing the shell does not kill the engine.Now,
options.detached
in this case makes Node start a new process group with the shell process as its leader; to terminate everything, the entire group needs to be killed. In the snippets above, you can see the group ID in the third column.Process group IDs in Linux are equal to the PIDs of their leaders; to send a signal to a group, negative values for PID are used. See e.g. this answer on StackOverflow.
This PR makes it so that the kill signal is indeed dispatched to the group. It's a single character change, but I feel like it deserved a detailed explanation.