xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
libxrpl
core
detail
Workers.cpp
1
#include <xrpl/core/detail/Workers.h>
2
3
#include <xrpl/beast/core/CurrentThreadName.h>
4
#include <xrpl/beast/core/LockFreeStack.h>
5
#include <xrpl/core/PerfLog.h>
6
7
#include <
mutex
>
8
#include <
string
>
9
#include <
utility
>
10
11
namespace
xrpl
{
12
13
Workers::Workers
(
14
Callback
& callback,
15
perf::PerfLog
* perfLog,
16
std::string
threadNames,
17
int
numberOfThreads)
18
:
callback_
(callback)
19
,
perfLog_
(perfLog)
20
,
threadNames_
(
std
::move(threadNames))
21
,
semaphore_
(0)
22
,
activeCount_
(0)
23
,
pauseCount_
(0)
24
,
runningTaskCount_
(0)
25
{
26
setNumberOfThreads
(numberOfThreads);
27
}
28
29
Workers::~Workers
()
30
{
31
stop
();
32
33
deleteWorkers
(
everyone_
);
34
}
35
36
int
37
Workers::getNumberOfThreads
() const noexcept
38
{
39
return
numberOfThreads_
;
40
}
41
42
// VFALCO NOTE if this function is called quickly to reduce then
43
// increase the number of threads, it could result in
44
// more paused threads being created than expected.
45
//
46
void
47
Workers::setNumberOfThreads
(
int
numberOfThreads)
48
{
49
static
int
kInstance{0};
50
if
(
numberOfThreads_
== numberOfThreads)
51
return
;
52
53
if
(
perfLog_
!=
nullptr
)
54
perfLog_
->resizeJobs(numberOfThreads);
55
56
if
(numberOfThreads >
numberOfThreads_
)
57
{
58
// Increasing the number of working threads
59
int
const
amount = numberOfThreads -
numberOfThreads_
;
60
61
for
(
int
i = 0; i < amount; ++i)
62
{
63
// See if we can reuse a paused worker
64
Worker
* worker =
paused_
.popFront();
65
66
if
(worker !=
nullptr
)
67
{
68
// If we got here then the worker thread is at [1]
69
// This will unblock their call to wait()
70
//
71
worker->notify();
72
}
73
else
74
{
75
worker =
new
Worker
(*
this
,
threadNames_
, kInstance++);
76
everyone_
.pushFront(worker);
77
}
78
}
79
}
80
else
81
{
82
// Decreasing the number of working threads
83
int
const
amount =
numberOfThreads_
- numberOfThreads;
84
85
for
(
int
i = 0; i < amount; ++i)
86
{
87
++
pauseCount_
;
88
89
// Pausing a thread counts as one "internal task"
90
semaphore_
.notify();
91
}
92
}
93
94
numberOfThreads_
= numberOfThreads;
95
}
96
97
void
98
Workers::stop
()
99
{
100
setNumberOfThreads
(0);
101
102
// Wait until all workers have paused AND no tasks are actively running.
103
// Both conditions are needed because allPaused_ (mutex-protected) and
104
// runningTaskCount_ (atomic) are not synchronized under the same lock,
105
// so allPaused_ can momentarily be true while a task is still finishing.
106
std::unique_lock<std::mutex>
lk{
mut_
};
107
cv_
.wait(lk, [
this
] {
return
allPaused_
&&
numberOfCurrentlyRunningTasks
() == 0; });
108
lk.
unlock
();
109
}
110
111
void
112
Workers::addTask
()
113
{
114
semaphore_
.notify();
115
}
116
117
int
118
Workers::numberOfCurrentlyRunningTasks
() const noexcept
119
{
120
return
runningTaskCount_
.load();
121
}
122
123
void
124
Workers::deleteWorkers
(
beast::LockFreeStack<Worker>
& stack)
125
{
126
for
(;;)
127
{
128
Worker
const
*
const
worker = stack.
popFront
();
129
130
if
(worker ==
nullptr
)
131
break
;
132
133
// This call blocks until the thread orderly exits
134
delete
worker;
135
}
136
}
137
138
//------------------------------------------------------------------------------
139
140
Workers::Worker::Worker
(
Workers
& workers,
std::string
threadName,
int
const
instance)
141
:
workers_
{workers},
threadName_
{
std
::move(threadName)},
instance_
{instance}
142
143
{
144
thread_
=
std::thread
{&
Workers::Worker::run
,
this
};
145
}
146
147
Workers::Worker::~Worker
()
148
{
149
{
150
std::scoped_lock
const
lock{
mutex_
};
151
++
wakeCount_
;
152
shouldExit_
=
true
;
153
}
154
155
wakeup_
.notify_one();
156
thread_
.join();
157
}
158
159
void
160
Workers::Worker::notify
()
161
{
162
std::scoped_lock
const
lock{
mutex_
};
163
++
wakeCount_
;
164
wakeup_
.notify_one();
165
}
166
167
void
168
Workers::Worker::run
()
169
{
170
bool
shouldExit =
true
;
171
do
172
{
173
// Increment the count of active workers, and if
174
// we are the first one then reset the "all paused" event
175
//
176
if
(++
workers_
.activeCount_ == 1)
177
{
178
std::scoped_lock
const
lk{
workers_
.mut_};
179
workers_
.allPaused_ =
false
;
180
}
181
182
for
(;;)
183
{
184
// Put the name back in case the callback changed it
185
beast::setCurrentThreadName
(
threadName_
);
186
187
// Acquire a task or "internal task."
188
//
189
workers_
.semaphore_.wait();
190
191
// See if there's a pause request. This
192
// counts as an "internal task."
193
//
194
int
pauseCount =
workers_
.pauseCount_.load();
195
196
if
(pauseCount > 0)
197
{
198
// Try to decrement
199
pauseCount = --
workers_
.pauseCount_;
200
201
if
(pauseCount >= 0)
202
{
203
// We got paused
204
break
;
205
}
206
207
// Undo our decrement
208
++
workers_
.pauseCount_;
209
}
210
211
// We couldn't pause so we must have gotten
212
// unblocked in order to process a task.
213
//
214
++
workers_
.runningTaskCount_;
215
workers_
.callback_.processTask(
instance_
);
216
217
// When the running task count drops to zero, wake stop() which
218
// may be waiting for both allPaused_ and zero running tasks.
219
// Locking mut_ before notify_all() prevents a lost wakeup:
220
// it serializes against the predicate check inside stop()'s
221
// cv_.wait(), ensuring the notification is not missed between
222
// the predicate evaluation and the actual sleep.
223
if
(--
workers_
.runningTaskCount_ == 0)
224
{
225
std::scoped_lock
const
lk{
workers_
.mut_};
226
workers_
.cv_.notify_all();
227
}
228
}
229
230
// Any worker that goes into the paused list must
231
// guarantee that it will eventually block on its
232
// event object.
233
//
234
workers_
.paused_.pushFront(
this
);
235
236
// Decrement the count of active workers, and if we
237
// are the last one then signal the "all paused" event.
238
//
239
if
(--
workers_
.activeCount_ == 0)
240
{
241
std::scoped_lock
const
lk{
workers_
.mut_};
242
workers_
.allPaused_ =
true
;
243
workers_
.cv_.notify_all();
244
}
245
246
// Set inactive thread name.
247
beast::setCurrentThreadName
(
"("
+
threadName_
+
")"
);
248
249
// [1] We will be here when the paused list is popped
250
//
251
// We block on our condition_variable, wakeup_, a requirement of being
252
// put into the paused list.
253
//
254
// wakeup_ will get signaled by either Worker::notify() or ~Worker.
255
{
256
std::unique_lock<std::mutex>
lock{
mutex_
};
257
wakeup_
.wait(lock, [
this
] {
return
this->
wakeCount_
> 0; });
258
259
shouldExit =
shouldExit_
;
260
--
wakeCount_
;
261
}
262
}
while
(!shouldExit);
263
}
264
265
}
// namespace xrpl
std::string
beast::LockFreeStack
Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
Definition
LockFreeStack.h:121
beast::LockFreeStack::popFront
Element * popFront()
Pop an element off the stack.
Definition
LockFreeStack.h:213
xrpl::Workers::Worker
Definition
Workers.h:174
xrpl::Workers::Worker::workers_
Workers & workers_
Definition
Workers.h:188
xrpl::Workers::Worker::~Worker
~Worker()
Definition
Workers.cpp:147
xrpl::Workers::Worker::run
void run()
Definition
Workers.cpp:168
xrpl::Workers::Worker::threadName_
std::string const threadName_
Definition
Workers.h:189
xrpl::Workers::Worker::thread_
std::thread thread_
Definition
Workers.h:192
xrpl::Workers::Worker::mutex_
std::mutex mutex_
Definition
Workers.h:193
xrpl::Workers::Worker::instance_
int const instance_
Definition
Workers.h:190
xrpl::Workers::Worker::wakeCount_
int wakeCount_
Definition
Workers.h:195
xrpl::Workers::Worker::notify
void notify()
Definition
Workers.cpp:160
xrpl::Workers::Worker::Worker
Worker(Workers &workers, std::string threadName, int const instance)
Definition
Workers.cpp:140
xrpl::Workers::Worker::wakeup_
std::condition_variable wakeup_
Definition
Workers.h:194
xrpl::Workers::Worker::shouldExit_
bool shouldExit_
Definition
Workers.h:196
xrpl::Workers::setNumberOfThreads
void setNumberOfThreads(int numberOfThreads)
Set the desired number of threads.
Definition
Workers.cpp:47
xrpl::Workers::semaphore_
Semaphore semaphore_
Definition
Workers.h:210
xrpl::Workers::stop
void stop()
Pause all threads and wait until they are paused.
Definition
Workers.cpp:98
xrpl::Workers::numberOfThreads_
int numberOfThreads_
Definition
Workers.h:211
xrpl::Workers::activeCount_
std::atomic< int > activeCount_
Definition
Workers.h:212
xrpl::Workers::mut_
std::mutex mut_
Definition
Workers.h:208
xrpl::Workers::paused_
beast::LockFreeStack< Worker, PausedTag > paused_
Definition
Workers.h:216
xrpl::Workers::numberOfCurrentlyRunningTasks
int numberOfCurrentlyRunningTasks() const noexcept
Get the number of currently executing calls of Callback::processTask.
Definition
Workers.cpp:118
xrpl::Workers::pauseCount_
std::atomic< int > pauseCount_
Definition
Workers.h:213
xrpl::Workers::callback_
Callback & callback_
Definition
Workers.h:204
xrpl::Workers::addTask
void addTask()
Add a task to be performed.
Definition
Workers.cpp:112
xrpl::Workers::~Workers
~Workers()
Definition
Workers.cpp:29
xrpl::Workers::Workers
Workers(Callback &callback, perf::PerfLog *perfLog, std::string threadNames="Worker", int numberOfThreads=static_cast< int >(std::thread::hardware_concurrency()))
Create the object.
Definition
Workers.cpp:13
xrpl::Workers::perfLog_
perf::PerfLog * perfLog_
Definition
Workers.h:205
xrpl::Workers::deleteWorkers
static void deleteWorkers(beast::LockFreeStack< Worker > &stack)
Definition
Workers.cpp:124
xrpl::Workers::threadNames_
std::string threadNames_
Definition
Workers.h:206
xrpl::Workers::allPaused_
bool allPaused_
Definition
Workers.h:209
xrpl::Workers::cv_
std::condition_variable cv_
Definition
Workers.h:207
xrpl::Workers::runningTaskCount_
std::atomic< int > runningTaskCount_
Definition
Workers.h:214
xrpl::Workers::everyone_
beast::LockFreeStack< Worker > everyone_
Definition
Workers.h:215
xrpl::Workers::getNumberOfThreads
int getNumberOfThreads() const noexcept
Retrieve the desired number of threads.
Definition
Workers.cpp:37
xrpl::perf::PerfLog
Singleton class that maintains performance counters and optionally writes Json-formatted data to a di...
Definition
PerfLog.h:33
mutex
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
Definition
CurrentThreadName.cpp:115
std
STL namespace.
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
std::scoped_lock
string
xrpl::Workers::Callback
Called to perform tasks as needed.
Definition
Workers.h:67
std::thread
std::unique_lock
std::unique_lock::unlock
T unlock(T... args)
utility
Generated by
1.17.0