fix #1199 by suppressing ODE in received callback (#1395)

This commit is contained in:
Andrew Stanton-Nurse 2018-02-01 15:08:52 -08:00 committed by GitHub
parent f58ea8133b
commit b61dc35ee6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 9 deletions

View File

@ -97,7 +97,18 @@ namespace Microsoft.AspNetCore.SignalR.Client
if (_needKeepAlive) if (_needKeepAlive)
{ {
_logger.ResettingKeepAliveTimer(); _logger.ResettingKeepAliveTimer();
_timeoutTimer.Change(ServerTimeout, Timeout.InfiniteTimeSpan);
// If the connection is disposed while this callback is firing, or if the callback is fired after dispose
// (which can happen because of some races), this will throw ObjectDisposedException. That's OK, because
// we don't need the timer anyway.
try
{
_timeoutTimer.Change(ServerTimeout, Timeout.InfiniteTimeSpan);
}
catch (ObjectDisposedException)
{
// This is OK!
}
} }
} }
@ -157,8 +168,10 @@ namespace Microsoft.AspNetCore.SignalR.Client
private async Task DisposeAsyncCore() private async Task DisposeAsyncCore()
{ {
_timeoutTimer.Dispose();
await _connection.DisposeAsync(); await _connection.DisposeAsync();
// Dispose the timer AFTER shutting down the connection.
_timeoutTimer.Dispose();
} }
// TODO: Client return values/tasks? // TODO: Client return values/tasks?

View File

@ -207,6 +207,21 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
Assert.Equal("Server timeout (100.00ms) elapsed without receiving a message from the server.", exception.Message); Assert.Equal("Server timeout (100.00ms) elapsed without receiving a message from the server.", exception.Message);
} }
[Fact]
public async Task OnReceivedAfterTimerDisposedDoesNotThrow()
{
var connection = new TestConnection();
var hubConnection = new HubConnection(connection, new JsonHubProtocol(), new LoggerFactory());
await hubConnection.StartAsync().OrTimeout();
await hubConnection.DisposeAsync().OrTimeout();
// Fire callbacks, they shouldn't fail
foreach (var registration in connection.Callbacks)
{
await registration.InvokeAsync(new byte[0]);
}
}
// Moq really doesn't handle out parameters well, so to make these tests work I added a manual mock -anurse // Moq really doesn't handle out parameters well, so to make these tests work I added a manual mock -anurse
private class MockHubProtocol : IHubProtocol private class MockHubProtocol : IHubProtocol
{ {

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
private bool _closed; private bool _closed;
private object _closedLock = new object(); private object _closedLock = new object();
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>(); public List<ReceiveCallback> Callbacks { get; } = new List<ReceiveCallback>();
public IFeatureCollection Features { get; } = new FeatureCollection(); public IFeatureCollection Features { get; } = new FeatureCollection();
@ -130,9 +130,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
while (_receivedMessages.Reader.TryRead(out var message)) while (_receivedMessages.Reader.TryRead(out var message))
{ {
ReceiveCallback[] callbackCopies; ReceiveCallback[] callbackCopies;
lock (_callbacks) lock (Callbacks)
{ {
callbackCopies = _callbacks.ToArray(); callbackCopies = Callbacks.ToArray();
} }
foreach (var callback in callbackCopies) foreach (var callback in callbackCopies)
@ -170,14 +170,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
public IDisposable OnReceived(Func<byte[], object, Task> callback, object state) public IDisposable OnReceived(Func<byte[], object, Task> callback, object state)
{ {
var receiveCallBack = new ReceiveCallback(callback, state); var receiveCallBack = new ReceiveCallback(callback, state);
lock (_callbacks) lock (Callbacks)
{ {
_callbacks.Add(receiveCallBack); Callbacks.Add(receiveCallBack);
} }
return new Subscription(receiveCallBack, _callbacks); return new Subscription(receiveCallBack, Callbacks);
} }
private class ReceiveCallback public class ReceiveCallback
{ {
private readonly Func<byte[], object, Task> _callback; private readonly Func<byte[], object, Task> _callback;
private readonly object _state; private readonly object _state;