mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
Merge bitcoin/bitcoin#28051: Get rid of shutdown.cpp/shutdown.h, use SignalInterrupt directly
6db04be102
Get rid of shutdown.cpp/shutdown.h, use SignalInterrupt directly (Ryan Ofsky)213542b625
refactor: Add InitContext function to initialize NodeContext with global pointers (Ryan Ofsky)feeb7b816a
refactor: Remove calls to StartShutdown from KernelNotifications (Ryan Ofsky)6824eecaf1
refactor: Remove call to StartShutdown from stop RPC (Ryan Ofsky)1d92d89edb
util: Get rid of uncaught exceptions thrown by SignalInterrupt class (Ryan Ofsky)ba93966368
refactor: Remove call to ShutdownRequested from IndexWaitSynced (Ryan Ofsky)42e5829d97
refactor: Remove call to ShutdownRequested from HTTPRequest (Ryan Ofsky)73133c36aa
refactor: Add NodeContext::shutdown member (Ryan Ofsky)f4a8bd6e2f
refactor: Remove call to StartShutdown from qt (Ryan Ofsky)f0c73c1336
refactor: Remove call to ShutdownRequested from rpc/mining (Ryan Ofsky)263b23f008
refactor: Remove call to ShutdownRequested from chainstate init (Ryan Ofsky) Pull request description: This change drops `shutdown.h` and `shutdown.cpp` files, replacing them with a `NodeContext::shutdown` member which is used to trigger shutdowns directly. This gets rid of an unnecessary layer of indirection, and allows getting rid of the `kernel::g_context` global. Additionally, this PR tries to improve error handling of `SignalInterrupt` code by marking relevant methods `[[nodiscard]]` to avoid the possibility of uncaught exceptions mentioned https://github.com/bitcoin/bitcoin/pull/27861#discussion_r1255496707. Behavior is changing In a few cases which are noted in individual commit messages. Particularly: GUI code more consistently interrupts RPCs when it is shutting down, shutdown state no longer persists between unit tests, the stop RPC now returns an RPC error if requesting shutdown fails instead of aborting, and other failed shutdown calls now log errors instead of aborting. This PR is a net reduction in lines of code, but in some cases the explicit error handling and lack of global shutdown functions do make it more verbose. The verbosity can be seen as good thing if it discourages more code from directly triggering shutdowns, and instead encourages code to return errors or send notifications that could be translated into shutdowns. Probably a number of existing shutdown calls could just be replaced by better error handling. ACKs for top commit: achow101: ACK6db04be102
TheCharlatan: Re-ACK6db04be102
maflcko: ACK6db04be102
👗 stickies-v: re-ACK6db04be102
Tree-SHA512: 7a34cb69085f37e813c43bdaded1a0cbf6c53bd95fdde96f0cb45346127fc934604c43bccd3328231ca2f1faf712a7418d047ceabd22ef2dca3c32ebb659e634
This commit is contained in:
commit
4ad5c71adb
43 changed files with 164 additions and 192 deletions
|
@ -271,7 +271,6 @@ BITCOIN_CORE_H = \
|
|||
script/sign.h \
|
||||
script/signingprovider.h \
|
||||
script/solver.h \
|
||||
shutdown.h \
|
||||
signet.h \
|
||||
streams.h \
|
||||
support/allocators/pool.h \
|
||||
|
@ -458,7 +457,6 @@ libbitcoin_node_a_SOURCES = \
|
|||
rpc/signmessage.cpp \
|
||||
rpc/txoutproof.cpp \
|
||||
script/sigcache.cpp \
|
||||
shutdown.cpp \
|
||||
signet.cpp \
|
||||
timedata.cpp \
|
||||
torcontrol.cpp \
|
||||
|
|
|
@ -125,14 +125,14 @@ int main(int argc, char* argv[])
|
|||
.blocks_dir = abs_datadir / "blocks",
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts};
|
||||
util::SignalInterrupt interrupt;
|
||||
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
|
||||
|
||||
node::CacheSizes cache_sizes;
|
||||
cache_sizes.block_tree_db = 2 << 20;
|
||||
cache_sizes.coins_db = 2 << 22;
|
||||
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
|
||||
node::ChainstateLoadOptions options;
|
||||
options.check_interrupt = [] { return false; };
|
||||
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
|
||||
if (status != node::ChainstateLoadStatus::SUCCESS) {
|
||||
std::cerr << "Failed to load Chain state from your datadir." << std::endl;
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <node/context.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <noui.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/check.h>
|
||||
#include <util/exception.h>
|
||||
#include <util/strencodings.h>
|
||||
|
@ -272,9 +271,7 @@ MAIN_FUNCTION
|
|||
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
|
||||
|
||||
// Start application
|
||||
if (AppInit(node)) {
|
||||
WaitForShutdown();
|
||||
} else {
|
||||
if (!AppInit(node) || !Assert(node.shutdown)->wait()) {
|
||||
node.exit_status = EXIT_FAILURE;
|
||||
}
|
||||
Interrupt(node);
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#include <netbase.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <rpc/protocol.h> // For HTTP status codes
|
||||
#include <shutdown.h>
|
||||
#include <sync.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/threadnames.h>
|
||||
#include <util/translation.h>
|
||||
|
@ -284,7 +284,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
|
|||
}
|
||||
}
|
||||
}
|
||||
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
|
||||
auto hreq{std::make_unique<HTTPRequest>(req, *static_cast<const util::SignalInterrupt*>(arg))};
|
||||
|
||||
// Early address-based allow check
|
||||
if (!ClientAllowed(hreq->GetPeer())) {
|
||||
|
@ -425,7 +425,7 @@ static void libevent_log_cb(int severity, const char *msg)
|
|||
LogPrintLevel(BCLog::LIBEVENT, level, "%s\n", msg);
|
||||
}
|
||||
|
||||
bool InitHTTPServer()
|
||||
bool InitHTTPServer(const util::SignalInterrupt& interrupt)
|
||||
{
|
||||
if (!InitHTTPAllowList())
|
||||
return false;
|
||||
|
@ -454,7 +454,7 @@ bool InitHTTPServer()
|
|||
evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
|
||||
evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
|
||||
evhttp_set_max_body_size(http, MAX_SIZE);
|
||||
evhttp_set_gencb(http, http_request_cb, nullptr);
|
||||
evhttp_set_gencb(http, http_request_cb, (void*)&interrupt);
|
||||
|
||||
if (!HTTPBindAddresses(http)) {
|
||||
LogPrintf("Unable to bind any endpoint for RPC server\n");
|
||||
|
@ -579,7 +579,8 @@ void HTTPEvent::trigger(struct timeval* tv)
|
|||
else
|
||||
evtimer_add(ev, tv); // trigger after timeval passed
|
||||
}
|
||||
HTTPRequest::HTTPRequest(struct evhttp_request* _req, bool _replySent) : req(_req), replySent(_replySent)
|
||||
HTTPRequest::HTTPRequest(struct evhttp_request* _req, const util::SignalInterrupt& interrupt, bool _replySent)
|
||||
: req(_req), m_interrupt(interrupt), replySent(_replySent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -639,7 +640,7 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
|
|||
void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
|
||||
{
|
||||
assert(!replySent && req);
|
||||
if (ShutdownRequested()) {
|
||||
if (m_interrupt) {
|
||||
WriteHeader("Connection", "close");
|
||||
}
|
||||
// Send event to main http thread to send reply message
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
static const int DEFAULT_HTTP_THREADS=4;
|
||||
static const int DEFAULT_HTTP_WORKQUEUE=16;
|
||||
static const int DEFAULT_HTTP_SERVER_TIMEOUT=30;
|
||||
|
@ -21,7 +25,7 @@ class HTTPRequest;
|
|||
/** Initialize HTTP server.
|
||||
* Call this before RegisterHTTPHandler or EventBase().
|
||||
*/
|
||||
bool InitHTTPServer();
|
||||
bool InitHTTPServer(const util::SignalInterrupt& interrupt);
|
||||
/** Start HTTP server.
|
||||
* This is separate from InitHTTPServer to give users race-condition-free time
|
||||
* to register their handlers between InitHTTPServer and StartHTTPServer.
|
||||
|
@ -57,10 +61,11 @@ class HTTPRequest
|
|||
{
|
||||
private:
|
||||
struct evhttp_request* req;
|
||||
const util::SignalInterrupt& m_interrupt;
|
||||
bool replySent;
|
||||
|
||||
public:
|
||||
explicit HTTPRequest(struct evhttp_request* req, bool replySent = false);
|
||||
explicit HTTPRequest(struct evhttp_request* req, const util::SignalInterrupt& interrupt, bool replySent = false);
|
||||
~HTTPRequest();
|
||||
|
||||
enum RequestMethod {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <node/context.h>
|
||||
#include <node/database_args.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <shutdown.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/thread.h>
|
||||
#include <util/translation.h>
|
||||
|
@ -32,7 +31,7 @@ template <typename... Args>
|
|||
void BaseIndex::FatalErrorf(const char* fmt, const Args&... args)
|
||||
{
|
||||
auto message = tfm::format(fmt, args...);
|
||||
node::AbortNode(m_chain->context()->exit_status, message);
|
||||
node::AbortNode(m_chain->context()->shutdown, m_chain->context()->exit_status, message);
|
||||
}
|
||||
|
||||
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
|
||||
|
|
67
src/init.cpp
67
src/init.cpp
|
@ -66,7 +66,6 @@
|
|||
#include <rpc/util.h>
|
||||
#include <scheduler.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <shutdown.h>
|
||||
#include <sync.h>
|
||||
#include <timedata.h>
|
||||
#include <torcontrol.h>
|
||||
|
@ -192,6 +191,16 @@ static void RemovePidFile(const ArgsManager& args)
|
|||
}
|
||||
}
|
||||
|
||||
static std::optional<util::SignalInterrupt> g_shutdown;
|
||||
|
||||
void InitContext(NodeContext& node)
|
||||
{
|
||||
assert(!g_shutdown);
|
||||
g_shutdown.emplace();
|
||||
|
||||
node.args = &gArgs;
|
||||
node.shutdown = &*g_shutdown;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -204,11 +213,9 @@ static void RemovePidFile(const ArgsManager& args)
|
|||
// The network-processing threads are all part of a thread group
|
||||
// created by AppInit() or the Qt main() function.
|
||||
//
|
||||
// A clean exit happens when StartShutdown() or the SIGTERM
|
||||
// signal handler sets ShutdownRequested(), which makes main thread's
|
||||
// WaitForShutdown() interrupts the thread group.
|
||||
// And then, WaitForShutdown() makes all other on-going threads
|
||||
// in the thread group join the main thread.
|
||||
// A clean exit happens when the SignalInterrupt object is triggered, which
|
||||
// makes the main thread's SignalInterrupt::wait() call return, and join all
|
||||
// other ongoing threads in the thread group to the main thread.
|
||||
// Shutdown() is then called to clean up database connections, and stop other
|
||||
// threads that should only be stopped after the main network-processing
|
||||
// threads have exited.
|
||||
|
@ -218,6 +225,11 @@ static void RemovePidFile(const ArgsManager& args)
|
|||
// shutdown thing.
|
||||
//
|
||||
|
||||
bool ShutdownRequested(node::NodeContext& node)
|
||||
{
|
||||
return bool{*Assert(node.shutdown)};
|
||||
}
|
||||
|
||||
#if HAVE_SYSTEM
|
||||
static void ShutdownNotify(const ArgsManager& args)
|
||||
{
|
||||
|
@ -382,7 +394,9 @@ void Shutdown(NodeContext& node)
|
|||
#ifndef WIN32
|
||||
static void HandleSIGTERM(int)
|
||||
{
|
||||
StartShutdown();
|
||||
// Return value is intentionally ignored because there is not a better way
|
||||
// of handling this failure in a signal handler.
|
||||
(void)(*Assert(g_shutdown))();
|
||||
}
|
||||
|
||||
static void HandleSIGHUP(int)
|
||||
|
@ -392,7 +406,10 @@ static void HandleSIGHUP(int)
|
|||
#else
|
||||
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
StartShutdown();
|
||||
if (!(*Assert(g_shutdown))()) {
|
||||
LogPrintf("Error: failed to send shutdown signal on Ctrl-C\n");
|
||||
return false;
|
||||
}
|
||||
Sleep(INFINITE);
|
||||
return true;
|
||||
}
|
||||
|
@ -690,8 +707,9 @@ static bool AppInitServers(NodeContext& node)
|
|||
const ArgsManager& args = *Assert(node.args);
|
||||
RPCServer::OnStarted(&OnRPCStarted);
|
||||
RPCServer::OnStopped(&OnRPCStopped);
|
||||
if (!InitHTTPServer())
|
||||
if (!InitHTTPServer(*Assert(node.shutdown))) {
|
||||
return false;
|
||||
}
|
||||
StartRPC();
|
||||
node.rpc_interruption_point = RpcInterruptionPoint;
|
||||
if (!StartHTTPRPC(&node))
|
||||
|
@ -1141,11 +1159,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
}, std::chrono::minutes{1});
|
||||
|
||||
// Check disk space every 5 minutes to avoid db corruption.
|
||||
node.scheduler->scheduleEvery([&args]{
|
||||
node.scheduler->scheduleEvery([&args, &node]{
|
||||
constexpr uint64_t min_disk_space = 50 << 20; // 50 MB
|
||||
if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) {
|
||||
LogPrintf("Shutting down due to lack of disk space!\n");
|
||||
StartShutdown();
|
||||
if (!(*Assert(node.shutdown))()) {
|
||||
LogPrintf("Error: failed to send shutdown signal after disk space check\n");
|
||||
}
|
||||
}
|
||||
}, std::chrono::minutes{5});
|
||||
|
||||
|
@ -1432,7 +1452,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
|
||||
// ********************************************************* Step 7: load block chain
|
||||
|
||||
node.notifications = std::make_unique<KernelNotifications>(node.exit_status);
|
||||
node.notifications = std::make_unique<KernelNotifications>(*Assert(node.shutdown), node.exit_status);
|
||||
ReadNotificationArgs(args, *node.notifications);
|
||||
fReindex = args.GetBoolArg("-reindex", false);
|
||||
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
|
||||
|
@ -1483,10 +1503,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
}
|
||||
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
|
||||
|
||||
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
|
||||
for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
|
||||
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
|
||||
|
||||
node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
|
||||
ChainstateManager& chainman = *node.chainman;
|
||||
|
||||
// This is defined and set here instead of inline in validation.h to avoid a hard
|
||||
|
@ -1516,7 +1536,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
|
||||
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
|
||||
options.require_full_verification = args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
|
||||
options.check_interrupt = ShutdownRequested;
|
||||
options.coins_error_cb = [] {
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
_("Error reading from database, shutting down."),
|
||||
|
@ -1551,7 +1570,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
return InitError(error);
|
||||
}
|
||||
|
||||
if (!fLoaded && !ShutdownRequested()) {
|
||||
if (!fLoaded && !ShutdownRequested(node)) {
|
||||
// first suggest a reindex
|
||||
if (!options.reindex) {
|
||||
bool fRet = uiInterface.ThreadSafeQuestion(
|
||||
|
@ -1560,7 +1579,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
|
||||
if (fRet) {
|
||||
fReindex = true;
|
||||
AbortShutdown();
|
||||
if (!Assert(node.shutdown)->reset()) {
|
||||
LogPrintf("Internal error: failed to reset shutdown signal.\n");
|
||||
}
|
||||
} else {
|
||||
LogPrintf("Aborted block database rebuild. Exiting.\n");
|
||||
return false;
|
||||
|
@ -1574,7 +1595,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
// As LoadBlockIndex can take several minutes, it's possible the user
|
||||
// requested to kill the GUI during the last operation. If so, exit.
|
||||
// As the program has not fully started yet, Shutdown() is possibly overkill.
|
||||
if (ShutdownRequested()) {
|
||||
if (ShutdownRequested(node)) {
|
||||
LogPrintf("Shutdown requested. Exiting.\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -1695,7 +1716,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
ImportBlocks(chainman, vImportFiles);
|
||||
if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
|
||||
LogPrintf("Stopping after block import\n");
|
||||
StartShutdown();
|
||||
if (!(*Assert(node.shutdown))()) {
|
||||
LogPrintf("Error: failed to send shutdown signal after finishing block import\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1715,16 +1738,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
// Wait for genesis block to be processed
|
||||
{
|
||||
WAIT_LOCK(g_genesis_wait_mutex, lock);
|
||||
// We previously could hang here if StartShutdown() is called prior to
|
||||
// We previously could hang here if shutdown was requested prior to
|
||||
// ImportBlocks getting started, so instead we just wait on a timer to
|
||||
// check ShutdownRequested() regularly.
|
||||
while (!fHaveGenesis && !ShutdownRequested()) {
|
||||
while (!fHaveGenesis && !ShutdownRequested(node)) {
|
||||
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
|
||||
}
|
||||
block_notify_genesis_wait_connection.disconnect();
|
||||
}
|
||||
|
||||
if (ShutdownRequested()) {
|
||||
if (ShutdownRequested(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,11 @@ namespace node {
|
|||
struct NodeContext;
|
||||
} // namespace node
|
||||
|
||||
/** Initialize node context shutdown and args variables. */
|
||||
void InitContext(node::NodeContext& node);
|
||||
/** Return whether node shutdown was requested. */
|
||||
bool ShutdownRequested(node::NodeContext& node);
|
||||
|
||||
/** Interrupt threads */
|
||||
void Interrupt(node::NodeContext& node);
|
||||
void Shutdown(node::NodeContext& node);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <common/args.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/echo.h>
|
||||
#include <interfaces/init.h>
|
||||
|
@ -23,7 +23,7 @@ class BitcoinGuiInit : public interfaces::Init
|
|||
public:
|
||||
BitcoinGuiInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
|
||||
{
|
||||
m_node.args = &gArgs;
|
||||
InitContext(m_node);
|
||||
m_node.init = this;
|
||||
}
|
||||
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <common/args.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/echo.h>
|
||||
#include <interfaces/init.h>
|
||||
|
@ -25,7 +25,7 @@ public:
|
|||
: m_node(node),
|
||||
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
|
||||
{
|
||||
m_node.args = &gArgs;
|
||||
InitContext(m_node);
|
||||
m_node.init = this;
|
||||
}
|
||||
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <common/args.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/echo.h>
|
||||
#include <interfaces/init.h>
|
||||
|
@ -20,7 +20,7 @@ class BitcoinQtInit : public interfaces::Init
|
|||
public:
|
||||
BitcoinQtInit()
|
||||
{
|
||||
m_node.args = &gArgs;
|
||||
InitContext(m_node);
|
||||
m_node.init = this;
|
||||
}
|
||||
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <common/args.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/echo.h>
|
||||
#include <interfaces/init.h>
|
||||
|
@ -22,7 +22,7 @@ class BitcoindInit : public interfaces::Init
|
|||
public:
|
||||
BitcoindInit(NodeContext& node) : m_node(node)
|
||||
{
|
||||
m_node.args = &gArgs;
|
||||
InitContext(m_node);
|
||||
m_node.init = this;
|
||||
}
|
||||
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
|
||||
|
|
|
@ -14,12 +14,8 @@
|
|||
|
||||
|
||||
namespace kernel {
|
||||
Context* g_context;
|
||||
|
||||
Context::Context()
|
||||
{
|
||||
assert(!g_context);
|
||||
g_context = this;
|
||||
std::string sha256_algo = SHA256AutoDetect();
|
||||
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
|
||||
RandomInit();
|
||||
|
@ -29,8 +25,6 @@ Context::Context()
|
|||
Context::~Context()
|
||||
{
|
||||
ECC_Stop();
|
||||
assert(g_context);
|
||||
g_context = nullptr;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -18,24 +18,9 @@ namespace kernel {
|
|||
//! State stored directly in this struct should be simple. More complex state
|
||||
//! should be stored to std::unique_ptr members pointing to opaque types.
|
||||
struct Context {
|
||||
//! Interrupt object that can be used to stop long-running kernel operations.
|
||||
util::SignalInterrupt interrupt;
|
||||
|
||||
//! Declare default constructor and destructor that are not inline, so code
|
||||
//! instantiating the kernel::Context struct doesn't need to #include class
|
||||
//! definitions for all the unique_ptr members.
|
||||
Context();
|
||||
~Context();
|
||||
};
|
||||
|
||||
//! Global pointer to kernel::Context for legacy code. New code should avoid
|
||||
//! using this, and require state it needs to be passed to it directly.
|
||||
//!
|
||||
//! Having this pointer is useful because it allows state be moved out of global
|
||||
//! variables into the kernel::Context struct before all global references to
|
||||
//! that state are removed. This allows the global references to be removed
|
||||
//! incrementally, instead of all at once.
|
||||
extern Context* g_context;
|
||||
} // namespace kernel
|
||||
|
||||
#endif // BITCOIN_KERNEL_CONTEXT_H
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <logging.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/translation.h>
|
||||
#include <warnings.h>
|
||||
|
||||
|
@ -16,12 +16,14 @@
|
|||
|
||||
namespace node {
|
||||
|
||||
void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message, bool shutdown)
|
||||
void AbortNode(util::SignalInterrupt* shutdown, std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message)
|
||||
{
|
||||
SetMiscWarning(Untranslated(debug_message));
|
||||
LogPrintf("*** %s\n", debug_message);
|
||||
InitError(user_message.empty() ? _("A fatal internal error occurred, see debug.log for details") : user_message);
|
||||
exit_status.store(EXIT_FAILURE);
|
||||
if (shutdown) StartShutdown();
|
||||
if (shutdown && !(*shutdown)()) {
|
||||
LogPrintf("Error: failed to send shutdown signal\n");
|
||||
};
|
||||
}
|
||||
} // namespace node
|
||||
|
|
|
@ -10,8 +10,12 @@
|
|||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
namespace node {
|
||||
void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message = {}, bool shutdown = true);
|
||||
void AbortNode(util::SignalInterrupt* shutdown, std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message = {});
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_ABORT_H
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <txdb.h>
|
||||
#include <uint256.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
|
@ -55,14 +56,14 @@ static ChainstateLoadResult CompleteChainstateInitialization(
|
|||
}
|
||||
}
|
||||
|
||||
if (options.check_interrupt && options.check_interrupt()) return {ChainstateLoadStatus::INTERRUPTED, {}};
|
||||
if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};
|
||||
|
||||
// LoadBlockIndex will load m_have_pruned if we've ever removed a
|
||||
// block file from disk.
|
||||
// Note that it also sets fReindex global based on the disk flag!
|
||||
// From here on, fReindex and options.reindex values may be different!
|
||||
if (!chainman.LoadBlockIndex()) {
|
||||
if (options.check_interrupt && options.check_interrupt()) return {ChainstateLoadStatus::INTERRUPTED, {}};
|
||||
if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};
|
||||
return {ChainstateLoadStatus::FAILURE, _("Error loading block database")};
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ struct ChainstateLoadOptions {
|
|||
bool require_full_verification{true};
|
||||
int64_t check_blocks{DEFAULT_CHECKBLOCKS};
|
||||
int64_t check_level{DEFAULT_CHECKLEVEL};
|
||||
std::function<bool()> check_interrupt;
|
||||
std::function<void()> coins_error_cb;
|
||||
};
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@ struct NodeContext {
|
|||
std::unique_ptr<kernel::Context> kernel;
|
||||
//! Init interface for initializing current process and connecting to other processes.
|
||||
interfaces::Init* init{nullptr};
|
||||
//! Interrupt object used to track whether node shutdown was requested.
|
||||
util::SignalInterrupt* shutdown{nullptr};
|
||||
std::unique_ptr<AddrMan> addrman;
|
||||
std::unique_ptr<CConnman> connman;
|
||||
std::unique_ptr<CTxMemPool> mempool;
|
||||
|
|
|
@ -39,13 +39,13 @@
|
|||
#include <primitives/transaction.h>
|
||||
#include <rpc/protocol.h>
|
||||
#include <rpc/server.h>
|
||||
#include <shutdown.h>
|
||||
#include <support/allocators/secure.h>
|
||||
#include <sync.h>
|
||||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <univalue.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
@ -120,14 +120,16 @@ public:
|
|||
}
|
||||
void startShutdown() override
|
||||
{
|
||||
StartShutdown();
|
||||
if (!(*Assert(Assert(m_context)->shutdown))()) {
|
||||
LogPrintf("Error: failed to send shutdown signal\n");
|
||||
}
|
||||
// Stop RPC for clean shutdown if any of waitfor* commands is executed.
|
||||
if (args().GetBoolArg("-server", false)) {
|
||||
InterruptRPC();
|
||||
StopRPC();
|
||||
}
|
||||
}
|
||||
bool shutdownRequested() override { return ShutdownRequested(); }
|
||||
bool shutdownRequested() override { return ShutdownRequested(*Assert(m_context)); };
|
||||
bool isSettingIgnored(const std::string& name) override
|
||||
{
|
||||
bool ignored = false;
|
||||
|
@ -749,7 +751,7 @@ public:
|
|||
{
|
||||
return chainman().IsInitialBlockDownload();
|
||||
}
|
||||
bool shutdownRequested() override { return ShutdownRequested(); }
|
||||
bool shutdownRequested() override { return ShutdownRequested(m_node); }
|
||||
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
|
||||
void initWarning(const bilingual_str& message) override { InitWarning(message); }
|
||||
void initError(const bilingual_str& message) override { InitError(message); }
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <logging.h>
|
||||
#include <node/abort.h>
|
||||
#include <node/interface_ui.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/check.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
|
@ -62,7 +61,9 @@ kernel::InterruptResult KernelNotifications::blockTip(SynchronizationState state
|
|||
{
|
||||
uiInterface.NotifyBlockTip(state, &index);
|
||||
if (m_stop_at_height && index.nHeight >= m_stop_at_height) {
|
||||
StartShutdown();
|
||||
if (!m_shutdown()) {
|
||||
LogPrintf("Error: failed to send shutdown signal after reaching stop height\n");
|
||||
}
|
||||
return kernel::Interrupted{};
|
||||
}
|
||||
return {};
|
||||
|
@ -85,12 +86,13 @@ void KernelNotifications::warning(const bilingual_str& warning)
|
|||
|
||||
void KernelNotifications::flushError(const std::string& debug_message)
|
||||
{
|
||||
AbortNode(m_exit_status, debug_message);
|
||||
AbortNode(&m_shutdown, m_exit_status, debug_message);
|
||||
}
|
||||
|
||||
void KernelNotifications::fatalError(const std::string& debug_message, const bilingual_str& user_message)
|
||||
{
|
||||
node::AbortNode(m_exit_status, debug_message, user_message, m_shutdown_on_fatal_error);
|
||||
node::AbortNode(m_shutdown_on_fatal_error ? &m_shutdown : nullptr,
|
||||
m_exit_status, debug_message, user_message);
|
||||
}
|
||||
|
||||
void ReadNotificationArgs(const ArgsManager& args, KernelNotifications& notifications)
|
||||
|
|
|
@ -16,6 +16,10 @@ class CBlockIndex;
|
|||
enum class SynchronizationState;
|
||||
struct bilingual_str;
|
||||
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
namespace node {
|
||||
|
||||
static constexpr int DEFAULT_STOPATHEIGHT{0};
|
||||
|
@ -23,7 +27,7 @@ static constexpr int DEFAULT_STOPATHEIGHT{0};
|
|||
class KernelNotifications : public kernel::Notifications
|
||||
{
|
||||
public:
|
||||
KernelNotifications(std::atomic<int>& exit_status) : m_exit_status{exit_status} {}
|
||||
KernelNotifications(util::SignalInterrupt& shutdown, std::atomic<int>& exit_status) : m_shutdown(shutdown), m_exit_status{exit_status} {}
|
||||
|
||||
[[nodiscard]] kernel::InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) override;
|
||||
|
||||
|
@ -42,6 +46,7 @@ public:
|
|||
//! Useful for tests, can be set to false to avoid shutdown on fatal error.
|
||||
bool m_shutdown_on_fatal_error{true};
|
||||
private:
|
||||
util::SignalInterrupt& m_shutdown;
|
||||
std::atomic<int>& m_exit_status;
|
||||
};
|
||||
|
||||
|
|
|
@ -661,7 +661,10 @@ int GuiMain(int argc, char* argv[])
|
|||
app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
|
||||
#if defined(Q_OS_WIN)
|
||||
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
|
||||
qApp->installNativeEventFilter(new WinShutdownMonitor());
|
||||
// Note: it is safe to call app.node() in the lambda below despite the fact
|
||||
// that app.createNode() hasn't been called yet, because native events will
|
||||
// not be processed until the Qt event loop is executed.
|
||||
qApp->installNativeEventFilter(new WinShutdownMonitor([&app] { app.node().startShutdown(); }));
|
||||
#endif
|
||||
// Install qDebug() message handler to route to debug.log
|
||||
qInstallMessageHandler(DebugMessageHandler);
|
||||
|
|
|
@ -315,7 +315,7 @@ public Q_SLOTS:
|
|||
/** Simply calls showNormalIfMinimized(true) */
|
||||
void toggleHidden();
|
||||
|
||||
/** called by a timer to check if ShutdownRequested() has been set **/
|
||||
/** called by a timer to check if shutdown has been requested */
|
||||
void detectShutdown();
|
||||
|
||||
/** Show progress dialog e.g. for verifychain */
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <qt/bitcoingui.h>
|
||||
#include <qt/networkstyle.h>
|
||||
#include <qt/rpcconsole.h>
|
||||
#include <shutdown.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <qt/winshutdownmonitor.h>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <shutdown.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
@ -25,7 +24,7 @@ bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pM
|
|||
{
|
||||
// Initiate a client shutdown after receiving a WM_QUERYENDSESSION and block
|
||||
// Windows session end until we have finished client shutdown.
|
||||
StartShutdown();
|
||||
m_shutdown_fn();
|
||||
*pnResult = FALSE;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#ifdef WIN32
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
#include <windef.h> // for HWND
|
||||
|
||||
|
@ -16,11 +17,16 @@
|
|||
class WinShutdownMonitor : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
WinShutdownMonitor(std::function<void()> shutdown_fn) : m_shutdown_fn{std::move(shutdown_fn)} {}
|
||||
|
||||
/** Implements QAbstractNativeEventFilter interface for processing Windows messages */
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult) override;
|
||||
|
||||
/** Register the reason for blocking shutdown on Windows to allow clean client exit */
|
||||
static void registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId);
|
||||
|
||||
private:
|
||||
std::function<void()> m_shutdown_fn;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <script/descriptor.h>
|
||||
#include <script/script.h>
|
||||
#include <script/signingprovider.h>
|
||||
#include <shutdown.h>
|
||||
#include <timedata.h>
|
||||
#include <txmempool.h>
|
||||
#include <univalue.h>
|
||||
|
@ -129,11 +128,11 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
|
|||
block_out.reset();
|
||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||
|
||||
while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainman.GetConsensus()) && !ShutdownRequested()) {
|
||||
while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainman.GetConsensus()) && !chainman.m_interrupt) {
|
||||
++block.nNonce;
|
||||
--max_tries;
|
||||
}
|
||||
if (max_tries == 0 || ShutdownRequested()) {
|
||||
if (max_tries == 0 || chainman.m_interrupt) {
|
||||
return false;
|
||||
}
|
||||
if (block.nNonce == std::numeric_limits<uint32_t>::max()) {
|
||||
|
@ -154,7 +153,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
|
|||
static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
|
||||
{
|
||||
UniValue blockHashes(UniValue::VARR);
|
||||
while (nGenerate > 0 && !ShutdownRequested()) {
|
||||
while (nGenerate > 0 && !chainman.m_interrupt) {
|
||||
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler{chainman.ActiveChainstate(), &mempool}.CreateNewBlock(coinbase_script));
|
||||
if (!pblocktemplate.get())
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
#include <common/args.h>
|
||||
#include <common/system.h>
|
||||
#include <logging.h>
|
||||
#include <node/context.h>
|
||||
#include <rpc/server_util.h>
|
||||
#include <rpc/util.h>
|
||||
#include <shutdown.h>
|
||||
#include <sync.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
#include <util/time.h>
|
||||
|
@ -179,7 +181,7 @@ static RPCHelpMan stop()
|
|||
{
|
||||
// Event loop will exit after current HTTP requests have been handled, so
|
||||
// this reply will get back to the client.
|
||||
StartShutdown();
|
||||
CHECK_NONFATAL((*CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown))());
|
||||
if (jsonRequest.params[0].isNum()) {
|
||||
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <shutdown.h>
|
||||
|
||||
#include <kernel/context.h>
|
||||
#include <logging.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <system_error>
|
||||
|
||||
void StartShutdown()
|
||||
{
|
||||
try {
|
||||
Assert(kernel::g_context)->interrupt();
|
||||
} catch (const std::system_error&) {
|
||||
LogPrintf("Sending shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void AbortShutdown()
|
||||
{
|
||||
Assert(kernel::g_context)->interrupt.reset();
|
||||
}
|
||||
|
||||
bool ShutdownRequested()
|
||||
{
|
||||
return bool{Assert(kernel::g_context)->interrupt};
|
||||
}
|
||||
|
||||
void WaitForShutdown()
|
||||
{
|
||||
try {
|
||||
Assert(kernel::g_context)->interrupt.wait();
|
||||
} catch (const std::system_error&) {
|
||||
LogPrintf("Reading shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_SHUTDOWN_H
|
||||
#define BITCOIN_SHUTDOWN_H
|
||||
|
||||
/** Request shutdown of the application. */
|
||||
void StartShutdown();
|
||||
|
||||
/** Clear shutdown flag. Only use this during init (before calling WaitForShutdown in any
|
||||
* thread), or in the unit tests. Calling it in other circumstances will cause a race condition.
|
||||
*/
|
||||
void AbortShutdown();
|
||||
|
||||
/** Returns true if a shutdown is requested, false otherwise. */
|
||||
bool ShutdownRequested();
|
||||
|
||||
/** Wait for StartShutdown to be called in any thread. This can only be used
|
||||
* from a single thread.
|
||||
*/
|
||||
void WaitForShutdown();
|
||||
|
||||
#endif // BITCOIN_SHUTDOWN_H
|
|
@ -143,7 +143,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
|
|||
BOOST_REQUIRE(filter_index.StartBackgroundSync());
|
||||
|
||||
// Allow filter index to catch up with the block index.
|
||||
IndexWaitSynced(filter_index);
|
||||
IndexWaitSynced(filter_index, *Assert(m_node.shutdown));
|
||||
|
||||
// Check that filter index has all blocks that were in the chain before it started.
|
||||
{
|
||||
|
|
|
@ -27,13 +27,13 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup)
|
|||
BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
|
||||
{
|
||||
const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)};
|
||||
KernelNotifications notifications{m_node.exit_status};
|
||||
KernelNotifications notifications{*Assert(m_node.shutdown), m_node.exit_status};
|
||||
const BlockManager::Options blockman_opts{
|
||||
.chainparams = *params,
|
||||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = notifications,
|
||||
};
|
||||
BlockManager blockman{m_node.kernel->interrupt, blockman_opts};
|
||||
BlockManager blockman{*Assert(m_node.shutdown), blockman_opts};
|
||||
// simulate adding a genesis block normally
|
||||
BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
|
||||
// simulate what happens during reindex
|
||||
|
@ -134,13 +134,13 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
|
|||
|
||||
BOOST_AUTO_TEST_CASE(blockmanager_flush_block_file)
|
||||
{
|
||||
KernelNotifications notifications{m_node.exit_status};
|
||||
KernelNotifications notifications{*Assert(m_node.shutdown), m_node.exit_status};
|
||||
node::BlockManager::Options blockman_opts{
|
||||
.chainparams = Params(),
|
||||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = notifications,
|
||||
};
|
||||
BlockManager blockman{m_node.kernel->interrupt, blockman_opts};
|
||||
BlockManager blockman{*Assert(m_node.shutdown), blockman_opts};
|
||||
|
||||
// Test blocks with no transactions, not even a coinbase
|
||||
CBlock block1;
|
||||
|
|
|
@ -35,7 +35,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
|
|||
|
||||
BOOST_REQUIRE(coin_stats_index.StartBackgroundSync());
|
||||
|
||||
IndexWaitSynced(coin_stats_index);
|
||||
IndexWaitSynced(coin_stats_index, *Assert(m_node.shutdown));
|
||||
|
||||
// Check that CoinStatsIndex works for genesis block.
|
||||
const CBlockIndex* genesis_block_index;
|
||||
|
@ -86,7 +86,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
|
|||
CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20};
|
||||
BOOST_REQUIRE(index.Init());
|
||||
BOOST_REQUIRE(index.StartBackgroundSync());
|
||||
IndexWaitSynced(index);
|
||||
IndexWaitSynced(index, *Assert(m_node.shutdown));
|
||||
std::shared_ptr<const CBlock> new_block;
|
||||
CBlockIndex* new_block_index = nullptr;
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <event2/buffer.h>
|
||||
|
@ -47,7 +48,8 @@ FUZZ_TARGET(http_request)
|
|||
return;
|
||||
}
|
||||
|
||||
HTTPRequest http_request{evreq, true};
|
||||
util::SignalInterrupt interrupt;
|
||||
HTTPRequest http_request{evreq, interrupt, true};
|
||||
const HTTPRequest::RequestMethod request_method = http_request.GetRequestMethod();
|
||||
(void)RequestMethodString(request_method);
|
||||
(void)http_request.GetURI();
|
||||
|
|
|
@ -33,7 +33,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
|
|||
BOOST_REQUIRE(txindex.StartBackgroundSync());
|
||||
|
||||
// Allow tx index to catch up with the block index.
|
||||
IndexWaitSynced(txindex);
|
||||
IndexWaitSynced(txindex, *Assert(m_node.shutdown));
|
||||
|
||||
// Check that txindex excludes genesis block transactions.
|
||||
const CBlock& genesis_block = Params().GenesisBlock();
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
#include <test/util/index.h>
|
||||
|
||||
#include <index/base.h>
|
||||
#include <shutdown.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/time.h>
|
||||
|
||||
void IndexWaitSynced(const BaseIndex& index)
|
||||
void IndexWaitSynced(const BaseIndex& index, const util::SignalInterrupt& interrupt)
|
||||
{
|
||||
while (!index.BlockUntilSyncedToCurrentChain()) {
|
||||
// Assert shutdown was not requested to abort the test, instead of looping forever, in case
|
||||
// there was an unexpected error in the index that caused it to stop syncing and request a shutdown.
|
||||
Assert(!ShutdownRequested());
|
||||
Assert(!interrupt);
|
||||
|
||||
UninterruptibleSleep(100ms);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
#define BITCOIN_TEST_UTIL_INDEX_H
|
||||
|
||||
class BaseIndex;
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
/** Block until the index is synced to the current chain */
|
||||
void IndexWaitSynced(const BaseIndex& index);
|
||||
void IndexWaitSynced(const BaseIndex& index, const util::SignalInterrupt& interrupt);
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_INDEX_H
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include <rpc/server.h>
|
||||
#include <scheduler.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <shutdown.h>
|
||||
#include <streams.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/random.h>
|
||||
|
@ -102,6 +101,7 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
|
|||
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
|
||||
m_args{}
|
||||
{
|
||||
m_node.shutdown = &m_interrupt;
|
||||
m_node.args = &gArgs;
|
||||
std::vector<const char*> arguments = Cat(
|
||||
{
|
||||
|
@ -179,7 +179,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
|||
|
||||
m_cache_sizes = CalculateCacheSizes(m_args);
|
||||
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status);
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(*Assert(m_node.shutdown), m_node.exit_status);
|
||||
|
||||
const ChainstateManager::Options chainman_opts{
|
||||
.chainparams = chainparams,
|
||||
|
@ -194,7 +194,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
|||
.blocks_dir = m_args.GetBlocksDirPath(),
|
||||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
|
||||
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
|
||||
.path = m_args.GetDataDirNet() / "blocks" / "index",
|
||||
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
|
||||
|
|
|
@ -47,6 +47,7 @@ static constexpr CAmount CENT{1000000};
|
|||
* This just configures logging, data dir and chain parameters.
|
||||
*/
|
||||
struct BasicTestingSetup {
|
||||
util::SignalInterrupt m_interrupt;
|
||||
node::NodeContext m_node; // keep as first member to be destructed last
|
||||
|
||||
explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {});
|
||||
|
|
|
@ -379,7 +379,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
|||
LOCK(::cs_main);
|
||||
chainman.ResetChainstates();
|
||||
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status);
|
||||
m_node.notifications = std::make_unique<KernelNotifications>(*Assert(m_node.shutdown), m_node.exit_status);
|
||||
const ChainstateManager::Options chainman_opts{
|
||||
.chainparams = ::Params(),
|
||||
.datadir = chainman.m_options.datadir,
|
||||
|
@ -394,7 +394,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
|||
// For robustness, ensure the old manager is destroyed before creating a
|
||||
// new one.
|
||||
m_node.chainman.reset();
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
|
||||
}
|
||||
return *Assert(m_node.chainman);
|
||||
}
|
||||
|
|
|
@ -30,15 +30,16 @@ SignalInterrupt::operator bool() const
|
|||
return m_flag;
|
||||
}
|
||||
|
||||
void SignalInterrupt::reset()
|
||||
bool SignalInterrupt::reset()
|
||||
{
|
||||
// Cancel existing interrupt by waiting for it, this will reset condition flags and remove
|
||||
// the token from the pipe.
|
||||
if (*this) wait();
|
||||
if (*this && !wait()) return false;
|
||||
m_flag = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SignalInterrupt::operator()()
|
||||
bool SignalInterrupt::operator()()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
|
@ -52,13 +53,14 @@ void SignalInterrupt::operator()()
|
|||
// Write an arbitrary byte to the write end of the pipe.
|
||||
int res = m_pipe_w.TokenWrite('x');
|
||||
if (res != 0) {
|
||||
throw std::ios_base::failure("Could not write interrupt token");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void SignalInterrupt::wait()
|
||||
bool SignalInterrupt::wait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
|
@ -66,9 +68,10 @@ void SignalInterrupt::wait()
|
|||
#else
|
||||
int res = m_pipe_r.TokenRead();
|
||||
if (res != 'x') {
|
||||
throw std::ios_base::failure("Did not read expected interrupt token");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
|
|
@ -30,9 +30,9 @@ class SignalInterrupt
|
|||
public:
|
||||
SignalInterrupt();
|
||||
explicit operator bool() const;
|
||||
void operator()();
|
||||
void reset();
|
||||
void wait();
|
||||
[[nodiscard]] bool operator()();
|
||||
[[nodiscard]] bool reset();
|
||||
[[nodiscard]] bool wait();
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_flag;
|
||||
|
|
Loading…
Add table
Reference in a new issue