Merge branch 'release/2.1' into dev
This commit is contained in:
commit
edada4acc7
|
|
@ -23,23 +23,35 @@ namespace GenericHostSample
|
|||
|
||||
public class ServiceBaseLifetime : ServiceBase, IHostLifetime
|
||||
{
|
||||
private Action<object> _startCallback;
|
||||
private Action<object> _stopCallback;
|
||||
private object _startState;
|
||||
private object _stopState;
|
||||
private TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();
|
||||
|
||||
public void RegisterDelayStartCallback(Action<object> callback, object state)
|
||||
public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
|
||||
{
|
||||
_startCallback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
_startState = state;
|
||||
|
||||
Run(this);
|
||||
ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
|
||||
}
|
||||
|
||||
public void RegisterStopCallback(Action<object> callback, object state)
|
||||
private IApplicationLifetime ApplicationLifetime { get; }
|
||||
|
||||
public Task WaitForStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_stopCallback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
_stopState = state;
|
||||
cancellationToken.Register(() => _delayStart.TrySetCanceled());
|
||||
ApplicationLifetime.ApplicationStopping.Register(Stop);
|
||||
|
||||
new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
|
||||
return _delayStart.Task;
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Run(this); // This blocks until the service is stopped.
|
||||
_delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_delayStart.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
|
|
@ -48,15 +60,18 @@ namespace GenericHostSample
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Called by base.Run when the service is ready to start.
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
_startCallback(_startState);
|
||||
_delayStart.TrySetResult(null);
|
||||
base.OnStart(args);
|
||||
}
|
||||
|
||||
// Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
|
||||
// That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
|
||||
protected override void OnStop()
|
||||
{
|
||||
_stopCallback(_stopState);
|
||||
ApplicationLifetime.StopApplication();
|
||||
base.OnStop();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -10,20 +9,10 @@ namespace Microsoft.Extensions.Hosting
|
|||
public interface IHostLifetime
|
||||
{
|
||||
/// <summary>
|
||||
/// Called at the start of <see cref="IHost.StartAsync(CancellationToken)"/> which will wait until the callback is invoked before
|
||||
/// Called at the start of <see cref="IHost.StartAsync(CancellationToken)"/> which will wait until it's compete before
|
||||
/// continuing. This can be used to delay startup until signaled by an external event.
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback that will be invoked when the host should continue.</param>
|
||||
/// <param name="state">State to pass to the callback.</param>
|
||||
void RegisterDelayStartCallback(Action<object> callback, object state);
|
||||
|
||||
/// <summary>
|
||||
/// Called at the start of <see cref="IHost.StartAsync(CancellationToken)"/> to register the given callback for initiating the
|
||||
/// application shutdown process.
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to invoke when an external signal indicates the application should stop.</param>
|
||||
/// <param name="state">State to pass to the callback.</param>
|
||||
void RegisterStopCallback(Action<object> callback, object state);
|
||||
Task WaitForStartAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Called from <see cref="IHost.StopAsync(CancellationToken)"/> to indicate that the host as stopped and clean up resources.
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ namespace Microsoft.Extensions.Hosting
|
|||
services.AddSingleton(_hostBuilderContext);
|
||||
services.AddSingleton(_appConfiguration);
|
||||
services.AddSingleton<IApplicationLifetime, ApplicationLifetime>();
|
||||
services.AddSingleton<IHostLifetime, ProcessLifetime>();
|
||||
services.AddSingleton<IHostLifetime, ConsoleLifetime>();
|
||||
services.AddSingleton<IHost, Host>();
|
||||
services.AddOptions();
|
||||
services.AddLogging();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.Extensions.Hosting.Internal
|
|||
|
||||
private IApplicationLifetime ApplicationLifetime { get; }
|
||||
|
||||
public void RegisterDelayStartCallback(Action<object> callback, object state)
|
||||
public Task WaitForStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Options.SuppressStatusMessages)
|
||||
{
|
||||
|
|
@ -38,18 +38,15 @@ namespace Microsoft.Extensions.Hosting.Internal
|
|||
});
|
||||
}
|
||||
|
||||
// Console applications start immediately.
|
||||
callback(state);
|
||||
}
|
||||
|
||||
public void RegisterStopCallback(Action<object> callback, object state)
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => callback(state);
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => ApplicationLifetime.StopApplication();
|
||||
Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true;
|
||||
callback(state);
|
||||
ApplicationLifetime.StopApplication();
|
||||
};
|
||||
|
||||
// Console applications start immediately.
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -36,13 +36,9 @@ namespace Microsoft.Extensions.Hosting.Internal
|
|||
{
|
||||
_logger.Starting();
|
||||
|
||||
var delayStart = new TaskCompletionSource<object>();
|
||||
cancellationToken.Register(obj => ((TaskCompletionSource<object>)obj).TrySetCanceled(), delayStart);
|
||||
_hostLifetime.RegisterDelayStartCallback(obj => ((TaskCompletionSource<object>)obj).TrySetResult(null), delayStart);
|
||||
_hostLifetime.RegisterStopCallback(obj => (obj as IApplicationLifetime)?.StopApplication(), _applicationLifetime);
|
||||
|
||||
await delayStart.Task;
|
||||
await _hostLifetime.WaitForStartAsync(cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
|
||||
|
||||
foreach (var hostedService in _hostedServices)
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Extensions.Hosting.Internal
|
||||
{
|
||||
public class ProcessLifetime : IHostLifetime
|
||||
{
|
||||
public void RegisterDelayStartCallback(Action<object> callback, object state)
|
||||
{
|
||||
// Never delays start.
|
||||
callback(state);
|
||||
}
|
||||
|
||||
public void RegisterStopCallback(Action<object> callback, object state)
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => callback(state);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,23 +10,16 @@ namespace Microsoft.Extensions.Hosting.Tests.Fakes
|
|||
public class FakeHostLifetime : IHostLifetime
|
||||
{
|
||||
public int StartCount { get; internal set; }
|
||||
public int StoppingCount { get; internal set; }
|
||||
public int StopCount { get; internal set; }
|
||||
|
||||
public Action<Action<object>, object> StartAction { get; set; }
|
||||
public Action<Action<object>, object> StoppingAction { get; set; }
|
||||
public Action<CancellationToken> StartAction { get; set; }
|
||||
public Action StopAction { get; set; }
|
||||
|
||||
public void RegisterDelayStartCallback(Action<object> callback, object state)
|
||||
public Task WaitForStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
StartCount++;
|
||||
StartAction?.Invoke(callback, state);
|
||||
}
|
||||
|
||||
public void RegisterStopCallback(Action<object> callback, object state)
|
||||
{
|
||||
StoppingCount++;
|
||||
StoppingAction?.Invoke(callback, state);
|
||||
StartAction?.Invoke(cancellationToken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -233,11 +233,10 @@ namespace Microsoft.Extensions.Hosting
|
|||
});
|
||||
services.AddSingleton<IHostLifetime>(_ => new FakeHostLifetime()
|
||||
{
|
||||
StartAction = (callback, state) =>
|
||||
StartAction = ct =>
|
||||
{
|
||||
lifetimeStart.Set();
|
||||
Assert.True(lifetimeContinue.WaitOne(TimeSpan.FromSeconds(5)));
|
||||
callback(state);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -259,7 +258,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
|
||||
lifetime = (FakeHostLifetime)host.Services.GetRequiredService<IHostLifetime>();
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +266,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
Assert.Equal(1, service.DisposeCount);
|
||||
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
@ -292,9 +289,10 @@ namespace Microsoft.Extensions.Hosting
|
|||
});
|
||||
services.AddSingleton<IHostLifetime>(_ => new FakeHostLifetime()
|
||||
{
|
||||
StartAction = (callback, state) =>
|
||||
StartAction = ct =>
|
||||
{
|
||||
lifetimeStart.Set();
|
||||
WaitHandle.WaitAny(new[] { lifetimeContinue, ct.WaitHandle });
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -308,7 +306,7 @@ namespace Microsoft.Extensions.Hosting
|
|||
Assert.False(serviceStarting.WaitOne(0));
|
||||
|
||||
cts.Cancel();
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(() => startTask);
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(() => startTask);
|
||||
Assert.False(serviceStarting.WaitOne(0));
|
||||
|
||||
lifetimeContinue.Set();
|
||||
|
|
@ -322,7 +320,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
|
||||
lifetime = (FakeHostLifetime)host.Services.GetRequiredService<IHostLifetime>();
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
@ -331,61 +328,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
Assert.Equal(1, service.DisposeCount);
|
||||
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HostLifetimeOnStoppingTriggersIApplicationLifetime()
|
||||
{
|
||||
var lifetimeRegistered = new ManualResetEvent(false);
|
||||
Action<object> stoppingAction = null;
|
||||
object stoppingState = null;
|
||||
FakeHostedService service;
|
||||
FakeHostLifetime lifetime;
|
||||
using (var host = CreateBuilder()
|
||||
.ConfigureServices((services) =>
|
||||
{
|
||||
services.AddSingleton<IHostedService, FakeHostedService>();
|
||||
services.AddSingleton<IHostLifetime>(_ => new FakeHostLifetime()
|
||||
{
|
||||
StartAction = (callback, state) => callback(state),
|
||||
StoppingAction = (callback, state) =>
|
||||
{
|
||||
stoppingAction = callback;
|
||||
stoppingState = state;
|
||||
lifetimeRegistered.Set();
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build())
|
||||
{
|
||||
await host.StartAsync();
|
||||
Assert.True(lifetimeRegistered.WaitOne(0));
|
||||
|
||||
var appLifetime = host.Services.GetRequiredService<IApplicationLifetime>();
|
||||
|
||||
stoppingAction(stoppingState);
|
||||
|
||||
Assert.True(appLifetime.ApplicationStopping.WaitHandle.WaitOne(TimeSpan.FromSeconds(5)));
|
||||
|
||||
service = (FakeHostedService)host.Services.GetRequiredService<IHostedService>();
|
||||
Assert.Equal(1, service.StartCount);
|
||||
Assert.Equal(0, service.StopCount);
|
||||
Assert.Equal(0, service.DisposeCount);
|
||||
|
||||
lifetime = (FakeHostLifetime)host.Services.GetRequiredService<IHostLifetime>();
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
Assert.Equal(1, service.StartCount);
|
||||
Assert.Equal(0, service.StopCount);
|
||||
Assert.Equal(1, service.DisposeCount);
|
||||
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
@ -398,10 +340,7 @@ namespace Microsoft.Extensions.Hosting
|
|||
.ConfigureServices((services) =>
|
||||
{
|
||||
services.AddSingleton<IHostedService, FakeHostedService>();
|
||||
services.AddSingleton<IHostLifetime>(_ => new FakeHostLifetime()
|
||||
{
|
||||
StartAction = (callback, state) => callback(state),
|
||||
});
|
||||
services.AddSingleton<IHostLifetime, FakeHostLifetime>();
|
||||
})
|
||||
.Build())
|
||||
{
|
||||
|
|
@ -414,7 +353,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
|
||||
lifetime = (FakeHostLifetime)host.Services.GetRequiredService<IHostLifetime>();
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(0, lifetime.StopCount);
|
||||
|
||||
await host.StopAsync();
|
||||
|
|
@ -424,7 +362,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
Assert.Equal(0, service.DisposeCount);
|
||||
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(1, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +370,6 @@ namespace Microsoft.Extensions.Hosting
|
|||
Assert.Equal(1, service.DisposeCount);
|
||||
|
||||
Assert.Equal(1, lifetime.StartCount);
|
||||
Assert.Equal(1, lifetime.StoppingCount);
|
||||
Assert.Equal(1, lifetime.StopCount);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue