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) 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_UTIL_BTCSIGNALS_H6#define BITCOIN_UTIL_BTCSIGNALS_H8#include <sync.h>10#include <algorithm>11#include <atomic>12#include <functional>13#include <memory>14#include <optional>15#include <type_traits>16#include <utility>17#include <vector>19/**20* btcsignals is a simple mechanism for signaling events to multiple subscribers.21* It is api-compatible with a minimal subset of boost::signals2.22*23* Rather than using a custom slot type, and the features/complexity that they24* imply, std::function is used to store the callbacks. Lifetime management of25* the callbacks is left up to the user.26*27* All usage is thread-safe except for interacting with a connection while28* copying/moving it on another thread.29*/31namespace btcsignals {33/// The default combiner, which only returns void.34class null_value35{36public:37using result_type = void;38};40/// A combiner, which checks if at least one callback returned true.41class any_of42{43public:44// This is the only supported combiner with a non-void return type. As45// such, its behavior is embedded into the signal functor.46using result_type = bool;47};49template <typename Signature, typename Combiner = null_value>50class signal;52/*53* State object representing the liveness of a registered callback.54* signal::connect() returns an enabled connection which can be held and55* disabled in the future.56*/57class connection58{59template <typename Signature, typename Combiner>60friend class signal;61/**62* Track liveness. Also serves as a tag for the constructor used by signal.63*/64class liveness65{66friend class connection;67std::atomic_bool m_connected{true};69void disconnect() { m_connected.store(false); }70public:71bool connected() const { return m_connected.load(); }72};74/**75* connections have shared_ptr-like copy and move semantics.76*/77std::shared_ptr<liveness> m_state{};79/**80* Only a signal can create an enabled connection.81*/82explicit connection(std::shared_ptr<liveness>&& state) : m_state{std::move(state)}{}84public:85/**86* The default constructor creates a connection with no associated signal87*/88constexpr connection() noexcept = default;90/**91* If a callback is associated with this connection, prevent it from being92* called in the future.93*94* If a connection is disabled as part of a signal's callback function, it95* will _not_ be executed in the current signal invocation.96*97* Note that disconnected callbacks are not removed from their owning98* signals here. They are garbage collected in signal::connect().99*/100void disconnect()101{102if (m_state) {103m_state->disconnect();104}105}107/**108* Returns true if this connection was created by a signal and has not been109* disabled.110*/111bool connected() const112{113return m_state && m_state->connected();114}115};117/*118* RAII-style connection management119*/120class scoped_connection121{122connection m_conn;124public:125explicit scoped_connection(connection rhs) noexcept : m_conn{std::move(rhs)} {}127scoped_connection(scoped_connection&&) noexcept = default;129/**130* For simplicity, disable copy construction and copy/move assignment.131*/132scoped_connection& operator=(scoped_connection&&) = delete;133scoped_connection& operator=(const scoped_connection&) = delete;134scoped_connection(const scoped_connection&) = delete;136void disconnect()137{138m_conn.disconnect();139}141~scoped_connection()142{143disconnect();144}145};147/*148* Functor for calling zero or more connected callbacks149*/150template <typename Signature, typename Combiner>151class signal152{153using function_type = std::function<Signature>;155/*156* Helper struct for maintaining a callback and its associated connection liveness157*/158struct connection_holder : connection::liveness {159template <typename Callable>160connection_holder(Callable&& callback) : m_callback{std::forward<Callable>(callback)}161{162}164const function_type m_callback;165};167mutable Mutex m_mutex;169std::vector<std::shared_ptr<connection_holder>> m_connections GUARDED_BY(m_mutex){};171public:172using result_type = Combiner::result_type;174constexpr signal() noexcept = default;175~signal() = default;177/*178* For simplicity, disable all moving/copying/assigning.179*/180signal(const signal&) = delete;181signal(signal&&) = delete;182signal& operator=(const signal&) = delete;183signal& operator=(signal&&) = delete;185/*186* Execute all enabled callbacks for the signal. Rather than allowing for187* custom combiners, the behavior of any_of is hard-coded here.188*189* Callbacks which return void require special handling.190*191* In order to avoid locking during the callbacks, the list of callbacks is192* cached before they are called. This allows a callback to call connect(),193* but the newly connected callback will not be run during the current194* signal invocation.195*196* Note that the parameters are accepted as universal references, though197* they are not perfectly forwarded as that could cause a use-after-move if198* more than one callback is enabled.199*/200template <typename... Args>201[[nodiscard]] result_type operator()(Args&&... args) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)202{203std::vector<std::shared_ptr<connection_holder>> connections;204{205LOCK(m_mutex);206connections = m_connections;207}208if constexpr (std::is_void_v<result_type>) {209static_assert(std::is_same_v<result_type, typename function_type::result_type>,210"Callback result type must be equal to the combiner result type (void).");211for (const auto& connection : connections) {212if (connection->connected()) {213connection->m_callback(args...);214}215}216} else {217static_assert(std::is_same_v<Combiner, any_of>,218"only the any_of combiner is supported and hard-coded into this functor.");219static_assert(std::is_same_v<result_type, typename function_type::result_type>,220"Callback result type must be equal to the combiner result type (bool).");221result_type ret{false};222for (const auto& connection : connections) {223if (connection->connected()) {224ret |= connection->m_callback(args...);225}226}227return ret;228}229}231/*232* Connect a new callback to the signal. A forwarding callable accepts233* anything that can be stored in a std::function.234*/235template <typename Callable>236connection connect(Callable&& func) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)237{238LOCK(m_mutex);240// Garbage-collect disconnected connections to prevent unbounded growth241std::erase_if(m_connections, [](const auto& holder) { return !holder->connected(); });243const auto& entry = m_connections.emplace_back(std::make_shared<connection_holder>(std::forward<Callable>(func)));244return connection(entry);245}247/*248* Returns true if there are no enabled callbacks249*/250[[nodiscard]] bool empty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
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.