46

I created a .NET Core console application running as a daemon on a Ubuntu 14.04 machine.

I want to stop the service without forcing it, being able to handle a kill event.

How can I achieve this?

user4388177
  • 2,433
  • 3
  • 16
  • 30
  • 5
    Care to share your code - turning a .NET Core console app into an Ubuntu daemon? – lasseschou Aug 30 '16 at 20:15
  • 3
    The only thing I did was copying a default init script from GitHub; the start function calls `dotnet library.dll` and the stop one: `kill dotnet library.dll`. – user4388177 Aug 31 '16 at 06:27
  • in Linux, are you running dotnet inside shell script, for instance, shell script calls `dotnet app.dll`. For us, this caused an issue. To mitigate it, we added `exec dotnet app.dll` which helped to propagate SIGTERM signal to dotnet application, and not let shell script handle the SIGTERM directly. – AAATechGuy May 23 '22 at 22:44

2 Answers2

59

.NET Core has considerably evolved since @Stefano's answer a year ago. In .NET Core 2.0, you can now use the well-known AppDomain.CurrentDomain.ProcessExit event instead of AssemblyLoadContext.Default.Unloading. It works fine for console applications on Linux, also in Docker.

Marc Sigrist
  • 3,964
  • 3
  • 22
  • 23
  • 1
    I made a simple example of how to handle this event: https://github.com/rquackenbush/DotNetSigTermTest – RQDQ Aug 20 '18 at 14:43
  • 5
    `AppDomain.CurrentDomain.ProcessExit` only allows for 2 seconds of cleanup, and there is no way to override it using managed code. – Parag Sep 24 '18 at 12:41
  • 1
    @Parag I think you misread the documentation, or it has been changed. This time limit is only in .NET Framework, not in .NET Core. https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.processexit?view=netframework-4.8 – Anders Emil Jun 12 '19 at 11:47
  • @AndersEmil I am sure it applies for .net core. Check the "Applies to" section in the docs link you sent. – Parag Jun 12 '19 at 12:23
  • 5
    @Parag the article is about the ProcessExit event, not the time limit. In the purple Note box it clearly says that the time limit does not exist in .NET Core, I'm surprised you miss it. – Anders Emil Jun 13 '19 at 06:29
  • 1
    @AndersEmil I re-read it and you are right. Sorry for the misinformation. – Parag Apr 09 '20 at 07:33
29

You want to be able to send a SIGTERM to the running process:

kill <PID>

And the process should handle it to shutdown correctly.

Unfortunately .NET Core is not well documented, but it is capable of handling Unix signals (in a different fashion from Mono). GitHub issue

If you use Ubuntu with Upstart, what you need is to have an init script that sends the the kill signal on a stop request: Example init script

Add a dependency to your project.json:

"System.Runtime.Loader": "4.0.0"

This will give you the AssemblyLoadContext.

Then you can handle the SIGTERM event:

AssemblyLoadContext.Default.Unloading += MethodInvokedOnSigTerm;

Note:

Using Mono, the correct way of handling it would be through the UnixSignal: Mono.Unix.Native.Signum.SIGTERM

EDIT:

As @Marc pointed out in his recent answer, this is not anymore the best way to achieve this. From .NET Core 2.0 AppDomain.CurrentDomain.ProcessExit is the supported event.

Stefano d'Antonio
  • 5,874
  • 3
  • 32
  • 45
  • This works if the application is stops on its own gracefully. But if the kill command is called it does not trigger "AssemblyLoadContext.Default.Unloading". Am I doing something wrong? – dcinadr Aug 15 '16 at 17:45
  • What version of .NET Core are you using? Are you modifying the load context at some point? – Stefano d'Antonio Aug 15 '16 at 18:37
  • Version: 1.0.0-preview2-003121 ... I have also noticed that when I run "dotnet run" it starts 2 processes. I think this is the reason it is not working because it kills one of the processes but not both. – dcinadr Aug 15 '16 at 19:54
  • 2
    Try getting to the published DLL folder and run using: `dotnet .dll` (without run) that should run just one process and you can try killing that one. – Stefano d'Antonio Aug 16 '16 at 11:57
  • Thank you. Yes, that is similar to how I got it working. Only difference I had was "dotnet exec .dll". – dcinadr Aug 18 '16 at 17:25
  • I didn't know about `exec` so I searched quickly and I found this: https://github.com/dotnet/cli/issues/2243 but I'm assuming has a similar behaviour to `dotnet `. – Stefano d'Antonio Aug 18 '16 at 18:58
  • @dcinadr Here it recommends to use it without exec, but I would be curious to know the difference if you know. https://github.com/dotnet/core/issues/77 – Stefano d'Antonio Aug 18 '16 at 19:01