feat(fs): await_no_writers closes the inherited-descriptor ETXTBSY race (0.1.20) - #352
Merged
Merged
Conversation
…ce (0.1.20) A child forked while a publisher's write descriptor is open keeps it until its own exec, so Linux refuses to execute the published file (ETXTBSY) even after the publisher closed its copy (zackees/soldr#3350). await_no_writers waits until no process holds the file open for writing, observed on Linux through a read lease; other hosts report Unobservable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Sep 25, 2026
Merged
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.
Fixes the root cause of zackees/soldr#3350 at the platform owner; first link of the kernal-api -> zccache -> soldr -> setup-soldr cascade.
Mechanism
A child forked while a publisher's write descriptor is open inherits it until its own
exec(O_CLOEXECcloses on exec, not fork).renamekeeps the inode, so Linux refuses to execute the published file (ETXTBSY) for that child's fork-to-exec window even after the publisher closed its copy. zccache and soldr guard this with two separate process-wide locks, so any spawn in the embedded daemon that takes the other lock (or none) still leaks.Fix
platform::fs::await_no_writers(path, timeout) -> WriterWait: waits until no process holds the file open for writing, whoever forked. Linux observes writers with a read lease (F_SETLEASE F_RDLCKis refused withEAGAINunder the same condition as exec'sETXTBSY); macOS and Windows reportUnobservable(Windows children inherit only inheritable handles). Publishers call it after closing and before handing the file to anything that executes it.Tests (RED -> GREEN)
platform_linux::fs_materialize::tests::a_descriptor_inherited_across_fork_is_waited_outforks a child that lingers before exec while the writer is open, closes the writer, asserts exec fails withETXTBSY, then awaits writers and execs successfully.exec after waiting: Os { code: 26, kind: ExecutableFileBusy, message: "Text file busy" }tests/fs/materialization.rs.Local: fmt,
clippy --all-features --all-targets -D warnings, fs + lib tests, Python compat. (current_executable_exposes_a_gnu_build_idfails locally on untouched main too; linker note settings.)Version 0.1.19 -> 0.1.20 for the release zccache and soldr will pin.
🤖 Generated with Claude Code