Skip to content

Commit c07935b

Browse files
committed
Merge bitcoin#28960: kernel: Remove dependency on CScheduler
d5228ef kernel: Remove dependency on CScheduler (TheCharlatan) 06069b3 scripted-diff: Rename MainSignals to ValidationSignals (TheCharlatan) 0d6d2b6 scripted-diff: Rename SingleThreadedSchedulerClient to SerialTaskRunner (TheCharlatan) 4abde2c [refactor] Make MainSignals RAII styled (TheCharlatan) 84f5c13 refactor: De-globalize g_signals (TheCharlatan) 473dd4b [refactor] Prepare for g_signals de-globalization (TheCharlatan) 3fba3d5 [refactor] Make signals optional in mempool and chainman (TheCharlatan) Pull request description: By defining a virtual interface class for the scheduler client, users of the kernel can now define their own event consuming infrastructure, without having to spawn threads or rely on the scheduler design. Removing `CScheduler` also allows removing the thread and exception modules from the kernel library. To make the `CMainSignals` class easier to use from a kernel library perspective, remove its global instantiation and adopt RAII practices. Renames `CMainSignals` to `ValidationSignals`, which more accurately describes its purpose and scope. Also make the `ValidationSignals` in the `ChainstateManager` and CTxMemPool` optional. This could be useful in the future for using or testing these classes without having to instantiate any form of signal handling. --- This PR is part of the [libbitcoinkernel project](bitcoin#27587). It improves the kernel API and removes two modules from the kernel library. ACKs for top commit: maflcko: re-ACK d5228ef 🌄 ryanofsky: Code review ACK d5228ef. Just comment change since last review. vasild: ACK d5228ef furszy: diff ACK d5228ef Tree-SHA512: e93a5f10eb6182effb84bb981859a7ce750e466efd8171045d8d9e7fe46e4065631d9f6f533c5967c4d34c9bb7d7a67e9f4593bd4c5b30cd7b3bbad7be7b331b
2 parents 1cd2e29 + d5228ef commit c07935b

40 files changed

+365
-289
lines changed

contrib/devtools/test_deterministic_coverage.sh

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@ NON_DETERMINISTIC_TESTS=(
1717
"blockfilter_index_tests/blockfilter_index_initial_sync" # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
1818
"coinselector_tests/knapsack_solver_test" # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
1919
"fs_tests/fsbridge_fstream" # deterministic test failure?
20-
"miner_tests/CreateNewBlock_validity" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
20+
"miner_tests/CreateNewBlock_validity" # validation.cpp: if (signals.CallbacksPending() > 10)
2121
"scheduler_tests/manythreads" # scheduler.cpp: CScheduler::serviceQueue()
2222
"scheduler_tests/singlethreadedscheduler_ordered" # scheduler.cpp: CScheduler::serviceQueue()
23-
"txvalidationcache_tests/checkinputs_test" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
24-
"txvalidationcache_tests/tx_mempool_block_doublespend" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
25-
"txindex_tests/txindex_initial_sync" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
26-
"txvalidation_tests/tx_mempool_reject_coinbase" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
27-
"validation_block_tests/processnewblock_signals_ordering" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
28-
"wallet_tests/coin_mark_dirty_immature_credit" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
29-
"wallet_tests/dummy_input_size_test" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
30-
"wallet_tests/importmulti_rescan" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
31-
"wallet_tests/importwallet_rescan" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
32-
"wallet_tests/ListCoins" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
33-
"wallet_tests/scan_for_wallet_transactions" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
34-
"wallet_tests/wallet_disableprivkeys" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
23+
"txvalidationcache_tests/checkinputs_test" # validation.cpp: if (signals.CallbacksPending() > 10)
24+
"txvalidationcache_tests/tx_mempool_block_doublespend" # validation.cpp: if (signals.CallbacksPending() > 10)
25+
"txindex_tests/txindex_initial_sync" # validation.cpp: if (signals.CallbacksPending() > 10)
26+
"txvalidation_tests/tx_mempool_reject_coinbase" # validation.cpp: if (signals.CallbacksPending() > 10)
27+
"validation_block_tests/processnewblock_signals_ordering" # validation.cpp: if (signals.CallbacksPending() > 10)
28+
"wallet_tests/coin_mark_dirty_immature_credit" # validation.cpp: if (signals.CallbacksPending() > 10)
29+
"wallet_tests/dummy_input_size_test" # validation.cpp: if (signals.CallbacksPending() > 10)
30+
"wallet_tests/importmulti_rescan" # validation.cpp: if (signals.CallbacksPending() > 10)
31+
"wallet_tests/importwallet_rescan" # validation.cpp: if (signals.CallbacksPending() > 10)
32+
"wallet_tests/ListCoins" # validation.cpp: if (signals.CallbacksPending() > 10)
33+
"wallet_tests/scan_for_wallet_transactions" # validation.cpp: if (signals.CallbacksPending() > 10)
34+
"wallet_tests/wallet_disableprivkeys" # validation.cpp: if (signals.CallbacksPending() > 10)
3535
)
3636

3737
TEST_BITCOIN_BINARY="src/test/test_bitcoin"

src/Makefile.am

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ BITCOIN_CORE_H = \
323323
util/spanparsing.h \
324324
util/string.h \
325325
util/syserror.h \
326+
util/task_runner.h \
326327
util/thread.h \
327328
util/threadinterrupt.h \
328329
util/threadnames.h \
@@ -966,7 +967,6 @@ libbitcoinkernel_la_SOURCES = \
966967
pubkey.cpp \
967968
random.cpp \
968969
randomenv.cpp \
969-
scheduler.cpp \
970970
script/interpreter.cpp \
971971
script/script.cpp \
972972
script/script_error.cpp \
@@ -983,7 +983,6 @@ libbitcoinkernel_la_SOURCES = \
983983
util/batchpriority.cpp \
984984
util/chaintype.cpp \
985985
util/check.cpp \
986-
util/exception.cpp \
987986
util/fs.cpp \
988987
util/fs_helpers.cpp \
989988
util/hasher.cpp \
@@ -994,7 +993,6 @@ libbitcoinkernel_la_SOURCES = \
994993
util/strencodings.cpp \
995994
util/string.cpp \
996995
util/syserror.cpp \
997-
util/thread.cpp \
998996
util/threadnames.cpp \
999997
util/time.cpp \
1000998
util/tokenpipe.cpp \

src/bench/wallet_balance.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
3939
generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
4040
generatetoaddress(test_setup->m_node, ADDRESS_WATCHONLY);
4141
}
42-
SyncWithValidationInterfaceQueue();
42+
// Calls SyncWithValidationInterfaceQueue
43+
wallet.chain().waitForNotificationsIfTipChanged(uint256::ZERO);
4344

4445
auto bal = GetBalance(wallet); // Cache
4546

src/bitcoin-chainstate.cpp

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@
2323
#include <node/caches.h>
2424
#include <node/chainstate.h>
2525
#include <random.h>
26-
#include <scheduler.h>
2726
#include <script/sigcache.h>
2827
#include <util/chaintype.h>
2928
#include <util/fs.h>
30-
#include <util/thread.h>
29+
#include <util/task_runner.h>
3130
#include <validation.h>
3231
#include <validationinterface.h>
3332

@@ -68,16 +67,7 @@ int main(int argc, char* argv[])
6867
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
6968
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
7069

71-
72-
// SETUP: Scheduling and Background Signals
73-
CScheduler scheduler{};
74-
// Start the lightweight task scheduler thread
75-
scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { scheduler.serviceQueue(); });
76-
77-
// Gather some entropy once per minute.
78-
scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
79-
80-
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
70+
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
8171

8272
class KernelNotifications : public kernel::Notifications
8373
{
@@ -118,6 +108,7 @@ int main(int argc, char* argv[])
118108
.chainparams = *chainparams,
119109
.datadir = abs_datadir,
120110
.notifications = *notifications,
111+
.signals = &validation_signals,
121112
};
122113
const node::BlockManager::Options blockman_opts{
123114
.chainparams = chainman_opts.chainparams,
@@ -235,9 +226,9 @@ int main(int argc, char* argv[])
235226

236227
bool new_block;
237228
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
238-
RegisterSharedValidationInterface(sc);
229+
validation_signals.RegisterSharedValidationInterface(sc);
239230
bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
240-
UnregisterSharedValidationInterface(sc);
231+
validation_signals.UnregisterSharedValidationInterface(sc);
241232
if (!new_block && accepted) {
242233
std::cerr << "duplicate" << std::endl;
243234
break;
@@ -287,10 +278,9 @@ int main(int argc, char* argv[])
287278
epilogue:
288279
// Without this precise shutdown sequence, there will be a lot of nullptr
289280
// dereferencing and UB.
290-
scheduler.stop();
291281
if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join();
292282

293-
GetMainSignals().FlushBackgroundCallbacks();
283+
validation_signals.FlushBackgroundCallbacks();
294284
{
295285
LOCK(cs_main);
296286
for (Chainstate* chainstate : chainman.GetAll()) {
@@ -300,5 +290,4 @@ int main(int argc, char* argv[])
300290
}
301291
}
302292
}
303-
GetMainSignals().UnregisterBackgroundSignalScheduler();
304293
}

src/index/base.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ bool BaseIndex::Init()
8989
return &m_chain->context()->chainman->GetChainstateForIndexing());
9090
// Register to validation interface before setting the 'm_synced' flag, so that
9191
// callbacks are not missed once m_synced is true.
92-
RegisterValidationInterface(this);
92+
m_chain->context()->validation_signals->RegisterValidationInterface(this);
9393

9494
CBlockLocator locator;
9595
if (!GetDB().ReadBestBlock(locator)) {
@@ -380,7 +380,7 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain() const
380380
}
381381

382382
LogPrintf("%s: %s is catching up on block notifications\n", __func__, GetName());
383-
SyncWithValidationInterfaceQueue();
383+
m_chain->context()->validation_signals->SyncWithValidationInterfaceQueue();
384384
return true;
385385
}
386386

@@ -399,7 +399,9 @@ bool BaseIndex::StartBackgroundSync()
399399

400400
void BaseIndex::Stop()
401401
{
402-
UnregisterValidationInterface(this);
402+
if (m_chain->context()->validation_signals) {
403+
m_chain->context()->validation_signals->UnregisterValidationInterface(this);
404+
}
403405

404406
if (m_thread_sync.joinable()) {
405407
m_thread_sync.join();

src/init.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ void Shutdown(NodeContext& node)
291291

292292
// Because these depend on each-other, we make sure that neither can be
293293
// using the other before destroying them.
294-
if (node.peerman) UnregisterValidationInterface(node.peerman.get());
294+
if (node.peerman && node.validation_signals) node.validation_signals->UnregisterValidationInterface(node.peerman.get());
295295
if (node.connman) node.connman->Stop();
296296

297297
StopTorControl();
@@ -317,7 +317,9 @@ void Shutdown(NodeContext& node)
317317
// fee estimator from validation interface.
318318
if (node.fee_estimator) {
319319
node.fee_estimator->Flush();
320-
UnregisterValidationInterface(node.fee_estimator.get());
320+
if (node.validation_signals) {
321+
node.validation_signals->UnregisterValidationInterface(node.fee_estimator.get());
322+
}
321323
}
322324

323325
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
@@ -332,7 +334,7 @@ void Shutdown(NodeContext& node)
332334

333335
// After there are no more peers/RPC left to give us new data which may generate
334336
// CValidationInterface callbacks, flush them...
335-
GetMainSignals().FlushBackgroundCallbacks();
337+
if (node.validation_signals) node.validation_signals->FlushBackgroundCallbacks();
336338

337339
// Stop and delete all indexes only after flushing background callbacks.
338340
if (g_txindex) {
@@ -367,17 +369,19 @@ void Shutdown(NodeContext& node)
367369

368370
#if ENABLE_ZMQ
369371
if (g_zmq_notification_interface) {
370-
UnregisterValidationInterface(g_zmq_notification_interface.get());
372+
if (node.validation_signals) node.validation_signals->UnregisterValidationInterface(g_zmq_notification_interface.get());
371373
g_zmq_notification_interface.reset();
372374
}
373375
#endif
374376

375377
node.chain_clients.clear();
376-
UnregisterAllValidationInterfaces();
377-
GetMainSignals().UnregisterBackgroundSignalScheduler();
378+
if (node.validation_signals) {
379+
node.validation_signals->UnregisterAllValidationInterfaces();
380+
}
378381
node.mempool.reset();
379382
node.fee_estimator.reset();
380383
node.chainman.reset();
384+
node.validation_signals.reset();
381385
node.scheduler.reset();
382386
node.kernel.reset();
383387

@@ -1138,17 +1142,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11381142

11391143
assert(!node.scheduler);
11401144
node.scheduler = std::make_unique<CScheduler>();
1145+
auto& scheduler = *node.scheduler;
11411146

11421147
// Start the lightweight task scheduler thread
1143-
node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { node.scheduler->serviceQueue(); });
1148+
scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { scheduler.serviceQueue(); });
11441149

11451150
// Gather some entropy once per minute.
1146-
node.scheduler->scheduleEvery([]{
1151+
scheduler.scheduleEvery([]{
11471152
RandAddPeriodic();
11481153
}, std::chrono::minutes{1});
11491154

11501155
// Check disk space every 5 minutes to avoid db corruption.
1151-
node.scheduler->scheduleEvery([&args, &node]{
1156+
scheduler.scheduleEvery([&args, &node]{
11521157
constexpr uint64_t min_disk_space = 50 << 20; // 50 MB
11531158
if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) {
11541159
LogPrintf("Shutting down due to lack of disk space!\n");
@@ -1158,7 +1163,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11581163
}
11591164
}, std::chrono::minutes{5});
11601165

1161-
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
1166+
assert(!node.validation_signals);
1167+
node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(scheduler));
1168+
auto& validation_signals = *node.validation_signals;
11621169

11631170
// Create client interfaces for wallets that are supposed to be loaded
11641171
// according to -wallet and -disablewallet options. This only constructs
@@ -1263,8 +1270,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
12631270

12641271
// Flush estimates to disk periodically
12651272
CBlockPolicyEstimator* fee_estimator = node.fee_estimator.get();
1266-
node.scheduler->scheduleEvery([fee_estimator] { fee_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL);
1267-
RegisterValidationInterface(fee_estimator);
1273+
scheduler.scheduleEvery([fee_estimator] { fee_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL);
1274+
validation_signals.RegisterValidationInterface(fee_estimator);
12681275
}
12691276

12701277
// Check port numbers
@@ -1435,7 +1442,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14351442
});
14361443

14371444
if (g_zmq_notification_interface) {
1438-
RegisterValidationInterface(g_zmq_notification_interface.get());
1445+
validation_signals.RegisterValidationInterface(g_zmq_notification_interface.get());
14391446
}
14401447
#endif
14411448

@@ -1449,6 +1456,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14491456
.chainparams = chainparams,
14501457
.datadir = args.GetDataDirNet(),
14511458
.notifications = *node.notifications,
1459+
.signals = &validation_signals,
14521460
};
14531461
Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
14541462

@@ -1478,6 +1486,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14781486

14791487
CTxMemPool::Options mempool_opts{
14801488
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
1489+
.signals = &validation_signals,
14811490
};
14821491
auto result{ApplyArgsManOptions(args, chainparams, mempool_opts)};
14831492
if (!result) {
@@ -1505,7 +1514,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15051514

15061515
// Drain the validation interface queue to ensure that the old indexes
15071516
// don't have any pending work.
1508-
SyncWithValidationInterfaceQueue();
1517+
Assert(node.validation_signals)->SyncWithValidationInterfaceQueue();
15091518

15101519
for (auto* index : node.indexes) {
15111520
index->Interrupt();
@@ -1594,7 +1603,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15941603
node.peerman = PeerManager::make(*node.connman, *node.addrman,
15951604
node.banman.get(), chainman,
15961605
*node.mempool, peerman_opts);
1597-
RegisterValidationInterface(node.peerman.get());
1606+
validation_signals.RegisterValidationInterface(node.peerman.get());
15981607

15991608
// ********************************************************* Step 8: start indexers
16001609

@@ -1900,7 +1909,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
19001909

19011910
connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
19021911

1903-
if (!node.connman->Start(*node.scheduler, connOptions)) {
1912+
if (!node.connman->Start(scheduler, connOptions)) {
19041913
return false;
19051914
}
19061915

@@ -1920,15 +1929,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
19201929
uiInterface.InitMessage(_("Done loading").translated);
19211930

19221931
for (const auto& client : node.chain_clients) {
1923-
client->start(*node.scheduler);
1932+
client->start(scheduler);
19241933
}
19251934

19261935
BanMan* banman = node.banman.get();
1927-
node.scheduler->scheduleEvery([banman]{
1936+
scheduler.scheduleEvery([banman]{
19281937
banman->DumpBanlist();
19291938
}, DUMP_BANS_INTERVAL);
19301939

1931-
if (node.peerman) node.peerman->StartScheduledTasks(*node.scheduler);
1940+
if (node.peerman) node.peerman->StartScheduledTasks(scheduler);
19321941

19331942
#if HAVE_SYSTEM
19341943
StartupNotify(args);

src/kernel/chainstatemanager_opts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <optional>
1919

2020
class CChainParams;
21+
class ValidationSignals;
2122

2223
static constexpr bool DEFAULT_CHECKPOINTS_ENABLED{true};
2324
static constexpr auto DEFAULT_MAX_TIP_AGE{24h};
@@ -44,6 +45,7 @@ struct ChainstateManagerOpts {
4445
DBOptions coins_db{};
4546
CoinsViewOptions coins_view{};
4647
Notifications& notifications;
48+
ValidationSignals* signals{nullptr};
4749
//! Number of script check worker threads. Zero means no parallel verification.
4850
int worker_threads_num{0};
4951
};

src/kernel/mempool_options.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <cstdint>
1414
#include <optional>
1515

16+
class ValidationSignals;
17+
1618
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
1719
static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
1820
/** Default for -maxmempool when blocksonly is set */
@@ -56,6 +58,8 @@ struct MemPoolOptions {
5658
bool full_rbf{DEFAULT_MEMPOOL_FULL_RBF};
5759
bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT};
5860
MemPoolLimits limits{};
61+
62+
ValidationSignals* signals{nullptr};
5963
};
6064
} // namespace kernel
6165

src/node/context.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class BanMan;
2020
class BaseIndex;
2121
class CBlockPolicyEstimator;
2222
class CConnman;
23+
class ValidationSignals;
2324
class CScheduler;
2425
class CTxMemPool;
2526
class ChainstateManager;
@@ -70,7 +71,10 @@ struct NodeContext {
7071
interfaces::WalletLoader* wallet_loader{nullptr};
7172
std::unique_ptr<CScheduler> scheduler;
7273
std::function<void()> rpc_interruption_point = [] {};
74+
//! Issues blocking calls about sync status, errors and warnings
7375
std::unique_ptr<KernelNotifications> notifications;
76+
//! Issues calls about blocks and transactions
77+
std::unique_ptr<ValidationSignals> validation_signals;
7478
std::atomic<int> exit_status{EXIT_SUCCESS};
7579

7680
//! Declare default constructor and destructor that are not inline, so code

0 commit comments

Comments
 (0)