DisposeAsync cancelled the MRTR continuations present when it iterated
_mrtrContinuations. A continuation published after that iteration escaped
cancellation entirely and could outlive the server.
Add a server-lifetime CancellationTokenSource, cancel it at the very start
of DisposeAsync — before the session handler is disposed and before the
sweep — and re-check it immediately after publishing a continuation. Either
the sweep sees the entry or the publisher sees the cancellation, so every
interleaving cancels the handler.
The issue proposed linking each handler's CTS to the server-lifetime token
instead. That would leak: the per-handler source is deliberately never
disposed, so a registration on a server-lifetime token is never released and
accumulates for every MRTR handler invocation the process ever runs. The
existing link to the per-request token is released when the request's source
is disposed, which is why it does not accumulate today. Publish-then-recheck
closes the same race without holding any per-request state on a
process-lifetime token.
Fixes #1707
Summary
Why
DisposeAsync previously cancelled the continuations present when it iterated _mrtrContinuations. A continuation published after that iteration could escape cancellation and remain active after server disposal.
Testing
No documentation changes are needed for this internal lifecycle fix.