fix(worker): StaWaitHelper regression tests + full-drain contract notes; consolidate wait P/Invoke
This commit is contained in:
@@ -31,11 +31,26 @@ public sealed class MxAccessWriteCompletionCache
|
||||
// worker the completion is recorded from inside the waiter's own pumpStep
|
||||
// (COM dispatches OnWriteComplete on the STA), so the message wake is what
|
||||
// does the work there; this handle covers the cross-thread producers — the
|
||||
// tests, and any future off-STA recorder. Not IDisposable on purpose: the
|
||||
// cache is co-owned by MxAccessBaseEventSink and MxAccessSession with no
|
||||
// single teardown owner, and a late COM callback must never Set a disposed
|
||||
// handle. One kernel event per session (one session per worker process),
|
||||
// released by the handle's finalizer.
|
||||
// tests, and any future off-STA recorder.
|
||||
//
|
||||
// Single-waiter invariant: an AutoResetEvent releases exactly one waiter per
|
||||
// Set. Today the sole waiter is the STA (one worker per session, and the
|
||||
// write path holds the apartment while it waits), so no wake is ever lost. A
|
||||
// second concurrent waiter would not hang — the loser still re-checks the
|
||||
// entry every fallback tick and still honors its deadline — it would just
|
||||
// fall back to 50 ms latency for that iteration.
|
||||
//
|
||||
// NOT IDisposable, and that is one invariant with the wait's use of
|
||||
// DangerousGetHandle: the cache is co-owned by MxAccessBaseEventSink and
|
||||
// MxAccessSession with no single teardown owner, so a late COM callback
|
||||
// could otherwise Set a disposed handle. StaWaitHelper deliberately does not
|
||||
// DangerousAddRef the handle; GC.KeepAlive there closes the *finalization*
|
||||
// race but not a dispose race — if this field were ever disposed while the
|
||||
// STA sat inside the wait, the OS could recycle that handle value and the
|
||||
// wait would silently attach to an unrelated object. Making these caches
|
||||
// IDisposable is therefore not a free refactor: it must add DangerousAddRef/
|
||||
// DangerousRelease around the wait in the same change. One kernel event per
|
||||
// session (one session per worker process), released by the finalizer.
|
||||
private readonly AutoResetEvent completionSignal = new(false);
|
||||
|
||||
/// <summary>Records the status rows of a fresh OnWriteComplete callback for the given handle pair.</summary>
|
||||
|
||||
Reference in New Issue
Block a user