Repository navigation
Refuse an Acquire that was queued behind Dispose [patch] - #36
Conversation
All three blockers checked disposed before taking gate, so an Acquire that passed the check and then waited on the lock while Dispose ran started new holds on a disposed blocker. IsBlocking stayed true and, on Linux, the systemd-inhibit helper ran until the process exited. Check disposed again once the lock is held. Fixes #33 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW2EzJMFdK7ZQBB2keC
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW2EzJMFdK7ZQBB2keC
|
SonarCloud Code Analysis fails on new-code coverage: 75.0%, against a required 80%. It's not a test failure, and I'm leaving it as is. The uncovered new lines are the Re-running won't change the result, because coverage is collected the same way every time. CI tests pass on ubuntu and macOS. SonarCloud isn't a required check, so it doesn't block merging. Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW5jpiuVuBRXSgitSY1
SonarCloud measures coverage on Windows, where the Linux-only race test is inconclusive, so the new lines under the lock were uncovered. These tests queue an Acquire behind Dispose for all three blockers; the check throws before any platform call, so they run anywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW5jpiuVuBRXSgitSY1
|



Fixes #33
What was wrong
The Linux, Windows and macOS blockers each called
ObjectDisposedException.ThrowIf(disposed, this)before takinggate. AnAcquirecould pass that check and then wait on the lock whileDisposeheld it. OnceDisposereturned, the waitingAcquirestarted new holds on a blocker that was already disposed.IsBlockingstayedtrue, and a secondDisposewas a no-op. On Linux thesystemd-inhibithelper kept running until the process exited.Change
disposedagain right after they takegate. The early check stays, so a disposed blocker still throwsObjectDisposedExceptionbefore Linux reportsPlatformNotSupportedException.LinuxSleepBlockergets aninternalconstructor that takes the helper paths. The public constructor resolves the paths fromPATHand calls it. This lets a test point the blocker at a stand-in helper.The Windows and macOS changes are the same one-line re-check. They were built here but not run, because those blockers only run on their own OS.
Tests
LinuxSleepBlockerTests.Acquire_QueuedBehindDispose_ThrowsAndStartsNoHelperuses a stand-insystemd-inhibitscript. The script records its PID, then runsexec cat. The test makes the race deterministic:gate, which it reads by reflection.Acquireon another thread and waits until that thread is blocked on the lock.Disposeon the test thread.Lockis re-entrant, so this works while the test holdsgate.gate.It then checks three things: the queued
AcquirethrewObjectDisposedException,IsBlockingisfalse, and no helper was started.Results on Linux:
Acquirestarted a helper.Generated by Claude Code