sdk: terminating `dotnet run` doesn't terminate child

Steps to reproduce

[Terminal 1]
$ dotnet new razor
$ dotnet run
Hosting environment: Production
Content root path: /home/mistria/runtime-EclipseApplication/tst
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
[Terminal 2]
$ ps aux | grep dotnet
mistria  24040  2.2  0.7 3296988 88604 pts/0   SLl+ 12:07   0:01 dotnet run
mistria  24143  1.1  0.5 21300684 71156 pts/0  SLl+ 12:07   0:00 dotnet exec /home/mistria/runtime-EclipseApplication/tst/bin/Debug/netcoreapp2.0/tst.dll
$ kill -15 24040 #SIGTERM on host
$ ps aux | grep dotnet
mistria  24143  1.1  0.5 21300684 71156 pts/0  SLl+ 12:07   0:00 dotnet exec /home/mistria/runtime-EclipseApplication/tst/bin/Debug/netcoreapp2.0/tst.dll
# child still there like a zombie

Expected behavior

Child process terminated

Actual behavior

Child process is still there This can be confusing for IDEs (like Eclipse IDE) which bind the “stop” button and other UI actions to the Terminate signal. In such case, the developer trying dotnet run from the IDE will face issues because of the zombi-ish processes

Environment data

dotnet --info output:

.NET Command Line Tools (2.0.1-servicing-006924)

Product Information:
 Version:            2.0.1-servicing-006924
 Commit SHA-1 hash:  1ed6be56ca

Runtime Environment:
 OS Name:     fedora
 OS Version:  26
 OS Platform: Linux
 RID:         fedora.26-x64
 Base Path:   /home/mistria/apps/dotnet-2/sdk/2.0.1-servicing-006924/

Microsoft .NET Core Shared Framework Host

  Version  : 2.0.0
  Build    : e8b8861ac7faf042c87a5c2f9f2d04c98b69f28d

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 25
  • Comments: 26 (8 by maintainers)

Most upvoted comments

Looks like this thread is still active but stale. It was originally open almost a year ago: do we have consensus that this is real and should be addressed somewhere and slotted for a future release?

@livarcocc

Steps to reproduce:

  • Create a project:
    • Ap.Ms.Test.csproj :
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
    <PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
    <PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
  </ItemGroup>

</Project>
  • UnitTest1.cs :
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace Ap.Ms.Test
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            while (true)
            {
                Assert.IsTrue(true);
            }
            Assert.IsTrue(true);
        }
    }
}
  • run dotnet test image
  • run procexp.exe and find parent donet.exe image
  • then kill parnet process (9864) image

Expected

  • All child processes are stopped

Actually

  • Childs continue running and lock assemblies

on @mickaelistria’s case, about dotnet razor, I don’t think it is dotnet’s job to terminate those processes. The CTRL+C message there is coming from asp.net itself and when you CTRL+C it is asp.net’s responsibility to terminate any extra processes that it may have initiated when it gets terminated.

Also, I couldn’t repro this right now when I tried.

As for dotnet test, @psmolkin would you happen to have a repro for it?

Hi @herebebeasties. Consistent SIGINT/Ctrl-C handling from dotnet run is a different issue and one I am also producing a fix for. SIGINT is sent to the foreground process group in POSIX and, similarly, to all processes attached to the current console on Windows. Thus, the children of dotnet run see the signal and can respond to it appropriately; for example, this is why Ctrl-C of an ASP.NET Core application running from dotnet run prints the shutdown message. The problem there is that the default handling of SIGINT from dotnet run causes a different exit status to the parent then it would have if the parent had spawned the child program directly. It also causes an immediate return back to the waiting parent and the child program may still be writing to stdout when handling the signal, resulting in inconsistent and undesirable output in an interactive shell session.

This issue is about sending SIGTERM or using task manager / taskkill to terminate the dotnet run process and having it reliably clean up the child processes. Unlike SIGINT, SIGTERM is usually sent with the kill command and that, unless you manually tell it to send to a process group, will only go to the individual process specified. On Windows, killing a process that has no message queues (e.g. a console .NET application like the dotnet CLI) from task manager / taskkill will rudely terminate the process, resulting in zombied children unless the application takes (complex) steps to prevent it.

getting this with 2.1.0-preview2-26406-04, both on Linux and on Windows

@Code-DJ Task Scheduler has been unreliable for ages. I’ve had better luck creating services that do this programatically.

I am considering making a fix that addresses this issue.

On POSIX systems, dotnet run would handle the SIGTERM signal (via AppDomain.CurrentDomain.ProcessExit) and then first attempt to kill its own pid as a process group (i.e. -pid), masking SIGTERM for its process to prevent reentry. This should kill all the children of dotnet that did not break away from the process group. However, if dotnet is not running in its own process group (i.e. it was spawned with an inherited pgid) then at best we can try to kill the child pid because Process.Start spawns new processes with inherited pgid (see dotnet/corefx#17412).

On Windows, we could potentially create a job object, associate the new process to the job, and set the job object to terminate all processes in the job upon rude termination of dotnet. I’m loathe to introduce job objects where we don’t have explicit control over the process tree, however. I know from experience that spawning user programs that suddenly find themselves in a job may fail when spawning their own processes (i.e. break-away).

Still, considering the fix would effectively translate the user action of kill -15 $dotnet_pid to kill -15 -- -$dotnet_pid (or use taskkill /t /pid $dotnet_pid on Windows), it seems like the workaround is trivial compared to the effort of a fix. The question comes down to user expectation and experience with process tree management, really.

If closing a asp.net app is not terminating the process, I believe this is an issue that should be investigated by the ASP.NET team. Please, file a new issue on aspnet/home.

Another possibility is that dotnet has three long running processes that stay around to help with inner loop performance: roslyn, msbuild and razor. You can terminate these processes by running dotnet build-server shutdown.

I’d really like this to be fixed. I’m just getting into the dotnet platform and would really like to take advantage of the speed of linux over windows for our server, but in testing we keep running into this issue which putting us down a bit.

How I encountered the issue

System

Ubuntu 18.04.1 LTS 1 cpu, 1gb ram (AWS EC2 free tier)

Install dotnet-sdk-2.1

wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1

Install Node.js

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

Create Angular / .net project

dotnet new angular -o angular-test-project

Start project

export ASPNETCORE_Environment=Development
dotnet build
dotnet run

Wait for the angular build to finish. After this, kill the project with Ctrl + C. Sometimes it successfully says Application is shutting down and then shuts down. Other times, it will just say Application is shutting down and then sit there forever meaning something is blocking in some way most likely (my best guess). If you Ctrl + C again after this, the memory will remain being used by this zombie process. You can see in the screenshot below I had to press Ctrl + C twice before it went back to bash. image

If you look in htop, it seems to be a bunch of ng processes which is strange. So maybe it is just something as simple as the ng serve is stuck open. You can see htop below. image

@omajid @livarcocc Hopefully this helps move this along in some way.

A few PowerShell (run as Administrator) one-liners to work around that:

# Terminate "dotnet run" process trees
Get-WmiObject Win32_Process -Filter "name = 'dotnet.exe'" | Where-Object -Property CommandLine -Like '*dotnet.exe" run*' | ForEach-Object { Get-Process -Id $_.ProcessId } | ForEach-Object { taskkill /F /T /PID $_.Id  }

# Terminate "dotnet watch run" process trees
Get-WmiObject Win32_Process -Filter "name = 'dotnet.exe'" | Where-Object -Property CommandLine -Like '*dotnet.exe" watch run*' | ForEach-Object { Get-Process -Id $_.ProcessId} | ForEach-Object { taskkill /F /T /PID $_.Id  }

Run at your own risk. You can adjust the CommandLine filter to be more specific. Which goes well with running dotnet --project .\project1.csproj which then allows you to match the particular .csproj file on the CommandLine. The Get-Process pipeline step is unnecessary but I left it in there for easier diagnosing.