load-linked / store-conditional
/ L-L / S-C /
Imagine borrowing a library book with a tripwire: you check the book out (and a sensor starts watching it), you make your notes, and when you try to return your edited copy the system only accepts it if nobody else touched the book while you had it. If anyone disturbed it, your update is rejected and you start over. Load-linked / store-conditional is a pair of instructions built on exactly this idea: load a value while placing a hidden reservation on its address, and later try to store — but the store succeeds only if nothing wrote to that address in between.
It comes as two instructions. Load-linked (LL) reads a memory location and quietly sets a reservation on it for this core. The core then computes a new value however it likes. Store-conditional (SC) attempts to write the new value back, but it succeeds only if the reservation is still intact — that is, only if no other core (and no interrupt) has written to that address since the LL; if anything did, SC fails and writes nothing, returning a fail flag. So a read-modify-write becomes: LL the value, compute, SC the result, and if SC fails, loop and retry. The reservation is tracked by the cache-coherence hardware: another core's write to the line clears your reservation.
LL/SC is the RISC world's answer to the same problem CAS solves on x86, and it sidesteps CAS's ABA flaw: SC fails on any intervening write to the address, even one that restored the original value, because it watches the address itself rather than comparing a value. Architectures like RISC-V, ARM, MIPS, and PowerPC provide LL/SC (RISC-V calls it LR/SC). The honest caveats: the reservation is fragile — a context switch, an interrupt, or an unrelated access can clear it on some implementations — so SC may fail spuriously, and the LL/SC region must be kept tiny, which is why higher-level atomics and locks are built on top of it rather than used raw.
Atomic increment with LL/SC: retry: r = LL(&counter); r = r + 1; ok = SC(&counter, r); if (!ok) goto retry. If another core writes counter between the LL and the SC, the reservation is cleared, SC returns failure, and we loop. Note it fails even if the other core wrote the same value back — unlike value-comparing CAS, so no ABA problem.
LL/SC watches the address, not the value, so any intervening write fails the store — neatly avoiding the ABA trap that bites CAS.
Store-conditional can fail spuriously (from an interrupt, context switch, or even unrelated access on some chips), so LL/SC is always used in a retry loop and the code between LL and SC must be kept very short.