diff --git a/src/Microsoft.AspNet.Hosting.Abstractions/IApplicationLifetime.cs b/src/Microsoft.AspNet.Hosting.Abstractions/IApplicationLifetime.cs index c229eb6e67..536e24c165 100644 --- a/src/Microsoft.AspNet.Hosting.Abstractions/IApplicationLifetime.cs +++ b/src/Microsoft.AspNet.Hosting.Abstractions/IApplicationLifetime.cs @@ -10,6 +10,12 @@ namespace Microsoft.AspNet.Hosting /// public interface IApplicationLifetime { + /// + /// Triggered when the application host has fully started and is about to wait + /// for a graceful shutdown. + /// + CancellationToken ApplicationStarted { get; } + /// /// Triggered when the application host is performing a graceful shutdown. /// Request may still be in flight. Shutdown will block until this event completes. @@ -25,4 +31,4 @@ namespace Microsoft.AspNet.Hosting /// CancellationToken ApplicationStopped { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNet.Hosting/ApplicationLifetime.cs b/src/Microsoft.AspNet.Hosting/ApplicationLifetime.cs index eb6dc364b9..ced615c889 100644 --- a/src/Microsoft.AspNet.Hosting/ApplicationLifetime.cs +++ b/src/Microsoft.AspNet.Hosting/ApplicationLifetime.cs @@ -11,9 +11,19 @@ namespace Microsoft.AspNet.Hosting /// public class ApplicationLifetime : IApplicationLifetime { + private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); + /// + /// Triggered when the application host has fully started and is about to wait + /// for a graceful shutdown. + /// + public CancellationToken ApplicationStarted + { + get { return _startedSource.Token; } + } + /// /// Triggered when the application host is performing a graceful shutdown. /// Request may still be in flight. Shutdown will block until this event completes. @@ -35,6 +45,21 @@ namespace Microsoft.AspNet.Hosting get { return _stoppedSource.Token; } } + /// + /// Signals the ApplicationStarted event and blocks until it completes. + /// + public void NotifyStarted() + { + try + { + _startedSource.Cancel(throwOnFirstException: false); + } + catch (Exception) + { + // TODO: LOG + } + } + /// /// Signals the ApplicationStopping event and blocks until it completes. /// @@ -65,4 +90,4 @@ namespace Microsoft.AspNet.Hosting } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs index 56659edc73..48a0069b92 100644 --- a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs +++ b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs @@ -79,6 +79,8 @@ namespace Microsoft.AspNet.Hosting.Internal } }); + _applicationLifetime.NotifyStarted(); + return new Disposable(() => { _applicationLifetime.NotifyStopping(); @@ -182,4 +184,4 @@ namespace Microsoft.AspNet.Hosting.Internal } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs index d776c1d481..6c613adcf7 100644 --- a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs +++ b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs @@ -90,6 +90,21 @@ namespace Microsoft.AspNet.Hosting Assert.Equal(1, _startInstances[0].DisposeCalls); } + [Fact] + public void HostingEngineNotifiesApplicationStarted() + { + var host = CreateBuilder() + .UseServer(this) + .Build(); + var applicationLifetime = host.ApplicationServices.GetRequiredService(); + + Assert.False(applicationLifetime.ApplicationStarted.IsCancellationRequested); + using (host.Start()) + { + Assert.True(applicationLifetime.ApplicationStarted.IsCancellationRequested); + } + } + [Fact] public void HostingEngineInjectsHostingEnvironment() {