Atomic file write with fsync
Skill kjuhwa/skills-hub/skills/jit-compilation/atomic-file-write-with-fsync
Wrap every cache write with a `put(path, data)` helper that calls `ofstream.write`, closes, then `fsync(open(path))` so other processes on distributed filesystems see the full file before it's read.From its SKILL.md
npx -y skills add kjuhwa/skills-hub --skill atomic-file-write-with-fsyncAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.6 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it
Atomic File Write With fsync Boilerplate
When to use
Your JIT writes intermediate artifacts (kernel.cu, kernel.ptx, kernel.cubin) into a cache directory on a shared filesystem. After write you invoke NVCC, which spawns child processes that read those files back. Without fsync:
- On NFS, the data is still in the client's write-behind cache when NVCC starts; the child sees zero bytes.
- On local ext4,
close()returns before the data flushes to disk; if the machine crashes 50ms later you have a cache entry that claims to exist but the file is empty.
Every file you want another process (including a child you just spawned) to read must be fsynced after close.
Steps
- Implement a single
put(path, data)helper — never write to disk without it:static void put(const std::filesystem::path& path, const std::string& data) { std::ofstream out(path, std::ios::binary); DG_HOST_ASSERT(out.write(data.data(), data.size())); out.close(); // closes the stream but may not flush // Reopen read-only and fsync — makes the data visible to other processes fsync_path(path); } fsync_pathopens then fsyncs then closes:
Read-only is enough —static void fsync_path(const std::filesystem::path& path) { const auto fd = ::open(path.c_str(), O_RDONLY); if (fd >= 0) { ::fsync(fd); ::close(fd); } }fsyncdoesn't care about mode.- After writing a full directory, fsync the directory too (recursively). The
fsync_dirpattern walks the tree bottom-up, fsyncing files first then the containing dirs:
Why fsync the directory? A rename or create inside it isn't durable until the directory's inode is synced — on NFS this is what makes thestatic void fsync_dir(const std::filesystem::path& dir_path) { for (const auto& entry: std::filesystem::directory_iterator(dir_path)) { if (entry.is_directory()) fsync_dir(entry.path()); else if (entry.is_regular_file()) fsync_path(entry.path()); } fsync_path(dir_path); // fsync the directory entry itself }rename-to-final-path visible to peers. - Order of operations for atomic commit:
put(tmp_dir / "kernel.cu", code)— fsync'd inside.- Run NVCC →
tmp_dir / "kernel.cubin"appears. fsync_dir(tmp_dir)— now all contents are durable.std::filesystem::rename(tmp_dir, final_dir)— atomic on most filesystems.
- Never use
sync_with_stdiotricks — they don't guarantee fsync.endldoesn't fsync either. The only durable action isfsync()on an open fd.
Evidence (from DeepGEMM)
csrc/jit/compiler.hpp:70-76:fsync_path— the 5-line open+fsync+close.csrc/jit/compiler.hpp:80-88:fsync_dir— bottom-up recursive.csrc/jit/compiler.hpp:90-98:put— the mandatory helper; the comment on line 95-96 calls out why:fsyncto ensure the data is visible to other processes (e.g., NVCC) on distributed filesystems, whereclose()alone does not guarantee persistence.csrc/jit/compiler.hpp:129-130:fsync_dir(tmp_dir_path)is called right before the directory rename.
Counter / Caveats
- fsync is expensive — on spinning disks it can take milliseconds. For a JIT that compiles 10 kernels in parallel, that's 10× the overhead. Worth it for correctness.
fsyncon a directory is not universally portable. It works on Linux and modern macOS; Windows'sFlushFileBufferson a directory handle is the closest equivalent but behaves differently. The DeepGEMM path is Linux-only.- Don't fsync every file unconditionally. On local SSD with
data=writebackmounts,fsyncis a no-op for the app but still triggers disk activity. Gate on "is the target filesystem remote?" if you have many writes per second. - Interaction with
O_DSYNC/O_SYNC: opening write-mode withO_SYNCmakes every write-syscall fsync'd but is 10× slower than batched-fsync-after-close. Prefer the close-then-fsync pattern.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.