Lock around Connection.End to make it thread-safe

This commit is contained in:
Stephen Halter 2015-08-14 12:17:06 -07:00
parent 4f0480a4d0
commit 32c4f314b6
1 changed files with 58 additions and 50 deletions

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.AspNet.Server.Kestrel.Networking;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Microsoft.AspNet.Server.Kestrel.Http namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
@ -16,8 +15,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private static readonly Action<UvStreamHandle, int, Exception, object> _readCallback = ReadCallback; private static readonly Action<UvStreamHandle, int, Exception, object> _readCallback = ReadCallback;
private static readonly Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback = AllocCallback; private static readonly Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback = AllocCallback;
private int _connectionState;
private static Libuv.uv_buf_t AllocCallback(UvStreamHandle handle, int suggestedSize, object state) private static Libuv.uv_buf_t AllocCallback(UvStreamHandle handle, int suggestedSize, object state)
{ {
return ((Connection)state).OnAlloc(handle, suggestedSize); return ((Connection)state).OnAlloc(handle, suggestedSize);
@ -32,6 +29,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private Frame _frame; private Frame _frame;
long _connectionId = 0; long _connectionId = 0;
private readonly object _stateLock = new object();
private ConnectionState _connectionState;
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) public Connection(ListenerContext context, UvStreamHandle socket) : base(context)
{ {
_socket = socket; _socket = socket;
@ -103,15 +103,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
} }
void IConnectionControl.End(ProduceEndType endType) void IConnectionControl.End(ProduceEndType endType)
{
lock (_stateLock)
{ {
switch (endType) switch (endType)
{ {
case ProduceEndType.SocketShutdownSend: case ProduceEndType.SocketShutdownSend:
if (Interlocked.CompareExchange(ref _connectionState, ConnectionState.Shutdown, ConnectionState.Open) if (_connectionState != ConnectionState.Open)
!= ConnectionState.Open)
{ {
return; return;
} }
_connectionState = ConnectionState.Shutdown;
KestrelTrace.Log.ConnectionWriteFin(_connectionId, 0); KestrelTrace.Log.ConnectionWriteFin(_connectionId, 0);
Thread.Post( Thread.Post(
@ -130,6 +132,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
this); this);
break; break;
case ProduceEndType.ConnectionKeepAlive: case ProduceEndType.ConnectionKeepAlive:
if (_connectionState != ConnectionState.Open)
{
return;
}
KestrelTrace.Log.ConnectionKeepAlive(_connectionId); KestrelTrace.Log.ConnectionKeepAlive(_connectionId);
_frame = new Frame(this); _frame = new Frame(this);
Thread.Post( Thread.Post(
@ -137,11 +144,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
_frame); _frame);
break; break;
case ProduceEndType.SocketDisconnect: case ProduceEndType.SocketDisconnect:
if (Interlocked.Exchange(ref _connectionState, ConnectionState.Disconnected) if (_connectionState == ConnectionState.Disconnected)
== ConnectionState.Disconnected)
{ {
return; return;
} }
_connectionState = ConnectionState.Disconnected;
KestrelTrace.Log.ConnectionDisconnect(_connectionId); KestrelTrace.Log.ConnectionDisconnect(_connectionId);
Thread.Post( Thread.Post(
@ -154,12 +161,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
break; break;
} }
} }
}
private static class ConnectionState private enum ConnectionState
{ {
public const int Open = 0; Open,
public const int Shutdown = 1; Shutdown,
public const int Disconnected = 2; Disconnected
} }
} }
} }