doc: update CoinsViewOverlay docstring to describe parallel fetching

Co-authored-by: l0rinc <pap.lorinc@gmail.com>
This commit is contained in:
Andrew Toth
2026-03-07 13:18:36 -05:00
parent ab2a379237
commit d69a3b20de

View File

@@ -564,13 +564,62 @@ private:
};
/**
* CCoinsViewCache overlay that avoids populating/mutating parent cache layers on cache misses.
* CCoinsViewCache subclass that asynchronously fetches most block input prevouts in parallel during ConnectBlock without
* mutating the base cache.
*
* This is achieved by fetching coins from the base view using PeekCoin() instead of GetCoin(),
* so intermediate CCoinsViewCache layers are not filled.
* Only used in ConnectBlock to pass as an ephemeral view that can be reset if the block is invalid.
* It provides the same interface as CCoinsViewCache.
* It adds an additional StartFetching method to provide the block.
*
* Used during ConnectBlock() as an ephemeral, resettable top-level view that is flushed only
* on success, so invalid blocks don't pollute the underlying cache.
* When a block is passed to StartFetching, the inputs of the block are flattened into a vector of InputToFetch
* objects. StartFetching then submits worker tasks to a ThreadPool and keeps the returned futures alive until fetching
* is stopped.
*
* ProcessInput() atomically fetches and increments m_input_head, so each thread can only access a single element of the
* m_inputs vector at a time. Workers race to claim inputs, so they may fetch elements in any order. If the fetched
* index is greater than or equal to the size of m_inputs, no more inputs can be fetched and false is returned.
*
* The worker claims the InputToFetch at this index, fetches the coin from the base cache and moves it into the
* InputToFetch object. The ready flag is then set with a release memory order. This allows the ready flag to be
* used as a memory fence, guaranteeing the coin being written to the object will have happened before another
* thread tests the flag with an acquire memory order.
* This assumes all base->PeekCoin() paths are safe for concurrent readers and do not mutate lower cache layers.
*
* When a coin is requested from the cache on the main thread and is not already in cacheCoins map, FetchCoinFromBase
* checks whether the next unconsumed entry in m_inputs has the requested outpoint. On a match, m_input_tail is advanced
* and the entry's ready flag is waited on with an acquire memory order until a worker has finished fetching it. The
* coin is then moved out and returned. Since the main thread is the only consumer of validation results, it blocks
* on the specific input it needs rather than racing workers for other inputs.
*
* StopFetching() is called in Flush() and in Reset() (the per-block teardown) so workers stop before the block they
* reference goes away. It stops fetching by moving m_input_head to the end of m_inputs (so workers quickly exit),
* then waits for all futures to complete and clears the per-block state (m_inputs and the head/tail counters).
*
* Workers advance m_input_head to fetch inputs. Main thread advances m_input_tail to consume.
*
* Before workers start:
*
* m_input_head
* m_input_tail
* │
* ▼
* ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
* m_inputs: │ waiting │ waiting │ waiting │ waiting │ waiting │ waiting │ waiting │ waiting │ waiting │
* │ │ │ │ │ │ │ │ │ │
* └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
*
* After workers start:
*
* Worker 2 Worker 0 Worker 3 Worker 1 m_input_head
* │ │ │ │ │
* ▼ ▼ ▼ ▼ ▼
* ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
* m_inputs: │ ready │ ready │fetching │ ready │fetching │fetching │fetching │ waiting │ waiting │
* │consumed │ ✓ │ ● │ ✓ │ ● │ ● │ ● │ │ │
* └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
* ▲
* │
* m_input_tail
*/
class CoinsViewOverlay : public CCoinsViewCache
{