From 4d02d2b316a41119e77448056339cb8fb9ae7b6a Mon Sep 17 00:00:00 2001 From: sedited Date: Fri, 30 Jan 2026 12:48:04 +0100 Subject: [PATCH] validation: Move block into BlockConnected signal This makes existing behaviour of the block's destructor triggering on the scheduler thread more explicit by moving it to the thread. The scheduler thread doing so is useful, since it does not block the thread doing validation while releasing a block's memory. Previously, both the caller and the queued event lambda held copies of the shared_ptr. The block would typically be freed on the scheduler thread - but only because it went out of scope before the queued event on the scheduler thread ran. If the scheduler ran first, the block would instead be freed on the validation thread. Now, ownership is transferred at each step when invoking the BlockConnected signal: connected_blocks yields via std::move, BlockConnected takes by value, and the event lambda move-captures the shared_ptr. Though it is possible that this only decrements the block's reference count, blocks are also read from disk in `ConnectTip`, which now explicitly results in their memory being released on the scheduler thread. --- src/validation.cpp | 4 ++-- src/validationinterface.cpp | 4 ++-- src/validationinterface.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 1efe109b6f6..f9cc7d78e1b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3391,9 +3391,9 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< } pindexNewTip = m_chain.Tip(); - for (const auto& [index, block] : connected_blocks) { + for (auto& [index, block] : std::move(connected_blocks)) { if (m_chainman.m_options.signals) { - m_chainman.m_options.signals->BlockConnected(chainstate_role, Assert(block), Assert(index)); + m_chainman.m_options.signals->BlockConnected(chainstate_role, std::move(Assert(block)), Assert(index)); } } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 3c142f8db28..3e939de964e 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -219,12 +219,12 @@ void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } -void ValidationSignals::BlockConnected(const ChainstateRole& role, const std::shared_ptr& pblock, const CBlockIndex* pindex) +void ValidationSignals::BlockConnected(const ChainstateRole& role, std::shared_ptr pblock, const CBlockIndex* pindex) { auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__, pblock->GetHash().ToString(), pindex->nHeight); - auto event = [role, pblock, pindex, this] { + auto event = [role, pblock = std::move(pblock), pindex, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); }); }; ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); diff --git a/src/validationinterface.h b/src/validationinterface.h index 4777e8dca8d..641afd555be 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -223,7 +223,7 @@ public: void TransactionAddedToMempool(const NewMempoolTransactionInfo&, uint64_t mempool_sequence); void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence); void MempoolTransactionsRemovedForBlock(const std::vector&, unsigned int nBlockHeight); - void BlockConnected(const kernel::ChainstateRole&, const std::shared_ptr&, const CBlockIndex* pindex); + void BlockConnected(const kernel::ChainstateRole&, std::shared_ptr, const CBlockIndex* pindex); void BlockDisconnected(const std::shared_ptr &, const CBlockIndex* pindex); void ChainStateFlushed(const kernel::ChainstateRole&, const CBlockLocator&); void BlockChecked(const std::shared_ptr&, const BlockValidationState&);