mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 13:10:13 +00:00
f4840bfd35
Follow-up to 28885f7b. RequestStop() called UnregisterWait(), nulling
_registeredWait. When a Changed handler calls RequestStop() and then
queues Stop()/Dispose() to another thread while the callback is still
running — exactly the RealmJoinWatcher appearance-handler shape — the
queued Stop() then saw a null _registeredWait, skipped the blocking
Unregister(waitObject), and returned BEFORE the in-flight callback
finished. That defeated the Thread.Join() parity guarantee on the most
important RequestStop path.
RequestStop() now only sets _stopped and Set()s the signal, leaving the
live registration in place so Stop()/Dispose() can wait on it. The Set()
wakes any still-pending wait, which drains as a no-op (proceed is false
once _stopped is set); a callback already in flight is awaited by Stop().
No deadlock: Stop runs on a different thread and never holds _sync during
the wait.
Test: deterministic guard — park the callback inside the handler, call
RequestStop(), then assert Stop() blocks until the handler completes
(release fired 250ms later on another thread). Proven to FAIL when the
UnregisterWait() is reintroduced into RequestStop(). Full V2.Core suite
1266/1266 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>