Fix deadlocks in startup and shutdown

This commit is contained in:
Stephen Halter 2017-04-13 11:40:21 -07:00
parent ed4a27a827
commit 6036f27f52
7 changed files with 18 additions and 59 deletions

View File

@ -287,7 +287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
var connectionHandler = new ConnectionHandler<TContext>(ipv4ListenOptions, serviceContext, application); var connectionHandler = new ConnectionHandler<TContext>(ipv4ListenOptions, serviceContext, application);
var transport = _transportFactory.Create(ipv4ListenOptions, connectionHandler); var transport = _transportFactory.Create(ipv4ListenOptions, connectionHandler);
_transports.Add(transport); _transports.Add(transport);
await transport.BindAsync(); await transport.BindAsync().ConfigureAwait(false);
} }
catch (AddressInUseException ex) catch (AddressInUseException ex)
{ {
@ -306,7 +306,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
var connectionHandler = new ConnectionHandler<TContext>(ipv6ListenOptions, serviceContext, application); var connectionHandler = new ConnectionHandler<TContext>(ipv6ListenOptions, serviceContext, application);
var transport = _transportFactory.Create(ipv6ListenOptions, connectionHandler); var transport = _transportFactory.Create(ipv6ListenOptions, connectionHandler);
_transports.Add(transport); _transports.Add(transport);
await transport.BindAsync(); await transport.BindAsync().ConfigureAwait(false);
} }
catch (AddressInUseException ex) catch (AddressInUseException ex)
{ {

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
private readonly UvStreamHandle _socket; private readonly UvStreamHandle _socket;
private IConnectionContext _connectionContext; private IConnectionContext _connectionContext;
private TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(); private TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private WritableBuffer? _currentWritableBuffer; private WritableBuffer? _currentWritableBuffer;

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
private async Task<bool> WalkConnectionsAsync(Action<LibuvConnectionManager, TaskCompletionSource<object>> action, TimeSpan timeout) private async Task<bool> WalkConnectionsAsync(Action<LibuvConnectionManager, TaskCompletionSource<object>> action, TimeSpan timeout)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_thread.Post(state => action(state, tcs), this); _thread.Post(state => action(state, tcs), this);

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
private readonly LibuvTransport _transport; private readonly LibuvTransport _transport;
private readonly IApplicationLifetime _appLifetime; private readonly IApplicationLifetime _appLifetime;
private readonly Thread _thread; private readonly Thread _thread;
private readonly TaskCompletionSource<object> _threadTcs = new TaskCompletionSource<object>(); private readonly TaskCompletionSource<object> _threadTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly UvLoopHandle _loop; private readonly UvLoopHandle _loop;
private readonly UvAsyncHandle _post; private readonly UvAsyncHandle _post;
private Queue<Work> _workAdding = new Queue<Work>(1024); private Queue<Work> _workAdding = new Queue<Work>(1024);
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
public Task StartAsync() public Task StartAsync()
{ {
var tcs = new TaskCompletionSource<int>(); var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
_thread.Start(tcs); _thread.Start(tcs);
return tcs.Task; return tcs.Task;
} }
@ -203,7 +203,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
public Task PostAsync<T>(Action<T> callback, T state) public Task PostAsync<T>(Action<T> callback, T state)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
lock (_workSync) lock (_workSync)
{ {
_workAdding.Enqueue(new Work _workAdding.Enqueue(new Work
@ -332,36 +332,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
try try
{ {
work.CallbackAdapter(work.Callback, work.State); work.CallbackAdapter(work.Callback, work.State);
if (work.Completion != null) work.Completion?.TrySetResult(null);
{
ThreadPool.QueueUserWorkItem(o =>
{
try
{
((TaskCompletionSource<object>)o).SetResult(null);
}
catch (Exception e)
{
_log.LogError(0, e, $"{nameof(LibuvThread)}.{nameof(DoPostWork)}");
}
}, work.Completion);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
if (work.Completion != null) if (work.Completion != null)
{ {
ThreadPool.QueueUserWorkItem(o => work.Completion.TrySetException(ex);
{
try
{
((TaskCompletionSource<object>)o).TrySetException(ex);
}
catch (Exception e)
{
_log.LogError(0, e, $"{nameof(LibuvThread)}.{nameof(DoPostWork)}");
}
}, work.Completion);
} }
else else
{ {

View File

@ -31,25 +31,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
EndPointInformation = endPointInformation; EndPointInformation = endPointInformation;
Thread = thread; Thread = thread;
var tcs = new TaskCompletionSource<int>(this); return Thread.PostAsync(listener =>
Thread.Post(state =>
{ {
var tcs2 = state; listener.ListenSocket = listener.CreateListenSocket();
try listener.ListenSocket.Listen(LibuvConstants.ListenBacklog, ConnectionCallback, listener);
{ }, this);
var listener = ((Listener) tcs2.Task.AsyncState);
listener.ListenSocket = listener.CreateListenSocket();
ListenSocket.Listen(LibuvConstants.ListenBacklog, ConnectionCallback, this);
tcs2.SetResult(0);
}
catch (Exception ex)
{
tcs2.SetException(ex);
}
}, tcs);
return tcs.Task;
} }
/// <summary> /// <summary>
@ -157,9 +143,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
// the exception that stopped the event loop will never be surfaced. // the exception that stopped the event loop will never be surfaced.
if (Thread.FatalError == null && ListenSocket != null) if (Thread.FatalError == null && ListenSocket != null)
{ {
await Thread.PostAsync(state => await Thread.PostAsync(listener =>
{ {
var listener = (Listener)state;
listener.ListenSocket.Dispose(); listener.ListenSocket.Dispose();
listener._closed = true; listener._closed = true;

View File

@ -53,8 +53,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
await StartAsync(endPointInformation, thread).ConfigureAwait(false); await StartAsync(endPointInformation, thread).ConfigureAwait(false);
await Thread.PostAsync(state => ((ListenerPrimary)state).PostCallback(), await Thread.PostAsync(listener => listener.PostCallback(), this).ConfigureAwait(false);
this).ConfigureAwait(false);
} }
private void PostCallback() private void PostCallback()
@ -175,9 +174,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
if (Thread.FatalError == null && ListenPipe != null) if (Thread.FatalError == null && ListenPipe != null)
{ {
await Thread.PostAsync(state => await Thread.PostAsync(listener =>
{ {
var listener = (ListenerPrimary)state;
listener.ListenPipe.Dispose(); listener.ListenPipe.Dispose();
foreach (var dispatchPipe in listener._dispatchPipes) foreach (var dispatchPipe in listener._dispatchPipes)

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
Thread = thread; Thread = thread;
DispatchPipe = new UvPipeHandle(Log); DispatchPipe = new UvPipeHandle(Log);
var tcs = new TaskCompletionSource<int>(this); var tcs = new TaskCompletionSource<int>(this, TaskCreationOptions.RunContinuationsAsynchronously);
Thread.Post(StartCallback, tcs); Thread.Post(StartCallback, tcs);
return tcs.Task; return tcs.Task;
} }
@ -185,9 +185,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
// the exception that stopped the event loop will never be surfaced. // the exception that stopped the event loop will never be surfaced.
if (Thread.FatalError == null) if (Thread.FatalError == null)
{ {
await Thread.PostAsync(state => await Thread.PostAsync(listener =>
{ {
var listener = (ListenerSecondary)state;
listener.DispatchPipe.Dispose(); listener.DispatchPipe.Dispose();
listener.FreeBuffer(); listener.FreeBuffer();