6
6
#define BITCOIN_CHECKQUEUE_H
7
7
8
8
#include < sync.h>
9
+ #include < tinyformat.h>
10
+ #include < util/threadnames.h>
9
11
10
12
#include < algorithm>
11
13
#include < vector>
12
14
13
- #include < boost/thread/condition_variable.hpp>
14
- #include < boost/thread/mutex.hpp>
15
-
16
15
template <typename T>
17
16
class CCheckQueueControl ;
18
17
@@ -31,61 +30,64 @@ class CCheckQueue
31
30
{
32
31
private:
33
32
// ! Mutex to protect the inner state
34
- boost::mutex mutex ;
33
+ Mutex m_mutex ;
35
34
36
35
// ! Worker threads block on this when out of work
37
- boost ::condition_variable condWorker ;
36
+ std ::condition_variable m_worker_cv ;
38
37
39
38
// ! Master thread blocks on this when out of work
40
- boost ::condition_variable condMaster ;
39
+ std ::condition_variable m_master_cv ;
41
40
42
41
// ! The queue of elements to be processed.
43
42
// ! As the order of booleans doesn't matter, it is used as a LIFO (stack)
44
- std::vector<T> queue;
43
+ std::vector<T> queue GUARDED_BY (m_mutex) ;
45
44
46
45
// ! The number of workers (including the master) that are idle.
47
- int nIdle;
46
+ int nIdle GUARDED_BY (m_mutex){ 0 } ;
48
47
49
48
// ! The total number of workers (including the master).
50
- int nTotal;
49
+ int nTotal GUARDED_BY (m_mutex){ 0 } ;
51
50
52
51
// ! The temporary evaluation result.
53
- bool fAllOk ;
52
+ bool fAllOk GUARDED_BY (m_mutex){ true } ;
54
53
55
54
/* *
56
55
* Number of verifications that haven't completed yet.
57
56
* This includes elements that are no longer queued, but still in the
58
57
* worker's own batches.
59
58
*/
60
- unsigned int nTodo;
59
+ unsigned int nTodo GUARDED_BY (m_mutex){ 0 } ;
61
60
62
61
// ! The maximum number of elements to be processed in one batch
63
- unsigned int nBatchSize;
62
+ const unsigned int nBatchSize;
63
+
64
+ std::vector<std::thread> m_worker_threads;
65
+ bool m_request_stop GUARDED_BY (m_mutex){false };
64
66
65
67
/* * Internal function that does bulk of the verification work. */
66
- bool Loop (bool fMaster = false )
68
+ bool Loop (bool fMaster )
67
69
{
68
- boost ::condition_variable& cond = fMaster ? condMaster : condWorker ;
70
+ std ::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv ;
69
71
std::vector<T> vChecks;
70
72
vChecks.reserve (nBatchSize);
71
73
unsigned int nNow = 0 ;
72
74
bool fOk = true ;
73
75
do {
74
76
{
75
- boost::unique_lock<boost::mutex> lock (mutex );
77
+ WAIT_LOCK (m_mutex, lock);
76
78
// first do the clean-up of the previous loop run (allowing us to do it in the same critsect)
77
79
if (nNow) {
78
80
fAllOk &= fOk ;
79
81
nTodo -= nNow;
80
82
if (nTodo == 0 && !fMaster )
81
83
// We processed the last element; inform the master it can exit and return the result
82
- condMaster .notify_one ();
84
+ m_master_cv .notify_one ();
83
85
} else {
84
86
// first iteration
85
87
nTotal++;
86
88
}
87
89
// logically, the do loop starts here
88
- while (queue.empty ()) {
90
+ while (queue.empty () && !m_request_stop ) {
89
91
if (fMaster && nTodo == 0 ) {
90
92
nTotal--;
91
93
bool fRet = fAllOk ;
@@ -98,6 +100,10 @@ class CCheckQueue
98
100
cond.wait (lock); // wait
99
101
nIdle--;
100
102
}
103
+ if (m_request_stop) {
104
+ return false ;
105
+ }
106
+
101
107
// Decide how many work units to process now.
102
108
// * Do not try to do everything at once, but aim for increasingly smaller batches so
103
109
// all workers finish approximately simultaneously.
@@ -106,7 +112,7 @@ class CCheckQueue
106
112
nNow = std::max (1U , std::min (nBatchSize, (unsigned int )queue.size () / (nTotal + nIdle + 1 )));
107
113
vChecks.resize (nNow);
108
114
for (unsigned int i = 0 ; i < nNow; i++) {
109
- // We want the lock on the mutex to be as short as possible, so swap jobs from the global
115
+ // We want the lock on the m_mutex to be as short as possible, so swap jobs from the global
110
116
// queue to the local batch vector instead of copying.
111
117
vChecks[i].swap (queue.back ());
112
118
queue.pop_back ();
@@ -124,40 +130,68 @@ class CCheckQueue
124
130
125
131
public:
126
132
// ! Mutex to ensure only one concurrent CCheckQueueControl
127
- boost::mutex ControlMutex ;
133
+ Mutex m_control_mutex ;
128
134
129
135
// ! Create a new check queue
130
- explicit CCheckQueue (unsigned int nBatchSizeIn) : nIdle(0 ), nTotal(0 ), fAllOk(true ), nTodo(0 ), nBatchSize(nBatchSizeIn) {}
136
+ explicit CCheckQueue (unsigned int nBatchSizeIn)
137
+ : nBatchSize(nBatchSizeIn)
138
+ {
139
+ }
131
140
132
- // ! Worker thread
133
- void Thread ( )
141
+ // ! Create a pool of new worker threads.
142
+ void StartWorkerThreads ( const int threads_num )
134
143
{
135
- Loop ();
144
+ {
145
+ LOCK (m_mutex);
146
+ nIdle = 0 ;
147
+ nTotal = 0 ;
148
+ fAllOk = true ;
149
+ }
150
+ assert (m_worker_threads.empty ());
151
+ for (int n = 0 ; n < threads_num; ++n) {
152
+ m_worker_threads.emplace_back ([this , n]() {
153
+ util::ThreadRename (strprintf (" scriptch.%i" , n));
154
+ Loop (false /* worker thread */ );
155
+ });
156
+ }
136
157
}
137
158
138
159
// ! Wait until execution finishes, and return whether all evaluations were successful.
139
160
bool Wait ()
140
161
{
141
- return Loop (true );
162
+ return Loop (true /* master thread */ );
142
163
}
143
164
144
165
// ! Add a batch of checks to the queue
145
166
void Add (std::vector<T>& vChecks)
146
167
{
147
- boost::unique_lock<boost::mutex> lock (mutex );
168
+ LOCK (m_mutex );
148
169
for (T& check : vChecks) {
149
170
queue.push_back (T ());
150
171
check.swap (queue.back ());
151
172
}
152
173
nTodo += vChecks.size ();
153
174
if (vChecks.size () == 1 )
154
- condWorker .notify_one ();
175
+ m_worker_cv .notify_one ();
155
176
else if (vChecks.size () > 1 )
156
- condWorker.notify_all ();
177
+ m_worker_cv.notify_all ();
178
+ }
179
+
180
+ // ! Stop all of the worker threads.
181
+ void StopWorkerThreads ()
182
+ {
183
+ WITH_LOCK (m_mutex, m_request_stop = true );
184
+ m_worker_cv.notify_all ();
185
+ for (std::thread& t : m_worker_threads) {
186
+ t.join ();
187
+ }
188
+ m_worker_threads.clear ();
189
+ WITH_LOCK (m_mutex, m_request_stop = false );
157
190
}
158
191
159
192
~CCheckQueue ()
160
193
{
194
+ assert (m_worker_threads.empty ());
161
195
}
162
196
163
197
};
@@ -181,7 +215,7 @@ class CCheckQueueControl
181
215
{
182
216
// passed queue is supposed to be unused, or nullptr
183
217
if (pqueue != nullptr ) {
184
- ENTER_CRITICAL_SECTION (pqueue->ControlMutex );
218
+ ENTER_CRITICAL_SECTION (pqueue->m_control_mutex );
185
219
}
186
220
}
187
221
@@ -205,7 +239,7 @@ class CCheckQueueControl
205
239
if (!fDone )
206
240
Wait ();
207
241
if (pqueue != nullptr ) {
208
- LEAVE_CRITICAL_SECTION (pqueue->ControlMutex );
242
+ LEAVE_CRITICAL_SECTION (pqueue->m_control_mutex );
209
243
}
210
244
}
211
245
};
0 commit comments