update Is a Compare-and-SwapStatus: Superseded by ADR-281 — its D1 is D8 there, D2 is part of D18, and D3 part of D16
Date: September 27, 2026
Target Version: Nikaia 0.0.223
Answers: #184, an update block says mut, may run
more than once, and the compiler picks the lock — ADR-110 §5’s
speed half: the compare-and-swap loop, the second lowering, explain naming
the row, and --sharing naming a large get
Refines: ADR-110 D2 (which values get the copy row),
ADR-037 D7 (a third answer beside plain and atomic)
Related: ADR-057 (a lock’s shape is decided per value),
ADR-065 (a door over several locks), docs/lock-free.md
ADR-110 D2 gives a value that fits a machine word a copy: the block runs on
it, the result is compare-and-swapped in, and the block runs again on a
collision, which D3 permits because nothing a block may do is observable twice.
It was built with every value in the address row — the block ran once under
the lock — because D3’s may is a permission. docs/lock-free.md had measured
what the other row buys: ×0.63–×0.67 of the crossing lock uncontended, several
times less under contention, and ×6.5 worse than the cheap single-thread
shape.
contracts::sharing already decides, per class of handles on one allocation,
whether nothing crosses a thread (plain, the cheap Local lock) or something
may (atomic, the Crossing lock), and the answer reaches every position —
the constructor, a parameter, a field — so one value is one Rust type. A third
answer joins them: word, the Arc count around nikaia_std::lock::Word,
an AtomicU64 whose update is D2’s loop and whose set_after is one
compare-and-swap. A class gets it where all of these hold:
atomic because of a proven crossing — a spawn body, a
parallel walk — and not because the floor answered for something unseen, a
public signature or field among them;SharedMut of a word-sized value: a number, a
bool, a char (SharedMut(0) is read as holding an i64);access_all or update_all.It never replaces the cheap shape, and at user_parallelism = no nothing gets
it.
update_all holds both locks across the block, and a retry cannot be held. So
a value such a door names keeps the crossing lock, and Word is not a
Door: were a value to reach one with this shape, the program would not
compile rather than race.
--sharing says which row, why a word kept its lock, and where a get copies.A word is reported as word with the line its update is a
compare-and-swap and there is no lock; a word-sized value that crosses and
kept its lock says why — a door over several locks, or the floor. And a get
on a SharedMut of a value that does not copy cheaply — a text, a list — is
named: big.get() copies the whole String out of the lock each time -
access reads it in place.
#184 is closed and deleted. A program means the same at every row; what changes is what a crossing counter costs.
All of it, in 0.0.223: nikaia_std::lock::Word and Bits; Count::Word,
holds_a_word, held_together, copied_out and kept_its_lock in
contracts::sharing; the typed hull literal; crates/nikaia/tests/word_locks.rs
and lock.rs’s tests, among them four threads adding to one word without a
lost update.