HFT-52 · Week 34 · Seqlocks

Seqlock: the re-check is the lock

One writer publishes a four-word top of book; readers copy it without writing anything shared. Step the threads in any order, then remove a piece of the reader and watch it return a book that never existed.

One writer, two readers, a 4-word top of book

Timeline of the last Writer: seq oddReader 1Reader 2solid = returned · hollow = retried · dashed = in progresstorn read returned
2 ms of simulated time per reader variant, at this writer rate
ReaderReads returned / sRetriedTorn reads returned / s

Orderings (as in the week 34 reader). The writer's odd store is followed by fence(Release), so it is ordered before the data stores; the even store is Release and pairs with the reader's Acquire load of s1, so a reader that sees an even s1 sees that write's data. The reader's fence(Acquire) keeps the relaxed data loads before the re-check: if any word came from a newer write, fence-to-fence synchronisation forces s2 to see at least that write's odd value, so s1 ≠ s2. Payload words are atomics accessed with Relaxed, so racing reads are not data races. Model: sequentially consistent interleaving of the steps shown (removing the fence is modelled as what it permits: the re-check load moving ahead of the copy). Timing: seq and the four words share one 64-byte line; a reader load costs ~1 ns from its cached copy and 70 ns when the writer has stored to the line since that reader last fetched it; a writer store costs ~2 ns, or 70 ns when it must first take the line back from a reader; a reader spends 80 ns using each snapshot (~10 M reads/s per reader); writes arrive as a seeded Poisson stream. On x86 the fences emit no instruction, which is why the missing-fence variant tends to pass tests there until the compiler moves the load; the other two variants are broken on every machine.