Skip to content

Commit 75135c6

Browse files
committed
Merge bitcoin/bitcoin#27861: kernel: Rm ShutdownRequested and AbortNode from validation code.
6eb33bd kernel: Add fatalError method to notifications (TheCharlatan) 7320db9 kernel: Add flushError method to notifications (TheCharlatan) 3fa9094 scripted-diff: Rename FatalError to FatalErrorf (TheCharlatan) edb55e2 kernel: Pass interrupt reference to chainman (TheCharlatan) e2d680a util: Add SignalInterrupt class and use in shutdown.cpp (TheCharlatan) Pull request description: Get rid of all `ShutdownRequested` calls in validation code by introducing an interrupt object that applications can use to cancel long-running kernel operations. Replace all `AbortNode` calls in validation code with new fatal error and flush error notifications so kernel applications can be notified about failures and choose how to handle them. --- This pull request is part of the `libbitcoinkernel` project bitcoin/bitcoin#27587 https://github.com/orgs/bitcoin/projects/3 and more specifically its "Step 2: Decouple most non-consensus code from libbitcoinkernel". The pull request mostly allows dropping the kernel dependency on shutdown.cpp. The only dependency left after this is a `StartShutdown` call which will be removed in followup PR bitcoin/bitcoin#27711. This PR also drops the last reference to the `uiInterface` global in kernel code. The process of moving the `uiInterface` out of the kernel was started in bitcoin/bitcoin#27636. This pull request contains a subset of patches originally proposed in #27711. It will be part of a series of changes required to make handling of interrupts (or in other words the current shutdown procedure) in the kernel library more transparent and less reliable on global mutable state. The set of patches contained here was originally proposed by @ryanofsky [here](bitcoin/bitcoin#27711 (comment)). ACKs for top commit: achow101: light ACK 6eb33bd hebasto: ACK 6eb33bd, I have reviewed the code and it looks OK. ryanofsky: Code review ACK 6eb33bd. No changes since last review other than rebase. Tree-SHA512: 7d2d05fa4805428a09466d43c11ae32946cbb25aa5e741b1eec9cd142e4de4bb311e13ebf1bb125ae490c9d08274f2d56c93314e10f3d69e7fec7445e504987c
2 parents c325f0f + 6eb33bd commit 75135c6

31 files changed

+394
-199
lines changed

src/Makefile.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ BITCOIN_CORE_H = \
205205
netbase.h \
206206
netgroup.h \
207207
netmessagemaker.h \
208+
node/abort.h \
208209
node/blockmanager_args.h \
209210
node/blockstorage.h \
210211
node/caches.h \
@@ -310,6 +311,7 @@ BITCOIN_CORE_H = \
310311
util/readwritefile.h \
311312
util/result.h \
312313
util/serfloat.h \
314+
util/signalinterrupt.h \
313315
util/sock.h \
314316
util/spanparsing.h \
315317
util/string.h \
@@ -399,6 +401,7 @@ libbitcoin_node_a_SOURCES = \
399401
net.cpp \
400402
net_processing.cpp \
401403
netgroup.cpp \
404+
node/abort.cpp \
402405
node/blockmanager_args.cpp \
403406
node/blockstorage.cpp \
404407
node/caches.cpp \
@@ -733,6 +736,7 @@ libbitcoin_util_a_SOURCES = \
733736
util/moneystr.cpp \
734737
util/rbf.cpp \
735738
util/readwritefile.cpp \
739+
util/signalinterrupt.cpp \
736740
util/thread.cpp \
737741
util/threadinterrupt.cpp \
738742
util/threadnames.cpp \
@@ -933,7 +937,6 @@ libbitcoinkernel_la_SOURCES = \
933937
logging.cpp \
934938
node/blockstorage.cpp \
935939
node/chainstate.cpp \
936-
node/interface_ui.cpp \
937940
node/utxo_snapshot.cpp \
938941
policy/feerate.cpp \
939942
policy/fees.cpp \
@@ -972,6 +975,7 @@ libbitcoinkernel_la_SOURCES = \
972975
util/moneystr.cpp \
973976
util/rbf.cpp \
974977
util/serfloat.cpp \
978+
util/signalinterrupt.cpp \
975979
util/strencodings.cpp \
976980
util/string.cpp \
977981
util/syserror.cpp \

src/bitcoin-chainstate.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ int main(int argc, char* argv[])
9797
{
9898
std::cout << "Warning: " << warning.original << std::endl;
9999
}
100+
void flushError(const std::string& debug_message) override
101+
{
102+
std::cerr << "Error flushing block data to disk: " << debug_message << std::endl;
103+
}
104+
void fatalError(const std::string& debug_message, const bilingual_str& user_message) override
105+
{
106+
std::cerr << "Error: " << debug_message << std::endl;
107+
std::cerr << (user_message.empty() ? "A fatal internal error occurred." : user_message.original) << std::endl;
108+
}
100109
};
101110
auto notifications = std::make_unique<KernelNotifications>();
102111

@@ -112,8 +121,9 @@ int main(int argc, char* argv[])
112121
const node::BlockManager::Options blockman_opts{
113122
.chainparams = chainman_opts.chainparams,
114123
.blocks_dir = abs_datadir / "blocks",
124+
.notifications = chainman_opts.notifications,
115125
};
116-
ChainstateManager chainman{chainman_opts, blockman_opts};
126+
ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts};
117127

118128
node::CacheSizes cache_sizes;
119129
cache_sizes.block_tree_db = 2 << 20;

src/index/base.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <interfaces/chain.h>
99
#include <kernel/chain.h>
1010
#include <logging.h>
11+
#include <node/abort.h>
1112
#include <node/blockstorage.h>
1213
#include <node/context.h>
1314
#include <node/database_args.h>
@@ -30,9 +31,10 @@ constexpr auto SYNC_LOG_INTERVAL{30s};
3031
constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
3132

3233
template <typename... Args>
33-
static void FatalError(const char* fmt, const Args&... args)
34+
void BaseIndex::FatalErrorf(const char* fmt, const Args&... args)
3435
{
35-
AbortNode(tfm::format(fmt, args...));
36+
auto message = tfm::format(fmt, args...);
37+
node::AbortNode(m_chain->context()->exit_status, message);
3638
}
3739

3840
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
@@ -197,7 +199,7 @@ void BaseIndex::ThreadSync()
197199
break;
198200
}
199201
if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
200-
FatalError("%s: Failed to rewind index %s to a previous chain tip",
202+
FatalErrorf("%s: Failed to rewind index %s to a previous chain tip",
201203
__func__, GetName());
202204
return;
203205
}
@@ -221,14 +223,14 @@ void BaseIndex::ThreadSync()
221223
CBlock block;
222224
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex);
223225
if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) {
224-
FatalError("%s: Failed to read block %s from disk",
226+
FatalErrorf("%s: Failed to read block %s from disk",
225227
__func__, pindex->GetBlockHash().ToString());
226228
return;
227229
} else {
228230
block_info.data = &block;
229231
}
230232
if (!CustomAppend(block_info)) {
231-
FatalError("%s: Failed to write block %s to index database",
233+
FatalErrorf("%s: Failed to write block %s to index database",
232234
__func__, pindex->GetBlockHash().ToString());
233235
return;
234236
}
@@ -294,7 +296,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
294296
const CBlockIndex* best_block_index = m_best_block_index.load();
295297
if (!best_block_index) {
296298
if (pindex->nHeight != 0) {
297-
FatalError("%s: First block connected is not the genesis block (height=%d)",
299+
FatalErrorf("%s: First block connected is not the genesis block (height=%d)",
298300
__func__, pindex->nHeight);
299301
return;
300302
}
@@ -312,7 +314,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
312314
return;
313315
}
314316
if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
315-
FatalError("%s: Failed to rewind index %s to a previous chain tip",
317+
FatalErrorf("%s: Failed to rewind index %s to a previous chain tip",
316318
__func__, GetName());
317319
return;
318320
}
@@ -325,7 +327,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
325327
// processed, and the index object being safe to delete.
326328
SetBestBlockIndex(pindex);
327329
} else {
328-
FatalError("%s: Failed to write block %s to index",
330+
FatalErrorf("%s: Failed to write block %s to index",
329331
__func__, pindex->GetBlockHash().ToString());
330332
return;
331333
}
@@ -345,7 +347,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
345347
}
346348

347349
if (!locator_tip_index) {
348-
FatalError("%s: First block (hash=%s) in locator was not found",
350+
FatalErrorf("%s: First block (hash=%s) in locator was not found",
349351
__func__, locator_tip_hash.ToString());
350352
return;
351353
}

src/index/base.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class BaseIndex : public CValidationInterface
9494

9595
virtual bool AllowPrune() const = 0;
9696

97+
template <typename... Args>
98+
void FatalErrorf(const char* fmt, const Args&... args);
99+
97100
protected:
98101
std::unique_ptr<interfaces::Chain> m_chain;
99102
Chainstate* m_chainstate{nullptr};

src/init.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,6 @@ bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
812812
// Enable heap terminate-on-corruption
813813
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
814814
#endif
815-
if (!InitShutdownState(exit_status)) {
816-
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
817-
}
818-
819815
if (!SetupNetworking()) {
820816
return InitError(Untranslated("Initializing networking failed."));
821817
}
@@ -988,7 +984,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
988984

989985
// Also report errors from parsing before daemonization
990986
{
991-
KernelNotifications notifications{};
987+
kernel::Notifications notifications{};
992988
ChainstateManager::Options chainman_opts_dummy{
993989
.chainparams = chainparams,
994990
.datadir = args.GetDataDirNet(),
@@ -1001,6 +997,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
1001997
BlockManager::Options blockman_opts_dummy{
1002998
.chainparams = chainman_opts_dummy.chainparams,
1003999
.blocks_dir = args.GetBlocksDirPath(),
1000+
.notifications = chainman_opts_dummy.notifications,
10041001
};
10051002
auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)};
10061003
if (!blockman_result) {
@@ -1411,7 +1408,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14111408

14121409
// ********************************************************* Step 7: load block chain
14131410

1414-
node.notifications = std::make_unique<KernelNotifications>();
1411+
node.notifications = std::make_unique<KernelNotifications>(node.exit_status);
14151412
fReindex = args.GetBoolArg("-reindex", false);
14161413
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
14171414
ChainstateManager::Options chainman_opts{
@@ -1425,6 +1422,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14251422
BlockManager::Options blockman_opts{
14261423
.chainparams = chainman_opts.chainparams,
14271424
.blocks_dir = args.GetBlocksDirPath(),
1425+
.notifications = chainman_opts.notifications,
14281426
};
14291427
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
14301428

@@ -1464,7 +1462,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14641462
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
14651463
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
14661464

1467-
node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
1465+
node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts);
14681466
ChainstateManager& chainman = *node.chainman;
14691467

14701468
node::ChainstateLoadOptions options;

src/kernel/blockmanager_opts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H
66
#define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H
77

8+
#include <kernel/notifications_interface.h>
89
#include <util/fs.h>
910

1011
#include <cstdint>
@@ -25,6 +26,7 @@ struct BlockManagerOpts {
2526
bool fast_prune{false};
2627
bool stop_after_block_import{DEFAULT_STOPAFTERBLOCKIMPORT};
2728
const fs::path blocks_dir;
29+
Notifications& notifications;
2830
};
2931

3032
} // namespace kernel

src/kernel/context.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414

1515

1616
namespace kernel {
17+
Context* g_context;
1718

1819
Context::Context()
1920
{
21+
assert(!g_context);
22+
g_context = this;
2023
std::string sha256_algo = SHA256AutoDetect();
2124
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
2225
RandomInit();
@@ -26,6 +29,8 @@ Context::Context()
2629
Context::~Context()
2730
{
2831
ECC_Stop();
32+
assert(g_context);
33+
g_context = nullptr;
2934
}
3035

3136
} // namespace kernel

src/kernel/context.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef BITCOIN_KERNEL_CONTEXT_H
66
#define BITCOIN_KERNEL_CONTEXT_H
77

8+
#include <util/signalinterrupt.h>
9+
810
#include <memory>
911

1012
namespace kernel {
@@ -16,12 +18,24 @@ namespace kernel {
1618
//! State stored directly in this struct should be simple. More complex state
1719
//! should be stored to std::unique_ptr members pointing to opaque types.
1820
struct Context {
21+
//! Interrupt object that can be used to stop long-running kernel operations.
22+
util::SignalInterrupt interrupt;
23+
1924
//! Declare default constructor and destructor that are not inline, so code
2025
//! instantiating the kernel::Context struct doesn't need to #include class
2126
//! definitions for all the unique_ptr members.
2227
Context();
2328
~Context();
2429
};
30+
31+
//! Global pointer to kernel::Context for legacy code. New code should avoid
32+
//! using this, and require state it needs to be passed to it directly.
33+
//!
34+
//! Having this pointer is useful because it allows state be moved out of global
35+
//! variables into the kernel::Context struct before all global references to
36+
//! that state are removed. This allows the global references to be removed
37+
//! incrementally, instead of all at once.
38+
extern Context* g_context;
2539
} // namespace kernel
2640

2741
#endif // BITCOIN_KERNEL_CONTEXT_H

src/kernel/mempool_persist.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
#include <logging.h>
1010
#include <primitives/transaction.h>
1111
#include <serialize.h>
12-
#include <shutdown.h>
1312
#include <streams.h>
1413
#include <sync.h>
1514
#include <txmempool.h>
1615
#include <uint256.h>
1716
#include <util/fs.h>
1817
#include <util/fs_helpers.h>
18+
#include <util/signalinterrupt.h>
1919
#include <util/time.h>
2020
#include <validation.h>
2121

@@ -95,7 +95,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
9595
} else {
9696
++expired;
9797
}
98-
if (ShutdownRequested())
98+
if (active_chainstate.m_chainman.m_interrupt)
9999
return false;
100100
}
101101
std::map<uint256, CAmount> mapDeltas;

src/kernel/notifications_interface.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
#ifndef BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H
66
#define BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H
77

8+
#include <util/translation.h>
9+
810
#include <cstdint>
911
#include <string>
1012

1113
class CBlockIndex;
1214
enum class SynchronizationState;
13-
struct bilingual_str;
1415

1516
namespace kernel {
1617

@@ -27,6 +28,23 @@ class Notifications
2728
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {}
2829
virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {}
2930
virtual void warning(const bilingual_str& warning) {}
31+
32+
//! The flush error notification is sent to notify the user that an error
33+
//! occurred while flushing block data to disk. Kernel code may ignore flush
34+
//! errors that don't affect the immediate operation it is trying to
35+
//! perform. Applications can choose to handle the flush error notification
36+
//! by logging the error, or notifying the user, or triggering an early
37+
//! shutdown as a precaution against causing more errors.
38+
virtual void flushError(const std::string& debug_message) {}
39+
40+
//! The fatal error notification is sent to notify the user when an error
41+
//! occurs in kernel code that can't be recovered from. After this
42+
//! notification is sent, whatever function triggered the error should also
43+
//! return an error code or raise an exception. Applications can choose to
44+
//! handle the fatal error notification by logging the error, or notifying
45+
//! the user, or triggering an early shutdown as a precaution against
46+
//! causing more errors.
47+
virtual void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) {}
3048
};
3149
} // namespace kernel
3250

0 commit comments

Comments
 (0)