Prevent ODE when ReadStart/Stop() is queued after disposal

This commit is contained in:
Stephen Halter 2017-02-02 20:29:33 -08:00
parent 5124adf450
commit e9e0cf7325
2 changed files with 36 additions and 18 deletions

View File

@ -6,18 +6,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{ {
private readonly long _maxSize; private readonly long _maxSize;
private readonly IConnectionControl _connectionControl; private readonly IConnectionControl _connectionControl;
private readonly KestrelThread _connectionThread;
private readonly object _lock = new object(); private readonly object _lock = new object();
private long _size; private long _size;
private bool _connectionPaused; private bool _connectionPaused;
public BufferSizeControl(long maxSize, IConnectionControl connectionControl, KestrelThread connectionThread) public BufferSizeControl(long maxSize, IConnectionControl connectionControl)
{ {
_maxSize = maxSize; _maxSize = maxSize;
_connectionControl = connectionControl; _connectionControl = connectionControl;
_connectionThread = connectionThread;
} }
private long Size private long Size
@ -50,9 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (!_connectionPaused && Size >= _maxSize) if (!_connectionPaused && Size >= _maxSize)
{ {
_connectionPaused = true; _connectionPaused = true;
_connectionThread.Post( _connectionControl.Pause();
(connectionControl) => ((IConnectionControl)connectionControl).Pause(),
_connectionControl);
} }
} }
} }
@ -73,9 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (_connectionPaused && Size < _maxSize) if (_connectionPaused && Size < _maxSize)
{ {
_connectionPaused = false; _connectionPaused = false;
_connectionThread.Post( _connectionControl.Resume();
(connectionControl) => ((IConnectionControl)connectionControl).Resume(),
_connectionControl);
} }
} }
} }

View File

@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (ServerOptions.Limits.MaxRequestBufferSize.HasValue) if (ServerOptions.Limits.MaxRequestBufferSize.HasValue)
{ {
_bufferSizeControl = new BufferSizeControl(ServerOptions.Limits.MaxRequestBufferSize.Value, this, Thread); _bufferSizeControl = new BufferSizeControl(ServerOptions.Limits.MaxRequestBufferSize.Value, this);
} }
Input = new SocketInput(Thread.Memory, ThreadPool, _bufferSizeControl); Input = new SocketInput(Thread.Memory, ThreadPool, _bufferSizeControl);
@ -285,22 +285,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
void IConnectionControl.Pause() void IConnectionControl.Pause()
{ {
Log.ConnectionPause(ConnectionId); Log.ConnectionPause(ConnectionId);
_socket.ReadStop();
// Even though this method is called on the event loop already,
// post anyway so the ReadStop() call doesn't get reordered
// relative to the ReadStart() call made in Resume().
Thread.Post(state => ((Connection)state).OnPausePosted(), this);
} }
void IConnectionControl.Resume() void IConnectionControl.Resume()
{ {
Log.ConnectionResume(ConnectionId); Log.ConnectionResume(ConnectionId);
try
// This is called from the consuming thread.
Thread.Post(state => ((Connection)state).OnResumePosted(), this);
}
private void OnPausePosted()
{
// It's possible that uv_close was called between the call to Thread.Post() and now.
if (!_socket.IsClosed)
{ {
_socket.ReadStart(_allocCallback, _readCallback, this); _socket.ReadStop();
} }
catch (UvException) }
private void OnResumePosted()
{
// It's possible that uv_close was called even before the call to Resume().
if (!_socket.IsClosed)
{ {
// ReadStart() can throw a UvException in some cases (e.g. socket is no longer connected). try
// This should be treated the same as OnRead() seeing a "normalDone" condition. {
Log.ConnectionReadFin(ConnectionId); _socket.ReadStart(_allocCallback, _readCallback, this);
Input.IncomingComplete(0, null); }
catch (UvException)
{
// ReadStart() can throw a UvException in some cases (e.g. socket is no longer connected).
// This should be treated the same as OnRead() seeing a "normalDone" condition.
Log.ConnectionReadFin(ConnectionId);
Input.IncomingComplete(0, null);
}
} }
} }