Browse Source

Fix concurrency-related bugs in ActivateBestChain

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: #13023
Rebased-From: a3ae8e68739023e5dba9e5cb190e707ed4603316
0.16
Jesse Cohen 7 years ago committed by MarcoFalke
parent
commit
bb79aaf93a
  1. 13
      src/validation.cpp

13
src/validation.cpp

@ -144,6 +144,12 @@ private:
*/ */
std::set<CBlockIndex*> g_failed_blocks; std::set<CBlockIndex*> g_failed_blocks;
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
*/
CCriticalSection m_cs_chainstate;
public: public:
CChain chainActive; CChain chainActive;
BlockMap mapBlockIndex; BlockMap mapBlockIndex;
@ -2451,6 +2457,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
@ -2562,6 +2569,12 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// sanely for performance or correctness! // sanely for performance or correctness!
AssertLockNotHeld(cs_main); 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 *pindexMostWork = nullptr; CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr; CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);

Loading…
Cancel
Save