Skip to content

Commit 4ad5c71

Browse files
committed
Merge bitcoin/bitcoin#28051: Get rid of shutdown.cpp/shutdown.h, use SignalInterrupt directly
6db04be Get rid of shutdown.cpp/shutdown.h, use SignalInterrupt directly (Ryan Ofsky) 213542b refactor: Add InitContext function to initialize NodeContext with global pointers (Ryan Ofsky) feeb7b8 refactor: Remove calls to StartShutdown from KernelNotifications (Ryan Ofsky) 6824eec refactor: Remove call to StartShutdown from stop RPC (Ryan Ofsky) 1d92d89 util: Get rid of uncaught exceptions thrown by SignalInterrupt class (Ryan Ofsky) ba93966 refactor: Remove call to ShutdownRequested from IndexWaitSynced (Ryan Ofsky) 42e5829 refactor: Remove call to ShutdownRequested from HTTPRequest (Ryan Ofsky) 73133c3 refactor: Add NodeContext::shutdown member (Ryan Ofsky) f4a8bd6 refactor: Remove call to StartShutdown from qt (Ryan Ofsky) f0c73c1 refactor: Remove call to ShutdownRequested from rpc/mining (Ryan Ofsky) 263b23f 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 bitcoin/bitcoin#27861 (comment). 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: ACK 6db04be TheCharlatan: Re-ACK 6db04be maflcko: ACK 6db04be 👗 stickies-v: re-ACK 6db04be Tree-SHA512: 7a34cb69085f37e813c43bdaded1a0cbf6c53bd95fdde96f0cb45346127fc934604c43bccd3328231ca2f1faf712a7418d047ceabd22ef2dca3c32ebb659e634
2 parents 9860471 + 6db04be commit 4ad5c71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+164
-192
lines changed

src/Makefile.am

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,6 @@ BITCOIN_CORE_H = \
271271
script/sign.h \
272272
script/signingprovider.h \
273273
script/solver.h \
274-
shutdown.h \
275274
signet.h \
276275
streams.h \
277276
support/allocators/pool.h \
@@ -458,7 +457,6 @@ libbitcoin_node_a_SOURCES = \
458457
rpc/signmessage.cpp \
459458
rpc/txoutproof.cpp \
460459
script/sigcache.cpp \
461-
shutdown.cpp \
462460
signet.cpp \
463461
timedata.cpp \
464462
torcontrol.cpp \

src/bitcoin-chainstate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ int main(int argc, char* argv[])
125125
.blocks_dir = abs_datadir / "blocks",
126126
.notifications = chainman_opts.notifications,
127127
};
128-
ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts};
128+
util::SignalInterrupt interrupt;
129+
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
129130

130131
node::CacheSizes cache_sizes;
131132
cache_sizes.block_tree_db = 2 << 20;
132133
cache_sizes.coins_db = 2 << 22;
133134
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
134135
node::ChainstateLoadOptions options;
135-
options.check_interrupt = [] { return false; };
136136
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
137137
if (status != node::ChainstateLoadStatus::SUCCESS) {
138138
std::cerr << "Failed to load Chain state from your datadir." << std::endl;

src/bitcoind.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include <node/context.h>
2121
#include <node/interface_ui.h>
2222
#include <noui.h>
23-
#include <shutdown.h>
2423
#include <util/check.h>
2524
#include <util/exception.h>
2625
#include <util/strencodings.h>
@@ -272,9 +271,7 @@ MAIN_FUNCTION
272271
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
273272

274273
// Start application
275-
if (AppInit(node)) {
276-
WaitForShutdown();
277-
} else {
274+
if (!AppInit(node) || !Assert(node.shutdown)->wait()) {
278275
node.exit_status = EXIT_FAILURE;
279276
}
280277
Interrupt(node);

src/httpserver.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
#include <netbase.h>
1616
#include <node/interface_ui.h>
1717
#include <rpc/protocol.h> // For HTTP status codes
18-
#include <shutdown.h>
1918
#include <sync.h>
2019
#include <util/check.h>
20+
#include <util/signalinterrupt.h>
2121
#include <util/strencodings.h>
2222
#include <util/threadnames.h>
2323
#include <util/translation.h>
@@ -284,7 +284,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
284284
}
285285
}
286286
}
287-
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
287+
auto hreq{std::make_unique<HTTPRequest>(req, *static_cast<const util::SignalInterrupt*>(arg))};
288288

289289
// Early address-based allow check
290290
if (!ClientAllowed(hreq->GetPeer())) {
@@ -425,7 +425,7 @@ static void libevent_log_cb(int severity, const char *msg)
425425
LogPrintLevel(BCLog::LIBEVENT, level, "%s\n", msg);
426426
}
427427

428-
bool InitHTTPServer()
428+
bool InitHTTPServer(const util::SignalInterrupt& interrupt)
429429
{
430430
if (!InitHTTPAllowList())
431431
return false;
@@ -454,7 +454,7 @@ bool InitHTTPServer()
454454
evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
455455
evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
456456
evhttp_set_max_body_size(http, MAX_SIZE);
457-
evhttp_set_gencb(http, http_request_cb, nullptr);
457+
evhttp_set_gencb(http, http_request_cb, (void*)&interrupt);
458458

459459
if (!HTTPBindAddresses(http)) {
460460
LogPrintf("Unable to bind any endpoint for RPC server\n");
@@ -579,7 +579,8 @@ void HTTPEvent::trigger(struct timeval* tv)
579579
else
580580
evtimer_add(ev, tv); // trigger after timeval passed
581581
}
582-
HTTPRequest::HTTPRequest(struct evhttp_request* _req, bool _replySent) : req(_req), replySent(_replySent)
582+
HTTPRequest::HTTPRequest(struct evhttp_request* _req, const util::SignalInterrupt& interrupt, bool _replySent)
583+
: req(_req), m_interrupt(interrupt), replySent(_replySent)
583584
{
584585
}
585586

@@ -639,7 +640,7 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
639640
void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
640641
{
641642
assert(!replySent && req);
642-
if (ShutdownRequested()) {
643+
if (m_interrupt) {
643644
WriteHeader("Connection", "close");
644645
}
645646
// Send event to main http thread to send reply message

src/httpserver.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include <optional>
1010
#include <string>
1111

12+
namespace util {
13+
class SignalInterrupt;
14+
} // namespace util
15+
1216
static const int DEFAULT_HTTP_THREADS=4;
1317
static const int DEFAULT_HTTP_WORKQUEUE=16;
1418
static const int DEFAULT_HTTP_SERVER_TIMEOUT=30;
@@ -21,7 +25,7 @@ class HTTPRequest;
2125
/** Initialize HTTP server.
2226
* Call this before RegisterHTTPHandler or EventBase().
2327
*/
24-
bool InitHTTPServer();
28+
bool InitHTTPServer(const util::SignalInterrupt& interrupt);
2529
/** Start HTTP server.
2630
* This is separate from InitHTTPServer to give users race-condition-free time
2731
* to register their handlers between InitHTTPServer and StartHTTPServer.
@@ -57,10 +61,11 @@ class HTTPRequest
5761
{
5862
private:
5963
struct evhttp_request* req;
64+
const util::SignalInterrupt& m_interrupt;
6065
bool replySent;
6166

6267
public:
63-
explicit HTTPRequest(struct evhttp_request* req, bool replySent = false);
68+
explicit HTTPRequest(struct evhttp_request* req, const util::SignalInterrupt& interrupt, bool replySent = false);
6469
~HTTPRequest();
6570

6671
enum RequestMethod {

src/index/base.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <node/context.h>
1414
#include <node/database_args.h>
1515
#include <node/interface_ui.h>
16-
#include <shutdown.h>
1716
#include <tinyformat.h>
1817
#include <util/thread.h>
1918
#include <util/translation.h>
@@ -32,7 +31,7 @@ template <typename... Args>
3231
void BaseIndex::FatalErrorf(const char* fmt, const Args&... args)
3332
{
3433
auto message = tfm::format(fmt, args...);
35-
node::AbortNode(m_chain->context()->exit_status, message);
34+
node::AbortNode(m_chain->context()->shutdown, m_chain->context()->exit_status, message);
3635
}
3736

3837
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)

src/init.cpp

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
#include <rpc/util.h>
6767
#include <scheduler.h>
6868
#include <script/sigcache.h>
69-
#include <shutdown.h>
7069
#include <sync.h>
7170
#include <timedata.h>
7271
#include <torcontrol.h>
@@ -192,6 +191,16 @@ static void RemovePidFile(const ArgsManager& args)
192191
}
193192
}
194193

194+
static std::optional<util::SignalInterrupt> g_shutdown;
195+
196+
void InitContext(NodeContext& node)
197+
{
198+
assert(!g_shutdown);
199+
g_shutdown.emplace();
200+
201+
node.args = &gArgs;
202+
node.shutdown = &*g_shutdown;
203+
}
195204

196205
//////////////////////////////////////////////////////////////////////////////
197206
//
@@ -204,11 +213,9 @@ static void RemovePidFile(const ArgsManager& args)
204213
// The network-processing threads are all part of a thread group
205214
// created by AppInit() or the Qt main() function.
206215
//
207-
// A clean exit happens when StartShutdown() or the SIGTERM
208-
// signal handler sets ShutdownRequested(), which makes main thread's
209-
// WaitForShutdown() interrupts the thread group.
210-
// And then, WaitForShutdown() makes all other on-going threads
211-
// in the thread group join the main thread.
216+
// A clean exit happens when the SignalInterrupt object is triggered, which
217+
// makes the main thread's SignalInterrupt::wait() call return, and join all
218+
// other ongoing threads in the thread group to the main thread.
212219
// Shutdown() is then called to clean up database connections, and stop other
213220
// threads that should only be stopped after the main network-processing
214221
// threads have exited.
@@ -218,6 +225,11 @@ static void RemovePidFile(const ArgsManager& args)
218225
// shutdown thing.
219226
//
220227

228+
bool ShutdownRequested(node::NodeContext& node)
229+
{
230+
return bool{*Assert(node.shutdown)};
231+
}
232+
221233
#if HAVE_SYSTEM
222234
static void ShutdownNotify(const ArgsManager& args)
223235
{
@@ -382,7 +394,9 @@ void Shutdown(NodeContext& node)
382394
#ifndef WIN32
383395
static void HandleSIGTERM(int)
384396
{
385-
StartShutdown();
397+
// Return value is intentionally ignored because there is not a better way
398+
// of handling this failure in a signal handler.
399+
(void)(*Assert(g_shutdown))();
386400
}
387401

388402
static void HandleSIGHUP(int)
@@ -392,7 +406,10 @@ static void HandleSIGHUP(int)
392406
#else
393407
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
394408
{
395-
StartShutdown();
409+
if (!(*Assert(g_shutdown))()) {
410+
LogPrintf("Error: failed to send shutdown signal on Ctrl-C\n");
411+
return false;
412+
}
396413
Sleep(INFINITE);
397414
return true;
398415
}
@@ -690,8 +707,9 @@ static bool AppInitServers(NodeContext& node)
690707
const ArgsManager& args = *Assert(node.args);
691708
RPCServer::OnStarted(&OnRPCStarted);
692709
RPCServer::OnStopped(&OnRPCStopped);
693-
if (!InitHTTPServer())
710+
if (!InitHTTPServer(*Assert(node.shutdown))) {
694711
return false;
712+
}
695713
StartRPC();
696714
node.rpc_interruption_point = RpcInterruptionPoint;
697715
if (!StartHTTPRPC(&node))
@@ -1141,11 +1159,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11411159
}, std::chrono::minutes{1});
11421160

11431161
// Check disk space every 5 minutes to avoid db corruption.
1144-
node.scheduler->scheduleEvery([&args]{
1162+
node.scheduler->scheduleEvery([&args, &node]{
11451163
constexpr uint64_t min_disk_space = 50 << 20; // 50 MB
11461164
if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) {
11471165
LogPrintf("Shutting down due to lack of disk space!\n");
1148-
StartShutdown();
1166+
if (!(*Assert(node.shutdown))()) {
1167+
LogPrintf("Error: failed to send shutdown signal after disk space check\n");
1168+
}
11491169
}
11501170
}, std::chrono::minutes{5});
11511171

@@ -1432,7 +1452,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14321452

14331453
// ********************************************************* Step 7: load block chain
14341454

1435-
node.notifications = std::make_unique<KernelNotifications>(node.exit_status);
1455+
node.notifications = std::make_unique<KernelNotifications>(*Assert(node.shutdown), node.exit_status);
14361456
ReadNotificationArgs(args, *node.notifications);
14371457
fReindex = args.GetBoolArg("-reindex", false);
14381458
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
@@ -1483,10 +1503,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14831503
}
14841504
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));
14851505

1486-
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
1506+
for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
14871507
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
14881508

1489-
node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts);
1509+
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
14901510
ChainstateManager& chainman = *node.chainman;
14911511

14921512
// 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)
15161536
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
15171537
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
15181538
options.require_full_verification = args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
1519-
options.check_interrupt = ShutdownRequested;
15201539
options.coins_error_cb = [] {
15211540
uiInterface.ThreadSafeMessageBox(
15221541
_("Error reading from database, shutting down."),
@@ -1551,7 +1570,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15511570
return InitError(error);
15521571
}
15531572

1554-
if (!fLoaded && !ShutdownRequested()) {
1573+
if (!fLoaded && !ShutdownRequested(node)) {
15551574
// first suggest a reindex
15561575
if (!options.reindex) {
15571576
bool fRet = uiInterface.ThreadSafeQuestion(
@@ -1560,7 +1579,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15601579
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
15611580
if (fRet) {
15621581
fReindex = true;
1563-
AbortShutdown();
1582+
if (!Assert(node.shutdown)->reset()) {
1583+
LogPrintf("Internal error: failed to reset shutdown signal.\n");
1584+
}
15641585
} else {
15651586
LogPrintf("Aborted block database rebuild. Exiting.\n");
15661587
return false;
@@ -1574,7 +1595,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15741595
// As LoadBlockIndex can take several minutes, it's possible the user
15751596
// requested to kill the GUI during the last operation. If so, exit.
15761597
// As the program has not fully started yet, Shutdown() is possibly overkill.
1577-
if (ShutdownRequested()) {
1598+
if (ShutdownRequested(node)) {
15781599
LogPrintf("Shutdown requested. Exiting.\n");
15791600
return false;
15801601
}
@@ -1695,7 +1716,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16951716
ImportBlocks(chainman, vImportFiles);
16961717
if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
16971718
LogPrintf("Stopping after block import\n");
1698-
StartShutdown();
1719+
if (!(*Assert(node.shutdown))()) {
1720+
LogPrintf("Error: failed to send shutdown signal after finishing block import\n");
1721+
}
16991722
return;
17001723
}
17011724

@@ -1715,16 +1738,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17151738
// Wait for genesis block to be processed
17161739
{
17171740
WAIT_LOCK(g_genesis_wait_mutex, lock);
1718-
// We previously could hang here if StartShutdown() is called prior to
1741+
// We previously could hang here if shutdown was requested prior to
17191742
// ImportBlocks getting started, so instead we just wait on a timer to
17201743
// check ShutdownRequested() regularly.
1721-
while (!fHaveGenesis && !ShutdownRequested()) {
1744+
while (!fHaveGenesis && !ShutdownRequested(node)) {
17221745
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
17231746
}
17241747
block_notify_genesis_wait_connection.disconnect();
17251748
}
17261749

1727-
if (ShutdownRequested()) {
1750+
if (ShutdownRequested(node)) {
17281751
return false;
17291752
}
17301753

src/init.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ namespace node {
2626
struct NodeContext;
2727
} // namespace node
2828

29+
/** Initialize node context shutdown and args variables. */
30+
void InitContext(node::NodeContext& node);
31+
/** Return whether node shutdown was requested. */
32+
bool ShutdownRequested(node::NodeContext& node);
33+
2934
/** Interrupt threads */
3035
void Interrupt(node::NodeContext& node);
3136
void Shutdown(node::NodeContext& node);

src/init/bitcoin-gui.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <common/args.h>
5+
#include <init.h>
66
#include <interfaces/chain.h>
77
#include <interfaces/echo.h>
88
#include <interfaces/init.h>
@@ -23,7 +23,7 @@ class BitcoinGuiInit : public interfaces::Init
2323
public:
2424
BitcoinGuiInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
2525
{
26-
m_node.args = &gArgs;
26+
InitContext(m_node);
2727
m_node.init = this;
2828
}
2929
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }

src/init/bitcoin-node.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <common/args.h>
5+
#include <init.h>
66
#include <interfaces/chain.h>
77
#include <interfaces/echo.h>
88
#include <interfaces/init.h>
@@ -25,7 +25,7 @@ class BitcoinNodeInit : public interfaces::Init
2525
: m_node(node),
2626
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
2727
{
28-
m_node.args = &gArgs;
28+
InitContext(m_node);
2929
m_node.init = this;
3030
}
3131
std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }

0 commit comments

Comments
 (0)