Don't rely on the finalizer to return blocks referenced by SocketInput

This commit is contained in:
Stephen Halter 2016-01-26 17:35:35 -08:00
parent 87bd60746b
commit 9c31907bac
6 changed files with 50 additions and 12 deletions

View File

@ -48,10 +48,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter
{ {
if (copyAsyncTask.IsFaulted) if (copyAsyncTask.IsFaulted)
{ {
SocketInput.AbortAwaiting();
_log.LogError(0, copyAsyncTask.Exception, "FilteredStreamAdapter.CopyToAsync"); _log.LogError(0, copyAsyncTask.Exception, "FilteredStreamAdapter.CopyToAsync");
} }
else if (copyAsyncTask.IsCanceled) else if (copyAsyncTask.IsCanceled)
{ {
SocketInput.AbortAwaiting();
_log.LogError("FilteredStreamAdapter.CopyToAsync canceled."); _log.LogError("FilteredStreamAdapter.CopyToAsync canceled.");
} }

View File

@ -123,6 +123,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
} }
} }
public void OnSocketClosed()
{
_rawSocketInput.Dispose();
// If a connection filter was applied there will be two SocketInputs.
// If a connection filter failed, SocketInput will be null.
if (SocketInput != null && SocketInput != _rawSocketInput)
{
SocketInput.Dispose();
}
}
private void ApplyConnectionFilter() private void ApplyConnectionFilter()
{ {
if (_filterContext.Connection != _libuvStream) if (_filterContext.Connection != _libuvStream)

View File

@ -334,7 +334,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
try try
{ {
ConnectionControl.End(ProduceEndType.SocketDisconnect); ConnectionControl.End(ProduceEndType.SocketDisconnect);
SocketInput.AbortAwaiting();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -145,14 +145,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
// If _requestAborted is set, the connection has already been closed. // If _requestAborted is set, the connection has already been closed.
if (Volatile.Read(ref _requestAborted) == 0) if (Volatile.Read(ref _requestAborted) == 0)
{ {
// Inform client no more data will ever arrive try
ConnectionControl.End(ProduceEndType.SocketShutdownSend); {
// Inform client no more data will ever arrive
ConnectionControl.End(ProduceEndType.SocketShutdownSend);
// Wait for client to either disconnect or send unexpected data // Wait for client to either disconnect or send unexpected data
await SocketInput; await SocketInput;
}
// Dispose socket finally
ConnectionControl.End(ProduceEndType.SocketDisconnect); {
// Ensure we *always* disconnect the socket.
// Dispose socket
ConnectionControl.End(ProduceEndType.SocketDisconnect);
}
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Http namespace Microsoft.AspNetCore.Server.Kestrel.Http
{ {
public class SocketInput : ICriticalNotifyCompletion public class SocketInput : ICriticalNotifyCompletion, IDisposable
{ {
private static readonly Action _awaitableIsCompleted = () => { }; private static readonly Action _awaitableIsCompleted = () => { };
private static readonly Action _awaitableIsNotCompleted = () => { }; private static readonly Action _awaitableIsNotCompleted = () => { };
@ -246,5 +246,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
throw new IOException(error.Message, error); throw new IOException(error.Message, error);
} }
} }
public void Dispose()
{
AbortAwaiting();
// Return all blocks
var block = _head;
while (block != null)
{
var returnBlock = block;
block = block.Next;
returnBlock.Pool.Return(returnBlock);
}
_head = null;
_tail = null;
}
} }
} }

View File

@ -614,17 +614,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{ {
if (SocketDisconnect == false || Self._socket.IsClosed) if (SocketDisconnect == false || Self._socket.IsClosed)
{ {
CompleteOnUvThread(); CompleteWithContextLock();
return; return;
} }
Self._socket.Dispose(); Self._socket.Dispose();
Self._connection.OnSocketClosed();
Self.ReturnAllBlocks(); Self.ReturnAllBlocks();
Self._log.ConnectionStop(Self._connectionId); Self._log.ConnectionStop(Self._connectionId);
CompleteOnUvThread(); CompleteWithContextLock();
} }
public void CompleteOnUvThread() public void CompleteWithContextLock()
{ {
if (Monitor.TryEnter(Self._contextLock)) if (Monitor.TryEnter(Self._contextLock))
{ {