Bitcoin ABC 0.34.2
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 <logging.h>
9#include <sync.h>
10#include <tinyformat.h>
11#include <util/threadnames.h>
12
13#include <algorithm>
14#include <iterator>
15#include <optional>
16#include <vector>
17
30template <typename T,
31 typename R =
32 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
34private:
37
39 std::condition_variable m_worker_cv;
40
42 std::condition_variable m_master_cv;
43
46 std::vector<T> queue GUARDED_BY(m_mutex);
47
49 int nIdle GUARDED_BY(m_mutex){0};
50
52 int nTotal GUARDED_BY(m_mutex){0};
53
55 std::optional<R> m_result GUARDED_BY(m_mutex);
56
62 unsigned int nTodo GUARDED_BY(m_mutex){0};
63
65 const unsigned int nBatchSize;
66
67 std::vector<std::thread> m_worker_threads;
68 bool m_request_stop GUARDED_BY(m_mutex){false};
69
74 std::optional<R> Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
75 std::condition_variable &cond = fMaster ? m_master_cv : m_worker_cv;
76 std::vector<T> vChecks;
77 vChecks.reserve(nBatchSize);
78 unsigned int nNow = 0;
79 std::optional<R> local_result;
80 bool do_work;
81 do {
82 {
83 WAIT_LOCK(m_mutex, lock);
84 // first do the clean-up of the previous loop run (allowing us
85 // to do it in the same critsect)
86 if (nNow) {
87 if (local_result.has_value() && !m_result.has_value()) {
88 std::swap(local_result, m_result);
89 }
90 nTodo -= nNow;
91 if (nTodo == 0 && !fMaster) {
92 // We processed the last element; inform the master it
93 // can exit and return the result
94 m_master_cv.notify_one();
95 }
96 } else {
97 // first iteration
98 nTotal++;
99 }
100 // logically, the do loop starts here
101 while (queue.empty() && !m_request_stop) {
102 if (fMaster && nTodo == 0) {
103 nTotal--;
104 std::optional<R> to_return = std::move(m_result);
105 // reset the status for new work later
106 m_result = std::nullopt;
107 // return the current status
108 return to_return;
109 }
110 nIdle++;
111 cond.wait(lock); // wait
112 nIdle--;
113 }
114 if (m_request_stop) {
115 // return value does not matter, because m_request_stop is
116 // only set in the destructor.
117 return std::nullopt;
118 }
119
120 // Decide how many work units to process now.
121 // * Do not try to do everything at once, but aim for
122 // increasingly smaller batches so all workers finish
123 // approximately simultaneously.
124 // * Try to account for idle jobs which will instantly start
125 // helping.
126 // * Don't do batches smaller than 1 (duh), or larger than
127 // nBatchSize.
128 nNow = std::max(
129 1U, std::min(nBatchSize, (unsigned int)queue.size() /
130 (nTotal + nIdle + 1)));
131 auto start_it = queue.end() - nNow;
132 vChecks.assign(std::make_move_iterator(start_it),
133 std::make_move_iterator(queue.end()));
134 queue.erase(start_it, queue.end());
135 // Check whether we need to do work at all
136 do_work = !m_result.has_value();
137 }
138 // execute work
139 if (do_work) {
140 for (T &check : vChecks) {
141 local_result = check();
142 if (local_result.has_value()) {
143 break;
144 }
145 }
146 }
147 vChecks.clear();
148 } while (true);
149 }
150
151public:
154
156 explicit CCheckQueue(unsigned int batch_size, int worker_threads_num)
157 : nBatchSize(batch_size) {
158 LogInfo("Script verification uses %d additional threads\n",
159 worker_threads_num);
160 m_worker_threads.reserve(worker_threads_num);
161 for (int n = 0; n < worker_threads_num; ++n) {
162 m_worker_threads.emplace_back([this, n]() {
163 util::ThreadRename(strprintf("scriptch.%i", n));
164 Loop(false /* worker thread */);
165 });
166 }
167 }
168
169 // Since this class manages its own resources, which is a thread
170 // pool `m_worker_threads`, copy and move operations are not appropriate.
171 CCheckQueue(const CCheckQueue &) = delete;
175
178 std::optional<R> Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
179 return Loop(true /* master thread */);
180 }
181
183 void Add(std::vector<T> &&vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) {
184 LOCK(m_mutex);
185 queue.insert(queue.end(), std::make_move_iterator(vChecks.begin()),
186 std::make_move_iterator(vChecks.end()));
187 nTodo += vChecks.size();
188 if (vChecks.size() == 1) {
189 m_worker_cv.notify_one();
190 } else if (vChecks.size() > 1) {
191 m_worker_cv.notify_all();
192 }
193 }
194
196 WITH_LOCK(m_mutex, m_request_stop = true);
197 m_worker_cv.notify_all();
198 for (std::thread &t : m_worker_threads) {
199 t.join();
200 }
201 }
202};
203
208template <typename T,
209 typename R =
210 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
212private:
215 bool fDone;
216
217public:
222 EXCLUSIVE_LOCK_FUNCTION(queueIn.m_control_mutex)
223 : m_queue(queueIn), m_lock(LOCK_ARGS(queueIn.m_control_mutex)),
224 fDone(false) {}
225
226 std::optional<R> Complete() {
227 auto ret = m_queue.Complete();
228 fDone = true;
229 return ret;
230 }
231
232 void Add(std::vector<T> &&vChecks) { m_queue.Add(std::move(vChecks)); }
233
235 if (!fDone) {
236 Complete();
237 }
238 }
239};
240
241#endif // BITCOIN_CHECKQUEUE_H
RAII-style controller object for a CCheckQueue that guarantees the passed queue is finished before co...
Definition: checkqueue.h:211
CCheckQueueControl & operator=(const CCheckQueueControl &)=delete
CCheckQueueControl(const CCheckQueueControl &)=delete
CCheckQueueControl()=delete
~CCheckQueueControl() UNLOCK_FUNCTION()
Definition: checkqueue.h:234
UniqueLock< Mutex > m_lock
Definition: checkqueue.h:214
CCheckQueue< T, R > & m_queue
Definition: checkqueue.h:213
CCheckQueueControl(CCheckQueue< T, R > &queueIn) EXCLUSIVE_LOCK_FUNCTION(queueIn.m_control_mutex)
Definition: checkqueue.h:221
std::optional< R > Complete()
Definition: checkqueue.h:226
void Add(std::vector< T > &&vChecks)
Definition: checkqueue.h:232
The verifications are represented by a type T, which must provide an operator(), returning an std::op...
Definition: checkqueue.h:33
bool m_request_stop GUARDED_BY(m_mutex)
Definition: checkqueue.h:68
std::optional< R > Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Join the execution until completion.
Definition: checkqueue.h:178
unsigned int nTodo GUARDED_BY(m_mutex)
Number of verifications that haven't completed yet.
Definition: checkqueue.h:62
Mutex m_control_mutex
Mutex to ensure only one concurrent CCheckQueueControl.
Definition: checkqueue.h:153
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:183
int nIdle GUARDED_BY(m_mutex)
The number of workers (including the master) that are idle.
Definition: checkqueue.h:49
CCheckQueue & operator=(CCheckQueue &&)=delete
int nTotal GUARDED_BY(m_mutex)
The total number of workers (including the master).
Definition: checkqueue.h:52
CCheckQueue(unsigned int batch_size, int worker_threads_num)
Create a new check queue.
Definition: checkqueue.h:156
Mutex m_mutex
Mutex to protect the inner state.
Definition: checkqueue.h:36
std::optional< R > Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Internal function that does bulk of the verification work.
Definition: checkqueue.h:74
std::condition_variable m_worker_cv
Worker threads block on this when out of work.
Definition: checkqueue.h:39
std::vector< std::thread > m_worker_threads
Definition: checkqueue.h:67
const unsigned int nBatchSize
The maximum number of elements to be processed in one batch.
Definition: checkqueue.h:65
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:42
CCheckQueue & operator=(const CCheckQueue &)=delete
Wrapper around std::unique_lock style lock for MutexType.
Definition: sync.h:168
#define LogInfo(...)
Definition: logging.h:413
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:304
#define LOCK_ARGS(cs)
Definition: sync.h:302
#define LOCK(cs)
Definition: sync.h:295
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:343
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
#define EXCLUSIVE_LOCK_FUNCTION(...)
Definition: threadsafety.h:49
#define SCOPED_LOCKABLE
Definition: threadsafety.h:44
#define UNLOCK_FUNCTION(...)
Definition: threadsafety.h:53
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1203