Memory Models & Atomics

an atomic operation and atomicity

Atomic comes from the Greek for 'uncuttable'. An atomic operation is one that, from every other thread's point of view, happens all-at-once or not-at-all — there is no moment at which another thread can catch it half-done. Think of a turnstile that admits exactly one person per push: you can never have two people wedged in it mid-rotation.

Why does this need a special guarantee? Even an innocent-looking line like count = count + 1 is, underneath, three steps: load count into a register, add one, store it back. If two threads run those three steps interleaved, both can load the same old value, both add one, both store — and you lose an increment. An atomic increment collapses load-add-store into a single indivisible step that the hardware refuses to interleave. Atomicity is the property of being indivisible in this sense; it says nothing yet about ORDERING relative to other variables (that is what memory orderings add).

Crucially, on real hardware atomicity is only free for small, naturally-sized values (a pointer, a 32- or 64-bit integer) that fit in one machine word and are properly aligned. Wider or unaligned objects may need a hidden lock. And atomicity alone is not enough for correctness: you almost always also need to constrain ordering and avoid the data-race rule. Atomic operations are the only legal way for two threads to touch the same non-atomic-free location concurrently without it being undefined behavior.

atomic_int counter = 0; in two threads: atomic_fetch_add(&counter, 1); is guaranteed to reach 2. With plain int counter and counter++ it is a data race and may end at 1.

The lost-update bug: non-atomic read-modify-write across threads silently drops increments.

Atomicity is about indivisibility, NOT about ordering or visibility — a relaxed atomic is fully atomic yet gives almost no ordering guarantees; do not assume 'atomic' means 'thread-safe and ordered'.

Also called
indivisible operation不可分割操作