diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1597,15 +1597,15 @@ BlockHash hash(uint256S(strHash)); CValidationState state; + CBlockIndex *pblockindex; { LOCK(cs_main); - CBlockIndex *pblockindex = LookupBlockIndex(hash); + pblockindex = LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - - FinalizeBlockAndInvalidate(config, state, pblockindex); } + FinalizeBlockAndInvalidate(config, state, pblockindex); if (state.IsValid()) { ActivateBestChain(config, state); diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -639,8 +639,7 @@ * A finalized block can not be reorged in any way. */ bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, - CBlockIndex *pindex) - EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CBlockIndex *pindex); /** Mark a block as invalid. */ bool InvalidateBlock(const Config &config, CValidationState &state, diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2829,6 +2829,14 @@ } } +static void LimitValidationInterfaceQueue() { + AssertLockNotHeld(cs_main); + + if (GetMainSignals().CallbacksPending() > 10) { + SyncWithValidationInterfaceQueue(); + } +} + /** * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block @@ -2863,15 +2871,13 @@ do { boost::this_thread::interruption_point(); - if (GetMainSignals().CallbacksPending() > 10) { - // Block until the validation queue drains. This should largely - // never happen in normal operation, however may happen during - // reindex, causing memory blowup if we run too far ahead. - // Note that if a validationinterface callback ends up calling - // ActivateBestChain this may lead to a deadlock! We should - // probably have a DEBUG_LOCKORDER test for this in the future. - SyncWithValidationInterfaceQueue(); - } + // Block until the validation queue drains. This should largely + // never happen in normal operation, however may happen during + // reindex, causing memory blowup if we run too far ahead. + // Note that if a validationinterface callback ends up calling + // ActivateBestChain this may lead to a deadlock! We should + // probably have a DEBUG_LOCKORDER test for this in the future. + LimitValidationInterfaceQueue(); { LOCK(cs_main); @@ -3034,6 +3040,9 @@ break; } + // Make sure the queue of validation callbacks doesn't grow unboundedly. + LimitValidationInterfaceQueue(); + LOCK(cs_main); if (!chainActive.Contains(pindex)) { @@ -3126,26 +3135,29 @@ bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, CBlockIndex *pindex) { - AssertLockHeld(cs_main); - if (!FinalizeBlockInternal(config, state, pindex)) { - // state is set by FinalizeBlockInternal. - return false; - } + { + LOCK(cs_main); + if (!FinalizeBlockInternal(config, state, pindex)) { + // state is set by FinalizeBlockInternal. + return false; + } - // We have a valid candidate, make sure it is not parked. - if (pindex->nStatus.isOnParkedChain()) { - UnparkBlock(pindex); + // We have a valid candidate, make sure it is not parked. + if (pindex->nStatus.isOnParkedChain()) { + UnparkBlock(pindex); + } } // If the finalized block is not on the active chain, we need to rewind. - if (!AreOnTheSameFork(pindex, chainActive.Tip())) { - const CBlockIndex *pindexFork = chainActive.FindFork(pindex); - CBlockIndex *pindexToInvalidate = - chainActive.Tip()->GetAncestor(pindexFork->nHeight + 1); - return InvalidateBlock(config, state, pindexToInvalidate); + if (AreOnTheSameFork(pindex, chainActive.Tip())) { + return true; } - return true; + const CBlockIndex *pindexFork = chainActive.FindFork(pindex); + CBlockIndex *pindexToInvalidate = + chainActive.Tip()->GetAncestor(pindexFork->nHeight + 1); + + return InvalidateBlock(config, state, pindexToInvalidate); } bool InvalidateBlock(const Config &config, CValidationState &state,