@@ -29,10 +29,9 @@ namespace Discord
private int _lastSeq;
private ImmutableDictionary<string, VoiceRegion> _voiceRegions;
private TaskCompletionSource<bool> _connectTask;
private CancellationTokenSource _cancelToken;
private CancellationTokenSource _cancelToken, _reconnectCancelToken ;
private Task _heartbeatTask, _guildDownloadTask, _reconnectTask;
private long _heartbeatTime;
private bool _isReconnecting;
private int _unavailableGuilds;
private long _lastGuildAvailableTime;
private int _nextAudioId;
@@ -124,7 +123,6 @@ namespace Discord
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
_isReconnecting = false;
await ConnectInternalAsync().ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
@@ -141,6 +139,9 @@ namespace Discord
if (LoginState != LoginState.LoggedIn)
throw new InvalidOperationException("You must log in before connecting.");
if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested)
_reconnectCancelToken.Cancel();
var state = ConnectionState;
if (state == ConnectionState.Connecting || state == ConnectionState.Connected)
await DisconnectInternalAsync(null).ConfigureAwait(false);
@@ -177,7 +178,6 @@ namespace Discord
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
_isReconnecting = false;
await DisconnectInternalAsync(null).ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
@@ -188,13 +188,15 @@ namespace Discord
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
_isReconnecting = false;
await DisconnectInternalAsync(ex).ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
}
private async Task DisconnectInternalAsync(Exception ex)
{
if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested)
_reconnectCancelToken.Cancel();
ulong guildId;
if (ConnectionState == ConnectionState.Disconnected) return;
@@ -234,29 +236,26 @@ namespace Discord
private async Task StartReconnectAsync(Exception ex)
{
//TODO: Is this thread-safe?
if (_reconnectTask != null) return;
_connectTask?.TrySetException(ex);
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
await DisconnectInternalAsync(ex).ConfigureAwait(false);
if (_reconnectTask != null) return;
_isReconnecting = true;
_reconnectTask = ReconnectInternalAsync();
await DisconnectInternalAsync(null).ConfigureAwait(false);
_reconnectCancelToken = new CancellationTokenSource();
_reconnectTask = ReconnectInternalAsync(_reconnectCancelToken.Token);
}
finally { _connectionLock.Release(); }
}
private async Task ReconnectInternalAsync()
private async Task ReconnectInternalAsync(CancellationToken cancelToken )
{
try
{
int nextReconnectDelay = 1000;
while (_isReconnecting )
while (!cancelToken.IsCancellationRequested )
{
try
{
await Task.Delay(nextReconnectDelay).ConfigureAwait(false);
await Task.Delay(nextReconnectDelay, cancelToken ).ConfigureAwait(false);
nextReconnectDelay *= 2;
if (nextReconnectDelay > 30000)
nextReconnectDelay = 30000;
@@ -264,6 +263,7 @@ namespace Discord
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
if (cancelToken.IsCancellationRequested) return;
await ConnectInternalAsync().ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
@@ -275,15 +275,10 @@ namespace Discord
}
}
}
catch (OperationCanceledException) { }
finally
{
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
_isReconnecting = false;
_reconnectTask = null;
}
finally { _connectionLock.Release(); }
_reconnectTask = null;
}
}
@@ -575,6 +570,7 @@ namespace Discord
{
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete
await _gatewayLogger.InfoAsync("Resumed previous session").ConfigureAwait(false);
}
return;
@@ -1489,17 +1485,32 @@ namespace Discord
}
await logger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
}
catch (OperationCanceledException ex )
catch (OperationCanceledException)
{
await logger.DebugAsync("Heartbeat Stopped", ex).ConfigureAwait(false);
await logger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
}
catch (Exception ex)
{
await logger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false);
}
}
private async Task WaitForGuildsAsync(CancellationToken cancelToken, ILogger logger)
{
await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false);
while ((_unavailableGuilds != 0) && (Environment.TickCount - _lastGuildAvailableTime < 2000))
await Task.Delay(500, cancelToken).ConfigureAwait(false);
await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false);
try
{
await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false);
while ((_unavailableGuilds != 0) && (Environment.TickCount - _lastGuildAvailableTime < 2000))
await Task.Delay(500, cancelToken).ConfigureAwait(false);
await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false);
}
catch (OperationCanceledException)
{
await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false);
}
catch (Exception ex)
{
await logger.ErrorAsync("GuildDownloader Errored", ex).ConfigureAwait(false);
}
}
private async Task SyncGuildsAsync()
{