Bitcoin ABC 0.33.10
P2P Digital Currency
checkqueue.h
Go to the documentation of this file.
1// Copyright (c) 2012-2018 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_CHECKQUEUE_H
6#define BITCOIN_CHECKQUEUE_H
7
8#include <sync.h>
9#include <tinyformat.h>
10#include <util/threadnames.h>
11
12#include <algorithm>
13#include <iterator>
14#include <optional>
15#include <vector>
16
29template <typename T,
30 typename R =
31 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
33private:
36
38 std::condition_variable m_worker_cv;
39
41 std::condition_variable m_master_cv;
42
45 std::vector<T> queue GUARDED_BY(m_mutex);
46
48 int nIdle GUARDED_BY(m_mutex){0};
49
51 int nTotal GUARDED_BY(m_mutex){0};
52
54 std::optional<R> m_result GUARDED_BY(m_mutex);
55
61 unsigned int nTodo GUARDED_BY(m_mutex){0};
62
64 const unsigned int nBatchSize;
65
66 std::vector<std::thread> m_worker_threads;
67 bool m_request_stop GUARDED_BY(m_mutex){false};
68
73 std::optional<R> Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
74 std::condition_variable &cond = fMaster ? m_master_cv : m_worker_cv;
75 std::vector<T> vChecks;
76 vChecks.reserve(nBatchSize);
77 unsigned int nNow = 0;
78 std::optional<R> local_result;
79 bool do_work;
80 do {
81 {
82 WAIT_LOCK(m_mutex, lock);
83 // first do the clean-up of the previous loop run (allowing us
84 // to do it in the same critsect)
85 if (nNow) {
86 if (local_result.has_value() && !m_result.has_value()) {
87 std::swap(local_result, m_result);
88 }
89 nTodo -= nNow;
90 if (nTodo == 0 && !fMaster) {
91 // We processed the last element; inform the master it
92 // can exit and return the result
93 m_master_cv.notify_one();
94 }
95 } else {
96 // first iteration
97 nTotal++;
98 }
99 // logically, the do loop starts here
100 while (queue.empty() && !m_request_stop) {
101 if (fMaster && nTodo == 0) {
102 nTotal--;
103 std::optional<R> to_return = std::move(m_result);
104 // reset the status for new work later
105 m_result = std::nullopt;
106 // return the current status
107 return to_return;
108 }
109 nIdle++;
110 cond.wait(lock); // wait
111 nIdle--;
112 }
113 if (m_request_stop) {
114 // return value does not matter, because m_request_stop is
115 // only set in the destructor.
116 return std::nullopt;
117 }
118
119 // Decide how many work units to process now.
120 // * Do not try to do everything at once, but aim for
121 // increasingly smaller batches so all workers finish
122 // approximately simultaneously.
123 // * Try to account for idle jobs which will instantly start
124 // helping.
125 // * Don't do batches smaller than 1 (duh), or larger than
126 // nBatchSize.
127 nNow = std::max(
128 1U, std::min(nBatchSize, (unsigned int)queue.size() /
129 (nTotal + nIdle + 1)));
130 auto start_it = queue.end() - nNow;
131 vChecks.assign(std::make_move_iterator(start_it),
132 std::make_move_iterator(queue.end()));
133 queue.erase(start_it, queue.end());
134 // Check whether we need to do work at all
135 do_work = !m_result.has_value();
136 }
137 // execute work
138 if (do_work) {
139 for (T &check : vChecks) {
140 local_result = check();
141 if (local_result.has_value()) {
142 break;
143 }
144 }
145 }
146 vChecks.clear();
147 } while (true);
148 }
149
150public:
153
155 explicit CCheckQueue(unsigned int batch_size, int worker_threads_num)
156 : nBatchSize(batch_size) {
157 m_worker_threads.reserve(worker_threads_num);
158 for (int n = 0; n < worker_threads_num; ++n) {
159 m_worker_threads.emplace_back([this, n]() {
160 util::ThreadRename(strprintf("scriptch.%i", n));
161 Loop(false /* worker thread */);
162 });
163 }
164 }
165
166 // Since this class manages its own resources, which is a thread
167 // pool `m_worker_threads`, copy and move operations are not appropriate.
168 CCheckQueue(const CCheckQueue &) = delete;
172
175 std::optional<R> Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
176 return Loop(true /* master thread */);
177 }
178
180 void Add(std::vector<T> &&vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
181 LOCK(m_mutex);
182 queue.insert(queue.end(), std::make_move_iterator(vChecks.begin()),
183 std::make_move_iterator(vChecks.end()));
184 nTodo += vChecks.size();
185 if (vChecks.size() == 1) {
186 m_worker_cv.notify_one();
187 } else if (vChecks.size() > 1) {
188 m_worker_cv.notify_all();
189 }
190 }
191
193 WITH_LOCK(m_mutex, m_request_stop = true);
194 m_worker_cv.notify_all();
195 for (std::thread &t : m_worker_threads) {
196 t.join();
197 }
198 }
199};
200
205template <typename T,
206 typename R =
207 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
209private:
211 bool fDone;
212
213public:
217 explicit CCheckQueueControl(CCheckQueue<T> *const pqueueIn)
218 : pqueue(pqueueIn), fDone(false) {
219 // passed queue is supposed to be unused, or nullptr
220 if (pqueue != nullptr) {
222 }
223 }
224
225 std::optional<R> Complete() {
226 if (pqueue == nullptr) {
227 return std::nullopt;
228 }
229 auto ret = pqueue->Complete();
230 fDone = true;
231 return ret;
232 }
233
234 void Add(std::vector<T> &&vChecks) {
235 if (pqueue != nullptr) {
236 pqueue->Add(std::move(vChecks));
237 }
238 }
239
241 if (!fDone) {
242 Complete();
243 }
244 if (pqueue != nullptr) {
246 }
247 }
248};
249
250#endif // BITCOIN_CHECKQUEUE_H
RAII-style controller object for a CCheckQueue that guarantees the passed queue is finished before co...
Definition: checkqueue.h:208
CCheckQueueControl & operator=(const CCheckQueueControl &)=delete
CCheckQueueControl(const CCheckQueueControl &)=delete
CCheckQueueControl()=delete
CCheckQueue< T, R > *const pqueue
Definition: checkqueue.h:210
CCheckQueueControl(CCheckQueue< T > *const pqueueIn)
Definition: checkqueue.h:217
std::optional< R > Complete()
Definition: checkqueue.h:225
void Add(std::vector< T > &&vChecks)
Definition: checkqueue.h:234
The verifications are represented by a type T, which must provide an operator(), returning an std::op...
Definition: checkqueue.h:32
bool m_request_stop GUARDED_BY(m_mutex)
Definition: checkqueue.h:67
std::optional< R > Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Join the execution until completion.
Definition: checkqueue.h:175
unsigned int nTodo GUARDED_BY(m_mutex)
Number of verifications that haven't completed yet.
Definition: checkqueue.h:61
Mutex m_control_mutex
Mutex to ensure only one concurrent CCheckQueueControl.
Definition: checkqueue.h:152
std::vector< T > queue GUARDED_BY(m_mutex)
The queue of elements to be processed.
CCheckQueue(const CCheckQueue &)=delete
void Add(std::vector< T > &&vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add a batch of checks to the queue.
Definition: checkqueue.h:180
int nIdle GUARDED_BY(m_mutex)
The number of workers (including the master) that are idle.
Definition: checkqueue.h:48
CCheckQueue & operator=(CCheckQueue &&)=delete
int nTotal GUARDED_BY(m_mutex)
The total number of workers (including the master).
Definition: checkqueue.h:51
CCheckQueue(unsigned int batch_size, int worker_threads_num)
Create a new check queue.
Definition: checkqueue.h:155
Mutex m_mutex
Mutex to protect the inner state.
Definition: checkqueue.h:35
std::optional< R > Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Internal function that does bulk of the verification work.
Definition: checkqueue.h:73
std::condition_variable m_worker_cv
Worker threads block on this when out of work.
Definition: checkqueue.h:38
std::vector< std::thread > m_worker_threads
Definition: checkqueue.h:66
const unsigned int nBatchSize
The maximum number of elements to be processed in one batch.
Definition: checkqueue.h:64
std::optional< R > m_result GUARDED_BY(m_mutex)
The temporary evaluation result.
CCheckQueue(CCheckQueue &&)=delete
std::condition_variable m_master_cv
Master thread blocks on this when out of work.
Definition: checkqueue.h:41
CCheckQueue & operator=(const CCheckQueue &)=delete
void ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name.
Definition: threadnames.cpp:48
#define WAIT_LOCK(cs, name)
Definition: sync.h:317
#define ENTER_CRITICAL_SECTION(cs)
Definition: sync.h:320
#define LEAVE_CRITICAL_SECTION(cs)
Definition: sync.h:326
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202