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) 2015-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#include <scheduler.h>7#include <sync.h>8#include <util/time.h>10#include <cassert>11#include <functional>12#include <utility>14CScheduler::CScheduler() = default;16CScheduler::~CScheduler()17{18assert(nThreadsServicingQueue == 0);19if (stopWhenEmpty) assert(taskQueue.empty());20}23void CScheduler::serviceQueue()24{25WAIT_LOCK(newTaskMutex, lock);26++nThreadsServicingQueue;28// newTaskMutex is locked throughout this loop EXCEPT29// when the thread is waiting or when the user's function30// is called.31while (!shouldStop()) {32try {33while (!shouldStop() && taskQueue.empty()) {34// Wait until there is something to do.35newTaskScheduled.wait(lock);36}38// Wait until either there is a new task, or until39// the time of the first item on the queue:41while (!shouldStop() && !taskQueue.empty()) {42std::chrono::steady_clock::time_point timeToWaitFor = taskQueue.begin()->first;43if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) {44break; // Exit loop after timeout, it means we reached the time of the event45}46}48// If there are multiple threads, the queue can empty while we're waiting (another49// thread may service the task we were waiting on).50if (shouldStop() || taskQueue.empty())51continue;53Function f = taskQueue.begin()->second;54taskQueue.erase(taskQueue.begin());56{57// Unlock before calling f, so it can reschedule itself or another task58// without deadlocking:59REVERSE_LOCK(lock, newTaskMutex);60f();61}62} catch (...) {63--nThreadsServicingQueue;64throw;65}66}67--nThreadsServicingQueue;68newTaskScheduled.notify_one();69}71void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::time_point t)72{73{74LOCK(newTaskMutex);75taskQueue.insert(std::make_pair(t, f));76}77newTaskScheduled.notify_one();78}80void CScheduler::MockForward(std::chrono::seconds delta_seconds)81{82assert(delta_seconds > 0s && delta_seconds <= 1h);84{85LOCK(newTaskMutex);87// use temp_queue to maintain updated schedule88std::multimap<std::chrono::steady_clock::time_point, Function> temp_queue;90for (const auto& element : taskQueue) {91temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second);92}94// point taskQueue to temp_queue95taskQueue = std::move(temp_queue);96}98// notify that the taskQueue needs to be processed99newTaskScheduled.notify_one();100}102static void Repeat(CScheduler& s, CScheduler::Function f, std::chrono::milliseconds delta)103{104f();105s.scheduleFromNow([=, &s] { Repeat(s, f, delta); }, delta);106}108void CScheduler::scheduleEvery(CScheduler::Function f, std::chrono::milliseconds delta)109{110scheduleFromNow([this, f, delta] { Repeat(*this, f, delta); }, delta);111}113size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point& first,114std::chrono::steady_clock::time_point& last) const115{116LOCK(newTaskMutex);117size_t result = taskQueue.size();118if (!taskQueue.empty()) {119first = taskQueue.begin()->first;120last = taskQueue.rbegin()->first;121}122return result;123}125bool CScheduler::AreThreadsServicingQueue() const126{127LOCK(newTaskMutex);128return nThreadsServicingQueue;129}132void SerialTaskRunner::MaybeScheduleProcessQueue()133{134{135LOCK(m_callbacks_mutex);136// Try to avoid scheduling too many copies here, but if we137// accidentally have two ProcessQueue's scheduled at once its138// not a big deal.139if (m_are_callbacks_running) return;140if (m_callbacks_pending.empty()) return;141}142m_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::steady_clock::now());143}145void SerialTaskRunner::ProcessQueue()146{147std::function<void()> callback;148{149LOCK(m_callbacks_mutex);150if (m_are_callbacks_running) return;151if (m_callbacks_pending.empty()) return;152m_are_callbacks_running = true;154callback = std::move(m_callbacks_pending.front());155m_callbacks_pending.pop_front();156}158// RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue159// to ensure both happen safely even if callback() throws.160struct RAIICallbacksRunning {161SerialTaskRunner* instance;162explicit RAIICallbacksRunning(SerialTaskRunner* _instance) : instance(_instance) {}163~RAIICallbacksRunning()164{165{166LOCK(instance->m_callbacks_mutex);167instance->m_are_callbacks_running = false;168}169instance->MaybeScheduleProcessQueue();170}171} raiicallbacksrunning(this);173callback();174}176void SerialTaskRunner::insert(std::function<void()> func)177{178{179LOCK(m_callbacks_mutex);180m_callbacks_pending.emplace_back(std::move(func));181}182MaybeScheduleProcessQueue();183}185void SerialTaskRunner::flush()186{187assert(!m_scheduler.AreThreadsServicingQueue());188bool should_continue = true;189while (should_continue) {190ProcessQueue();191LOCK(m_callbacks_mutex);192should_continue = !m_callbacks_pending.empty();193}194}196size_t SerialTaskRunner::size()197{198LOCK(m_callbacks_mutex);199return m_callbacks_pending.size();200}