-
Notifications
You must be signed in to change notification settings - Fork 5k
Expand Process.Kill to Optionally Kill a Process Tree #24617
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
Comments
Implementation note for Windows: https://github.com/dotnet/corefx/issues/25855#issuecomment-351116254 |
I think it should be an enum not a bool. I'd expose at least three flags (even if DirectChildren will be outvoted I'd still vote for making it an enum):
|
Good thought about the enum, @krwq! I like how it makes it easier to add variations of behavior down the road. For example, if it becomes desirable to do 'Entire Tree + Wait for Exit'. |
@bgribaudo let's update the proposal with enum if you feel this is a good idea. What about something like this for now (DirectChildren can wait until someone requests it and likely will be a part of implementation of EntireTree anyway): enum ProcessGroup
{
OnlySelf, // or CurrentProcess perhaps
EntireTree
} |
@krwq, done. Thanks! |
Implementation note on Windows: using Task Manager's "kill entire tree" functionality does two interesting things: (a) it won't kill the taskmgr process itself even if it's a child of the tree to be killed; and (b) it keeps going even in the face of failure, choosing to report a single "something went wrong" error at the very end. |
We don't see the value in an enum as it seems unlikely that we'd do more than the two options. All examples we could come up with would likely result in more parameters. The usability of booleans on call sides have been solved in C# with named parameters. We shouldn't make the parameter optional as there is no way this will ever be used by the compiler as we'd still have the parameterless version (which cannot be removed as that would be a binary breaking change). |
@bgribaudo it took a while, but this is approved. Any interest in offering a PR, so it can be in 3.0? |
For clarity this is what we agreed: public class Process : .... {
public void Kill(bool entireProcessTree);
} |
Thanks, @danmosemsft! Edited the top question so that it reflects the approved version. |
So @bgribaudo do you want to take a shot at the implementation? Even just one of Windows and Linux if you want. |
Implementation NotesMicrosoft.Build.Utilities.ProcessExtensions looks like a great starting place. Implementation may be as simple as copying-and-pasting the relevant code, tweaking + adding relevant tests. aspnet Common's Microsoft.Extensions.Internal.ProcessHelper/CLI's Microsoft.DotNet.Tools.Test.Utilities.ProcessExtensions show an alternative approach for terminating a Windows process tree that uses |
I'm not aware of any problems with MSBuild's P/Invoke approach after the .NET Core port. Note that we only call it from https://github.com/Microsoft/msbuild/blob/cc451ea752ef6cfb04d9a3abab05f28f9a2fec41/src/Utilities/ProcessExtensions.cs#L14-L45 which has some cross-platform options that seem to be behaving ok. |
Yes, I'll be glad to give it a try. :-) |
Yes I suggest copying https://github.com/Microsoft/msbuild/blob/master/src/Utilities/ProcessExtensions.cs#L14. If you have a Mac, or can host a Linux VM, either should be fine for testing Unix behavior. |
For the tests, I would take a look through the existing tests for Process/ProcessStartInfo etc and copy the pattern. Make sure you save your work before you test for bugs in your implementation of killing the process tree 😆 |
@danmosemsft, do you have any ideas on how to do something like https://github.com/dotnet/corefx/blob/9f02f69e3d9a455ec7bd1927eae59f138f37112b/src/System.Diagnostics.Process/tests/ProcessWaitingTests.cs#L222-L241 but where the code that starts the root process (line 241) can get ahold of the process IDs of child 1, 2 & 3? Unfortunately, I can't simply define variables for the PIDs in that outer scope then assign to those variables from within each child's create Func because "Field marshaling is not supported by RemoteInvoke". Context: |
@bgribaudo I think you can communicate PIDs back to the main process through i.e. AnonymousPipe (that will require setting handle inheritance but I'd check first if this doesn't happen by default). One platform which might be slightly trickier to test than others is UWP - I think it should be fine to skip testing there for now (please file an issue on that if you decide to skip it). Other option is to simply print it out to stdout and make sure all of the child processes reprint their subprocesses stdouts. |
@bgribaudo RemoteInvoke can take input parameters, so long as they can be converted easily to strings. It passes them as command line parameters. On return, you can only get the return code, so for more, you'd have to use a file or pipe etc. See src\CoreFx.Private.TestUtilities\src\System\Diagnostics\RemoteExecutorTestBase.cs for all the overloads. |
Thanks, @krwq & @danmosemsft! |
done. |
Thanks! Any chance to have this in .NET Standard 2.1+? It makes no sense we are encouraged to use .NET Standard for .NET Core class libraries if they don't have such an essential feature. Thanks in advance. |
So, almost 4 months later I find myself with the same issue. Any chance of an answer of getting this into .NET Standard 2.1+? |
@darkguy2008 .NET Standard 2.0/2.1 cannot be extended - it represents a point in time state of the intersection of several platforms. There is no plan currently for a .NET Standard 2.2. Can you use .NET Core 3.1 or later? Otherwise, there may be libraries that offer this functionality, or you could copy and paste the code here (which I think would be quite involved unfortunately). |
@danmosemsft thanks for your reply. I see, that's quite unfortunate though. Then, how does one go into creating .NET Core libraries with such capability? If I use the dotnet CLI, class libraries are created with .NET Standard, and Console apps are created with .NET Core 3.1. Is it possible to update the CLI (or VS 2019, then) to use .NET Core 3.1 for class libraries? Code isn't too relevant, basically .NET Standard 2.0+ lacks the bool argument in Process.Kill(). I ended up copying https://raw.githubusercontent.com/dotnet/cli/master/test/Microsoft.DotNet.Tools.Tests.Utilities/Extensions/ProcessExtensions.cs into my code, but it would be a good idea for .NET Standard to have it. |
You should be able to just change the target Framework to netcoreapp3.1 https://docs.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks |
I did that once, but it didn't felt right, I try to play along with what the official tools dictate, and my class libs were being created with .NET Standard 2.0 so I went with it (most I did was upgrading them to 2.1 for some features I needed). So this means netcoreapp3.1 is the way to go for both apps and class libs, right? Maybe the CLI/VS2019 tools should be updated, or the documentation you linked me to improved so netstandard2.0 and 2.1 are marked as deprecated and meant to be replaced with netcoreapp3.1? In any case, thanks for your response :) |
.NET Standard gives you the most "reach"...target NS2.0 and your one assembly will work on.NET Framework, Xamarin, Unity, and.NET Core. If you only plan to run it on.NET Core, you don't need to be limited to that API set. That is why it starts you off with.NET Standard 2.0 |
@darkguy2008 .Net Standard is not deprecated, it can still be useful, depending on what you want. Though the default will change to |
Issue
.Net Standard does not provide a means to kill a process tree (that is, a given process and all of its child/descendant processes).
In the Windows world, this can be done by assembling the list of descendant processes to terminate using WMI's Win32_Process management object, performance counters or P/Invoke, then killing each one, then killing the parent/root process. However, since .Net Standard doesn't provide a way to enumerate child/descendant processes, it's impossible for a user to directly implement this approach in a cross-platform way using only .Net Standard.
If the ability to enumerate child processes were added (as #24423 proposes), there'd still be complexities involved with using the child process list to kill a process tree as care is needed to ensure that unrelated processes aren't inadvertently terminated because of process ID reuse. This proposal saves the developer from needing to worry about such complexities and instead provides a simple, one-line way to terminate a process tree.
Rationale
Sometimes, it's necessary to end a process tree. :-)
For example,
Microsoft.DotNet.Tools.Test.Utilities.ProcessExtension
relies on different code paths to handle killing a process tree on Windows and Linux. Both code paths involve invoking one or more command-line processes. If this proposal were implemented, presumably this logic could be replaced with a single-line kill all method call followed possibly by a call toWaithForExit(int milliseconds)
. No multiple code paths. No need to invoke command-line processes.Another example: A project I'm currently working on uses integration tests that use
Process
to calldotnet run
to start a server process. When the test has completed, it's possible to use Process'sKill()
to end the parent dotnet process. However, this leaves child processes still running—which causes problems.Proposal
Extend
Process
as follows:Original (ignore—left for reference purposes):
Revision (ignore—left for reference purposes):
Approved Version:
With this change,
Kill
will behave exactly as it does at current if it is called with no arguments or withfalse
as its argument. However, when called withtrue
as its argument, in a platform-appropriate way all descendant processes will be killed, followed by the process represented by the currentProcess
instance.[edited by @danmosemsft to add c# formatting only]
The text was updated successfully, but these errors were encountered: