10

I need to notify systemd that my service has started up successfully, and a task it needs to run after startup requires that the server is already listening on the target Unix domain socket.

I am using IWebHost::Run to start the server, and that is a blocking call. Additionally, I am unable to find any obvious way to set a delegate or callback event for successful initialization.

Anyone?

aremmell
  • 1,151
  • 1
  • 10
  • 15

5 Answers5

4

You may use Microsoft.AspNetCore.Hosting.IApplicationLifetime:

/// <summary>
/// Triggered when the application host has fully started and is about to wait
/// for a graceful shutdown.
/// </summary>
CancellationToken ApplicationStarted { get; }

Look into this SO post for the configuration example.

Set
  • 47,577
  • 22
  • 132
  • 150
3
  • On .Net Core 1.x it is safe to just run IWebHost.Start() and assume that the server is initialized afterwards (instead of Run() that blocks the thread). Check the source.

    var host = new WebHostBuilder()
        .UseKestrel()
        (...)
        .Build();
    
    host.Start();
    
  • If you are using .NET Core 2.0 Preview 1 (or later), the source is different, the synchronous method is not available anymore so you should await IWebHost.StartAsync() and assume everything is ready afterwards.

Gerardo Grignoli
  • 14,058
  • 7
  • 57
  • 68
1

This is what I ended up going with, for now. Seems to be working fine:

host.Start();

Log.Information("Press Ctrl+C to shut down...");
Console.CancelKeyPress += OnConsoleCancelKeyPress;

var waitHandles = new WaitHandle[] {
    CancelTokenSource.Token.WaitHandle
};

WaitHandle.WaitAll(waitHandles);
Log.Information("Shutting down...");

Then, in the Ctrl+C event handler:

private static void OnConsoleCancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
    Log.Debug("Got Ctrl+C from console.");
    CancelTokenSource.Cancel();
}
aremmell
  • 1,151
  • 1
  • 10
  • 15
  • 7
    How does this code helps you detect when Kestrel has finished initializing? – Gerardo Grignoli Jun 20 '17 at 23:17
  • Didn't you say it was safe to assume Kestrel was ready after the Start() call unless I'm using 2.0 preview 1? That comment about it being async shouldn't be there. – aremmell Jun 22 '17 at 00:23
  • I asked because I didn't know and your answer was not clear to me. I thought you were hacking the Cancel event to detect end of the initialization. Then researched a little bit and found that Start is safe, so I posted it, and assumed your code worked by miracle, if at all. Now I believe you coded how to block the current thread properly. All the misunderstanding could have been avoided with some wordings in your answer. – Gerardo Grignoli Jun 22 '17 at 16:24
1

A working sample from .NET Core 6:

In your Program.cs, you will have the following (with more or less embellishment, depending on the middleware):

var builder = WebApplication.CreateBuilder(args);

/* ... kestrel configuration, middleware setup ... */

var app = builder.Build();

app.Services.GetService<IHostApplicationLifetime>()!.ApplicationStarted.Register(() =>
{
    /* any code you put here will execute
     * after the host has started listening */
    Console.WriteLine("Kestrel has started listening");
});

/* this is a blocking call.
 * the start event occurs from "in there" */
app.Run();

Working principle: The event handler with the Console.WriteLine call will be called after app.Run() is already blocking the main thread, at the exact moment when the server is first ready and will accept requests.

Cee McSharpface
  • 8,493
  • 3
  • 36
  • 77
0

This is what I did to overcome the issue.

1- I registered ApplicationStopped event. So that it brute force terminates the app by calling Kill() method of the current process.

public void Configure(IHostApplicationLifetime appLifetime) {
 appLifetime.ApplicationStarted.Register(() => {
  Console.WriteLine("Press Ctrl+C to shut down.");
 });

 appLifetime.ApplicationStopped.Register(() => {
  Console.WriteLine("Terminating application...");
  System.Diagnostics.Process.GetCurrentProcess().Kill();
 });
}

See IHostApplicationLifetime docs


2- Don't forget to use the UseConsoleLifetime() while building the host.

Host.CreateDefaultBuilder(args).UseConsoleLifetime(opts => opts.SuppressStatusMessages = true);

See useconsolelifetime docs

Alper Ebicoglu
  • 8,884
  • 1
  • 49
  • 55