Skip to content

Commit 53f2ff2

Browse files
committed
Manage connection state with a Monitor instead of a SemaphoreSlim
1 parent 7d2a692 commit 53f2ff2

File tree

5 files changed

+144
-142
lines changed

5 files changed

+144
-142
lines changed

src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.netcoreapp3.0.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ public HttpConnectionContext(string id, System.IO.Pipelines.IDuplexPipe transpor
8989
public Microsoft.AspNetCore.Http.HttpContext HttpContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
9090
public override System.Collections.Generic.IDictionary<object, object> Items { get { throw null; } set { } }
9191
public System.DateTime LastSeenUtc { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
92+
public System.DateTime? LastSeenUtcIfInactive { get { throw null; } }
9293
public System.Threading.Tasks.Task PreviousPollTask { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
93-
public System.Threading.SemaphoreSlim StateLock { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
94-
public Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus Status { get { throw null; } set { } }
94+
public Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
9595
public Microsoft.AspNetCore.Connections.TransferFormat SupportedFormats { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
9696
public override System.IO.Pipelines.IDuplexPipe Transport { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
9797
public System.Threading.Tasks.Task TransportTask { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
@@ -100,9 +100,10 @@ public HttpConnectionContext(string id, System.IO.Pipelines.IDuplexPipe transpor
100100
public System.Threading.SemaphoreSlim WriteLock { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
101101
[System.Diagnostics.DebuggerStepThroughAttribute]
102102
public System.Threading.Tasks.Task DisposeAsync(bool closeGracefully = false) { throw null; }
103+
public void MarkInactive() { }
103104
public void OnHeartbeat(System.Action<object> action, object state) { }
104105
public void TickHeartbeat() { }
105-
public bool TryChangeState(Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus from, Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus to) { throw null; }
106+
public Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus TryChangeState(Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus from, Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionStatus to) { throw null; }
106107
}
107108
public partial class HttpConnectionDispatcher
108109
{
@@ -122,7 +123,6 @@ public void CloseConnections() { }
122123
[System.Diagnostics.DebuggerStepThroughAttribute]
123124
public System.Threading.Tasks.Task DisposeAndRemoveAsync(Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext connection, bool closeGracefully) { throw null; }
124125
public void RemoveConnection(string id) { }
125-
[System.Diagnostics.DebuggerStepThroughAttribute]
126126
public System.Threading.Tasks.Task ScanAsync() { throw null; }
127127
public void Start() { }
128128
public bool TryGetConnection(string id, out Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext connection) { throw null; }

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ public class HttpConnectionContext : ConnectionContext,
2828
IHttpTransportFeature,
2929
IConnectionInherentKeepAliveFeature
3030
{
31+
private readonly object _stateLock = new object();
3132
private readonly object _itemsLock = new object();
3233
private readonly object _heartbeatLock = new object();
3334
private List<(Action<object> handler, object state)> _heartbeatHandlers;
3435
private readonly ILogger _logger;
3536
private PipeWriterStream _applicationStream;
3637
private IDuplexPipe _application;
3738
private IDictionary<object, object> _items;
38-
private int _status = (int)HttpConnectionStatus.Inactive;
3939

4040
// This tcs exists so that multiple calls to DisposeAsync all wait asynchronously
4141
// on the same task
@@ -83,7 +83,6 @@ public HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe appli
8383
public HttpTransportType TransportType { get; set; }
8484

8585
public SemaphoreSlim WriteLock { get; } = new SemaphoreSlim(1, 1);
86-
public SemaphoreSlim StateLock { get; } = new SemaphoreSlim(1, 1);
8786

8887
// Used for testing only
8988
internal Task DisposeAndRemoveTask { get; set; }
@@ -96,7 +95,18 @@ public HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe appli
9695

9796
public DateTime LastSeenUtc { get; set; }
9897

99-
public HttpConnectionStatus Status { get => (HttpConnectionStatus)_status; set => Interlocked.Exchange(ref _status, (int)value); }
98+
public DateTime? LastSeenUtcIfInactive
99+
{
100+
get
101+
{
102+
lock (_stateLock)
103+
{
104+
return Status == HttpConnectionStatus.Inactive ? (DateTime?)LastSeenUtc : null;
105+
}
106+
}
107+
}
108+
109+
public HttpConnectionStatus Status { get; set; } = HttpConnectionStatus.Inactive;
100110

101111
public override string ConnectionId { get; set; }
102112

@@ -184,29 +194,29 @@ public async Task DisposeAsync(bool closeGracefully = false)
184194
{
185195
Task disposeTask;
186196

187-
await StateLock.WaitAsync();
188197
try
189198
{
190-
if (Status == HttpConnectionStatus.Disposed)
199+
lock (_stateLock)
191200
{
192-
disposeTask = _disposeTcs.Task;
193-
}
194-
else
195-
{
196-
Status = HttpConnectionStatus.Disposed;
201+
if (Status == HttpConnectionStatus.Disposed)
202+
{
203+
disposeTask = _disposeTcs.Task;
204+
}
205+
else
206+
{
207+
Status = HttpConnectionStatus.Disposed;
197208

198-
Log.DisposingConnection(_logger, ConnectionId);
209+
Log.DisposingConnection(_logger, ConnectionId);
199210

200-
var applicationTask = ApplicationTask ?? Task.CompletedTask;
201-
var transportTask = TransportTask ?? Task.CompletedTask;
211+
var applicationTask = ApplicationTask ?? Task.CompletedTask;
212+
var transportTask = TransportTask ?? Task.CompletedTask;
202213

203-
disposeTask = WaitOnTasks(applicationTask, transportTask, closeGracefully);
214+
disposeTask = WaitOnTasks(applicationTask, transportTask, closeGracefully);
215+
}
204216
}
205217
}
206218
finally
207219
{
208-
StateLock.Release();
209-
210220
Cancellation?.Dispose();
211221

212222
Cancellation = null;
@@ -310,9 +320,31 @@ private async Task WaitOnTasks(Task applicationTask, Task transportTask, bool cl
310320
}
311321
}
312322

313-
public bool TryChangeState(HttpConnectionStatus from, HttpConnectionStatus to)
323+
public HttpConnectionStatus TryChangeState(HttpConnectionStatus from, HttpConnectionStatus to)
314324
{
315-
return Interlocked.CompareExchange(ref _status, (int)to, (int)from) == (int)from;
325+
lock (_stateLock)
326+
{
327+
if (Status == from)
328+
{
329+
Status = to;
330+
return from;
331+
}
332+
333+
return Status;
334+
}
335+
}
336+
337+
public void MarkInactive()
338+
{
339+
lock (_stateLock)
340+
{
341+
LastSeenUtc = DateTime.UtcNow;
342+
343+
if (Status == HttpConnectionStatus.Active)
344+
{
345+
Status = HttpConnectionStatus.Inactive;
346+
}
347+
}
316348
}
317349

318350
private static class Log

0 commit comments

Comments
 (0)