diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 00:42:19 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 00:42:25 +0900 |
| commit | 2c9b4c83298ea67e2e7b576fd7209b5bc782f23c (patch) | |
| tree | 436eb0a22fd01613bc65d3389adf567b78617522 | |
| parent | 62022f1e192e76b44076a3da0279ccd6bb10c4ef (diff) | |
| download | LunaticChat-2c9b4c83298ea67e2e7b576fd7209b5bc782f23c.tar.gz LunaticChat-2c9b4c83298ea67e2e7b576fd7209b5bc782f23c.tar.bz2 LunaticChat-2c9b4c83298ea67e2e7b576fd7209b5bc782f23c.zip | |
fix: report queued work dropped at shutdown
Cancelling the scope kills the worker coroutines but does not close their
channels, so trySend kept reporting success for work nothing would ever read.
The warning that exists precisely to avoid dropping a message in silence was
therefore unreachable in the case it was written for.
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueue.kt | 6 | ||||
| -rw-r--r-- | platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueueTest.kt | 15 |
2 files changed, 20 insertions, 1 deletions
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueue.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueue.kt index 0769823..f5e4a28 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueue.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueue.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.util.UUID import java.util.concurrent.ConcurrentHashMap @@ -34,7 +35,10 @@ class PerPlayerWorkQueue( playerId: UUID, work: suspend () -> Unit, ) { - val accepted = queues.computeIfAbsent(playerId) { startWorker() }.trySend(work).isSuccess + // Checked rather than left to trySend: cancelling the scope kills the worker coroutines but + // does not close their channels, so after shutdown trySend would keep reporting success for + // work nothing will ever read. + val accepted = scope.isActive && queues.computeIfAbsent(playerId) { startWorker() }.trySend(work).isSuccess if (!accepted) { // Reachable once the scope is cancelled at shutdown, or if the player's queue is // released in the same tick as their command. Dropping a message in silence is worse diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueueTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueueTest.kt index 95b7700..2d96cba 100644 --- a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueueTest.kt +++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/PerPlayerWorkQueueTest.kt @@ -3,6 +3,7 @@ package dev.m1sk9.lunaticChat.paper import dev.m1sk9.lunaticChat.paper.TestUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout @@ -134,4 +135,18 @@ class PerPlayerWorkQueueTest { assertTrue(logger.severeMessages.any { it.contains("Queued work failed") }) } + + @Test + fun `work submitted after shutdown is reported rather than silently dropped`() { + val logger = TestUtils.TestLogger() + val scope = CoroutineScope(Dispatchers.Default) + val queue = PerPlayerWorkQueue(scope, logger) + val completed = ConcurrentLinkedQueue<String>() + + scope.cancel() + queue.submit(alice) { completed.add("never") } + + assertTrue(completed.isEmpty()) + assertTrue(logger.warningMessages.any { it.contains("their queue is closed") }) + } } |
