Two defects in CrowdSecReporter shutdown, both reachable only on a shard that actually has CrowdSec configured with items still queued. 1. FlushRemainingOnStop blocked with GetAwaiter().GetResult() on a ValueTask. Beyond being unsupported in general -- an IValueTaskSource-backed ValueTask does not wait there, it throws -- it hung. Stop() runs on the main thread, where Main installs EventLoopContext as SynchronizationContext.Current, and nothing in CrowdSecAlertClient used ConfigureAwait(false), so the send's continuation was posted to a queue only LoopContext.ExecuteTasks() drains -- and by HandleClosed() the loop has stopped ticking. The thread waited on a continuation only that thread could run. The 3s budget did not help: it completes the HTTP call, not the resumption. Run the flush through Task.Run so the whole chain lives on the pool with no context to capture, block once on a real Task, and bound it with Wait so a wedged send costs a few seconds of shutdown rather than the process. Add ConfigureAwait(false) across the client and drain loop per rule #10 so the pool hop is defense in depth rather than the only thing holding it up. 2. Start() did Task.Run(() => DrainLoop(...)) where DrainLoop returned ValueTask. There is no Task.Run(Func<ValueTask>) overload, so it bound to Task.Run<TResult>(Func<TResult>) and produced a Task<ValueTask> that completes at the first suspending await, not when the loop exits -- silently upcast by the Task _drainTask field. Stop()'s drain-exited handshake was therefore a no-op (Wait returned true in ~0ms), letting the flush read a SingleReader channel concurrently with a live drain loop, which is precisely the hazard that handshake documents itself as preventing. It also made the loop fire-and-forget and swallowed any fault escaping it. Return Task so it binds to Task.Run(Func<Task>) and unwraps. A loop awaited once has nothing to gain from ValueTask. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| Application | ||
| BuildTool | ||
| Logger | ||
| Server | ||
| Server.Tests | ||
| UOContent | ||
| UOContent.Tests | ||