Skip to content

Commit 5837e34

Browse files
committed
Merge bitcoin/bitcoin#30967: refactor: Replace g_genesis_wait_cv with m_tip_block_cv
fa22e5c refactor: Remove dead code that assumed tip == nullptr (MarcoFalke) fa2e443 refactor: Replace g_genesis_wait_cv with m_tip_block_cv (MarcoFalke) fa7f52a refactor: Use wait_for predicate to check for interrupt (MarcoFalke) 5ca28ef refactor: Split up NodeContext shutdown_signal and shutdown_request (Ryan Ofsky) fad8e7f bugfix: Mark m_tip_block_cv as guarded by m_tip_block_mutex (MarcoFalke) fa18586 refactor: Add missing GUARDED_BY(m_tip_block_mutex) (MarcoFalke) fa4c075 doc: Clarify waitTipChanged docs (MarcoFalke) Pull request description: `g_genesis_wait_cv` is similar to `m_tip_block_cv` but shuffling everything through a redundant `boost::signals2`. So remove it, along with some other dead code, as well as minor fixups. ACKs for top commit: ryanofsky: Code review ACK fa22e5c (just rebased since last review) Sjors: ACK fa22e5c TheCharlatan: ACK fa22e5c Tree-SHA512: a2cb59b651aaf85a3574723adfe403487566788ad945933b0458816ccc841fce08ca77b31afbd2d6adb5bf1deed7229c028bee74fb4bbaf6576e9edcfa0ad817
2 parents a9f6a57 + fa22e5c commit 5837e34

20 files changed

+87
-114
lines changed

src/bitcoind.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ MAIN_FUNCTION
274274
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
275275

276276
// Start application
277-
if (!AppInit(node) || !Assert(node.shutdown)->wait()) {
277+
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
278278
node.exit_status = EXIT_FAILURE;
279279
}
280280
Interrupt(node);

src/index/base.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ template <typename... Args>
3131
void BaseIndex::FatalErrorf(util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
3232
{
3333
auto message = tfm::format(fmt, args...);
34-
node::AbortNode(m_chain->context()->shutdown, m_chain->context()->exit_status, Untranslated(message), m_chain->context()->warnings.get());
34+
node::AbortNode(m_chain->context()->shutdown_request, m_chain->context()->exit_status, Untranslated(message), m_chain->context()->warnings.get());
3535
}
3636

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

src/init.cpp

+28-48
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,14 @@ void InitContext(NodeContext& node)
207207
g_shutdown.emplace();
208208

209209
node.args = &gArgs;
210-
node.shutdown = &*g_shutdown;
210+
node.shutdown_signal = &*g_shutdown;
211+
node.shutdown_request = [&node] {
212+
assert(node.shutdown_signal);
213+
if (!(*node.shutdown_signal)()) return false;
214+
// Wake any threads that may be waiting for the tip to change.
215+
if (node.notifications) WITH_LOCK(node.notifications->m_tip_block_mutex, node.notifications->m_tip_block_cv.notify_all());
216+
return true;
217+
};
211218
}
212219

213220
//////////////////////////////////////////////////////////////////////////////
@@ -235,7 +242,7 @@ void InitContext(NodeContext& node)
235242

236243
bool ShutdownRequested(node::NodeContext& node)
237244
{
238-
return bool{*Assert(node.shutdown)};
245+
return bool{*Assert(node.shutdown_signal)};
239246
}
240247

241248
#if HAVE_SYSTEM
@@ -286,7 +293,7 @@ void Shutdown(NodeContext& node)
286293

287294
StopHTTPRPC();
288295
StopREST();
289-
StopRPC(&node);
296+
StopRPC();
290297
StopHTTPServer();
291298
for (const auto& client : node.chain_clients) {
292299
client->flush();
@@ -678,21 +685,6 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
678685
argsman.AddHiddenArgs(hidden_args);
679686
}
680687

681-
static bool fHaveGenesis = false;
682-
static GlobalMutex g_genesis_wait_mutex;
683-
static std::condition_variable g_genesis_wait_cv;
684-
685-
static void BlockNotifyGenesisWait(const CBlockIndex* pBlockIndex)
686-
{
687-
if (pBlockIndex != nullptr) {
688-
{
689-
LOCK(g_genesis_wait_mutex);
690-
fHaveGenesis = true;
691-
}
692-
g_genesis_wait_cv.notify_all();
693-
}
694-
}
695-
696688
#if HAVE_SYSTEM
697689
static void StartupNotify(const ArgsManager& args)
698690
{
@@ -707,7 +699,7 @@ static void StartupNotify(const ArgsManager& args)
707699
static bool AppInitServers(NodeContext& node)
708700
{
709701
const ArgsManager& args = *Assert(node.args);
710-
if (!InitHTTPServer(*Assert(node.shutdown))) {
702+
if (!InitHTTPServer(*Assert(node.shutdown_signal))) {
711703
return false;
712704
}
713705
StartRPC();
@@ -1216,7 +1208,7 @@ static ChainstateLoadResult InitAndLoadChainstate(
12161208
};
12171209
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
12181210
try {
1219-
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
1211+
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown_signal), chainman_opts, blockman_opts);
12201212
} catch (std::exception& e) {
12211213
return {ChainstateLoadStatus::FAILURE_FATAL, strprintf(Untranslated("Failed to initialize ChainstateManager: %s"), e.what())};
12221214
}
@@ -1327,7 +1319,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
13271319
constexpr uint64_t min_disk_space = 50 << 20; // 50 MB
13281320
if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) {
13291321
LogError("Shutting down due to lack of disk space!\n");
1330-
if (!(*Assert(node.shutdown))()) {
1322+
if (!(Assert(node.shutdown_request))()) {
13311323
LogError("Failed to send shutdown signal after disk space check\n");
13321324
}
13331325
}
@@ -1608,8 +1600,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16081600

16091601
// ********************************************************* Step 7: load block chain
16101602

1611-
node.notifications = std::make_unique<KernelNotifications>(*Assert(node.shutdown), node.exit_status, *Assert(node.warnings));
1612-
ReadNotificationArgs(args, *node.notifications);
1603+
node.notifications = std::make_unique<KernelNotifications>(Assert(node.shutdown_request), node.exit_status, *Assert(node.warnings));
1604+
auto& kernel_notifications{*node.notifications};
1605+
ReadNotificationArgs(args, kernel_notifications);
16131606

16141607
// cache size calculations
16151608
CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size());
@@ -1649,7 +1642,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16491642
return false;
16501643
}
16511644
do_reindex = true;
1652-
if (!Assert(node.shutdown)->reset()) {
1645+
if (!Assert(node.shutdown_signal)->reset()) {
16531646
LogError("Internal error: failed to reset shutdown signal.\n");
16541647
}
16551648
std::tie(status, error) = InitAndLoadChainstate(
@@ -1761,15 +1754,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17611754
}
17621755
}
17631756

1764-
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
1765-
// No locking, as this happens before any background thread is started.
1766-
boost::signals2::connection block_notify_genesis_wait_connection;
1767-
if (WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip() == nullptr)) {
1768-
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
1769-
} else {
1770-
fHaveGenesis = true;
1771-
}
1772-
17731757
#if HAVE_SYSTEM
17741758
const std::string block_notify = args.GetArg("-blocknotify", "");
17751759
if (!block_notify.empty()) {
@@ -1794,7 +1778,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17941778
ImportBlocks(chainman, vImportFiles);
17951779
if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
17961780
LogPrintf("Stopping after block import\n");
1797-
if (!(*Assert(node.shutdown))()) {
1781+
if (!(Assert(node.shutdown_request))()) {
17981782
LogError("Failed to send shutdown signal after finishing block import\n");
17991783
}
18001784
return;
@@ -1814,15 +1798,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18141798
});
18151799

18161800
// Wait for genesis block to be processed
1817-
{
1818-
WAIT_LOCK(g_genesis_wait_mutex, lock);
1819-
// We previously could hang here if shutdown was requested prior to
1820-
// ImportBlocks getting started, so instead we just wait on a timer to
1821-
// check ShutdownRequested() regularly.
1822-
while (!fHaveGenesis && !ShutdownRequested(node)) {
1823-
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
1824-
}
1825-
block_notify_genesis_wait_connection.disconnect();
1801+
if (WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip() == nullptr)) {
1802+
WAIT_LOCK(kernel_notifications.m_tip_block_mutex, lock);
1803+
kernel_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) {
1804+
return !kernel_notifications.m_tip_block.IsNull() || ShutdownRequested(node);
1805+
});
18261806
}
18271807

18281808
if (ShutdownRequested(node)) {
@@ -1831,17 +1811,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18311811

18321812
// ********************************************************* Step 12: start node
18331813

1834-
//// debug print
18351814
int64_t best_block_time{};
18361815
{
1837-
LOCK(cs_main);
1816+
LOCK(chainman.GetMutex());
1817+
const auto& tip{*Assert(chainman.ActiveTip())};
18381818
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
1839-
chain_active_height = chainman.ActiveChain().Height();
1840-
best_block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : chainman.GetParams().GenesisBlock().GetBlockTime();
1819+
chain_active_height = tip.nHeight;
1820+
best_block_time = tip.GetBlockTime();
18411821
if (tip_info) {
18421822
tip_info->block_height = chain_active_height;
18431823
tip_info->block_time = best_block_time;
1844-
tip_info->verification_progress = GuessVerificationProgress(chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
1824+
tip_info->verification_progress = GuessVerificationProgress(chainman.GetParams().TxData(), &tip);
18451825
}
18461826
if (tip_info && chainman.m_best_header) {
18471827
tip_info->header_height = chainman.m_best_header->nHeight;

src/interfaces/mining.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ class Mining
6161
virtual std::optional<BlockRef> getTip() = 0;
6262

6363
/**
64-
* Waits for the tip to change
64+
* Waits for the connected tip to change. If the tip was not connected on
65+
* startup, this will wait.
6566
*
6667
* @param[in] current_tip block hash of the current chain tip. Function waits
67-
* for the chain tip to change if this matches, otherwise
68-
* it returns right away.
68+
* for the chain tip to differ from this.
6969
* @param[in] timeout how long to wait for a new tip
7070
* @returns Hash and height of the current chain tip after this call.
7171
*/

src/node/abort.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515

1616
namespace node {
1717

18-
void AbortNode(util::SignalInterrupt* shutdown, std::atomic<int>& exit_status, const bilingual_str& message, node::Warnings* warnings)
18+
void AbortNode(const std::function<bool()>& shutdown_request, std::atomic<int>& exit_status, const bilingual_str& message, node::Warnings* warnings)
1919
{
2020
if (warnings) warnings->Set(Warning::FATAL_INTERNAL_ERROR, message);
2121
InitError(_("A fatal internal error occurred, see debug.log for details: ") + message);
2222
exit_status.store(EXIT_FAILURE);
23-
if (shutdown && !(*shutdown)()) {
23+
if (shutdown_request && !shutdown_request()) {
2424
LogError("Failed to send shutdown signal\n");
2525
};
2626
}

src/node/abort.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@
66
#define BITCOIN_NODE_ABORT_H
77

88
#include <atomic>
9+
#include <functional>
910

1011
struct bilingual_str;
1112

12-
namespace util {
13-
class SignalInterrupt;
14-
} // namespace util
15-
1613
namespace node {
1714
class Warnings;
18-
void AbortNode(util::SignalInterrupt* shutdown, std::atomic<int>& exit_status, const bilingual_str& message, node::Warnings* warnings);
15+
void AbortNode(const std::function<bool()>& shutdown_request, std::atomic<int>& exit_status, const bilingual_str& message, node::Warnings* warnings);
1916
} // namespace node
2017

2118
#endif // BITCOIN_NODE_ABORT_H

src/node/context.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ struct NodeContext {
5959
std::unique_ptr<ECC_Context> ecc_context;
6060
//! Init interface for initializing current process and connecting to other processes.
6161
interfaces::Init* init{nullptr};
62+
//! Function to request a shutdown.
63+
std::function<bool()> shutdown_request;
6264
//! Interrupt object used to track whether node shutdown was requested.
63-
util::SignalInterrupt* shutdown{nullptr};
65+
util::SignalInterrupt* shutdown_signal{nullptr};
6466
std::unique_ptr<AddrMan> addrman;
6567
std::unique_ptr<CConnman> connman;
6668
std::unique_ptr<CTxMemPool> mempool;

src/node/interfaces.cpp

+8-13
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,15 @@ class NodeImpl : public Node
134134
}
135135
void startShutdown() override
136136
{
137-
if (!(*Assert(Assert(m_context)->shutdown))()) {
137+
NodeContext& ctx{*Assert(m_context)};
138+
if (!(Assert(ctx.shutdown_request))()) {
138139
LogError("Failed to send shutdown signal\n");
139140
}
141+
140142
// Stop RPC for clean shutdown if any of waitfor* commands is executed.
141143
if (args().GetBoolArg("-server", false)) {
142144
InterruptRPC();
143-
StopRPC(m_context);
145+
StopRPC();
144146
}
145147
}
146148
bool shutdownRequested() override { return ShutdownRequested(*Assert(m_context)); };
@@ -938,19 +940,12 @@ class MinerImpl : public Mining
938940

939941
BlockRef waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override
940942
{
941-
// Interrupt check interval
942-
const MillisecondsDouble tick{1000};
943-
auto now{std::chrono::steady_clock::now()};
944-
auto deadline = now + timeout;
945-
// std::chrono does not check against overflow
946-
if (deadline < now) deadline = std::chrono::steady_clock::time_point::max();
943+
if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono
947944
{
948945
WAIT_LOCK(notifications().m_tip_block_mutex, lock);
949-
while ((notifications().m_tip_block == uint256() || notifications().m_tip_block == current_tip) && !chainman().m_interrupt) {
950-
now = std::chrono::steady_clock::now();
951-
if (now >= deadline) break;
952-
notifications().m_tip_block_cv.wait_until(lock, std::min(deadline, now + tick));
953-
}
946+
notifications().m_tip_block_cv.wait_for(lock, timeout, [&]() EXCLUSIVE_LOCKS_REQUIRED(notifications().m_tip_block_mutex) {
947+
return (notifications().m_tip_block != current_tip && notifications().m_tip_block != uint256::ZERO) || chainman().m_interrupt;
948+
});
954949
}
955950
// Must release m_tip_block_mutex before locking cs_main, to avoid deadlocks.
956951
LOCK(::cs_main);

src/node/kernel_notifications.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ kernel::InterruptResult KernelNotifications::blockTip(SynchronizationState state
5858

5959
uiInterface.NotifyBlockTip(state, &index);
6060
if (m_stop_at_height && index.nHeight >= m_stop_at_height) {
61-
if (!m_shutdown()) {
61+
if (!m_shutdown_request()) {
6262
LogError("Failed to send shutdown signal after reaching stop height\n");
6363
}
6464
return kernel::Interrupted{};
@@ -90,12 +90,12 @@ void KernelNotifications::warningUnset(kernel::Warning id)
9090

9191
void KernelNotifications::flushError(const bilingual_str& message)
9292
{
93-
AbortNode(&m_shutdown, m_exit_status, message, &m_warnings);
93+
AbortNode(m_shutdown_request, m_exit_status, message, &m_warnings);
9494
}
9595

9696
void KernelNotifications::fatalError(const bilingual_str& message)
9797
{
98-
node::AbortNode(m_shutdown_on_fatal_error ? &m_shutdown : nullptr,
98+
node::AbortNode(m_shutdown_on_fatal_error ? m_shutdown_request : nullptr,
9999
m_exit_status, message, &m_warnings);
100100
}
101101

src/node/kernel_notifications.h

+8-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <atomic>
1515
#include <cstdint>
16+
#include <functional>
1617

1718
class ArgsManager;
1819
class CBlockIndex;
@@ -23,10 +24,6 @@ namespace kernel {
2324
enum class Warning;
2425
} // namespace kernel
2526

26-
namespace util {
27-
class SignalInterrupt;
28-
} // namespace util
29-
3027
namespace node {
3128

3229
class Warnings;
@@ -35,8 +32,8 @@ static constexpr int DEFAULT_STOPATHEIGHT{0};
3532
class KernelNotifications : public kernel::Notifications
3633
{
3734
public:
38-
KernelNotifications(util::SignalInterrupt& shutdown, std::atomic<int>& exit_status, node::Warnings& warnings)
39-
: m_shutdown(shutdown), m_exit_status{exit_status}, m_warnings{warnings} {}
35+
KernelNotifications(const std::function<bool()>& shutdown_request, std::atomic<int>& exit_status, node::Warnings& warnings)
36+
: m_shutdown_request(shutdown_request), m_exit_status{exit_status}, m_warnings{warnings} {}
4037

4138
[[nodiscard]] kernel::InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) override EXCLUSIVE_LOCKS_REQUIRED(!m_tip_block_mutex);
4239

@@ -58,12 +55,14 @@ class KernelNotifications : public kernel::Notifications
5855
bool m_shutdown_on_fatal_error{true};
5956

6057
Mutex m_tip_block_mutex;
61-
std::condition_variable m_tip_block_cv;
58+
std::condition_variable m_tip_block_cv GUARDED_BY(m_tip_block_mutex);
6259
//! The block for which the last blockTip notification was received for.
63-
uint256 m_tip_block;
60+
//! The initial ZERO means that no block has been connected yet, which may
61+
//! be true even long after startup, until shutdown.
62+
uint256 m_tip_block GUARDED_BY(m_tip_block_mutex){uint256::ZERO};
6463

6564
private:
66-
util::SignalInterrupt& m_shutdown;
65+
const std::function<bool()>& m_shutdown_request;
6766
std::atomic<int>& m_exit_status;
6867
node::Warnings& m_warnings;
6968
};

src/rpc/server.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ static RPCHelpMan stop()
169169
{
170170
// Event loop will exit after current HTTP requests have been handled, so
171171
// this reply will get back to the client.
172-
CHECK_NONFATAL((*CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown))());
172+
CHECK_NONFATAL((CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown_request))());
173173
if (jsonRequest.params[0].isNum()) {
174174
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
175175
}
@@ -294,7 +294,7 @@ void InterruptRPC()
294294
});
295295
}
296296

297-
void StopRPC(const std::any& context)
297+
void StopRPC()
298298
{
299299
static std::once_flag g_rpc_stop_flag;
300300
// This function could be called twice if the GUI has been started with -server=1.
@@ -303,9 +303,6 @@ void StopRPC(const std::any& context)
303303
LogDebug(BCLog::RPC, "Stopping RPC\n");
304304
WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
305305
DeleteAuthCookie();
306-
node::NodeContext& node = EnsureAnyNodeContext(context);
307-
// The notifications interface doesn't exist between initialization step 4a and 7.
308-
if (node.notifications) node.notifications->m_tip_block_cv.notify_all();
309306
LogDebug(BCLog::RPC, "RPC stopped.\n");
310307
});
311308
}

0 commit comments

Comments
 (0)