mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
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:
parent
213542b625
commit
6db04be102
10 changed files with 47 additions and 121 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 \
|
||||
|
@ -459,7 +458,6 @@ libbitcoin_node_a_SOURCES = \
|
|||
rpc/signmessage.cpp \
|
||||
rpc/txoutproof.cpp \
|
||||
script/sigcache.cpp \
|
||||
shutdown.cpp \
|
||||
signet.cpp \
|
||||
timedata.cpp \
|
||||
torcontrol.cpp \
|
||||
|
|
|
@ -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>
|
||||
|
@ -185,7 +184,6 @@ static bool AppInit(NodeContext& node)
|
|||
}
|
||||
|
||||
node.kernel = std::make_unique<kernel::Context>();
|
||||
node.shutdown = &node.kernel->interrupt; // TEMPORARY: will go away when kernel->interrupt member is removed
|
||||
if (!AppInitSanityChecks(*node.kernel))
|
||||
{
|
||||
// InitError will have been called with detailed error, which ends up on console
|
||||
|
@ -273,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);
|
||||
|
|
55
src/init.cpp
55
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,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ namespace node {
|
|||
struct NodeContext;
|
||||
} // namespace node
|
||||
|
||||
/** Initialize node context variables. */
|
||||
/** 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
@ -99,7 +99,6 @@ public:
|
|||
if (!AppInitParameterInteraction(args())) return false;
|
||||
|
||||
m_context->kernel = std::make_unique<kernel::Context>();
|
||||
m_context->shutdown = &m_context->kernel->interrupt; // TEMPORARY: will go away when kernel->interrupt member is removed
|
||||
if (!AppInitSanityChecks(*m_context->kernel)) return false;
|
||||
|
||||
if (!AppInitLockDataDirectory()) return false;
|
||||
|
@ -121,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;
|
||||
|
@ -750,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); }
|
||||
|
|
|
@ -1,43 +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()
|
||||
{
|
||||
if (!Assert(kernel::g_context)->interrupt()) {
|
||||
LogPrintf("Sending shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void AbortShutdown()
|
||||
{
|
||||
if (!Assert(kernel::g_context)->interrupt.reset()) {
|
||||
LogPrintf("Reading shutdown token failed\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShutdownRequested()
|
||||
{
|
||||
return bool{Assert(kernel::g_context)->interrupt};
|
||||
}
|
||||
|
||||
void WaitForShutdown()
|
||||
{
|
||||
if (!Assert(kernel::g_context)->interrupt.wait()) {
|
||||
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
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue