5#ifndef BITCOIN_CHECKQUEUE_H
6#define BITCOIN_CHECKQUEUE_H
31 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
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;
86 if (local_result.has_value() && !m_result.has_value()) {
87 std::swap(local_result, m_result);
90 if (nTodo == 0 && !fMaster) {
93 m_master_cv.notify_one();
100 while (queue.empty() && !m_request_stop) {
101 if (fMaster && nTodo == 0) {
103 std::optional<R> to_return = std::move(m_result);
105 m_result = std::nullopt;
113 if (m_request_stop) {
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());
135 do_work = !m_result.has_value();
139 for (T &check : vChecks) {
140 local_result = check();
141 if (local_result.has_value()) {
156 : nBatchSize(nBatchSizeIn) {}
165 m_result = std::nullopt;
167 assert(m_worker_threads.empty());
168 for (
int n = 0; n < threads_num; ++n) {
169 m_worker_threads.emplace_back([
this, n]() {
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();
197 WITH_LOCK(m_mutex, m_request_stop =
true);
198 m_worker_cv.notify_all();
199 for (std::thread &t : m_worker_threads) {
202 m_worker_threads.clear();
203 WITH_LOCK(m_mutex, m_request_stop =
false);
215 std::remove_cvref_t<decltype(std::declval<T>()().value())>>
226 : pqueue(pqueueIn), fDone(false) {
228 if (pqueue !=
nullptr) {
234 if (pqueue ==
nullptr) {
242 void Add(std::vector<T> &&vChecks) {
243 if (pqueue !=
nullptr) {
244 pqueue->
Add(std::move(vChecks));
252 if (pqueue !=
nullptr) {
RAII-style controller object for a CCheckQueue that guarantees the passed queue is finished before co...
CCheckQueueControl & operator=(const CCheckQueueControl &)=delete
CCheckQueueControl(const CCheckQueueControl &)=delete
CCheckQueueControl()=delete
CCheckQueue< T, R > *const pqueue
CCheckQueueControl(CCheckQueue< T > *const pqueueIn)
std::optional< R > Complete()
void Add(std::vector< T > &&vChecks)
The verifications are represented by a type T, which must provide an operator(), returning an std::op...
bool m_request_stop GUARDED_BY(m_mutex)
std::optional< R > Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Join the execution until completion.
unsigned int nTodo GUARDED_BY(m_mutex)
Number of verifications that haven't completed yet.
Mutex m_control_mutex
Mutex to ensure only one concurrent CCheckQueueControl.
std::vector< T > queue GUARDED_BY(m_mutex)
The queue of elements to be processed.
void Add(std::vector< T > &&vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add a batch of checks to the queue.
int nIdle GUARDED_BY(m_mutex)
The number of workers (including the master) that are idle.
int nTotal GUARDED_BY(m_mutex)
The total number of workers (including the master).
Mutex m_mutex
Mutex to protect the inner state.
std::optional< R > Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Internal function that does bulk of the verification work.
std::condition_variable m_worker_cv
Worker threads block on this when out of work.
std::vector< std::thread > m_worker_threads
const unsigned int nBatchSize
The maximum number of elements to be processed in one batch.
std::optional< R > m_result GUARDED_BY(m_mutex)
The temporary evaluation result.
CCheckQueue(unsigned int nBatchSizeIn)
Create a new check queue.
void StopWorkerThreads() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Stop all of the worker threads.
void StartWorkerThreads(const int threads_num) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Create a pool of new worker threads.
std::condition_variable m_master_cv
Master thread blocks on this when out of work.
void ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name.
#define WAIT_LOCK(cs, name)
#define ENTER_CRITICAL_SECTION(cs)
#define LEAVE_CRITICAL_SECTION(cs)
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
#define EXCLUSIVE_LOCKS_REQUIRED(...)