#23 - Implement graceful shutdown.

This commit is contained in:
Chris Ross 2014-05-30 15:49:14 -07:00
parent a2fd1e1d90
commit 43ae61f7bc
6 changed files with 143 additions and 3 deletions

View File

@ -0,0 +1,72 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
namespace Microsoft.AspNet.Hosting
{
/// <summary>
/// Allows consumers to perform cleanup during a graceful shutdown.
/// </summary>
public class ApplicationLifetime : IApplicationLifetime
{
private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource();
private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource();
public ApplicationLifetime()
{
}
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// Request may still be in flight. Shutdown will block until this event completes.
/// </summary>
/// <returns></returns>
public CancellationToken ApplicationStopping
{
get { return _stoppingSource.Token; }
}
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// All requests should be complete at this point. Shutdown will block
/// until this event completes.
/// </summary>
/// <returns></returns>
public CancellationToken ApplicationStopped
{
get { return _stoppedSource.Token; }
}
/// <summary>
/// Signals the ApplicationStopping event and blocks until it completes.
/// </summary>
public void SignalStopping()
{
try
{
_stoppingSource.Cancel(throwOnFirstException: false);
}
catch (Exception)
{
// TODO: LOG
}
}
/// <summary>
/// Signals the ApplicationStopped event and blocks until it completes.
/// </summary>
public void SignalStopped()
{
try
{
_stoppedSource.Cancel(throwOnFirstException: false);
}
catch (Exception)
{
// TODO: LOG
}
}
}
}

View File

@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Hosting
{
public IServiceProvider Services { get; set; }
public IConfiguration Configuration { get; set; }
public ApplicationLifetime Lifetime { get; set; }
public IBuilder Builder { get; set; }

View File

@ -21,6 +21,7 @@ using Microsoft.AspNet.Hosting.Builder;
using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Hosting.Startup;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
namespace Microsoft.AspNet.Hosting
{
@ -45,6 +46,7 @@ namespace Microsoft.AspNet.Hosting
public IDisposable Start(HostingContext context)
{
EnsureLifetime(context);
EnsureBuilder(context);
EnsureServerFactory(context);
InitalizeServerFactory(context);
@ -55,11 +57,24 @@ namespace Microsoft.AspNet.Hosting
return new Disposable(() =>
{
context.Lifetime.SignalStopping();
server.Dispose();
context.Lifetime.SignalStopped();
pipeline.Dispose();
});
}
private void EnsureLifetime(HostingContext context)
{
if (context.Lifetime == null)
{
context.Lifetime = new ApplicationLifetime();
}
var serviceCollection = new ServiceCollection();
serviceCollection.AddInstance<IApplicationLifetime>(context.Lifetime);
context.Services = serviceCollection.BuildServiceProvider(context.Services);
}
private void EnsureBuilder(HostingContext context)
{
if (context.Builder != null)

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Hosting
{
/// <summary>
/// Allows consumers to perform cleanup during a graceful shutdown.
/// </summary>
[AssemblyNeutral]
public interface IApplicationLifetime
{
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// Request may still be in flight. Shutdown will block until this event completes.
/// </summary>
/// <returns></returns>
CancellationToken ApplicationStopping { get; }
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// All requests should be complete at this point. Shutdown will block
/// until this event completes.
/// </summary>
/// <returns></returns>
CancellationToken ApplicationStopped { get; }
}
}

View File

@ -20,6 +20,7 @@
<Content Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="ApplicationLifetime.cs" />
<Compile Include="Builder\BuilderFactory.cs" />
<Compile Include="Builder\HttpContextFactory.cs" />
<Compile Include="Builder\IBuilderFactory.cs" />
@ -27,6 +28,7 @@
<Compile Include="HostingContext.cs" />
<Compile Include="HostingEngine.cs" />
<Compile Include="HostingServices.cs" />
<Compile Include="IApplicationLifetime.cs" />
<Compile Include="IHostingEngine.cs" />
<Compile Include="PipelineInstance.cs" />
<Compile Include="PlatformHelper.cs" />
@ -45,4 +47,4 @@
<Compile Include="WebApplication.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -17,6 +17,8 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
@ -67,11 +69,29 @@ namespace Microsoft.AspNet.Hosting
throw new Exception("TODO: IHostingEngine service not available exception");
}
using (engine.Start(context))
var appShutdownService = _serviceProvider.GetService<IApplicationShutdown>();
if (appShutdownService == null)
{
throw new Exception("TODO: IApplicationShutdown service not available");
}
var shutdownHandle = new ManualResetEvent(false);
var serverShutdown = engine.Start(context);
appShutdownService.ShutdownRequested.Register(() =>
{
serverShutdown.Dispose();
shutdownHandle.Set();
});
Task ignored = Task.Run(() =>
{
Console.WriteLine("Started");
Console.ReadLine();
}
appShutdownService.RequestShutdown();
});
shutdownHandle.WaitOne();
}
}
}