Prevent callback overruns in InvalidateBlock and RewindBlockIndex

This commit is contained in:
Pieter Wuille 2019-02-13 17:17:59 -08:00
parent 9bb32eb571
commit 9ce9c37004

View file

@ -2642,6 +2642,14 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
} }
} }
static void LimitValidationInterfaceQueue() {
AssertLockNotHeld(cs_main);
if (GetMainSignals().CallbacksPending() > 10) {
SyncWithValidationInterfaceQueue();
}
}
/** /**
* Make the best chain active, in multiple steps. The result is either failure * 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 * or an activated best chain. pblock is either nullptr or a pointer to a block
@ -2670,15 +2678,13 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
do { do {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
if (GetMainSignals().CallbacksPending() > 10) {
// Block until the validation queue drains. This should largely // Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during // never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead. // reindex, causing memory blowup if we run too far ahead.
// Note that if a validationinterface callback ends up calling // Note that if a validationinterface callback ends up calling
// ActivateBestChain this may lead to a deadlock! We should // ActivateBestChain this may lead to a deadlock! We should
// probably have a DEBUG_LOCKORDER test for this in the future. // probably have a DEBUG_LOCKORDER test for this in the future.
SyncWithValidationInterfaceQueue(); LimitValidationInterfaceQueue();
}
{ {
LOCK(cs_main); LOCK(cs_main);
@ -2796,6 +2802,9 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
while (true) { while (true) {
if (ShutdownRequested()) break; if (ShutdownRequested()) break;
// Make sure the queue of validation callbacks doesn't grow unboundedly.
LimitValidationInterfaceQueue();
LOCK(cs_main); LOCK(cs_main);
if (!chainActive.Contains(pindex)) break; if (!chainActive.Contains(pindex)) break;
pindex_was_in_chain = true; pindex_was_in_chain = true;
@ -4285,6 +4294,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
tip = tip->pprev; tip = tip->pprev;
} }
// Make sure the queue of validation callbacks doesn't grow unboundedly.
LimitValidationInterfaceQueue();
// Occasionally flush state to disk. // Occasionally flush state to disk.
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));