diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1384,11 +1384,20 @@ // scan for better chains in the block chain database, that are not yet // connected in the active best chain - BlockValidationState state; - if (!ActivateBestChain(config, state)) { - LogPrintf("Failed to connect best block (%s)\n", state.ToString()); - StartShutdown(); - return; + + // We can't hold cs_main during ActivateBestChain even though we're + // accessing the g_chainman unique_ptrs since ABC requires us not to be + // holding cs_main, so retrieve the relevant pointers before the ABC + // call. + for (CChainState *chainstate : + WITH_LOCK(::cs_main, return g_chainman.GetAll())) { + BlockValidationState state; + if (!chainstate->ActivateBestChain(config, state, nullptr)) { + LogPrintf("Failed to connect best block (%s)\n", + state.ToString()); + StartShutdown(); + return; + } } if (gArgs.GetBoolArg("-stopafterblockimport", diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -104,7 +104,7 @@ // Reset global state to avoid interfering with later tests. AbortShutdown(); UnloadBlockIndex(); - g_chainman.Reset(); + WITH_LOCK(::cs_main, g_chainman.Reset()); } //! Entry point for BitcoinGUI tests. diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -1134,14 +1134,41 @@ //! free the chainstate contents immediately after it finishes validation //! to cautiously avoid a case where some other part of the system is still //! using this pointer (e.g. net_processing). + //! + //! Once this pointer is set to a corresponding chainstate, it will not + //! be reset until init.cpp:Shutdown(). This means it is safe to acquire + //! the contents of this pointer with ::cs_main held, release the lock, + //! and then use the reference without concern of it being deconstructed. + //! + //! This is especially important when, e.g., calling ActivateBestChain() + //! on all chainstates because we are not able to hold ::cs_main going into + //! that call. std::unique_ptr m_ibd_chainstate; //! A chainstate initialized on the basis of a UTXO snapshot. If this is //! non-null, it is always our active chainstate. + //! + //! Once this pointer is set to a corresponding chainstate, it will not + //! be reset until init.cpp:Shutdown(). This means it is safe to acquire + //! the contents of this pointer with ::cs_main held, release the lock, + //! and then use the reference without concern of it being deconstructed. + //! + //! This is especially important when, e.g., calling ActivateBestChain() + //! on all chainstates because we are not able to hold ::cs_main going into + //! that call. std::unique_ptr m_snapshot_chainstate; //! Points to either the ibd or snapshot chainstate; indicates our //! most-work chain. + //! + //! Once this pointer is set to a corresponding chainstate, it will not + //! be reset until init.cpp:Shutdown(). This means it is safe to acquire + //! the contents of this pointer with ::cs_main held, release the lock, + //! and then use the reference without concern of it being deconstructed. + //! + //! This is especially important when, e.g., calling ActivateBestChain() + //! on all chainstates because we are not able to hold ::cs_main going into + //! that call. CChainState *m_active_chainstate{nullptr}; //! If true, the assumed-valid chainstate has been fully validated @@ -1205,7 +1232,7 @@ void Reset(); }; -extern ChainstateManager g_chainman; +extern ChainstateManager g_chainman GUARDED_BY(::cs_main); /** @returns the most-work valid chainstate. */ CChainState &ChainstateActive(); diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -62,11 +62,13 @@ ChainstateManager g_chainman; CChainState &ChainstateActive() { + LOCK(::cs_main); assert(g_chainman.m_active_chainstate); return *g_chainman.m_active_chainstate; } CChain &ChainActive() { + LOCK(::cs_main); return ::ChainstateActive().m_chain; } @@ -790,6 +792,7 @@ static CBlockIndex const *pindexBestForkBase = nullptr; BlockMap &BlockIndex() { + LOCK(::cs_main); return g_chainman.m_blockman.m_block_index; } @@ -5187,7 +5190,7 @@ // continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { BlockValidationState state; - if (!ActivateBestChain(config, state)) { + if (!ActivateBestChain(config, state, nullptr)) { break; } }