Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2012-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#ifndef BITCOIN_CHECKQUEUE_H6#define BITCOIN_CHECKQUEUE_H8#include <sync.h>9#include <tinyformat.h>10#include <util/log.h>11#include <util/threadnames.h>13#include <algorithm>14#include <iterator>15#include <optional>16#include <vector>18/**19* Queue for verifications that have to be performed.20* The verifications are represented by a type T, which must provide an21* operator(), returning an std::optional<R>.22*23* The overall result of the computation is std::nullopt if all invocations24* return std::nullopt, or one of the other results otherwise.25*26* One thread (the master) is assumed to push batches of verifications27* onto the queue, where they are processed by N-1 worker threads. When28* the master is done adding work, it temporarily joins the worker pool29* as an N'th worker, until all jobs are done.30*31*/32template <typename T, typename R = std::remove_cvref_t<decltype(std::declval<T>()().value())>>33class CCheckQueue34{35private:36//! Mutex to protect the inner state37Mutex m_mutex;39//! Worker threads block on this when out of work40std::condition_variable m_worker_cv;42//! Master thread blocks on this when out of work43std::condition_variable m_master_cv;45//! The queue of elements to be processed.46//! As the order of booleans doesn't matter, it is used as a LIFO (stack)47std::vector<T> queue GUARDED_BY(m_mutex);49//! The number of workers (including the master) that are idle.50int nIdle GUARDED_BY(m_mutex){0};52//! The total number of workers (including the master).53int nTotal GUARDED_BY(m_mutex){0};55//! The temporary evaluation result.56std::optional<R> m_result GUARDED_BY(m_mutex);58/**59* Number of verifications that haven't completed yet.60* This includes elements that are no longer queued, but still in the61* worker's own batches.62*/63unsigned int nTodo GUARDED_BY(m_mutex){0};65//! The maximum number of elements to be processed in one batch66const unsigned int nBatchSize;68std::vector<std::thread> m_worker_threads;69bool m_request_stop GUARDED_BY(m_mutex){false};71/// \anchor checkqueue72/** Internal function that does bulk of the verification work. If fMaster, return the final result. */73std::optional<R> Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)74{75std::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv;76std::vector<T> vChecks;77vChecks.reserve(nBatchSize);78unsigned int nNow = 0;79std::optional<R> local_result;80bool do_work;81do {82{83WAIT_LOCK(m_mutex, lock);84// first do the clean-up of the previous loop run (allowing us to do it in the same critsect)85if (nNow) {86if (local_result.has_value() && !m_result.has_value()) {87std::swap(local_result, m_result);88}89nTodo -= nNow;90if (nTodo == 0 && !fMaster) {91// We processed the last element; inform the master it can exit and return the result92m_master_cv.notify_one();93}94} else {95// first iteration96nTotal++;97}98// logically, the do loop starts here99while (queue.empty() && !m_request_stop) {100if (fMaster && nTodo == 0) {101nTotal--;102std::optional<R> to_return = std::move(m_result);103// reset the status for new work later104m_result = std::nullopt;105// return the current status106return to_return;107}108nIdle++;109cond.wait(lock); // wait110nIdle--;111}112if (m_request_stop) {113// return value does not matter, because m_request_stop is only set in the destructor.114return std::nullopt;115}117// Decide how many work units to process now.118// * Do not try to do everything at once, but aim for increasingly smaller batches so119// all workers finish approximately simultaneously.120// * Try to account for idle jobs which will instantly start helping.121// * Don't do batches smaller than 1 (duh), or larger than nBatchSize.122nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1)));123auto start_it = queue.end() - nNow;124vChecks.assign(std::make_move_iterator(start_it), std::make_move_iterator(queue.end()));125queue.erase(start_it, queue.end());126// Check whether we need to do work at all127do_work = !m_result.has_value();128}129// execute work130if (do_work) {131for (T& check : vChecks) {132local_result = check();133if (local_result.has_value()) break;134}135}136vChecks.clear();137} while (true);138}140public:141//! Mutex to ensure only one concurrent CCheckQueueControl142Mutex m_control_mutex;144//! Create a new check queue145explicit CCheckQueue(unsigned int batch_size, int worker_threads_num)146: nBatchSize(batch_size)147{148LogInfo("Script verification uses %d additional threads", worker_threads_num);149m_worker_threads.reserve(worker_threads_num);150for (int n = 0; n < worker_threads_num; ++n) {151m_worker_threads.emplace_back([this, n]() {152util::ThreadRename(strprintf("scriptch.%02i", n));153Loop(false /* worker thread */);154});155}156}158// Since this class manages its own resources, which is a thread159// pool `m_worker_threads`, copy and move operations are not appropriate.160CCheckQueue(const CCheckQueue&) = delete;161CCheckQueue& operator=(const CCheckQueue&) = delete;162CCheckQueue(CCheckQueue&&) = delete;163CCheckQueue& operator=(CCheckQueue&&) = delete;165//! Join the execution until completion. If at least one evaluation wasn't successful, return166//! its error.167std::optional<R> Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)168{169return Loop(true /* master thread */);170}172//! Add a batch of checks to the queue173void Add(std::vector<T>&& vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)174{175if (vChecks.empty()) {176return;177}179{180LOCK(m_mutex);181queue.insert(queue.end(), std::make_move_iterator(vChecks.begin()), std::make_move_iterator(vChecks.end()));182nTodo += vChecks.size();183}185if (vChecks.size() == 1) {186m_worker_cv.notify_one();187} else {188m_worker_cv.notify_all();189}190}192~CCheckQueue()193{194WITH_LOCK(m_mutex, m_request_stop = true);195m_worker_cv.notify_all();196for (std::thread& t : m_worker_threads) {197t.join();198}199}201bool HasThreads() const { return !m_worker_threads.empty(); }202};204/**205* RAII-style controller object for a CCheckQueue that guarantees the passed206* queue is finished before continuing.207*/208template <typename T, typename R = std::remove_cvref_t<decltype(std::declval<T>()().value())>>209class SCOPED_LOCKABLE CCheckQueueControl210{211private:212CCheckQueue<T, R>& m_queue;213UniqueLock<Mutex> m_lock;214bool fDone;216public:217CCheckQueueControl() = delete;218CCheckQueueControl(const CCheckQueueControl&) = delete;219CCheckQueueControl& operator=(const CCheckQueueControl&) = delete;220explicit CCheckQueueControl(CCheckQueue<T>& queueIn) EXCLUSIVE_LOCK_FUNCTION(queueIn.m_control_mutex) : m_queue(queueIn), m_lock(LOCK_ARGS(queueIn.m_control_mutex)), fDone(false) {}222std::optional<R> Complete()223{224auto ret = m_queue.Complete();225fDone = true;226return ret;227}229void Add(std::vector<T>&& vChecks)230{231m_queue.Add(std::move(vChecks));232}234~CCheckQueueControl() UNLOCK_FUNCTION()235{236if (!fDone)237Complete();238}239};241#endif // BITCOIN_CHECKQUEUE_H