Fix NRE when aborting connection or client disconects

This commit is contained in:
Pavel Krymets 2016-05-31 09:24:46 -07:00
parent 6224f5b6e8
commit bb92cc1c29
3 changed files with 73 additions and 38 deletions

View File

@ -27,9 +27,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private MemoryPoolBlock _tail; private MemoryPoolBlock _tail;
private MemoryPoolBlock _pinned; private MemoryPoolBlock _pinned;
private int _consumingState;
private object _sync = new object(); private object _sync = new object();
private bool _consuming;
private bool _disposed;
public SocketInput(MemoryPool memory, IThreadPool threadPool) public SocketInput(MemoryPool memory, IThreadPool threadPool)
{ {
_memory = memory; _memory = memory;
@ -163,12 +165,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public MemoryPoolIterator ConsumingStart() public MemoryPoolIterator ConsumingStart()
{ {
if (Interlocked.CompareExchange(ref _consumingState, 1, 0) != 0) lock (_sync)
{ {
throw new InvalidOperationException("Already consuming input."); if (_consuming)
{
throw new InvalidOperationException("Already consuming input.");
}
_consuming = true;
return new MemoryPoolIterator(_head);
} }
return new MemoryPoolIterator(_head);
} }
public void ConsumingComplete( public void ConsumingComplete(
@ -180,38 +185,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
lock (_sync) lock (_sync)
{ {
if (!consumed.IsDefault) if (!_disposed)
{
if (!consumed.IsDefault)
{
returnStart = _head;
returnEnd = consumed.Block;
_head = consumed.Block;
_head.Start = consumed.Index;
}
if (!examined.IsDefault &&
examined.IsEnd &&
RemoteIntakeFin == false &&
_awaitableError == null)
{
_manualResetEvent.Reset();
Interlocked.CompareExchange(
ref _awaitableState,
_awaitableIsNotCompleted,
_awaitableIsCompleted);
}
}
else
{ {
returnStart = _head; returnStart = _head;
returnEnd = consumed.Block; returnEnd = null;
_head = consumed.Block; _head = null;
_head.Start = consumed.Index; _tail = null;
} }
if (!examined.IsDefault && ReturnBlocks(returnStart, returnEnd);
examined.IsEnd &&
RemoteIntakeFin == false && if (!_consuming)
_awaitableError == null)
{ {
_manualResetEvent.Reset(); throw new InvalidOperationException("No ongoing consuming operation to complete.");
Interlocked.CompareExchange(
ref _awaitableState,
_awaitableIsNotCompleted,
_awaitableIsCompleted);
} }
} _consuming = false;
while (returnStart != returnEnd)
{
var returnBlock = returnStart;
returnStart = returnStart.Next;
returnBlock.Pool.Return(returnBlock);
}
if (Interlocked.CompareExchange(ref _consumingState, 0, 1) != 1)
{
throw new InvalidOperationException("No ongoing consuming operation to complete.");
} }
} }
@ -286,20 +297,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public void Dispose() public void Dispose()
{ {
AbortAwaiting(); lock (_sync)
{
AbortAwaiting();
// Return all blocks if (!_consuming)
var block = _head; {
while (block != null) ReturnBlocks(_head, null);
_head = null;
_tail = null;
}
_disposed = true;
}
}
private static void ReturnBlocks(MemoryPoolBlock block, MemoryPoolBlock end)
{
while (block != end)
{ {
var returnBlock = block; var returnBlock = block;
block = block.Next; block = block.Next;
returnBlock.Pool.Return(returnBlock); returnBlock.Pool.Return(returnBlock);
} }
_head = null;
_tail = null;
} }
} }
} }

View File

@ -29,6 +29,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
public static Task<int> GetCancelledZeroTask(CancellationToken cancellationToken = default(CancellationToken)) public static Task<int> GetCancelledZeroTask(CancellationToken cancellationToken = default(CancellationToken))
{ {
#if NETSTANDARD1_3 #if NETSTANDARD1_3
// Make sure cancellationToken is cancelled before passing to Task.FromCanceled
if (!cancellationToken.IsCancellationRequested)
{
cancellationToken = new CancellationToken(true);
}
return Task.FromCanceled<int>(cancellationToken); return Task.FromCanceled<int>(cancellationToken);
#else #else
var tcs = new TaskCompletionSource<int>(); var tcs = new TaskCompletionSource<int>();

View File

@ -104,5 +104,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var stream = new FrameRequestStream(); var stream = new FrameRequestStream();
await stream.FlushAsync(); await stream.FlushAsync();
} }
[Fact]
public void AbortCausesReadToCancel()
{
var stream = new FrameRequestStream();
stream.StartAcceptingReads(null);
stream.Abort();
var task = stream.ReadAsync(new byte[1], 0, 1);
Assert.True(task.IsCanceled);
}
} }
} }