Skip to content

Commit d5228ef

Browse files
committed
kernel: Remove dependency on CScheduler
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.
1 parent 06069b3 commit d5228ef

File tree

8 files changed

+85
-37
lines changed

8 files changed

+85
-37
lines changed

src/Makefile.am

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ BITCOIN_CORE_H = \
326326
util/spanparsing.h \
327327
util/string.h \
328328
util/syserror.h \
329+
util/task_runner.h \
329330
util/thread.h \
330331
util/threadinterrupt.h \
331332
util/threadnames.h \
@@ -975,7 +976,6 @@ libbitcoinkernel_la_SOURCES = \
975976
pubkey.cpp \
976977
random.cpp \
977978
randomenv.cpp \
978-
scheduler.cpp \
979979
script/interpreter.cpp \
980980
script/script.cpp \
981981
script/script_error.cpp \
@@ -992,7 +992,6 @@ libbitcoinkernel_la_SOURCES = \
992992
util/batchpriority.cpp \
993993
util/chaintype.cpp \
994994
util/check.cpp \
995-
util/exception.cpp \
996995
util/fs.cpp \
997996
util/fs_helpers.cpp \
998997
util/hasher.cpp \
@@ -1003,7 +1002,6 @@ libbitcoinkernel_la_SOURCES = \
10031002
util/strencodings.cpp \
10041003
util/string.cpp \
10051004
util/syserror.cpp \
1006-
util/thread.cpp \
10071005
util/threadnames.cpp \
10081006
util/time.cpp \
10091007
util/tokenpipe.cpp \

src/bitcoin-chainstate.cpp

Lines changed: 2 additions & 13 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-
ValidationSignals validation_signals{scheduler};
78-
79-
// Gather some entropy once per minute.
80-
scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
70+
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
8171

8272
class KernelNotifications : public kernel::Notifications
8373
{
@@ -288,7 +278,6 @@ int main(int argc, char* argv[])
288278
epilogue:
289279
// Without this precise shutdown sequence, there will be a lot of nullptr
290280
// dereferencing and UB.
291-
scheduler.stop();
292281
if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join();
293282

294283
validation_signals.FlushBackgroundCallbacks();

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11641164
}, std::chrono::minutes{5});
11651165

11661166
assert(!node.validation_signals);
1167-
node.validation_signals = std::make_unique<ValidationSignals>(scheduler);
1167+
node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(scheduler));
11681168
auto& validation_signals = *node.validation_signals;
11691169

11701170
// Create client interfaces for wallets that are supposed to be loaded

src/scheduler.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <attributes.h>
99
#include <sync.h>
1010
#include <threadsafety.h>
11+
#include <util/task_runner.h>
1112

1213
#include <chrono>
1314
#include <condition_variable>
@@ -120,12 +121,16 @@ class CScheduler
120121
* B() will be able to observe all of the effects of callback A() which executed
121122
* before it.
122123
*/
123-
class SerialTaskRunner
124+
class SerialTaskRunner : public util::TaskRunnerInterface
124125
{
125126
private:
126127
CScheduler& m_scheduler;
127128

128129
Mutex m_callbacks_mutex;
130+
131+
// We are not allowed to assume the scheduler only runs in one thread,
132+
// but must ensure all callbacks happen in-order, so we end up creating
133+
// our own queue here :(
129134
std::list<std::function<void()>> m_callbacks_pending GUARDED_BY(m_callbacks_mutex);
130135
bool m_are_callbacks_running GUARDED_BY(m_callbacks_mutex) = false;
131136

@@ -141,15 +146,15 @@ class SerialTaskRunner
141146
* Practically, this means that callbacks can behave as if they are executed
142147
* in order by a single thread.
143148
*/
144-
void insert(std::function<void()> func) EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
149+
void insert(std::function<void()> func) override EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
145150

146151
/**
147152
* Processes all remaining queue members on the calling thread, blocking until queue is empty
148153
* Must be called after the CScheduler has no remaining processing threads!
149154
*/
150-
void flush() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
155+
void flush() override EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
151156

152-
size_t size() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
157+
size_t size() override EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
153158
};
154159

155160
#endif // BITCOIN_SCHEDULER_H

src/test/util/setup_common.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
171171
// from blocking due to queue overrun.
172172
m_node.scheduler = std::make_unique<CScheduler>();
173173
m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
174-
m_node.validation_signals = std::make_unique<ValidationSignals>(*m_node.scheduler);
174+
m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
175175

176176
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
177177
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));

src/util/task_runner.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_UTIL_TASK_RUNNER_H
6+
#define BITCOIN_UTIL_TASK_RUNNER_H
7+
8+
#include <cstddef>
9+
#include <functional>
10+
11+
namespace util {
12+
13+
/** @file
14+
* This header provides an interface and simple implementation for a task
15+
* runner. Another threaded, serial implementation using a queue is available in
16+
* the scheduler module's SerialTaskRunner.
17+
*/
18+
19+
class TaskRunnerInterface
20+
{
21+
public:
22+
virtual ~TaskRunnerInterface() {}
23+
24+
/**
25+
* The callback can either be queued for later/asynchronous/threaded
26+
* processing, or be executed immediately for synchronous processing.
27+
*/
28+
29+
virtual void insert(std::function<void()> func) = 0;
30+
31+
/**
32+
* Forces the processing of all pending events.
33+
*/
34+
virtual void flush() = 0;
35+
36+
/**
37+
* Returns the number of currently pending events.
38+
*/
39+
virtual size_t size() = 0;
40+
};
41+
42+
class ImmediateTaskRunner : public TaskRunnerInterface
43+
{
44+
public:
45+
void insert(std::function<void()> func) override { func(); }
46+
void flush() override {}
47+
size_t size() override { return 0; }
48+
};
49+
50+
} // namespace util
51+
52+
#endif // BITCOIN_UTIL_TASK_RUNNER_H

src/validationinterface.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
#include <validationinterface.h>
77

8-
#include <attributes.h>
98
#include <chain.h>
109
#include <consensus/validation.h>
1110
#include <kernel/chain.h>
1211
#include <kernel/mempool_entry.h>
1312
#include <logging.h>
1413
#include <primitives/block.h>
1514
#include <primitives/transaction.h>
16-
#include <scheduler.h>
15+
#include <util/check.h>
16+
#include <util/task_runner.h>
1717

1818
#include <future>
1919
#include <unordered_map>
@@ -42,12 +42,10 @@ class ValidationSignalsImpl
4242
std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
4343

4444
public:
45-
// We are not allowed to assume the scheduler only runs in one thread,
46-
// but must ensure all callbacks happen in-order, so we end up creating
47-
// our own queue here :(
48-
SerialTaskRunner m_task_runner;
45+
std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
4946

50-
explicit ValidationSignalsImpl(CScheduler& scheduler LIFETIMEBOUND) : m_task_runner(scheduler) {}
47+
explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)
48+
: m_task_runner{std::move(Assert(task_runner))} {}
5149

5250
void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
5351
{
@@ -94,19 +92,19 @@ class ValidationSignalsImpl
9492
}
9593
};
9694

97-
ValidationSignals::ValidationSignals(CScheduler& scheduler)
98-
: m_internals{std::make_unique<ValidationSignalsImpl>(scheduler)} {}
95+
ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
96+
: m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
9997

10098
ValidationSignals::~ValidationSignals() {}
10199

102100
void ValidationSignals::FlushBackgroundCallbacks()
103101
{
104-
m_internals->m_task_runner.flush();
102+
m_internals->m_task_runner->flush();
105103
}
106104

107105
size_t ValidationSignals::CallbacksPending()
108106
{
109-
return m_internals->m_task_runner.size();
107+
return m_internals->m_task_runner->size();
110108
}
111109

112110
void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
@@ -140,7 +138,7 @@ void ValidationSignals::UnregisterAllValidationInterfaces()
140138

141139
void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)
142140
{
143-
m_internals->m_task_runner.insert(std::move(func));
141+
m_internals->m_task_runner->insert(std::move(func));
144142
}
145143

146144
void ValidationSignals::SyncWithValidationInterfaceQueue()
@@ -162,7 +160,7 @@ void ValidationSignals::SyncWithValidationInterfaceQueue()
162160
do { \
163161
auto local_name = (name); \
164162
LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__); \
165-
m_internals->m_task_runner.insert([=] { \
163+
m_internals->m_task_runner->insert([=] { \
166164
LOG_EVENT(fmt, local_name, __VA_ARGS__); \
167165
event(); \
168166
}); \

src/validationinterface.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
#include <memory>
1818
#include <vector>
1919

20+
namespace util {
21+
class TaskRunnerInterface;
22+
} // namespace util
23+
2024
class BlockValidationState;
2125
class CBlock;
2226
class CBlockIndex;
2327
struct CBlockLocator;
24-
class CScheduler;
2528
enum class MemPoolRemovalReason;
2629
struct RemovedMempoolTransactionInfo;
2730
struct NewMempoolTransactionInfo;
@@ -160,7 +163,10 @@ class ValidationSignals {
160163
std::unique_ptr<ValidationSignalsImpl> m_internals;
161164

162165
public:
163-
ValidationSignals(CScheduler& scheduler LIFETIMEBOUND);
166+
// The task runner will block validation if it calls its insert method's
167+
// func argument synchronously. In this class func contains a loop that
168+
// dispatches a single validation event to all subscribers sequentially.
169+
explicit ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner);
164170

165171
~ValidationSignals();
166172

0 commit comments

Comments
 (0)