mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-10 22:58:40 +02:00
validation: make IsInitialBlockDownload() lock-free
`ChainstateManager::IsInitialBlockDownload()` is queried on hot paths and previously acquired `cs_main` internally, contributing to lock contention. Cache the IBD status in `m_cached_is_ibd`, and introduce `ChainstateManager::UpdateIBDStatus()` to latch it once block loading has finished and the current chain tip has enough work and is recent. Call the updater after tip updates and after `ImportBlocks()` completes. Since `IsInitialBlockDownload()` no longer updates the cache, drop `mutable` from `m_cached_is_ibd` and only update it from `UpdateIBDStatus()` under `cs_main`. Update the new unit test to showcase the new `UpdateIBDStatus()`. Co-authored-by: Patrick Strateman <patrick.strateman@gmail.com> Co-authored-by: Martin Zumsande <mzumsande@gmail.com>
This commit is contained in:
@@ -1939,23 +1939,15 @@ void Chainstate::InitCoinsCache(size_t cache_size_bytes)
|
||||
m_coins_views->InitCache();
|
||||
}
|
||||
|
||||
// Note that though this is marked const, we may end up modifying `m_cached_is_ibd`, which
|
||||
// is a performance-related implementation detail. This function must be marked
|
||||
// `const` so that `CValidationInterface` clients (which are given a `const Chainstate*`)
|
||||
// can call it.
|
||||
// This function must be marked `const` so that `CValidationInterface` clients
|
||||
// (which are given a `const Chainstate*`) can call it.
|
||||
//
|
||||
// It is lock-free and depends on `m_cached_is_ibd`, which is latched by
|
||||
// `UpdateIBDStatus()`.
|
||||
//
|
||||
bool ChainstateManager::IsInitialBlockDownload() const
|
||||
{
|
||||
// Optimization: pre-test latch before taking the lock.
|
||||
if (!m_cached_is_ibd.load(std::memory_order_relaxed)) return false;
|
||||
|
||||
LOCK(cs_main);
|
||||
if (!m_cached_is_ibd.load(std::memory_order_relaxed)) return false;
|
||||
if (m_blockman.LoadingBlocks()) return true;
|
||||
if (!ActiveChain().IsTipRecent(MinimumChainWork(), m_options.max_tip_age)) return true;
|
||||
LogInfo("Leaving InitialBlockDownload (latching to false)");
|
||||
m_cached_is_ibd.store(false, std::memory_order_relaxed);
|
||||
return false;
|
||||
return m_cached_is_ibd.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Chainstate::CheckForkWarningConditions()
|
||||
@@ -2999,6 +2991,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
|
||||
}
|
||||
|
||||
m_chain.SetTip(*pindexDelete->pprev);
|
||||
m_chainman.UpdateIBDStatus();
|
||||
|
||||
UpdateTip(pindexDelete->pprev);
|
||||
// Let wallets know transactions went from 1-confirmed to
|
||||
@@ -3128,6 +3121,7 @@ bool Chainstate::ConnectTip(
|
||||
}
|
||||
// Update m_chain & related variables.
|
||||
m_chain.SetTip(*pindexNew);
|
||||
m_chainman.UpdateIBDStatus();
|
||||
UpdateTip(pindexNew);
|
||||
|
||||
const auto time_6{SteadyClock::now()};
|
||||
@@ -3331,6 +3325,15 @@ static SynchronizationState GetSynchronizationState(bool init, bool blockfiles_i
|
||||
return SynchronizationState::INIT_DOWNLOAD;
|
||||
}
|
||||
|
||||
void ChainstateManager::UpdateIBDStatus()
|
||||
{
|
||||
if (!m_cached_is_ibd.load(std::memory_order_relaxed)) return;
|
||||
if (m_blockman.LoadingBlocks()) return;
|
||||
if (!CurrentChainstate().m_chain.IsTipRecent(MinimumChainWork(), m_options.max_tip_age)) return;
|
||||
LogInfo("Leaving InitialBlockDownload (latching to false)");
|
||||
m_cached_is_ibd.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool ChainstateManager::NotifyHeaderTip()
|
||||
{
|
||||
bool fNotify = false;
|
||||
@@ -4614,6 +4617,7 @@ bool Chainstate::LoadChainTip()
|
||||
return false;
|
||||
}
|
||||
m_chain.SetTip(*pindex);
|
||||
m_chainman.UpdateIBDStatus();
|
||||
tip = m_chain.Tip();
|
||||
|
||||
// Make sure our chain tip before shutting down scores better than any other candidate
|
||||
|
||||
Reference in New Issue
Block a user