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;
@ -104,62 +104,70 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
void IConnectionControl.End(ProduceEndType endType) void IConnectionControl.End(ProduceEndType endType)
{ {
switch (endType) lock (_stateLock)
{ {
case ProduceEndType.SocketShutdownSend: switch (endType)
if (Interlocked.CompareExchange(ref _connectionState, ConnectionState.Shutdown, ConnectionState.Open) {
!= ConnectionState.Open) case ProduceEndType.SocketShutdownSend:
{ if (_connectionState != ConnectionState.Open)
return;
}
KestrelTrace.Log.ConnectionWriteFin(_connectionId, 0);
Thread.Post(
x =>
{ {
KestrelTrace.Log.ConnectionWriteFin(_connectionId, 1); return;
var self = (Connection)x; }
var shutdown = new UvShutdownReq(); _connectionState = ConnectionState.Shutdown;
shutdown.Init(self.Thread.Loop);
shutdown.Shutdown(self._socket, (req, status, state) => KestrelTrace.Log.ConnectionWriteFin(_connectionId, 0);
Thread.Post(
x =>
{ {
KestrelTrace.Log.ConnectionWriteFin(_connectionId, 1); KestrelTrace.Log.ConnectionWriteFin(_connectionId, 1);
req.Dispose(); var self = (Connection)x;
}, null); var shutdown = new UvShutdownReq();
}, shutdown.Init(self.Thread.Loop);
this); shutdown.Shutdown(self._socket, (req, status, state) =>
break; {
case ProduceEndType.ConnectionKeepAlive: KestrelTrace.Log.ConnectionWriteFin(_connectionId, 1);
KestrelTrace.Log.ConnectionKeepAlive(_connectionId); req.Dispose();
_frame = new Frame(this); }, null);
Thread.Post( },
x => ((Frame)x).Consume(), this);
_frame); break;
break; case ProduceEndType.ConnectionKeepAlive:
case ProduceEndType.SocketDisconnect: if (_connectionState != ConnectionState.Open)
if (Interlocked.Exchange(ref _connectionState, ConnectionState.Disconnected)
== ConnectionState.Disconnected)
{
return;
}
KestrelTrace.Log.ConnectionDisconnect(_connectionId);
Thread.Post(
x =>
{ {
KestrelTrace.Log.ConnectionStop(_connectionId); return;
((UvHandle)x).Dispose(); }
},
_socket); KestrelTrace.Log.ConnectionKeepAlive(_connectionId);
break; _frame = new Frame(this);
Thread.Post(
x => ((Frame)x).Consume(),
_frame);
break;
case ProduceEndType.SocketDisconnect:
if (_connectionState == ConnectionState.Disconnected)
{
return;
}
_connectionState = ConnectionState.Disconnected;
KestrelTrace.Log.ConnectionDisconnect(_connectionId);
Thread.Post(
x =>
{
KestrelTrace.Log.ConnectionStop(_connectionId);
((UvHandle)x).Dispose();
},
_socket);
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
} }
} }
} }