2026-07-17
Atomic operations are expensive: a LOCK CMPXCHG on x86 costs 20-40 cycles even uncontended, and much more under contention because it forces cache-line ownership. But if your data is per-CPU, you never actually need atomics — no other CPU touches it. The problem: how do you guarantee your read-modify-write of "the current CPU's counter" isn't interrupted by a migration to another CPU midway through?
Restartable Sequences (rseq), added in Linux 4.18, solve this. Your thread registers a small struct via the rseq(2) syscall. It contains a pointer to a critical section descriptor and a field the kernel writes: the current CPU ID. You then write a "commit sequence" — a small block of instructions bracketed by two labels — that:
The magic: the kernel, on every context switch, checks whether the preempted thread's instruction pointer is inside a registered critical section. If yes, it rewrites RIP to the abort handler — a fallback label you provided — so when the thread resumes (possibly on a new CPU), it restarts the sequence with the correct CPU ID. The commit store is the linearization point: either it executed and you're done, or the kernel aborted you before it.
Real-world example: glibc's tcache in recent versions uses rseq for per-CPU freelists in malloc. Google's tcmalloc uses it heavily — their per-CPU cache achieves ~5ns allocation, versus ~15ns with a per-thread cache plus atomic refills. The LTTng tracer uses rseq for lock-free per-CPU ring buffers at ~10ns per event.
Rule of thumb: if your data structure is naturally partitioned per-CPU and the fast path fits in ~10 instructions, rseq beats atomics by 3-5x. If migrations are rare (typical: <0.1% of iterations abort), the amortized cost is just a plain load and store. If you're bouncing between CPUs constantly, it degrades — but so does everything else.
Gotchas: the critical section must be branch-free between start and commit (branches make abort semantics ambiguous). You cannot call functions inside it. Signal handlers running inside a critical section also trigger abort — which is correct, since the signal handler might migrate or modify state. Most implementations write the critical section in hand-written assembly with the descriptor in a special ELF section (__rseq_cs) that ld links into an array.
