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

View File

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

View File

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

View File

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

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
Thread = thread;
DispatchPipe = new UvPipeHandle(Log);
var tcs = new TaskCompletionSource<int>(this);
var tcs = new TaskCompletionSource<int>(this, TaskCreationOptions.RunContinuationsAsynchronously);
Thread.Post(StartCallback, tcs);
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.
if (Thread.FatalError == null)
{
await Thread.PostAsync(state =>
await Thread.PostAsync(listener =>
{
var listener = (ListenerSecondary)state;
listener.DispatchPipe.Dispose();
listener.FreeBuffer();