Clean up LibuvOutputConsumer (#1744)
* Clean up LibuvOutputConsumer - Added UvShutdownReq.ShutdownAsync - Added Debug.Assert in LibuvAwaitable since it should never race.
This commit is contained in:
parent
650a3ccc26
commit
db44f5b672
|
|
@ -2,9 +2,9 @@
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
|
|
@ -49,10 +49,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
public void OnCompleted(Action continuation)
|
||||||
{
|
{
|
||||||
|
// There should never be a race between IsCompleted and OnCompleted since both operations
|
||||||
|
// should always be on the libuv thread
|
||||||
|
|
||||||
if (_callback == _callbackCompleted ||
|
if (_callback == _callbackCompleted ||
|
||||||
Interlocked.CompareExchange(ref _callback, continuation, null) == _callbackCompleted)
|
Interlocked.CompareExchange(ref _callback, continuation, null) == _callbackCompleted)
|
||||||
{
|
{
|
||||||
Task.Run(continuation);
|
Debug.Fail($"{typeof(LibuvAwaitable<TRequest>)}.{nameof(OnCompleted)} raced with {nameof(IsCompleted)}, running callback inline.");
|
||||||
|
|
||||||
|
// Just run it inline
|
||||||
|
continuation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
private readonly UvStreamHandle _socket;
|
private readonly UvStreamHandle _socket;
|
||||||
private readonly string _connectionId;
|
private readonly string _connectionId;
|
||||||
private readonly ILibuvTrace _log;
|
private readonly ILibuvTrace _log;
|
||||||
|
|
||||||
private readonly WriteReqPool _writeReqPool;
|
|
||||||
private readonly IPipeReader _pipe;
|
private readonly IPipeReader _pipe;
|
||||||
|
|
||||||
public LibuvOutputConsumer(
|
public LibuvOutputConsumer(
|
||||||
|
|
@ -26,17 +24,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
ILibuvTrace log)
|
ILibuvTrace log)
|
||||||
{
|
{
|
||||||
_pipe = pipe;
|
_pipe = pipe;
|
||||||
// We need to have empty pipe at this moment so callback
|
|
||||||
// get's scheduled
|
|
||||||
_thread = thread;
|
_thread = thread;
|
||||||
_socket = socket;
|
_socket = socket;
|
||||||
_connectionId = connectionId;
|
_connectionId = connectionId;
|
||||||
_log = log;
|
_log = log;
|
||||||
_writeReqPool = thread.WriteReqPool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WriteOutputAsync()
|
public async Task WriteOutputAsync()
|
||||||
{
|
{
|
||||||
|
var pool = _thread.WriteReqPool;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var result = await _pipe.ReadAsync();
|
var result = await _pipe.ReadAsync();
|
||||||
|
|
@ -46,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
{
|
{
|
||||||
if (!buffer.IsEmpty)
|
if (!buffer.IsEmpty)
|
||||||
{
|
{
|
||||||
var writeReq = _writeReqPool.Allocate();
|
var writeReq = pool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -62,14 +59,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Make sure we return the writeReq to the pool
|
// Make sure we return the writeReq to the pool
|
||||||
_writeReqPool.Return(writeReq);
|
pool.Return(writeReq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsCancelled)
|
if (result.IsCancelled)
|
||||||
{
|
{
|
||||||
// Send a FIN
|
// Send a FIN
|
||||||
await ShutdownAsync();
|
_log.ConnectionWriteFin(_connectionId);
|
||||||
|
|
||||||
|
using (var shutdownReq = new UvShutdownReq(_log))
|
||||||
|
{
|
||||||
|
shutdownReq.Init(_thread);
|
||||||
|
var shutdownResult = await shutdownReq.ShutdownAsync(_socket);
|
||||||
|
|
||||||
|
_log.ConnectionWroteFin(_connectionId, shutdownResult.Status);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure no data is written after uv_shutdown
|
// Ensure no data is written after uv_shutdown
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -105,32 +111,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ShutdownAsync()
|
|
||||||
{
|
|
||||||
var tcs = new TaskCompletionSource<object>();
|
|
||||||
_log.ConnectionWriteFin(_connectionId);
|
|
||||||
|
|
||||||
var shutdownReq = new UvShutdownReq(_log);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
shutdownReq.Init(_thread);
|
|
||||||
shutdownReq.Shutdown(_socket, (req, status, state) =>
|
|
||||||
{
|
|
||||||
req.Dispose();
|
|
||||||
_log.ConnectionWroteFin(_connectionId, status);
|
|
||||||
|
|
||||||
tcs.TrySetResult(null);
|
|
||||||
},
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
shutdownReq.Dispose();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tcs.Task;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking
|
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking
|
||||||
{
|
{
|
||||||
|
|
@ -12,10 +13,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
|
||||||
{
|
{
|
||||||
private readonly static LibuvFunctions.uv_shutdown_cb _uv_shutdown_cb = UvShutdownCb;
|
private readonly static LibuvFunctions.uv_shutdown_cb _uv_shutdown_cb = UvShutdownCb;
|
||||||
|
|
||||||
private Action<UvShutdownReq, int, object> _callback;
|
private Action<UvShutdownReq, int, Exception, object> _callback;
|
||||||
private object _state;
|
private object _state;
|
||||||
|
private LibuvAwaitable<UvShutdownReq> _awaitable = new LibuvAwaitable<UvShutdownReq>();
|
||||||
|
|
||||||
public UvShutdownReq(ILibuvTrace logger) : base (logger)
|
public UvShutdownReq(ILibuvTrace logger) : base(logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,14 +26,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
|
||||||
var loop = thread.Loop;
|
var loop = thread.Loop;
|
||||||
|
|
||||||
CreateMemory(
|
CreateMemory(
|
||||||
loop.Libuv,
|
loop.Libuv,
|
||||||
loop.ThreadId,
|
loop.ThreadId,
|
||||||
loop.Libuv.req_size(LibuvFunctions.RequestType.SHUTDOWN));
|
loop.Libuv.req_size(LibuvFunctions.RequestType.SHUTDOWN));
|
||||||
|
|
||||||
base.Init(thread);
|
base.Init(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, object> callback, object state)
|
public LibuvAwaitable<UvShutdownReq> ShutdownAsync(UvStreamHandle handle)
|
||||||
|
{
|
||||||
|
Shutdown(handle, LibuvAwaitable<UvShutdownReq>.Callback, _awaitable);
|
||||||
|
return _awaitable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, Exception, object> callback, object state)
|
||||||
{
|
{
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
_state = state;
|
_state = state;
|
||||||
|
|
@ -41,9 +49,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
|
||||||
private static void UvShutdownCb(IntPtr ptr, int status)
|
private static void UvShutdownCb(IntPtr ptr, int status)
|
||||||
{
|
{
|
||||||
var req = FromIntPtr<UvShutdownReq>(ptr);
|
var req = FromIntPtr<UvShutdownReq>(ptr);
|
||||||
req._callback(req, status, req._state);
|
|
||||||
|
var callback = req._callback;
|
||||||
req._callback = null;
|
req._callback = null;
|
||||||
|
|
||||||
|
var state = req._state;
|
||||||
req._state = null;
|
req._state = null;
|
||||||
|
|
||||||
|
Exception error = null;
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
req.Libuv.Check(status, out error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callback(req, status, error, state);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
req._log.LogError(0, ex, "UvShutdownCb");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue