aboutsummaryrefslogtreecommitdiff
path: root/src/init.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/init.cpp')
-rw-r--r--src/init.cpp80
1 files changed, 52 insertions, 28 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 3d6d04a1ec..39ee9fe60d 100644
--- a/src/init.cpp
+++ b/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)
{
@@ -301,7 +313,7 @@ void Shutdown(NodeContext& node)
DumpMempool(*node.mempool, MempoolPath(*node.args));
}
- // Drop transactions we were still watching, record fee estimations and Unregister
+ // Drop transactions we were still watching, record fee estimations and unregister
// fee estimator from validation interface.
if (node.fee_estimator) {
node.fee_estimator->Flush();
@@ -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;
}
@@ -689,8 +706,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))
@@ -1036,13 +1054,14 @@ static bool LockDataDirectory(bool probeOnly)
{
// Make sure only a single Bitcoin process is using the data directory.
const fs::path& datadir = gArgs.GetDataDirNet();
- if (!DirIsWritable(datadir)) {
+ switch (util::LockDirectory(datadir, ".lock", probeOnly)) {
+ case util::LockResult::ErrorWrite:
return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir)));
- }
- if (!LockDirectory(datadir, ".lock", probeOnly)) {
+ case util::LockResult::ErrorLock:
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), fs::PathToString(datadir), PACKAGE_NAME));
- }
- return true;
+ case util::LockResult::Success: return true;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
bool AppInitSanityChecks(const kernel::Context& kernel)
@@ -1129,11 +1148,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});
@@ -1420,7 +1441,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);
@@ -1471,10 +1492,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
@@ -1504,7 +1525,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."),
@@ -1539,7 +1559,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(
@@ -1548,7 +1568,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;
@@ -1562,7 +1584,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;
}
@@ -1683,7 +1705,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;
}
@@ -1703,16 +1727,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;
}