Skip to content

Throw for a PID file that can't be read or written instead of reporting another instance [patch] - #209

Open
matt-edmondson wants to merge 2 commits into
mainfrom
fix/187-access-errors-are-faults
Open

matt-edmondson wants to merge 2 commits into
mainfrom
fix/187-access-errors-are-faults

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #187

What was wrong

ReadPidFileState and TryWritePidFile treated every IOException and UnauthorizedAccessException as "another instance holds the PID file". An access failure doesn't clear by itself, though. A PID file or folder left owned by root (one run under sudo is enough) made ShouldLaunch() return false on every launch, and ExitIfAlreadyRunning() exited 0 with no message.

Change

  • Only a lock held by another process now counts as another instance. Every failure is still retried as before; the platform's lock error decides only what happens once the retries run out:
    • Windows: ERROR_SHARING_VIOLATION / ERROR_LOCK_VIOLATION (0x20 / 0x21 in the HRESULT)
    • Linux: .NET reports a file another process opened with FileShare.None as the bare EWOULDBLOCK errno (11). I checked this on .NET 10.
    • macOS / FreeBSD: EWOULDBLOCK is 35
    • Any other Unix keeps the old behaviour: every I/O failure counts as another instance.
  • Any other failure that outlasts the retries is thrown as an UnauthorizedAccessException or IOException of the same kind. The message names the PID file, says this is not another instance, and points at the ownership cause. The original exception is kept as InnerException.
  • Updated the XML docs on ShouldLaunch, ExitIfAlreadyRunning and the internals, and the README feature line.

Of the two options the issue offered, I chose throwing over "launch without the guarantee": a loud failure seemed safer than silently dropping the guarantee the library exists for. No public API is added. Callers that never saw an exception before can now get one in this fault case, which is the point of the change. If you'd rather call that [minor], change the tag.

Tests

  • TryWritePidFile_WhenPidFileCannotBeReplaced_ShouldReturnFalse asserted the old behaviour. It is now ..._ShouldThrowNamingThePidFile.
  • New: IsAlreadyRunning_WhenPidFileCannotBeRead_ShouldThrow, ShouldLaunch_WhenPidFileCannotBeReadOrWritten_ShouldThrowInsteadOfReturningFalse. Both put a directory where the PID file belongs, which fails on every platform and for root.
  • New: ShouldLaunch_WhenPidFolderIsReadOnly_ShouldThrowInsteadOfReturningFalse. This is the issue's case B: a folder the user can't write to. Folder modes don't stop root, so it is Inconclusive on Windows and for a privileged process.
  • The existing ShouldLaunch_WhenPidFileIsHeldExclusively_ShouldReturnFalseWithoutThrowing still passes. It shows that a real lock is still read as another instance on Linux.

Verified locally on Linux / .NET 10:

  • As root: 44 passed, 1 skipped (the read-only-folder test).
  • As an unprivileged user: 45/45 passed.
  • With the library change reverted, all four new tests fail and the other 41 pass.

Not covered

#188 (wall-clock steps defeating the start-time check) touches the same PID-file path. It is left for its own PR because its preferred fix, holding a lifetime lock, is a larger redesign.


Generated by Claude Code

…ng another instance [patch]

An access failure on the PID file or its folder, such as one left owned by root after a run
under sudo, was read as another instance running, so the app exited 0 on every launch with no
message. Only a lock held by another process now counts as another instance. Any other failure
that outlasts the retries is thrown, naming the PID file.

Fixes #187

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW2iZzaJLpHHri8a4vS
…s on

The check now takes the platform as a parameter, so each platform's lock error code is covered
whichever operating system runs the tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW2iZzaJLpHHri8a4vS
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants