Skip to content

fix(fspy): unify shared memory on a sparse temp file across all platforms - #576

Open
wan9chi wants to merge 1 commit into
mainfrom
fspy-sparse-file-shm
Open

fix(fspy): unify shared memory on a sparse temp file across all platforms#576
wan9chi wants to merge 1 commit into
mainfrom
fspy-sparse-file-shm

Conversation

@wan9chi

@wan9chi wan9chi commented Jul 28, 2026

Copy link
Copy Markdown
Member

Motivation

Codex CLI's and Claude Code's default sandboxes deny the primitives fspy's Unix shared memory was built on: the macOS Seatbelt profile denies shm_open (ipc-posix-shm-write-create, #563), and both sandboxes block Unix-domain sockets, which the Linux memfd broker depended on. Plain files in the temp directory are writable under both sandboxes — the existing IPC lock file is already created in temp_dir() successfully before the shm/socket step fails.

The Windows backend already backed its mapping with a sparse temp file and differed only in how other processes attach (a named section). This replaces all three backends with one file-backed implementation attached by path: std::fs::File plus memmap2, with the file's absolute path as the identifier. One implementation, one set of lifetime semantics, no broker, no tokio requirement, no global object names. The platform-specific parts shrink to four short passages: the creation flags, FSCTL_SET_SPARSE on NTFS, mode(0o600) on Unix, and how the owner's cleanup removes the name.

Lifetime semantics converge as a result: dropping the owner makes the name disappear and later opens fail, while existing views stay usable — now on Windows as well, where closing the delete-on-close handle applies the delete disposition even while other processes hold views, because those are section references rather than handles. Crash cleanup is strictly better on Windows, where the kernel closing handles deletes the file; on Unix a crashed runner leaves a sparse temp file for the standard reapers.

One platform detail did not survive contact with CI and is worth a reviewer's eye: Windows maps the file itself rather than through memmap2, because memmap2 duplicates the file handle and holds it for the mapping's lifetime, and FILE_FLAG_DELETE_ON_CLOSE only fires once every handle is closed. With memmap2 on both sides, a process that merely opened a view kept the owner's cleanup from taking effect. Mapping the section directly and keeping only the view restores the semantics above.

Refs #563.

🤖 Generated with Claude Code

wan9chi commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

fspy benchmark

linux

dynamic/launch             change  -3.51%  [-16.28% .. +10.90%]  overhead   +51.93%
dynamic/access             change  -2.43%  [-17.08% ..  +1.07%]  overhead   +53.41%
static/launch              change  +2.75%  [ -8.87% .. +13.86%]  overhead  +159.47%
static/access              change  +0.05%  [ -8.37% .. +10.70%]  overhead +1251.19%

macos

dynamic/launch             change  +1.46%  [ -2.78% ..  +6.88%]  overhead  +232.28%
dynamic/access             change  -2.26%  [-52.33% ..  +4.35%]  overhead   +21.07%

windows

dynamic/launch             change  -0.43%  [ -4.11% ..  +2.32%]  overhead   +30.77%
dynamic/access             change  +0.53%  [ -0.73% ..  +1.22%]  overhead    +8.56%

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f029ee6c9

ℹ️ 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".

io::Error::new(io::ErrorKind::InvalidInput, "shared-memory size exceeds u64")
})?;

let backing_dir = temp_dir().join(BACKING_DIR);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Publish an actually absolute backing path

When the creating process has a relative TMPDIR (supported on Unix), temp_dir() returns that relative path and this join preserves it, despite id being documented as absolute. A process opening the mapping from a different working directory then resolves the identifier elsewhere and fails; the new subprocess test misses this because it changes only the child's temporary-directory variables. Resolve the temporary directory against the creator's current directory before publishing the identifier.

AGENTS.md reference: AGENTS.md:L142-L145

Useful? React with 👍 / 👎.

@wan9chi
wan9chi force-pushed the fspy-sparse-file-shm branch 3 times, most recently from b0ab415 to 73b40b8 Compare July 29, 2026 01:22
…orms

Codex CLI's and Claude Code's default sandboxes deny the primitives fspy's
Unix shared memory was built on: the macOS Seatbelt profile denies
`shm_open` (`ipc-posix-shm-write-create`), and both sandboxes block
Unix-domain sockets, which the Linux memfd broker depended on. Plain files
in the temp directory are writable under both.

Replace all three backends with one file-backed implementation attached by
path: a sparse temporary file plus `memmap2`, with the file's absolute path
as the identifier. No broker, no tokio requirement, no global object names.

Lifetime semantics converge too: dropping the owner makes the name
disappear and later opens fail, while existing views stay usable — now on
Windows as well, where closing the delete-on-close handle applies the delete
disposition even while other processes hold views.

Refs #563.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wan9chi
wan9chi force-pushed the fspy-sparse-file-shm branch from 73b40b8 to 941a231 Compare July 29, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant