mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-21 09:06:37 +08:00
a504c3e248
The macOS legs of the Build and Test workflow intermittently failed the assembly-level teardown of the Sessions (also History and Client) test suites with "Certificate leak detected: 1 instance(s) created but not disposed" while every test passed (master run 33004556144: created=16777, disposed=16776; PR run 33207357247: created=17373, disposed=17372). The count held steady through the teardown's full quiescence window, so the missing disposal was permanent, not a slow in-flight drain. RejectedCertificateProcessor.EnqueueAsync takes a per-certificate AddRef for the queued WriteRequest before TryWrite. Once CertificateManager.DisposeAsync has completed the channel writer, the bounded DropOldest channel refuses TryWrite and the WriteAsync fallback throws ChannelClosedException, abandoning the request: nothing releases the references it owns, so every certificate in the chain keeps one leaked reference for the life of the process. A validation that races manager disposal — a keep-alive, reconnect or fire-and-forget channel teardown rejecting a certificate while the fixture tears down — lands in exactly this window, and the slower loaded macOS runners make that race likely. The abandoned request also left FlushRejectedAsync waiters hanging on a drain marker that never resolves, and surfaced unobserved ChannelClosedExceptions through the fire-and-forget EnqueueRejectedCertificate discard. Route both enqueue paths through a fallback that, when the write cannot commit the request to the channel, disposes the request's chain (releasing the per-certificate references), resolves its completion as "not processed" so drain waiters are unblocked, and swallows only the benign ChannelClosedException shutdown race; cancellation still propagates. The teardown leak detector needs no change: its quiescence poll was correctly reporting a genuine leak. The new regression test reproduces the CI signature deterministically against the previous code (ChannelClosedException plus "Certificate leak detected: 1 instance(s) created but not disposed (created=1, disposed=0)" in the assembly teardown) and passes with the fix. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>