2026-06-16
A seqlock (sequence lock) is a synchronization primitive optimized for the case where reads vastly outnumber writes and writes are short. Unlike RCU, it works on mutable in-place data. Unlike a rwlock, readers never block writers and never write to shared cache lines.
The mechanism is a single sequence counter, incremented twice per write: once at the start (making it odd) and once at the end (making it even again). Readers do this:
The brilliance: readers issue zero stores. No cache line bouncing, no atomic RMW, no contention between readers. Writers take a spinlock against other writers, bump the counter, write the data, bump again.
The canonical user: jiffies_64 and gettimeofday() on 32-bit systems. A 64-bit timestamp can't be read atomically on a 32-bit CPU — you'd see torn values mid-update (low half wrapped, high half not yet incremented). The kernel wraps the read in a seqlock. The vDSO uses the same pattern: when you call clock_gettime(), you're doing a seqlock read of a kernel-maintained timekeeper structure mapped into your address space. Millions of calls per second, zero syscalls, zero cache-line writes from readers.
The catch: readers may execute on stale data mid-update. Code in the read section must tolerate torn reads — no dereferencing potentially-garbage pointers, no dividing by a value that might be momentarily zero. The retry happens after the bad code already ran. The Linux kernel uses READ_ONCE() and avoids dependent loads inside the critical section for this reason.
Rule of thumb: use a seqlock when reads outnumber writes by ~100:1 or more, the protected data is small (a few cache lines), and the read section is straight-line code that tolerates seeing inconsistent intermediate state. If reads are expensive, the retry tax compounds; if writes are frequent, readers livelock retrying.
Cost comparison on contended reads (rough cycles, x86):
Seqlocks sit between rwlocks and RCU: more flexible than RCU (you can mutate in place) but readers must be retry-safe.
