Work around for potential deadlock in DiscordProvider Dispose

Workaround for https://github.com/Remora/Remora.Discord/issues/305
This commit is contained in:
Dominion
2023-06-03 01:04:11 -04:00
parent 95307b3e70
commit 34a7e58bd4
@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
@@ -17,6 +19,7 @@ using Remora.Discord.API.Abstractions.Results;
using Remora.Discord.API.Objects;
using Remora.Discord.Gateway;
using Remora.Discord.Gateway.Extensions;
using Remora.Discord.Gateway.Services;
using Remora.Rest.Core;
using Remora.Rest.Results;
using Remora.Results;
@@ -223,7 +226,35 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
await base.DisposeAsync();
await serviceProvider.DisposeAsync();
#if NET7_0_OR_GREATER
#error This hack needs to be removed after updating Remora.Discord
#endif
// https://github.com/Remora/Remora.Discord/issues/305
var responderDispatchService = serviceProvider.GetRequiredService<ResponderDispatchService>();
var serviceProviderDisposeTask = serviceProvider.DisposeAsync().AsTask();
var timeout = AsyncDelayer.Delay(TimeSpan.FromSeconds(5), default); // DCT: None available
await Task.WhenAny(timeout, serviceProviderDisposeTask);
if (!serviceProviderDisposeTask.IsCompleted)
{
// HACK HACK HACK, there's a potential deadlock in the ResponderDispatchService
var responderDispatchServiceType = responderDispatchService.GetType();
var dispatcherTask = (Task)responderDispatchServiceType.GetField("_dispatcher", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(responderDispatchService);
var finalizerTask = (Task)responderDispatchServiceType.GetField("_finalizer", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(responderDispatchService);
if (dispatcherTask.IsCompleted && !finalizerTask.IsCompleted)
{
// deadlocked, force close the channel
var channel = (Channel<Task<IReadOnlyList<Result>>>)responderDispatchServiceType.GetField("_respondersToFinalize", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(responderDispatchService);
channel.Writer.TryComplete();
}
}
await serviceProviderDisposeTask;
Logger.LogTrace("ServiceProvider disposed");
// this line is purely here to shutup CA2213. It should always be null
@@ -660,6 +691,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
localGatewayCts.Cancel();
var gatewayResult = await localGatewayTask;
Logger.LogTrace("Gateway task complete");
if (!gatewayResult.IsSuccess)
Logger.LogWarning("Gateway issue: {result}", gatewayResult.LogFormat());