POSIX
Compare-and-Swap
Filesystem
Synchronization
Concurrency

Compare-and-Swap over POSIX-compliant filesystem objects

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

There is no standard POSIX system call that gives you a true memory-style compare-and-swap operation over arbitrary filesystem objects. What POSIX does give you are a few atomic building blocks, especially rename, link, mkdir, and open with exclusive-creation semantics, and you combine those to approximate optimistic concurrency or atomic replacement.

Why Filesystem CAS Is Harder Than Memory CAS

In memory, compare-and-swap means "replace value X with value Y only if the current value is still X." That is a single primitive operating on one memory location.

Filesystems are different:

  • file contents may be large, not one machine word
  • metadata and data may change independently
  • POSIX atomicity guarantees are narrower than people assume
  • many operations are atomic only for names, not for full content validation

So the honest answer is that POSIX filesystems do not expose a general-purpose CAS primitive over file contents.

What POSIX Does Guarantee Well: Atomic Rename

The strongest common building block is atomic replacement through rename. If you write new data to a temporary file and then rename it over the old path on the same filesystem, other processes see either the old file or the new file, not a half-written mixture.

c
1#include <stdio.h>
2#include <unistd.h>
3
4int main(void) {
5    if (rename("new.tmp", "current.txt") != 0) {
6        perror("rename");
7        return 1;
8    }
9    return 0;
10}

This gives atomic replace, which is often what people actually need. But notice the missing part: there is no built-in compare step that checks whether current.txt still has an expected version before the replace happens.

Simulating CAS with Versioned Names

A common pattern is to store immutable versions under unique names and then atomically switch a pointer-like filename.

For example:

  1. read the current version marker
  2. write a new file with a new version name
  3. atomically rename a reference file or symlink to point at the new version
  4. fail if the expected old version is no longer current

This is not a single POSIX CAS instruction, but it approximates optimistic concurrency if you keep version information separate from the payload.

Exclusive Creation as a Coordination Primitive

Another useful tool is open with O_CREAT | O_EXCL, which creates a file only if it does not already exist.

c
1#include <fcntl.h>
2#include <stdio.h>
3#include <unistd.h>
4
5int main(void) {
6    int fd = open("lockfile", O_CREAT | O_EXCL | O_WRONLY, 0644);
7    if (fd == -1) {
8        perror("open");
9        return 1;
10    }
11
12    write(fd, "locked\n", 7);
13    close(fd);
14    return 0;
15}

This is useful for leader election, one-writer claims, and coarse-grained optimistic coordination. It is not full compare-and-swap over file content, but it is a standard atomic filesystem primitive that solves many practical races.

Directory Creation as an Atomic Claim

mkdir is also commonly used as an atomic claim mechanism because creating a directory either succeeds or fails if the path already exists.

bash
1if mkdir job.lock 2>/dev/null; then
2  echo "lock acquired"
3else
4  echo "someone else owns the lock"
5fi

This works well in shell scripts and simple operational tooling. Again, it is not a byte-level CAS. It is a name-based atomic ownership pattern.

What You Cannot Rely On

You should not assume that reading a file, comparing its content in user space, and then writing back a replacement is atomic. Another process can always change the file between your read and write unless you add coordination.

So the naive pattern:

  1. read file
  2. compare expected content
  3. write file if unchanged

is not safe by itself under concurrency.

To make it safe, you usually need one of:

  • a lock file or directory lock
  • atomic rename around versioned content
  • advisory locking such as fcntl
  • a higher-level database or coordination service

Common Pitfalls

  • Assuming POSIX provides a built-in compare-and-swap syscall for file contents.
  • Treating atomic rename as though it also performs a compare step.
  • Reading and rewriting a file optimistically without any locking or version check.
  • Forgetting that rename atomicity depends on staying within the same filesystem.
  • Expecting name-level atomicity to solve every content-level concurrency problem.

Summary

  • POSIX does not provide a general CAS primitive over filesystem objects.
  • Atomic rename is the main tool for safe replace operations.
  • 'open(..., O_CREAT | O_EXCL) and mkdir are useful atomic claim primitives.'
  • To approximate CAS, combine versioning, locks, and atomic name updates.
  • If you need stronger compare-and-update guarantees, a database or coordination service is often the better abstraction.

Course illustration
Course illustration

All Rights Reserved.