Get rid of shutdown.cpp/shutdown.h, use SignalInterrupt directly

This change is mostly a refectoring that removes some code and gets rid of an
unnecessary layer of indirection after #27861

But it is not a pure refactoring since StartShutdown, AbortShutdown, and
WaitForShutdown functions used to abort on failure, and the replacement code
logs or returns errors instead.
This commit is contained in:
Ryan Ofsky
2023-07-07 17:32:54 -04:00
parent 213542b625
commit 6db04be102
10 changed files with 47 additions and 121 deletions

View File

@@ -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,9 +191,15 @@ 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;
}
//////////////////////////////////////////////////////////////////////////////
@@ -208,11 +213,9 @@ void InitContext(NodeContext& node)
// 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.
@@ -222,6 +225,11 @@ void InitContext(NodeContext& node)
// shutdown thing.
//
bool ShutdownRequested(node::NodeContext& node)
{
return bool{*Assert(node.shutdown)};
}
#if HAVE_SYSTEM
static void ShutdownNotify(const ArgsManager& args)
{
@@ -386,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)
@@ -396,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;
}
@@ -1145,11 +1158,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});
@@ -1487,7 +1502,7 @@ 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>(*Assert(node.shutdown), chainman_opts, blockman_opts);
@@ -1554,7 +1569,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(
@@ -1563,7 +1578,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;
@@ -1577,7 +1594,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;
}
@@ -1698,7 +1715,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;
}
@@ -1718,16 +1737,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;
}