ADR-238: A Word-Sized Value That Crosses Is the Word, and update Is a Compare-and-Swap

Status: 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

1. Context

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.

2. Decisions

D1 — The word row is a third answer of the analysis that picks the count.

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:

It never replaces the cheap shape, and at user_parallelism = no nothing gets it.

D2 — A door over several locks keeps the lock.

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.

D3 — --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.

3. Consequences

#184 is closed and deleted. A program means the same at every row; what changes is what a crossing counter costs.

4. What is built

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.

This site is open source. Improve this page.