mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-28 07:48:01 +00:00
Background ledger acquisition reads and writes SHAMap::state_, SHAMap::full_, SHAMap::ledgerSeq_ and SHAMapInnerNode::fullBelowGen_ concurrently with the thread driving it, so all four are std::atomic. state_ is read through state() and written through setInvalid() and the existing setters, and SHAMapState carries an explicit std::uint8_t underlying type. finishFetch() withdraws full_ with an exchange behind a relaxed load, so exactly one of the reader threads that miss reports the gap, and a map that is already not full stays off the exclusive-write path: full_ shares a cache line with state_ and ledgerSeq_, and a walk posts up to 512 reads per pass. ledgerSeq_ is read through ledgerSeq() and relaxed both ways, since it only serves as a lookup hint for a nodestore keyed by hash. Ledger::setFull() sets each map's ledger sequence before its full flag. A release store publishes only what is sequenced before it, and the finishFetch() thread that wins the exchange on the flag reads the sequence after that, so this is the order that makes the sequence visible to the once-only gap report. Static assertions pin the three SHAMap members lock-free, and pin fullBelowGen_'s size and alignment to those of a plain std::uint32_t, so SHAMapInnerNode's packed layout stays byte-identical and isFullBelow() takes no mutex once per node of every walk. Its accessors are relaxed, since a generation is only ever compared for equality and the children it vouches for are published through the node's own child lock. Three tests cover this: sixteen unresolvable branches posted at a backed map with four nodestore reader threads, so finishFetch() runs concurrently for one map and the single gap report is observable; that Ledger::setFull() publishes the sequence that report names; and that every node the sync path hands to a filter carries it.