From f18155e7ea68cd7c94a2f185458f3c8ea2438882 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Mon, 1 Jun 2026 19:08:32 -0500 Subject: [PATCH] fix: correct RedisQueue disposal ordering to prevent ObjectDisposedException during shutdown Reorder RedisQueue.Dispose() to signal cancellation first via SignalDispose(), wait for workers and maintenance task to terminate, then call base.Dispose() which disposes the CTS last. Previously base.Dispose() was called first which destroyed the CTS while background tasks were still running. Fixes #165 --- src/Foundatio.Redis/Queues/RedisQueue.cs | 6 +++++- tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Foundatio.Redis/Queues/RedisQueue.cs b/src/Foundatio.Redis/Queues/RedisQueue.cs index dd718cd..368a324 100644 --- a/src/Foundatio.Redis/Queues/RedisQueue.cs +++ b/src/Foundatio.Redis/Queues/RedisQueue.cs @@ -809,7 +809,10 @@ private LoadedLuaScript GetScript(LoadedLuaScript? script) // IConnectionMultiplexer supports IAsyncDisposable since SE.Redis 2.6.66. public override void Dispose() { - base.Dispose(); + if (IsDisposed) + return; + + SignalDispose(); _connectionMultiplexer.ConnectionRestored -= ConnectionMultiplexerOnConnectionRestored; if (_isSubscribed) @@ -853,6 +856,7 @@ public override void Dispose() } WaitForMaintenanceTask(); + base.Dispose(); _cache.Dispose(); } diff --git a/tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs b/tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs index 0a4115a..35754d2 100644 --- a/tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs +++ b/tests/Foundatio.Redis.Tests/Queues/RedisQueueTests.cs @@ -128,6 +128,12 @@ public override Task DequeueAsync_WithDispose_AutoAbandonsEntryAsync() return base.DequeueAsync_WithDispose_AutoAbandonsEntryAsync(); } + [Fact] + public override Task Dispose_WithMaintenanceRunning_DoesNotThrowObjectDisposedException() + { + return base.Dispose_WithMaintenanceRunning_DoesNotThrowObjectDisposedException(); + } + [Fact] public override Task DequeueWaitWillGetSignaledAsync() {