Skip to content

Commit

Permalink
Fix concurrency-related bugs in ActivateBestChain
Browse files Browse the repository at this point in the history
If multiple threads are invoking ActivateBestChain, it was possible to have
them working towards different tips, and we could arrive at a less work tip
than we should.  Fix this by introducing a ChainState lock which must
be held for the entire duration of ActivateBestChain to enforce
exclusion in ABC.

Github-Pull: #2290
Rebased-From: a51a755
  • Loading branch information
skeees authored and furszy committed Apr 8, 2021
1 parent fb19c3a commit 39f6eaf
Showing 1 changed file with 15 additions and 3 deletions.
18 changes: 15 additions & 3 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,17 @@ struct CBlockIndexWorkComparator {
CBlockIndex* pindexBestInvalid;

/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though.
*/
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though.
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;

/**
* the ChainState Mutex
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
*/
Mutex m_cs_chainstate;

/** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;

Expand Down Expand Up @@ -2226,6 +2232,12 @@ bool ActivateBestChain(CValidationState& state, std::shared_ptr<const CBlock> pb
// sanely for performance or correctness!
AssertLockNotHeld(cs_main);

// ABC maintains a fair degree of expensive-to-calculate internal state
// because this function periodically releases cs_main so that it does not lock up other threads for too long
// during large connects - and to allow for e.g. the callback queue to drain
// we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
LOCK(m_cs_chainstate);

CBlockIndex* pindexNewTip = nullptr;
CBlockIndex* pindexMostWork = nullptr;
do {
Expand Down

0 comments on commit 39f6eaf

Please sign in to comment.