Synchronize the background scan and closing of connections

- It was possible for the application to be torn down during
a background scan. When that happened, the timer would be disposed
before the end of the scan and would throw an ObjectDisposedException
when timer resumption happened. This change introduces a lock that avoids that
race.

Fixes #161
This commit is contained in:
David Fowler 2017-01-26 07:50:29 +00:00
parent 162cd1fc06
commit 05cc7792df
1 changed files with 66 additions and 38 deletions

View File

@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Sockets
private readonly ConcurrentDictionary<string, ConnectionState> _connections = new ConcurrentDictionary<string, ConnectionState>(); private readonly ConcurrentDictionary<string, ConnectionState> _connections = new ConcurrentDictionary<string, ConnectionState>();
private Timer _timer; private Timer _timer;
private readonly ILogger<ConnectionManager> _logger; private readonly ILogger<ConnectionManager> _logger;
private object _executionLock = new object();
private bool _disposed;
public ConnectionManager(ILogger<ConnectionManager> logger) public ConnectionManager(ILogger<ConnectionManager> logger)
{ {
@ -25,11 +27,19 @@ namespace Microsoft.AspNetCore.Sockets
public void Start() public void Start()
{ {
lock (_executionLock)
{
if (_disposed)
{
return;
}
if (_timer == null) if (_timer == null)
{ {
_timer = new Timer(Scan, this, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); _timer = new Timer(Scan, this, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
} }
} }
}
public bool TryGetConnection(string id, out ConnectionState state) public bool TryGetConnection(string id, out ConnectionState state)
{ {
@ -77,6 +87,13 @@ namespace Microsoft.AspNetCore.Sockets
private void Scan() private void Scan()
{ {
lock (_executionLock)
{
if (_disposed)
{
return;
}
// 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);
@ -115,9 +132,19 @@ namespace Microsoft.AspNetCore.Sockets
_timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
} }
} }
}
public void CloseConnections() public void CloseConnections()
{ {
lock (_executionLock)
{
if (_disposed)
{
return;
}
_disposed = true;
// Stop firing the timer // Stop firing the timer
_timer?.Dispose(); _timer?.Dispose();
@ -130,6 +157,7 @@ namespace Microsoft.AspNetCore.Sockets
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(5)); Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(5));
} }
}
public async Task DisposeAndRemoveAsync(ConnectionState state) public async Task DisposeAndRemoveAsync(ConnectionState state)
{ {