fix(fspy): replace the IPC file lock with an in-mapping close gate - #577
Draft
wan9chi wants to merge 1 commit into
Draft
fix(fspy): replace the IPC file lock with an in-mapping close gate#577wan9chi wants to merge 1 commit into
wan9chi wants to merge 1 commit into
Conversation
This was referenced Jul 28, 2026
fspy benchmarklinuxmacoswindows |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb42117817
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
wan9chi
force-pushed
the
fspy-close-gate
branch
2 times, most recently
from
July 30, 2026 04:36
38e7f20 to
5d642d1
Compare
wan9chi
force-pushed
the
fspy-close-gate
branch
2 times, most recently
from
July 31, 2026 07:09
c0720e2 to
8b4e19a
Compare
wan9chi
force-pushed
the
fspy-close-gate
branch
from
August 4, 2026 10:17
8b4e19a to
5bae097
Compare
wan9chi
force-pushed
the
fspy-close-gate
branch
2 times, most recently
from
August 10, 2026 06:59
bbb60ad to
800f15d
Compare
wan9chi
force-pushed
the
fspy-close-gate
branch
from
August 10, 2026 07:06
800f15d to
38c76b1
Compare
wan9chi
force-pushed
the
fspy-close-gate
branch
2 times, most recently
from
August 10, 2026 07:26
5e9003e to
f4e2f6e
Compare
The old quiescence protocol attached "may write" to the shared mapping but "is still writing" to a file-lock descriptor. A descendant that closes descriptors it does not recognize released the lock while keeping full write access to the mapping, so the receiver could read frames while a straggler was mutating them. Put the gate in the shared memory itself, where a writer cannot drop it while still being able to write: one atomic word admits and counts claims, and the runner's close is a single `fetch_or` at root-process exit that fences all future claims and reports whether any write was in flight. Zero in flight proves every admitted claim ran to completion and the memory is frozen; anything else means the run is conservatively not cached. Tracking now stops when the root process exits instead of waiting for lingering descendants, so a task that leaks a daemon no longer blocks the read step, and post-exit accesses are treated as what they are: racy with respect to the task's contract. Closes #544. Closes #396. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wan9chi
force-pushed
the
fspy-close-gate
branch
from
August 10, 2026 07:32
f4e2f6e to
ee9318c
Compare
wan9chi
marked this pull request as draft
August 10, 2026 07:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
The current quiescence protocol attaches "may write" to a shared mapping but "is still writing" to a file-lock descriptor. A descendant that closes descriptors it does not recognize releases the lock while it keeps write access to the mapping, so the receiver can read frames while a straggler mutates them. The #544 investigation pinned this as the root cause.
This PR replaces the lock with a gate stored in the shared memory itself, where a writer cannot lose it while it can still write. One atomic word admits and counts each claim. The runner closes with one
fetch_orat root-process exit, which fences all future claims and reports whether a write was still in flight. A zero count proves every claim ran to completion: the memory is frozen, and the runner reads and caches. A nonzero count means the run is not cached. In-flight windows last microseconds and the runner closes milliseconds afterwaitreturns, so a nonzero count is rare. The closed bit and the count share one word on purpose; with two atomics, a writer could check the flag before the close and publish its count after it, which is the file lock's race in new clothes.The close drops the shared memory's keeper, so the attach window ends with the close: a process that starts later fails at
open, and a sender that attached earlier has its claims refused. On Linux, the seccomp supervisor'sstopalso returns without waiting: each listener task hands over what it recorded and a detached task keeps answering notifications, so a live filtered process keeps working while its later accesses go unrecorded. Draining those listeners to EOF used to mean waiting for every filtered descendant to exit, which reintroduced the daemon hang this PR removes.A process may also call
exitwhile another of its threads is mid-send, which would abandon a gate guard and mark the whole run incomplete. The preload now interposesexitand_exitand waits the few microseconds until in-flight sends finish before the process dies. Signals and crashes still skip this, and the runner handles those by not caching the run. Windows parity is a follow-up.Semantics change: fspy no longer waits for lingering descendants after the root process exits, and it does not track accesses made after that exit. Collecting the trace no longer delays
vp run; a task that leaks a daemon no longer blocks the read step. This fixes #396, where the receiver's exclusive flock waited on shared flocks inherited by Playwright/Chromium'ssetsidtree. Stdio pipe draining can still hold a run open and is tracked separately in #485.Closes #544
Closes #396
🤖 Generated with Claude Code