kernel: Pass interrupt reference to chainman

This and the following commit seek to decouple the libbitcoinkernel
library from the shutdown code. As a library, it should it should have
its own flexible interrupt infrastructure without relying on node-wide
globals.

The commit takes the first step towards this goal by de-globalising
`ShutdownRequested` calls in kernel code.

Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
Co-authored-by: TheCharlatan <seb.kung@gmail.com>
This commit is contained in:
TheCharlatan 2023-05-17 12:43:23 +02:00
parent e2d680a32d
commit edb55e2777
No known key found for this signature in database
GPG Key ID: 9B79B45691DB4173
12 changed files with 55 additions and 40 deletions

View File

@ -113,7 +113,7 @@ int main(int argc, char* argv[])
.chainparams = chainman_opts.chainparams, .chainparams = chainman_opts.chainparams,
.blocks_dir = abs_datadir / "blocks", .blocks_dir = abs_datadir / "blocks",
}; };
ChainstateManager chainman{chainman_opts, blockman_opts}; ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts};
node::CacheSizes cache_sizes; node::CacheSizes cache_sizes;
cache_sizes.block_tree_db = 2 << 20; cache_sizes.block_tree_db = 2 << 20;

View File

@ -1462,7 +1462,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(mempool_opts); node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts);
ChainstateManager& chainman = *node.chainman; ChainstateManager& chainman = *node.chainman;
node::ChainstateLoadOptions options; node::ChainstateLoadOptions options;

View File

@ -9,13 +9,13 @@
#include <logging.h> #include <logging.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <serialize.h> #include <serialize.h>
#include <shutdown.h>
#include <streams.h> #include <streams.h>
#include <sync.h> #include <sync.h>
#include <txmempool.h> #include <txmempool.h>
#include <uint256.h> #include <uint256.h>
#include <util/fs.h> #include <util/fs.h>
#include <util/fs_helpers.h> #include <util/fs_helpers.h>
#include <util/signalinterrupt.h>
#include <util/time.h> #include <util/time.h>
#include <validation.h> #include <validation.h>
@ -95,7 +95,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
} else { } else {
++expired; ++expired;
} }
if (ShutdownRequested()) if (active_chainstate.m_chainman.m_interrupt)
return false; return false;
} }
std::map<uint256, CAmount> mapDeltas; std::map<uint256, CAmount> mapDeltas;

View File

@ -13,12 +13,12 @@
#include <logging.h> #include <logging.h>
#include <pow.h> #include <pow.h>
#include <reverse_iterator.h> #include <reverse_iterator.h>
#include <shutdown.h>
#include <signet.h> #include <signet.h>
#include <streams.h> #include <streams.h>
#include <undo.h> #include <undo.h>
#include <util/batchpriority.h> #include <util/batchpriority.h>
#include <util/fs.h> #include <util/fs.h>
#include <util/signalinterrupt.h>
#include <validation.h> #include <validation.h>
#include <map> #include <map>
@ -250,7 +250,8 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
bool BlockManager::LoadBlockIndex() bool BlockManager::LoadBlockIndex()
{ {
if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) { if (!m_block_tree_db->LoadBlockIndexGuts(
GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }, m_interrupt)) {
return false; return false;
} }
@ -260,7 +261,7 @@ bool BlockManager::LoadBlockIndex()
CBlockIndexHeightOnlyComparator()); CBlockIndexHeightOnlyComparator());
for (CBlockIndex* pindex : vSortedByHeight) { for (CBlockIndex* pindex : vSortedByHeight) {
if (ShutdownRequested()) return false; if (m_interrupt) return false;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
@ -890,8 +891,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
} }
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent); chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
if (ShutdownRequested()) { if (chainman.m_interrupt) {
LogPrintf("Shutdown requested. Exit %s\n", __func__); LogPrintf("Interrupt requested. Exit %s\n", __func__);
return; return;
} }
nFile++; nFile++;
@ -909,8 +910,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
if (file) { if (file) {
LogPrintf("Importing blocks file %s...\n", fs::PathToString(path)); LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
chainman.ActiveChainstate().LoadExternalBlockFile(file); chainman.ActiveChainstate().LoadExternalBlockFile(file);
if (ShutdownRequested()) { if (chainman.m_interrupt) {
LogPrintf("Shutdown requested. Exit %s\n", __func__); LogPrintf("Interrupt requested. Exit %s\n", __func__);
return; return;
} }
} else { } else {

View File

@ -33,6 +33,9 @@ struct FlatFilePos;
namespace Consensus { namespace Consensus {
struct Params; struct Params;
} }
namespace util {
class SignalInterrupt;
} // namespace util
namespace node { namespace node {
@ -153,10 +156,12 @@ private:
public: public:
using Options = kernel::BlockManagerOpts; using Options = kernel::BlockManagerOpts;
explicit BlockManager(Options opts) explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts)
: m_prune_mode{opts.prune_target > 0}, : m_prune_mode{opts.prune_target > 0},
m_opts{std::move(opts)} {}; m_opts{std::move(opts)},
m_interrupt{interrupt} {};
const util::SignalInterrupt& m_interrupt;
std::atomic<bool> m_importing{false}; std::atomic<bool> m_importing{false};
BlockMap m_block_index GUARDED_BY(cs_main); BlockMap m_block_index GUARDED_BY(cs_main);

View File

@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
.chainparams = *params, .chainparams = *params,
.blocks_dir = m_args.GetBlocksDirPath(), .blocks_dir = m_args.GetBlocksDirPath(),
}; };
BlockManager blockman{blockman_opts}; BlockManager blockman{m_node.kernel->interrupt, blockman_opts};
CChain chain {}; CChain chain {};
// simulate adding a genesis block normally // simulate adding a genesis block normally
BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);

View File

@ -197,7 +197,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
.chainparams = chainman_opts.chainparams, .chainparams = chainman_opts.chainparams,
.blocks_dir = m_args.GetBlocksDirPath(), .blocks_dir = m_args.GetBlocksDirPath(),
}; };
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{ m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index", .path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db), .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),

View File

@ -393,7 +393,7 @@ struct SnapshotTestSetup : TestChain100Setup {
// For robustness, ensure the old manager is destroyed before creating a // For robustness, ensure the old manager is destroyed before creating a
// new one. // new one.
m_node.chainman.reset(); m_node.chainman.reset();
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
} }
return *Assert(m_node.chainman); return *Assert(m_node.chainman);
} }

View File

@ -9,8 +9,8 @@
#include <logging.h> #include <logging.h>
#include <pow.h> #include <pow.h>
#include <random.h> #include <random.h>
#include <shutdown.h>
#include <uint256.h> #include <uint256.h>
#include <util/signalinterrupt.h>
#include <util/translation.h> #include <util/translation.h>
#include <util/vector.h> #include <util/vector.h>
@ -291,7 +291,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
return true; return true;
} }
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex) bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt)
{ {
AssertLockHeld(::cs_main); AssertLockHeld(::cs_main);
std::unique_ptr<CDBIterator> pcursor(NewIterator()); std::unique_ptr<CDBIterator> pcursor(NewIterator());
@ -299,7 +299,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load m_block_index // Load m_block_index
while (pcursor->Valid()) { while (pcursor->Valid()) {
if (ShutdownRequested()) return false; if (interrupt) return false;
std::pair<uint8_t, uint256> key; std::pair<uint8_t, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) { if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
CDiskBlockIndex diskindex; CDiskBlockIndex diskindex;

View File

@ -29,6 +29,9 @@ class uint256;
namespace Consensus { namespace Consensus {
struct Params; struct Params;
}; };
namespace util {
class SignalInterrupt;
} // namespace util
//! -dbcache default (MiB) //! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 450; static const int64_t nDefaultDbCache = 450;
@ -98,7 +101,7 @@ public:
void ReadReindexing(bool &fReindexing); void ReadReindexing(bool &fReindexing);
bool WriteFlag(const std::string &name, bool fValue); bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue); bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex) bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main); EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
}; };

View File

@ -50,6 +50,7 @@
#include <util/hasher.h> #include <util/hasher.h>
#include <util/moneystr.h> #include <util/moneystr.h>
#include <util/rbf.h> #include <util/rbf.h>
#include <util/signalinterrupt.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/time.h> #include <util/time.h>
#include <util/trace.h> #include <util/trace.h>
@ -3183,11 +3184,11 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
break; break;
} }
// We check shutdown only after giving ActivateBestChainStep a chance to run once so that we // We check interrupt only after giving ActivateBestChainStep a chance to run once so that we
// never shutdown before connecting the genesis block during LoadChainTip(). Previously this // never interrupt before connecting the genesis block during LoadChainTip(). Previously this
// caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks // caused an assert() failure during interrupt in such cases as the UTXO DB flushing checks
// that the best block hash is non-null. // that the best block hash is non-null.
if (ShutdownRequested()) break; if (m_chainman.m_interrupt) break;
} while (pindexNewTip != pindexMostWork); } while (pindexNewTip != pindexMostWork);
CheckBlockIndex(); CheckBlockIndex();
@ -3277,7 +3278,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// Disconnect (descendants of) pindex, and mark them invalid. // Disconnect (descendants of) pindex, and mark them invalid.
while (true) { while (true) {
if (ShutdownRequested()) break; if (m_chainman.m_interrupt) break;
// Make sure the queue of validation callbacks doesn't grow unboundedly. // Make sure the queue of validation callbacks doesn't grow unboundedly.
LimitValidationInterfaceQueue(); LimitValidationInterfaceQueue();
@ -4079,7 +4080,7 @@ void Chainstate::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_f
{ {
if (!m_mempool) return; if (!m_mempool) return;
::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function); ::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
m_mempool->SetLoadTried(!ShutdownRequested()); m_mempool->SetLoadTried(!m_chainman.m_interrupt);
} }
bool Chainstate::LoadChainTip() bool Chainstate::LoadChainTip()
@ -4212,7 +4213,7 @@ VerifyDBResult CVerifyDB::VerifyDB(
skipped_l3_checks = true; skipped_l3_checks = true;
} }
} }
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED; if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;
} }
if (pindexFailure) { if (pindexFailure) {
LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions); LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
@ -4245,7 +4246,7 @@ VerifyDBResult CVerifyDB::VerifyDB(
LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString()); LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
return VerifyDBResult::CORRUPTED_BLOCK_DB; return VerifyDBResult::CORRUPTED_BLOCK_DB;
} }
if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED; if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;
} }
} }
@ -4413,7 +4414,7 @@ bool ChainstateManager::LoadBlockIndex()
} }
for (CBlockIndex* pindex : vSortedByHeight) { for (CBlockIndex* pindex : vSortedByHeight) {
if (ShutdownRequested()) return false; if (m_interrupt) return false;
if (pindex->IsAssumedValid() || if (pindex->IsAssumedValid() ||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) { (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
@ -4519,7 +4520,7 @@ void Chainstate::LoadExternalBlockFile(
// such as a block fails to deserialize. // such as a block fails to deserialize.
uint64_t nRewind = blkdat.GetPos(); uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) { while (!blkdat.eof()) {
if (ShutdownRequested()) return; if (m_chainman.m_interrupt) return;
blkdat.SetPos(nRewind); blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure nRewind++; // start one byte further next time, in case of failure
@ -5152,13 +5153,13 @@ struct StopHashingException : public std::exception
{ {
const char* what() const throw() override const char* what() const throw() override
{ {
return "ComputeUTXOStats interrupted by shutdown."; return "ComputeUTXOStats interrupted.";
} }
}; };
static void SnapshotUTXOHashBreakpoint() static void SnapshotUTXOHashBreakpoint(const util::SignalInterrupt& interrupt)
{ {
if (ShutdownRequested()) throw StopHashingException(); if (interrupt) throw StopHashingException();
} }
bool ChainstateManager::PopulateAndValidateSnapshot( bool ChainstateManager::PopulateAndValidateSnapshot(
@ -5235,7 +5236,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// If our average Coin size is roughly 41 bytes, checking every 120,000 coins // If our average Coin size is roughly 41 bytes, checking every 120,000 coins
// means <5MB of memory imprecision. // means <5MB of memory imprecision.
if (coins_processed % 120000 == 0) { if (coins_processed % 120000 == 0) {
if (ShutdownRequested()) { if (m_interrupt) {
return false; return false;
} }
@ -5292,7 +5293,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
try { try {
maybe_stats = ComputeUTXOStats( maybe_stats = ComputeUTXOStats(
CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, SnapshotUTXOHashBreakpoint); CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
} catch (StopHashingException const&) { } catch (StopHashingException const&) {
return false; return false;
} }
@ -5470,7 +5471,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation(
CoinStatsHashType::HASH_SERIALIZED, CoinStatsHashType::HASH_SERIALIZED,
&ibd_coins_db, &ibd_coins_db,
m_blockman, m_blockman,
SnapshotUTXOHashBreakpoint); [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
} catch (StopHashingException const&) { } catch (StopHashingException const&) {
return SnapshotCompletionResult::STATS_FAILED; return SnapshotCompletionResult::STATS_FAILED;
} }
@ -5579,9 +5580,10 @@ static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts)
return std::move(opts); return std::move(opts);
} }
ChainstateManager::ChainstateManager(Options options, node::BlockManager::Options blockman_options) ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options)
: m_options{Flatten(std::move(options))}, : m_interrupt{interrupt},
m_blockman{std::move(blockman_options)} {} m_options{Flatten(std::move(options))},
m_blockman{interrupt, std::move(blockman_options)} {}
ChainstateManager::~ChainstateManager() ChainstateManager::~ChainstateManager()
{ {

View File

@ -62,6 +62,9 @@ class SnapshotMetadata;
namespace Consensus { namespace Consensus {
struct Params; struct Params;
} // namespace Consensus } // namespace Consensus
namespace util {
class SignalInterrupt;
} // namespace util
/** Maximum number of dedicated script-checking threads allowed */ /** Maximum number of dedicated script-checking threads allowed */
static const int MAX_SCRIPTCHECK_THREADS = 15; static const int MAX_SCRIPTCHECK_THREADS = 15;
@ -959,7 +962,7 @@ private:
public: public:
using Options = kernel::ChainstateManagerOpts; using Options = kernel::ChainstateManagerOpts;
explicit ChainstateManager(Options options, node::BlockManager::Options blockman_options); explicit ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options);
const CChainParams& GetParams() const { return m_options.chainparams; } const CChainParams& GetParams() const { return m_options.chainparams; }
const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); } const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); }
@ -982,6 +985,7 @@ public:
*/ */
RecursiveMutex& GetMutex() const LOCK_RETURNED(::cs_main) { return ::cs_main; } RecursiveMutex& GetMutex() const LOCK_RETURNED(::cs_main) { return ::cs_main; }
const util::SignalInterrupt& m_interrupt;
const Options m_options; const Options m_options;
std::thread m_load_block; std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed //! A single BlockManager instance is shared across each constructed