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.
501//! connection is destroyed. This is used to interrupt method calls that are502//! still executing at time of disconnection.503kj::Canceler m_canceler;505//! Cleanup functions to run if connection is broken unexpectedly. List506//! will be empty if all ProxyClient are destroyed cleanly before the507//! connection is destroyed.508CleanupList m_sync_cleanup_fns;509};511//! Vat id for server side of connection. Required argument to RpcSystem::bootStrap()512//!513//! "Vat" is Cap'n Proto nomenclature for a host of various objects that facilitates514//! bidirectional communication with other vats; it is often but not always 1-1 with515//! processes. Cap'n Proto doesn't reference clients or servers per se; instead everything516//! is just a vat.517//!518//! See also: https://github.com/capnproto/capnproto/blob/9021f0c722b36cb11e3690b0860939255ebad39c/c%2B%2B/src/capnp/rpc.capnp#L42-L56519struct ServerVatId520{521::capnp::word scratch[4]{};522::capnp::MallocMessageBuilder message{scratch};523::capnp::rpc::twoparty::VatId::Builder vat_id{message.getRoot<::capnp::rpc::twoparty::VatId>()};524ServerVatId() { vat_id.setSide(::capnp::rpc::twoparty::Side::SERVER); }525};527template <typename Interface, typename Impl>528ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client client,529Connection* connection,530bool destroy_connection)531: m_client(std::move(client)), m_context(connection)533{534MP_LOG(*m_context.loop, Log::Debug) << "Creating " << CxxTypeName(*this) << " " << this;535// Handler for the connection getting destroyed before this client object.536auto disconnect_cb = m_context.connection->addSyncCleanup([this]() {537// Release client capability by move-assigning to temporary.538{539typename Interface::Client(std::move(m_client));540}541Lock lock{m_context.loop->m_mutex};542m_context.connection = nullptr;543});545// Two shutdown sequences are supported:546//547// - A normal sequence where client proxy objects are deleted by external548// code that no longer needs them549//550// - A garbage collection sequence where the connection or event loop shuts551// down while external code is still holding client references.552//553// The first case is handled here when m_context.connection is not null. The554// second case is handled by the disconnect_cb function, which sets555// m_context.connection to null so nothing happens here.556m_context.cleanup_fns.emplace_front([this, destroy_connection, disconnect_cb]{557{558// If the capnp interface defines a destroy method, call it to destroy559// the remote object, waiting for it to be deleted server side. If the560// capnp interface does not define a destroy method, this will just call561// an empty stub defined in the ProxyClientBase class and do nothing.562// Exceptions are caught and logged rather than propagated because563// ~ProxyClientBase is noexcept and the peer may be gone by the time564// this runs.565if (kj::runCatchingExceptions([&]{ Sub::destroy(*this); }) != nullptr) {566MP_LOG(*m_context.loop, Log::Warning) << "Remote destroy call failed during cleanup. Continuing.";567}569// FIXME: Could just invoke removed addCleanup fn here instead of duplicating code570m_context.loop->sync([&]() {571// Remove disconnect callback on cleanup so it doesn't run and try572// to access this object after it's destroyed. This call needs to573// run inside loop->sync() on the event loop thread because574// otherwise, if there were an ill-timed disconnect, the575// onDisconnect handler could fire and delete the Connection object576// before the removeSyncCleanup call.577if (m_context.connection) m_context.connection->removeSyncCleanup(disconnect_cb);579// Release client capability by move-assigning to temporary.580{581typename Interface::Client(std::move(m_client));582}583if (destroy_connection) {584delete m_context.connection;585m_context.connection = nullptr;586}587});588}589});590Sub::construct(*this);591}593template <typename Interface, typename Impl>594ProxyClientBase<Interface, Impl>::~ProxyClientBase() noexcept595{596MP_LOG(*m_context.loop, Log::Debug) << "Cleaning up " << CxxTypeName(*this) << " " << this;597CleanupRun(m_context.cleanup_fns);598MP_LOG(*m_context.loop, Log::Debug) << "Destroying " << CxxTypeName(*this) << " " << this;599}601template <typename Interface, typename Impl>602ProxyServerBase<Interface, Impl>::ProxyServerBase(std::shared_ptr<Impl> impl, Connection& connection)603: m_impl(std::move(impl)), m_context(&connection)604{605MP_LOG(*m_context.loop, Log::Debug) << "Creating " << CxxTypeName(*this) << " " << this;606assert(m_impl);607}609//! ProxyServer destructor, called from the EventLoop thread by Cap'n Proto610//! garbage collection code after there are no more references to this object.611//! This will typically happen when the corresponding ProxyClient object on the612//! other side of the connection is destroyed. It can also happen earlier if the613//! connection is broken or destroyed. In the latter case this destructor will614//! typically be called inside m_rpc_system.reset() call in the ~Connection615//! destructor while the Connection object still exists. However, because616//! ProxyServer objects are refcounted, and the Connection object could be617//! destroyed while asynchronous IPC calls are still in-flight, it's possible618//! for this destructor to be called after the Connection object no longer619//! exists, so it is NOT valid to dereference the m_context.connection pointer620//! from this function.621template <typename Interface, typename Impl>622ProxyServerBase<Interface, Impl>::~ProxyServerBase()623{624MP_LOG(*m_context.loop, Log::Debug) << "Cleaning up " << CxxTypeName(*this) << " " << this;625if (m_impl) {626// If impl is non-null at this point, it means no client is waiting for627// the m_impl server object to be destroyed synchronously. This can628// happen either if the interface did not define a "destroy" method (see629// invokeDestroy method below), or if a destroy method was defined, but630// the connection was broken before it could be called.631//632// In either case, be conservative and run the cleanup on an633// asynchronous thread, to avoid destructors or cleanup functions634// blocking or deadlocking the current EventLoop thread, since they635// could be making IPC calls.636//637// Technically this is a little too conservative since if the interface638// defines a "destroy" method, but the destroy method does not accept a639// Context parameter specifying a worker thread, the cleanup method640// would run on the EventLoop thread normally (when connection is641// unbroken), but will not run on the EventLoop thread now (when642// connection is broken). Probably some refactoring of the destructor643// and invokeDestroy function is possible to make this cleaner and more644// consistent.645m_context.loop->addAsyncCleanup([impl=std::move(m_impl), fns=std::move(m_context.cleanup_fns)]() mutable {646impl.reset();647CleanupRun(fns);648});649}650assert(m_context.cleanup_fns.empty());651MP_LOG(*m_context.loop, Log::Debug) << "Destroying " << CxxTypeName(*this) << " " << this;652}654//! If the capnp interface defined a special "destroy" method, as described the655//! ProxyClientBase class, this method will be called and synchronously destroy656//! m_impl before returning to the client.657//!658//! If the capnp interface does not define a "destroy" method, this will never659//! be called and the ~ProxyServerBase destructor will be responsible for660//! deleting m_impl asynchronously, whenever the ProxyServer object gets garbage661//! collected by Cap'n Proto.662//!663//! This method is called in the same way other proxy server methods are called,664//! via the serverInvoke function. Basically serverInvoke just calls this as a665//! substitute for a non-existent m_impl->destroy() method. If the destroy666//! method has any parameters or return values they will be handled in the667//! normal way by PassField/ReadField/BuildField functions. Particularly if a668//! Context.thread parameter was passed, this method will run on the worker669//! thread specified by the client. Otherwise it will run on the EventLoop670//! thread, like other server methods without an assigned thread.671template <typename Interface, typename Impl>672void ProxyServerBase<Interface, Impl>::invokeDestroy()673{674m_impl.reset();675CleanupRun(m_context.cleanup_fns);676}678//! Map from Connection to local or remote thread handle which will be used over679//! that connection. This map will typically only contain one entry, but can680//! contain multiple if a single thread makes IPC calls over multiple681//! connections. A std::optional value type is used to avoid the map needing to682//! be locked while ProxyClient<Thread> objects are constructed, see683//! ThreadContext "Synchronization note" below.684using ConnThreads = std::map<Connection*, std::optional<ProxyClient<Thread>>>;685using ConnThread = ConnThreads::iterator;687// Retrieve ProxyClient<Thread> object associated with this connection from a688// map, or create a new one and insert it into the map. Return map iterator and689// inserted bool.690std::tuple<ConnThread, bool> SetThread(GuardedRef<ConnThreads> threads, Connection* connection, const std::function<Thread::Client()>& make_thread);692//! The thread_local ThreadContext g_thread_context struct provides information693//! about individual threads and a way of communicating between them. Because694//! it's a thread local struct, each ThreadContext instance is initialized by695//! the thread that owns it.696//!697//! ThreadContext is used for any client threads created externally which make698//! IPC calls, and for server threads created by699//! ProxyServer<ThreadMap>::makeThread() which execute IPC calls for clients.700//!701//! In both cases, the struct holds information like the thread name, and a702//! Waiter object where the EventLoop can post incoming IPC requests to execute703//! on the thread. The struct also holds ConnThread maps associating the thread704//! with local and remote ProxyClient<Thread> objects.705struct ThreadContext706{707//! Identifying string for debug.708std::string thread_name;710//! Waiter object used to allow remote clients to execute code on this711//! thread. For server threads created by712//! ProxyServer<ThreadMap>::makeThread(), this is initialized in that713//! function. Otherwise, for client threads created externally, this is714//! initialized the first time the thread tries to make an IPC call. Having715//! a waiter is necessary for threads making IPC calls in case a server they716//! are calling expects them to execute a callback during the call, before717//! it sends a response.718//!719//! For IPC client threads, the Waiter pointer is never cleared and the Waiter720//! just gets destroyed when the thread does. For server threads created by721//! makeThread(), this pointer is set to null in the ~ProxyServer<Thread> as722//! a signal for the thread to exit and destroy itself. In both cases, the723//! same Waiter object is used across different calls and only created and724//! destroyed once for the lifetime of the thread.725std::unique_ptr<Waiter> waiter = nullptr;727//! When client is making a request to a server, this is the728//! `callbackThread` argument it passes in the request, used by the server729//! in case it needs to make callbacks into the client that need to execute730//! while the client is waiting. This will be set to a local thread object.731//!732//! Synchronization note: The callback_thread and request_thread maps are733//! only ever accessed internally by this thread's destructor and externally734//! by Cap'n Proto event loop threads. Since it's possible for IPC client735//! threads to make calls over different connections that could have736//! different event loops, these maps are guarded by Waiter::m_mutex in case737//! different event loop threads add or remove map entries simultaneously.738//! However, individual ProxyClient<Thread> objects in the maps will only be739//! associated with one event loop and guarded by EventLoop::m_mutex. So740//! Waiter::m_mutex does not need to be held while accessing individual741//! ProxyClient<Thread> instances, and may even need to be released to742//! respect lock order and avoid locking Waiter::m_mutex before743//! EventLoop::m_mutex.744ConnThreads callback_threads MP_GUARDED_BY(waiter->m_mutex);746//! When client is making a request to a server, this is the `thread`747//! argument it passes in the request, used to control which thread on748//! server will be responsible for executing it. If client call is being749//! made from a local thread, this will be a remote thread object returned750//! by makeThread. If a client call is being made from a thread currently
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.