'BackgroundService not restarted by SCM

Goal:

Running a .net 6.0 BackgroundService on Windows that is restarted automatically when an unhandled exception occurs.

Minimal example:

Program.cs

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .UseWindowsService(options =>
    {
        options.ServiceName = "ASampleService";
    })
    .Build();
await host.RunAsync();

Worker.cs

namespace SampleWindowsService
{
    public class Worker : BackgroundService
    {
        [...]

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
                throw new Exception();
            }
        }
    }
}

sc failure config

sc failure ASampleService reset=0 actions= restart/5000

Expected behaviour

The service should crash 10 seconds after start and then be restarted 5 seconds after it crashes. It should do so indefinitely. The system event log should contain a log line that the ASampleService crashed and will be restarted by the service control manager within 5000 milliseconds.

Actual behaviour

The service crashes and produces a logline in the application event log "Service stopped successfully" and is never restarted.

Question

I'm guessing the "stopped successfully" logline is at the heart of the issue. According to this article :

“A service is considered failed when it terminates without reporting a status of SERVICE_STOPPED to the service controller.”

However, I have not found a way to prevent the service from "stopping successfully", even overriding the StopAsync function of BackgroundService or OnStop of IHostApplicationLifecycle and throwing an Exception there, did not prevent this log line.

I've been digging into the source code to understand what's happening and I'm beginning to think that I might need to work with a lower level abstraction to get this to work. However, if anyone here knows how to get this to work, I'd be most interested in hearing about your approach.



Solution 1:[1]

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // simulate some work here
                await Task.Delay(TimeSpan.FromSeconds(5));

                // something went wrong
                throw new Exception();
            }
        }
        catch
        {
            // prevent "Stopped successfully"
            Environment.Exit(1);
        }
    }

The article in the .net documentation will also be updated. You may also want to set the exit code to the HResult of the exception.

With this code, the scm will restart the service and the System log will show the correct message:

The SampleService service terminated unexpectedly. It has done this 1 time(s). The following corrective action will be taken in 5000 milliseconds: Restart the service.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 isam2k