diff --git a/src/Discord.Net/API/DiscordAPIClient.cs b/src/Discord.Net/API/DiscordAPIClient.cs index 4c819075e..826eb0920 100644 --- a/src/Discord.Net/API/DiscordAPIClient.cs +++ b/src/Discord.Net/API/DiscordAPIClient.cs @@ -234,6 +234,15 @@ namespace Discord.API } finally { _connectionLock.Release(); } } + public async Task DisconnectAsync(Exception ex) + { + await _connectionLock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync().ConfigureAwait(false); + } + finally { _connectionLock.Release(); } + } private async Task DisconnectInternalAsync() { if (_gatewayClient == null) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 48bc52f15..34d96216d 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -298,9 +298,9 @@ namespace Discord private async Task WriteInitialLog() { if (this is DiscordSocketClient) - await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, {DiscordConfig.GatewayEncoding})").ConfigureAwait(false); + await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.FullVersion} (API v{DiscordConfig.APIVersion}, {DiscordConfig.GatewayEncoding})").ConfigureAwait(false); else - await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); + await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.FullVersion} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false); diff --git a/src/Discord.Net/DiscordSocketClient.cs b/src/Discord.Net/DiscordSocketClient.cs index 973357625..b6ed19f5b 100644 --- a/src/Discord.Net/DiscordSocketClient.cs +++ b/src/Discord.Net/DiscordSocketClient.cs @@ -29,10 +29,9 @@ namespace Discord private int _lastSeq; private ImmutableDictionary _voiceRegions; private TaskCompletionSource _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() {