Inside Bitcoin
Opening the selected file…
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) 2011-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 <sync.h>7#include <logging/timer.h>8#include <tinyformat.h>9#include <util/log.h>10#include <util/stdmutex.h>11#include <util/strencodings.h>12#include <util/threadnames.h>14#include <map>15#include <mutex>16#include <set>17#include <system_error>18#include <thread>19#include <type_traits>20#include <unordered_map>21#include <utility>22#include <vector>24#ifdef DEBUG_LOCKCONTENTION26template <typename LockType>27void ContendedLock(std::string_view name, std::string_view file, int nLine, LockType& lock)28{29LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", name, file, nLine), BCLog::LOCK);30lock.lock();31}32template void ContendedLock(std::string_view name, std::string_view file, int nLine, std::unique_lock<std::mutex>& lock);33template void ContendedLock(std::string_view name, std::string_view file, int nLine, std::unique_lock<std::recursive_mutex>& lock);35#endif37#ifdef DEBUG_LOCKORDER38//39// Early deadlock detection.40// Problem being solved:41// Thread 1 locks A, then B, then C42// Thread 2 locks D, then C, then A43// --> may result in deadlock between the two threads, depending on when they run.44// Solution implemented here:45// Keep track of pairs of locks: (A before B), (A before C), etc.46// Complain if any thread tries to lock in a different order.47//49struct CLockLocation {50CLockLocation(51const char* pszName,52const char* pszFile,53int nLine,54bool fTryIn,55std::string&& thread_name)56: fTry(fTryIn),57mutexName(pszName),58sourceFile(pszFile),59m_thread_name(std::move(thread_name)),60sourceLine(nLine) {}62std::string ToString() const63{64return strprintf(65"'%s' in %s:%s%s (in thread '%s')",66mutexName, sourceFile, sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);67}69std::string Name() const70{71return mutexName;72}74private:75bool fTry;76std::string mutexName;77std::string sourceFile;78const std::string m_thread_name;79int sourceLine;80};82using LockStackItem = std::pair<void*, CLockLocation>;83using LockStack = std::vector<LockStackItem>;84using LockStacks = std::unordered_map<std::thread::id, LockStack>;86using LockPair = std::pair<void*, void*>;87using LockOrders = std::map<LockPair, LockStack>;88using InvLockOrders = std::set<LockPair>;90struct LockData {91LockStacks m_lock_stacks GUARDED_BY(dd_mutex);92LockOrders lockorders GUARDED_BY(dd_mutex);93InvLockOrders invlockorders GUARDED_BY(dd_mutex);94StdMutex dd_mutex;95};97LockData& GetLockData() {98// This approach guarantees that the object is not destroyed until after its last use.99// The operating system automatically reclaims all the memory in a program's heap when that program exits.100// Since the ~LockData() destructor is never called, the LockData class and all101// its subclasses must have implicitly-defined destructors.102static LockData& lock_data = *new LockData();103return lock_data;104}106static void potential_deadlock_detected(const LockPair& mismatch, const LockStack& s1, const LockStack& s2)107{108LogError("POTENTIAL DEADLOCK DETECTED");109LogError("Previous lock order was:");110for (const LockStackItem& i : s1) {111std::string prefix{};112if (i.first == mismatch.first) {113prefix = " (1)";114}115if (i.first == mismatch.second) {116prefix = " (2)";117}118LogError("%s %s", prefix, i.second.ToString());119}121std::string mutex_a, mutex_b;122LogError("Current lock order is:");123for (const LockStackItem& i : s2) {124std::string prefix{};125if (i.first == mismatch.first) {126prefix = " (1)";127mutex_a = i.second.Name();128}129if (i.first == mismatch.second) {130prefix = " (2)";131mutex_b = i.second.Name();132}133LogError("%s %s", prefix, i.second.ToString());134}135if (g_debug_lockorder_abort) {136tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order for %s, details in debug log.\n", s2.back().second.ToString());137abort();138}139throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));140}142static void double_lock_detected(const void* mutex, const LockStack& lock_stack)143{144LogError("DOUBLE LOCK DETECTED");145LogError("Lock order:");146for (const LockStackItem& i : lock_stack) {147std::string prefix{};148if (i.first == mutex) {149prefix = " (*)";150}151LogError("%s %s", prefix, i.second.ToString());152}153if (g_debug_lockorder_abort) {154tfm::format(std::cerr,155"Assertion failed: detected double lock for %s, details in debug log.\n",156lock_stack.back().second.ToString());157abort();158}159throw std::logic_error("double lock detected");160}162template <typename MutexType>163static void push_lock(MutexType* c, const CLockLocation& locklocation)164{165constexpr bool is_recursive_mutex =166std::is_base_of_v<RecursiveMutex, MutexType> ||167std::is_base_of_v<std::recursive_mutex, MutexType>;169LockData& lockdata = GetLockData();170STDLOCK(lockdata.dd_mutex);172LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];173lock_stack.emplace_back(c, locklocation);174for (size_t j = 0; j < lock_stack.size() - 1; ++j) {175const LockStackItem& i = lock_stack[j];176if (i.first == c) {177if (is_recursive_mutex) {178break;179}180// It is not a recursive mutex and it appears in the stack two times:181// at position `j` and at the end (which we added just before this loop).182// Can't allow locking the same (non-recursive) mutex two times from the183// same thread as that results in an undefined behavior.184auto lock_stack_copy = lock_stack;185lock_stack.pop_back();186double_lock_detected(c, lock_stack_copy);187// double_lock_detected() does not return.188}190const LockPair p1 = std::make_pair(i.first, c);191if (lockdata.lockorders.contains(p1))192continue;194const LockPair p2 = std::make_pair(c, i.first);195if (lockdata.lockorders.contains(p2)) {196auto lock_stack_copy = lock_stack;197lock_stack.pop_back();198potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy);199// potential_deadlock_detected() does not return.200}202lockdata.lockorders.emplace(p1, lock_stack);203lockdata.invlockorders.insert(p2);204}205}207static void pop_lock()208{209LockData& lockdata = GetLockData();210STDLOCK(lockdata.dd_mutex);212LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];213lock_stack.pop_back();214if (lock_stack.empty()) {215lockdata.m_lock_stacks.erase(std::this_thread::get_id());216}217}219template <typename MutexType>220void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry)221{222push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));223}224template void EnterCritical(const char*, const char*, int, std::mutex*, bool);225template void EnterCritical(const char*, const char*, int, std::recursive_mutex*, bool);227void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)228{229LockData& lockdata = GetLockData();230STDLOCK(lockdata.dd_mutex);232const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];233if (!lock_stack.empty()) {234const auto& lastlock = lock_stack.back();235if (lastlock.first == cs) {236lockname = lastlock.second.Name();237return;238}239}241LogError("INCONSISTENT LOCK ORDER DETECTED");242LogError("Current lock order (least recent first) is:");243for (const LockStackItem& i : lock_stack) {244LogError(" %s", i.second.ToString());245}246if (g_debug_lockorder_abort) {247tfm::format(std::cerr, "%s:%s %s was not most recent critical section locked, details in debug log.\n", file, line, guardname);248abort();249}250throw std::logic_error(strprintf("%s was not most recent critical section locked", guardname));
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.