Fixed reconnect deadlocks and Connect not completing on resumes.
This commit is contained in:
@@ -234,6 +234,15 @@ namespace Discord.API
|
|||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
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()
|
private async Task DisconnectInternalAsync()
|
||||||
{
|
{
|
||||||
if (_gatewayClient == null)
|
if (_gatewayClient == null)
|
||||||
|
|||||||
@@ -298,9 +298,9 @@ namespace Discord
|
|||||||
private async Task WriteInitialLog()
|
private async Task WriteInitialLog()
|
||||||
{
|
{
|
||||||
if (this is DiscordSocketClient)
|
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
|
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($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false);
|
||||||
await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false);
|
await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false);
|
||||||
await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false);
|
await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false);
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ namespace Discord
|
|||||||
private int _lastSeq;
|
private int _lastSeq;
|
||||||
private ImmutableDictionary<string, VoiceRegion> _voiceRegions;
|
private ImmutableDictionary<string, VoiceRegion> _voiceRegions;
|
||||||
private TaskCompletionSource<bool> _connectTask;
|
private TaskCompletionSource<bool> _connectTask;
|
||||||
private CancellationTokenSource _cancelToken;
|
private CancellationTokenSource _cancelToken, _reconnectCancelToken;
|
||||||
private Task _heartbeatTask, _guildDownloadTask, _reconnectTask;
|
private Task _heartbeatTask, _guildDownloadTask, _reconnectTask;
|
||||||
private long _heartbeatTime;
|
private long _heartbeatTime;
|
||||||
private bool _isReconnecting;
|
|
||||||
private int _unavailableGuilds;
|
private int _unavailableGuilds;
|
||||||
private long _lastGuildAvailableTime;
|
private long _lastGuildAvailableTime;
|
||||||
private int _nextAudioId;
|
private int _nextAudioId;
|
||||||
@@ -124,7 +123,6 @@ namespace Discord
|
|||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isReconnecting = false;
|
|
||||||
await ConnectInternalAsync().ConfigureAwait(false);
|
await ConnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
finally { _connectionLock.Release(); }
|
||||||
@@ -141,6 +139,9 @@ namespace Discord
|
|||||||
if (LoginState != LoginState.LoggedIn)
|
if (LoginState != LoginState.LoggedIn)
|
||||||
throw new InvalidOperationException("You must log in before connecting.");
|
throw new InvalidOperationException("You must log in before connecting.");
|
||||||
|
|
||||||
|
if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested)
|
||||||
|
_reconnectCancelToken.Cancel();
|
||||||
|
|
||||||
var state = ConnectionState;
|
var state = ConnectionState;
|
||||||
if (state == ConnectionState.Connecting || state == ConnectionState.Connected)
|
if (state == ConnectionState.Connecting || state == ConnectionState.Connected)
|
||||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||||
@@ -177,7 +178,6 @@ namespace Discord
|
|||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isReconnecting = false;
|
|
||||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
finally { _connectionLock.Release(); }
|
||||||
@@ -188,13 +188,15 @@ namespace Discord
|
|||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isReconnecting = false;
|
|
||||||
await DisconnectInternalAsync(ex).ConfigureAwait(false);
|
await DisconnectInternalAsync(ex).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
finally { _connectionLock.Release(); }
|
||||||
}
|
}
|
||||||
private async Task DisconnectInternalAsync(Exception ex)
|
private async Task DisconnectInternalAsync(Exception ex)
|
||||||
{
|
{
|
||||||
|
if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested)
|
||||||
|
_reconnectCancelToken.Cancel();
|
||||||
|
|
||||||
ulong guildId;
|
ulong guildId;
|
||||||
|
|
||||||
if (ConnectionState == ConnectionState.Disconnected) return;
|
if (ConnectionState == ConnectionState.Disconnected) return;
|
||||||
@@ -234,29 +236,26 @@ namespace Discord
|
|||||||
|
|
||||||
private async Task StartReconnectAsync(Exception ex)
|
private async Task StartReconnectAsync(Exception ex)
|
||||||
{
|
{
|
||||||
//TODO: Is this thread-safe?
|
_connectTask?.TrySetException(ex);
|
||||||
if (_reconnectTask != null) return;
|
|
||||||
|
|
||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DisconnectInternalAsync(ex).ConfigureAwait(false);
|
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||||
if (_reconnectTask != null) return;
|
_reconnectCancelToken = new CancellationTokenSource();
|
||||||
_isReconnecting = true;
|
_reconnectTask = ReconnectInternalAsync(_reconnectCancelToken.Token);
|
||||||
_reconnectTask = ReconnectInternalAsync();
|
|
||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
finally { _connectionLock.Release(); }
|
||||||
}
|
}
|
||||||
private async Task ReconnectInternalAsync()
|
private async Task ReconnectInternalAsync(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int nextReconnectDelay = 1000;
|
int nextReconnectDelay = 1000;
|
||||||
while (_isReconnecting)
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(nextReconnectDelay).ConfigureAwait(false);
|
await Task.Delay(nextReconnectDelay, cancelToken).ConfigureAwait(false);
|
||||||
nextReconnectDelay *= 2;
|
nextReconnectDelay *= 2;
|
||||||
if (nextReconnectDelay > 30000)
|
if (nextReconnectDelay > 30000)
|
||||||
nextReconnectDelay = 30000;
|
nextReconnectDelay = 30000;
|
||||||
@@ -264,6 +263,7 @@ namespace Discord
|
|||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (cancelToken.IsCancellationRequested) return;
|
||||||
await ConnectInternalAsync().ConfigureAwait(false);
|
await ConnectInternalAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally { _connectionLock.Release(); }
|
finally { _connectionLock.Release(); }
|
||||||
@@ -275,15 +275,10 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
_reconnectTask = null;
|
||||||
try
|
|
||||||
{
|
|
||||||
_isReconnecting = false;
|
|
||||||
_reconnectTask = null;
|
|
||||||
}
|
|
||||||
finally { _connectionLock.Release(); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,6 +570,7 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
|
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);
|
await _gatewayLogger.InfoAsync("Resumed previous session").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1489,17 +1485,32 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
await logger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
|
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)
|
private async Task WaitForGuildsAsync(CancellationToken cancelToken, ILogger logger)
|
||||||
{
|
{
|
||||||
await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false);
|
try
|
||||||
while ((_unavailableGuilds != 0) && (Environment.TickCount - _lastGuildAvailableTime < 2000))
|
{
|
||||||
await Task.Delay(500, cancelToken).ConfigureAwait(false);
|
await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false);
|
||||||
await logger.DebugAsync("GuildDownloader Stopped").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()
|
private async Task SyncGuildsAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user