From 76be10f6e9773af59a39c2d139fee83d1b8ba3d5 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 9 May 2021 11:01:00 -0400 Subject: [PATCH 1/2] Minor tweaks to DiscordProvider Changed how Connected is determined. If we're in the process of connecting or disconnecting, let the library finish those operations before attempting our reconnect. The endless jobs are a lack of client.Ready never triggering. Adjusted the call order to address what looks like a potential deadlock scenario for this. Hopefully fixes #1250 --- .../Components/Chat/Providers/DiscordProvider.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 1817808d27..6a3bc1fcac 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers sealed class DiscordProvider : Provider { /// - public override bool Connected => client.ConnectionState == ConnectionState.Connected; + public override bool Connected => client.ConnectionState != ConnectionState.Disconnected; /// public override string BotMention @@ -194,10 +194,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers Logger.LogTrace("Logged in."); cancellationToken.ThrowIfCancellationRequested(); - await client.StartAsync().ConfigureAwait(false); - - Logger.LogTrace("Started."); - var channelsAvailable = new TaskCompletionSource(); Task ReadyCallback() { @@ -208,6 +204,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers client.Ready += ReadyCallback; try { + await client.StartAsync().ConfigureAwait(false); + + Logger.LogTrace("Started."); using (cancellationToken.Register(() => channelsAvailable.SetCanceled())) await channelsAvailable.Task.ConfigureAwait(false); } From 4e19a10f93f567a31a3e7d5128ee565c2f816d45 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 9 May 2021 11:22:33 -0400 Subject: [PATCH 2/2] Fixes Discord Disconnect not updating Connected Last commit revealed an issue where client.ConnectionState would still be set to ConnectionState.Disconnecting after this function returned. Using some callbacks and a key Task.Yield prevents this. --- .../Chat/Providers/DiscordProvider.cs | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 6a3bc1fcac..0c8a1f5fbb 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -231,9 +231,61 @@ namespace Tgstation.Server.Host.Components.Chat.Providers try { cancellationToken.ThrowIfCancellationRequested(); - await client.StopAsync().ConfigureAwait(false); - Logger.LogTrace("Stopped."); - await client.LogoutAsync().ConfigureAwait(false); + var disconnectTcs = new TaskCompletionSource(); + Task DisconnectCallback(Exception exception) + { + if (exception != null) + Logger.LogTrace(exception, "Error stopping discord client!"); + + disconnectTcs.TrySetResult(null); + return Task.CompletedTask; + } + + try + { + client.Disconnected += DisconnectCallback; + + await client.StopAsync().ConfigureAwait(false); + + Logger.LogTrace("Waiting for disconnect callback..."); + using (cancellationToken.Register(() => disconnectTcs.SetCanceled())) + await disconnectTcs.Task.ConfigureAwait(false); + + // https://github.com/discord-net/Discord.Net/blob/8afef8245cfd1f8b56956dd4b4577ed3c6904be5/src/Discord.Net.WebSocket/ConnectionManager.cs#L176 + // State isn't set to disconnected until AFTER the callback fires + // Meaning if we check this.Connected right now it will still return true + // Yielding here will prevent this + await Task.Yield(); + + Logger.LogTrace("Stop async complete."); + } + finally + { + client.Disconnected -= DisconnectCallback; + } + + cancellationToken.ThrowIfCancellationRequested(); + var logoutTcs = new TaskCompletionSource(); + Task LogoutCallback() + { + logoutTcs.TrySetResult(null); + return Task.CompletedTask; + } + + client.LoggedOut += LogoutCallback; + try + { + await client.LogoutAsync().ConfigureAwait(false); + + Logger.LogTrace("Waiting for logout callback..."); + using (cancellationToken.Register(() => logoutTcs.SetCanceled())) + await logoutTcs.Task.ConfigureAwait(false); + } + finally + { + client.LoggedOut -= LogoutCallback; + } + Logger.LogDebug("Disconnected!"); } catch (OperationCanceledException)