Use Monitor.TryEnter/Exit in the Scan loop (#189)
- Avoids overlapping Scans - Noops if dispose is being called at the same time
This commit is contained in:
parent
633c572a22
commit
fc93c47789
|
|
@ -87,7 +87,16 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
||||||
private void Scan()
|
private void Scan()
|
||||||
{
|
{
|
||||||
lock (_executionLock)
|
// If we couldn't get the lock it means one of 2 things is true:
|
||||||
|
// - We're about to dispose so we don't care to run the scan callback anyways.
|
||||||
|
// - The previous Scan took long enough that the next scan tried to run in parallel
|
||||||
|
// In either case just do nothing and end the timer callback as soon as possible
|
||||||
|
if (!Monitor.TryEnter(_executionLock))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
{
|
{
|
||||||
|
|
@ -97,40 +106,40 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
// Pause the timer while we're running
|
// Pause the timer while we're running
|
||||||
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
|
|
||||||
try
|
// Scan the registered connections looking for ones that have timed out
|
||||||
|
foreach (var c in _connections)
|
||||||
{
|
{
|
||||||
// Scan the registered connections looking for ones that have timed out
|
var status = ConnectionState.ConnectionStatus.Inactive;
|
||||||
foreach (var c in _connections)
|
var lastSeenUtc = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var status = ConnectionState.ConnectionStatus.Inactive;
|
c.Value.Lock.Wait();
|
||||||
var lastSeenUtc = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
try
|
// Capture the connection state
|
||||||
{
|
status = c.Value.Status;
|
||||||
c.Value.Lock.Wait();
|
|
||||||
|
|
||||||
// Capture the connection state
|
lastSeenUtc = c.Value.LastSeenUtc;
|
||||||
status = c.Value.Status;
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
c.Value.Lock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
lastSeenUtc = c.Value.LastSeenUtc;
|
// Once the decision has been made to to dispose we don't check the status again
|
||||||
}
|
if (status == ConnectionState.ConnectionStatus.Inactive && (DateTimeOffset.UtcNow - lastSeenUtc).TotalSeconds > 5)
|
||||||
finally
|
{
|
||||||
{
|
var ignore = DisposeAndRemoveAsync(c.Value);
|
||||||
c.Value.Lock.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once the decision has been made to to dispose we don't check the status again
|
|
||||||
if (status == ConnectionState.ConnectionStatus.Inactive && (DateTimeOffset.UtcNow - lastSeenUtc).TotalSeconds > 5)
|
|
||||||
{
|
|
||||||
var ignore = DisposeAndRemoveAsync(c.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
{
|
finally
|
||||||
// Resume once we finished processing all connections
|
{
|
||||||
_timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
// Resume once we finished processing all connections
|
||||||
}
|
_timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
// Exit the lock now
|
||||||
|
Monitor.Exit(_executionLock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue