From 8c624a0032fbaeaaf365beb2b38bb645ee8483ad Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Thu, 7 May 2026 00:54:43 +0200 Subject: [PATCH] fix(threads): mark PendingMessage thread as background, document RetentionSweep rationale --- HellionChat/MessageManager.cs | 10 +++++++++- HellionChat/Plugin.cs | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/HellionChat/MessageManager.cs b/HellionChat/MessageManager.cs index c655b0a..983591f 100644 --- a/HellionChat/MessageManager.cs +++ b/HellionChat/MessageManager.cs @@ -66,7 +66,15 @@ internal class MessageManager : IAsyncDisposable Store = new MessageStore(DatabasePath()); - PendingMessageThread = new Thread(() => ProcessPendingMessages(PendingThreadCancellationToken.Token)); + // IsBackground = true so a stuck worker never blocks plugin unload. + // The worker has its own cancellation path via PendingThreadCancellationToken, + // and DisposeAsync waits up to 10s for cooperative shutdown. The + // background flag is the safety net for the case where cooperative + // shutdown fails to drain the queue in time. + PendingMessageThread = new Thread(() => ProcessPendingMessages(PendingThreadCancellationToken.Token)) + { + IsBackground = true, + }; PendingMessageThread.Start(); ContentIdResolverHook = Plugin.GameInteropProvider.HookFromAddress(RaptureLogModule.MemberFunctionPointers.AddMsgSourceEntry, ContentIdResolver); diff --git a/HellionChat/Plugin.cs b/HellionChat/Plugin.cs index 9744593..d0d349c 100755 --- a/HellionChat/Plugin.cs +++ b/HellionChat/Plugin.cs @@ -691,6 +691,11 @@ public sealed class Plugin : IDalamudPlugin policy[(int)(ushort)type] = days; var defaultDays = Config.RetentionDefaultDays; + // IsBackground = true for the same reason as PendingMessageThread: + // a stuck sweep must never block plugin unload. RunRetentionSweepIfDue + // guards the run-frequency, and the sweep itself uses the framework's + // cooperative cancellation pattern. The background flag is the safety + // net if the sweep ever takes longer than expected. new Thread(() => { // Bail out cheaply if a manual sweep is already in flight; the