Skip to content

🐛 fix(soft): clean up a marker whose identity read failed - #869

Merged
gaborbernat merged 2 commits into
tox-dev:mainfrom
simpleqt:fix/soft-marker-identity-retry
Oct 28, 2026
Merged

gaborbernat merged 2 commits into
tox-dev:mainfrom
simpleqt:fix/soft-marker-identity-retry

Conversation

@simpleqt

@simpleqt simpleqt commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

A SoftFileLock acquisition that failed while reading its new marker's identity left an empty marker on the path. On Windows nothing reclaims such a marker: a contender never breaks an empty one, so every later acquisition on that path timed out, with the default timeout=-1 forever. The read fails when a network or FUSE mount reports a transient EIO, or when an interrupt lands during the call.

The rollback now reads the identity again through the descriptor it still owns before it closes it. This attempt created the marker with O_EXCL and never published it, so the retry describes the file this process made; the unlink still checks that identity against the path, so a successor's marker is never removed. A read that fails twice keeps the previous behavior and leaves the marker to stale handling.

A SoftFileLock acquisition creates its marker with O_EXCL and then reads the new file's identity, which the rollback
needs to recognize its own marker while cleaning up. When that read failed transiently - the EIO a network or FUSE
mount reports, the same one UnixFileLock already tolerates - the rollback had no identity to work with.

On Windows the marker cleanup after the close is identity-checked, so it did nothing and the empty marker stayed on the
path while no instance held the lock. Nothing ever reclaims one: it is not the strict sentinel, it is empty so its
owner cannot be probed, and an unparsable lock is only self-healed once `_marker_seen` has watched it unchanged for
MALFORMED_LOCK_AGE_THRESHOLD - a state the acquiring instance never reaches, and each new contender starts the watch
over. The result is a lock file that blocks the path for good: every later acquisition times out reporting "an owner
that recorded no PID", which points at a holder that does not exist. On Unix the same failure left the marker for the
2-second malformed-marker grace instead of releasing the path immediately.

The rollback now retries the read while it still owns the descriptor - the capture is `os.fstat` on a descriptor this
attempt opened itself, so a second attempt normally answers - and passes the result to both cleanup paths.
`remove_own_file` accepts an already-read `os.stat_result`, so Unix no longer repeats a read that just failed.

Tests: `test_transient_identity_failure_still_cleans_the_marker` covers both the identity read and the underlying
`os.fstat`, asserting the failed acquisition leaves the path free for the next contender. It fails on the base source
(marker still on disk) and passes with the fix.

Signed-off-by: simpleqt <2015900818@qq.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 09:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The rollback already owns the descriptor it created the marker through, so
reading the identity again there is enough to recognize the marker. That
replaces the saved stat result, the retry helper and the optional identity
argument on remove_own_file, which refetches the stat itself on Unix.

The test patches only os.fstat, the boundary that fails, and delegates every
later call to the real function, so pytest's capture keeps working under the
patch. The identity helper is a tuple read that cannot fail on its own, so the
test drops that case, along with the contender acquisition that checked a second
behavior after the assertion.
@gaborbernat gaborbernat changed the title fix(soft): clean up a marker whose identity capture failed 🐛 fix(soft): clean up a marker whose identity read failed Oct 28, 2026
@gaborbernat gaborbernat reopened this Oct 28, 2026
@gaborbernat
gaborbernat merged commit 2d4b738 into tox-dev:main Oct 28, 2026
73 of 102 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants