POSIX
Compare-and-Swap
Filesystem
Synchronization
Concurrency

Compare-and-Swap over POSIX-compliant filesystem objects

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

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.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.