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 MP_PROXY_IO_H6#define MP_PROXY_IO_H8#include <mp/proxy.h>9#include <mp/util.h>11#include <mp/proxy.capnp.h>13#include <capnp/rpc-twoparty.h>15#include <assert.h>16#include <algorithm>17#include <condition_variable>18#include <cstdlib>19#include <functional>20#include <kj/function.h>21#include <map>22#include <memory>23#include <optional>24#include <sstream>25#include <string>26#include <thread>28namespace mp {29struct ThreadContext;30struct Listener;32struct InvokeContext33{34Connection& connection;35};37struct ClientInvokeContext : InvokeContext38{39ThreadContext& thread_context;40ClientInvokeContext(Connection& conn, ThreadContext& thread_context)41: InvokeContext{conn}, thread_context{thread_context}42{43}44};46template <typename ProxyServer, typename CallContext_>47struct ServerInvokeContext : InvokeContext48{49using CallContext = CallContext_;51ProxyServer& proxy_server;52CallContext& call_context;53int req;54//! For IPC methods that execute asynchronously, not on the event-loop55//! thread: lock preventing the event-loop thread from freeing the params or56//! results structs if the request is canceled while the worker thread is57//! reading params (`call_context.getParams()`) or writing results58//! (`call_context.getResults()`).59Lock* cancel_lock{nullptr};60//! For IPC methods that execute asynchronously, not on the event-loop61//! thread, this is set to true if the IPC call was canceled by the client62//! or canceled by a disconnection. If the call runs on the event-loop63//! thread, it can't be canceled. This should be accessed with cancel_lock64//! held if it is not null, since in the asynchronous case it is accessed65//! from multiple threads.66bool request_canceled{false};68ServerInvokeContext(ProxyServer& proxy_server, CallContext& call_context, int req)69: InvokeContext{*proxy_server.m_context.connection}, proxy_server{proxy_server}, call_context{call_context}, req{req}70{71}72};74template <typename Interface, typename Params, typename Results>75using ServerContext = ServerInvokeContext<ProxyServer<Interface>, ::capnp::CallContext<Params, Results>>;77template <>78struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>79{80using ProxyClientBase::ProxyClientBase;81// https://stackoverflow.com/questions/22357887/comparing-two-mapiterators-why-does-it-need-the-copy-constructor-of-stdpair82ProxyClient(const ProxyClient&) = delete;83~ProxyClient();85//! Reference to callback function that is run if there is a sudden86//! disconnect and the Connection object is destroyed before this87//! ProxyClient<Thread> object. The callback will destroy this object and88//! remove its entry from the thread's request_threads or callback_threads89//! map. It will also reset m_disconnect_cb so the destructor does not90//! access it. In the normal case where there is no sudden disconnect, the91//! destructor will unregister m_disconnect_cb so the callback is never run.92//! Since this variable is accessed from multiple threads, accesses should93//! be guarded with the associated Waiter::m_mutex.94std::optional<CleanupIt> m_disconnect_cb;95};97template <>98struct ProxyServer<Thread> final : public Thread::Server99{100public:101ProxyServer(Connection& connection, ThreadContext& thread_context, std::thread&& thread);102~ProxyServer();103kj::Promise<void> getName(GetNameContext context) override;105//! Run a callback function fn returning T on this thread. The function will106//! be queued and executed as soon as the thread is idle, and when fn107//! returns, the promise returned by this method will be fulfilled with the108//! value fn returned.109template<typename T, typename Fn>110kj::Promise<T> post(Fn&& fn);112EventLoopRef m_loop;113ThreadContext& m_thread_context;114std::thread m_thread;115//! Promise signaled when m_thread_context.waiter is ready and there is no116//! post() callback function waiting to execute.117kj::Promise<void> m_thread_ready{kj::READY_NOW};118};120//! Handler for kj::TaskSet failed task events.121class LoggingErrorHandler : public kj::TaskSet::ErrorHandler122{123public:124LoggingErrorHandler(EventLoop& loop) : m_loop(loop) {}125void taskFailed(kj::Exception&& exception) override;126EventLoop& m_loop;127};129//! Log flags. Update stringify function if changed!130enum class Log {131Trace = 0,132Debug,133Info,134Warning,135Error,136Raise,137};139kj::StringPtr KJ_STRINGIFY(Log flags);141struct LogMessage {143//! Message to be logged144std::string message;146//! The severity level of this message147Log level;148};150using LogFn = std::function<void(LogMessage)>;152struct LogOptions {154//! External logging callback.155LogFn log_fn;157//! Maximum number of characters to use when representing158//! request and response structs as strings.159size_t max_chars{200};161//! Messages with a severity level less than log_level will not be162//! reported.163Log log_level{Log::Trace};164};166class Logger167{168public:169Logger(const LogOptions& options, Log log_level) : m_options(options), m_log_level(log_level) {}171Logger(Logger&&) = delete;172Logger& operator=(Logger&&) = delete;173Logger(const Logger&) = delete;174Logger& operator=(const Logger&) = delete;176~Logger() noexcept(false)177{178if (enabled()) m_options.log_fn({std::move(m_buffer).str(), m_log_level});179}181template <typename T>182friend Logger& operator<<(Logger& logger, T&& value)183{184if (logger.enabled()) logger.m_buffer << std::forward<T>(value);185return logger;186}188template <typename T>189friend Logger& operator<<(Logger&& logger, T&& value)190{191return logger << std::forward<T>(value);192}194explicit operator bool() const195{196return enabled();197}199private:200bool enabled() const201{202return m_options.log_fn && m_log_level >= m_options.log_level;203}205const LogOptions& m_options;206Log m_log_level;207std::ostringstream m_buffer;208};210#define MP_LOGPLAIN(loop, ...) if (mp::Logger logger{(loop).m_log_opts, __VA_ARGS__}; logger) logger212#define MP_LOG(loop, ...) MP_LOGPLAIN(loop, __VA_ARGS__) << "{" << LongThreadName((loop).m_exe_name) << "} "214std::string LongThreadName(const char* exe_name);216//! Event loop implementation.217//!218//! Cap'n Proto threading model is very simple: all I/O operations are219//! asynchronous and must be performed on a single thread. This includes:220//!221//! - Code starting an asynchronous operation (calling a function that returns a222//! promise object)223//! - Code notifying that an asynchronous operation is complete (code using a224//! fulfiller object)225//! - Code handling a completed operation (code chaining or waiting for a promise)226//!227//! All of this code needs to access shared state, and there is no mutex that228//! can be acquired to lock this state because Cap'n Proto229//! assumes it will only be accessed from one thread. So all this code needs to230//! actually run on one thread, and the EventLoop::loop() method is the entry point for231//! this thread. ProxyClient and ProxyServer objects that use other threads and232//! need to perform I/O operations post to this thread using EventLoop::post()233//! and EventLoop::sync() methods.234//!235//! Specifically, because ProxyClient methods can be called from arbitrary236//! threads, and ProxyServer methods can run on arbitrary threads, ProxyClient237//! methods use the EventLoop thread to send requests, and ProxyServer methods238//! use the thread to return results.239//!240//! Based on https://groups.google.com/d/msg/capnproto/TuQFF1eH2-M/g81sHaTAAQAJ241class EventLoop242{243public:244//! Construct event loop object with default logging options.245EventLoop(const char* exe_name, LogFn log_fn, void* context = nullptr)246: EventLoop(exe_name, LogOptions{std::move(log_fn)}, context){}248//! Construct event loop object with specified logging options.249EventLoop(const char* exe_name, LogOptions log_opts, void* context = nullptr);
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.