.netdocker.net-coredocker-for-windows

Can a .NET Core application on Windows trap a SIGTERM event?


I'm curious if a console app written in .NET Core running on Windows can intercept a SIGKILL event and basically know it's being terminated. Here's the code I'm trying:

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine($"Hello from PID {Process.GetCurrentProcess().Id}, press CTRL+C to exit.");

        AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += context =>
        {
            Console.WriteLine("SIGTERM received, exiting program...");
        };

        Console.CancelKeyPress += (s, e) =>
        {
            Console.WriteLine("SIGINT received, exiting program...");
            Environment.Exit(0);
        };

        try
        {
            await Task.Delay(Timeout.Infinite);
        }
        finally
        {
            Console.WriteLine("Finally executed..");
        }

    }

When I run the program from the command line, I can terminate it with the CTRL+C key combination. This will fire CancelKeyPress as well as Unloading. If I terminate the program other ways (Using the Windows Task Manager "End Process" function, or the Stop-Process PowerShell command), the process simply ends without any output written to the console.

This is part of a larger goal, which is to trap a Docker container shutting down. On Windows, the docker stop command will kill the main process using SIGTERM. This behavior is configurable on Linux Docker using the --stop-signal or STOPSIGNAL features, but those things have not been implemented on Windows.


Solution

  • On Windows, the docker stop command will kill the main process using SIGTERM.

    As of version 1709 (for both host and base image), Windows containers using the base image microsoft/windowsservercore or microsoft/nanoserver will send CTRL_CLOSE_EVENT to the main process only.

    As of version 1803, Windows containers using those base images will send CTRL_SHUTDOWN_EVENT to all processes running in the container.

    So, depending on your versions, Docker may send CTRL_CLOSE_EVENT or CTRL_SHUTDOWN_EVENT.

    These are different than the Ctrl-C signal, which is CTRL_C_EVENT. .NET's Console.CancelKeyPress only handles CTRL_C_EVENT (or the closely-related CTRL_BREAK_EVENT), so it will not be called for CTRL_CLOSE_EVENT or CTRL_SHUTDOWN_EVENT.

    If I terminate the program other ways (Using the Windows Task Manager "End Process" function, or the Stop-Process PowerShell command), the process simply ends without any output written to the console.

    It is not possible to handle all kill signals. I believe Task Manager these days tries to do a "gentle" close which in this case is probably sending CTRL_CLOSE_EVENT, but it is also entirely possible to just TerminateProcess a process, which kills it immediately without any signals or anything.

    But recognizing a Docker-initiated shutdown and responding to it is possible, since it sends a nice signal first and only terminates after a timeout.

    The .NET Runtime will recognize CTRL_CLOSE_EVENT as a process exit request, but since newer versions of Docker for Windows have switched to CTRL_SHUTDOWN_EVENT, I believe that .NET will simply not do a graceful shutdown for Windows containers anymore.

    The only workaround I'm aware of is to install your own Console control event handler. I recommend handling CTRL_C_EVENT, CTRL_CLOSE_EVENT, and CTRL_SHUTDOWN_EVENT all as just a generic "shutdown" signal.