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.
751//! handling a server request, this will be set to the `callbackThread`752//! request thread argument passed in that request.753//!754//! Synchronization note: \ref callback_threads note applies here as well.755ConnThreads request_threads MP_GUARDED_BY(waiter->m_mutex);757//! Whether this thread is a capnp event loop thread. Not really used except758//! to assert false if there's an attempt to execute a blocking operation759//! which could deadlock the thread.760bool loop_thread = false;761};763template<typename T, typename Fn>764kj::Promise<T> ProxyServer<Thread>::post(Fn&& fn)765{766auto ready = kj::newPromiseAndFulfiller<void>(); // Signaled when waiter is ready to post again.767auto cancel_monitor_ptr = kj::heap<CancelMonitor>();768CancelMonitor& cancel_monitor = *cancel_monitor_ptr;769// Keep a reference to the ProxyServer<Thread> instance by assigning it to770// the self variable. ProxyServer instances are reference-counted and if the771// client drops its reference, this variable keeps the instance alive until772// the thread finishes executing. The self variable needs to be destroyed on773// the event loop thread so it is freed in a sync() call below.774auto self = thisCap();775auto ret = m_thread_ready.then([this, self = std::move(self), fn = std::forward<Fn>(fn), ready_fulfiller = kj::mv(ready.fulfiller), cancel_monitor_ptr = kj::mv(cancel_monitor_ptr)]() mutable {776auto result = kj::newPromiseAndFulfiller<T>(); // Signaled when fn() is called, with its return value.777bool posted = m_thread_context.waiter->post([this, self = std::move(self), fn = std::forward<Fn>(fn), ready_fulfiller = kj::mv(ready_fulfiller), result_fulfiller = kj::mv(result.fulfiller), cancel_monitor_ptr = kj::mv(cancel_monitor_ptr)]() mutable {778// Fulfill ready.promise now, as soon as the Waiter starts executing779// this lambda, so the next ProxyServer<Thread>::post() call can780// immediately call waiter->post(). It is important to do this781// before calling fn() because fn() can make an IPC call back to the782// client, which can make another IPC call to this server thread.783// (This typically happens when IPC methods take std::function784// parameters.) When this happens the second call to the server785// thread should not be blocked waiting for the first call.786m_loop->sync([ready_fulfiller = kj::mv(ready_fulfiller)]() mutable {787ready_fulfiller->fulfill();788ready_fulfiller = nullptr;789});790std::optional<T> result_value;791kj::Maybe<kj::Exception> exception{kj::runCatchingExceptions([&]{ result_value.emplace(fn(*cancel_monitor_ptr)); })};792m_loop->sync([this, &result_value, &exception, self = kj::mv(self), result_fulfiller = kj::mv(result_fulfiller), cancel_monitor_ptr = kj::mv(cancel_monitor_ptr)]() mutable {793// Destroy CancelMonitor here before fulfilling or rejecting the794// promise so it doesn't get triggered when the promise is795// destroyed.796cancel_monitor_ptr = nullptr;797// Send results to the fulfiller. Technically it would be ok to798// skip this if promise was canceled, but it's simpler to just799// do it unconditionally.800KJ_IF_MAYBE(e, exception) {801assert(!result_value);802result_fulfiller->reject(kj::mv(*e));803} else {804assert(result_value);805result_fulfiller->fulfill(kj::mv(*result_value));806result_value.reset();807}808result_fulfiller = nullptr;809// Use evalLater to destroy the ProxyServer<Thread> self810// reference, if it is the last reference, because the811// ProxyServer<Thread> destructor needs to join the thread,812// which can't happen until this sync() block has exited.813m_loop->m_task_set->add(kj::evalLater([self = kj::mv(self)] {}));814});815});816// Assert that calling Waiter::post did not fail. It could only return817// false if a new function was posted before the previous one finished818// executing, but new functions are only posted when m_thread_ready is819// signaled, so this should never happen.820assert(posted);821return kj::mv(result.promise);822}).attach(kj::heap<CancelProbe>(cancel_monitor));823m_thread_ready = kj::mv(ready.promise);824return ret;825}827//! Given stream file descriptor, make a new ProxyClient object to send requests828//! over the stream. Also create a new Connection object embedded in the829//! client that is freed when the client is closed.830template <typename InitInterface>831std::unique_ptr<ProxyClient<InitInterface>> ConnectStream(EventLoop& loop, int fd)832{833typename InitInterface::Client init_client(nullptr);834std::unique_ptr<Connection> connection;835loop.sync([&] {836auto stream =837loop.m_io_context.lowLevelProvider->wrapSocketFd(fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP);838connection = std::make_unique<Connection>(loop, kj::mv(stream));839init_client = connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<InitInterface>();840Connection* connection_ptr = connection.get();841connection->onDisconnect([&loop, connection_ptr] {842MP_LOG(loop, Log::Warning) << "IPC client: unexpected network disconnect.";843delete connection_ptr;844});845});846return std::make_unique<ProxyClient<InitInterface>>(847kj::mv(init_client), connection.release(), /* destroy_connection= */ true);848}850//! Given stream and init objects, construct a new ProxyServer object that851//! handles requests from the stream by calling the init object. Embed the852//! ProxyServer in a Connection object that is stored and erased if853//! disconnected. This should be called from the event loop thread.854template <typename InitInterface, typename InitImpl, typename OnDisconnect>855void _Serve(EventLoop& loop, kj::Own<kj::AsyncIoStream>&& stream, InitImpl& init, OnDisconnect&& on_disconnect)856{857loop.m_incoming_connections.emplace_front(loop, kj::mv(stream), [&](Connection& connection) {858// Disable deleter so proxy server object doesn't attempt to delete the859// init implementation when the proxy client is destroyed or860// disconnected.861return kj::heap<ProxyServer<InitInterface>>(std::shared_ptr<InitImpl>(&init, [](InitImpl*){}), connection);862});863auto it = loop.m_incoming_connections.begin();864MP_LOG(loop, Log::Info) << "IPC server: socket connected.";865if (loop.testing_hook_connected) loop.testing_hook_connected();866it->onDisconnect([&loop, it, on_disconnect = std::forward<OnDisconnect>(on_disconnect)]() mutable {867MP_LOG(loop, Log::Info) << "IPC server: socket disconnected.";868loop.m_incoming_connections.erase(it);869on_disconnect();870if (loop.testing_hook_disconnected) loop.testing_hook_disconnected();871});872}874struct Listener875{876explicit Listener(kj::Own<kj::ConnectionReceiver>&& receiver, std::optional<size_t> max_connections)877: m_receiver(kj::mv(receiver)), m_max_connections(max_connections) {}879bool atCapacity() const880{881return m_max_connections && m_active_connections >= *m_max_connections;882}884kj::Own<kj::ConnectionReceiver> m_receiver;885std::optional<size_t> m_max_connections;886size_t m_active_connections{0};887};889template <typename InitInterface, typename InitImpl>890void _Listen(const std::shared_ptr<Listener>& listener, EventLoop& loop, InitImpl& init)891{892if (listener->atCapacity()) return;894auto* receiver = listener->m_receiver.get();895loop.m_task_set->add(receiver->accept().then(896[&loop, &init, listener](kj::Own<kj::AsyncIoStream>&& stream) {897++listener->m_active_connections;898_Serve<InitInterface>(loop, kj::mv(stream), init, [&loop, &init, listener] {899const bool resume_accept{listener->atCapacity()};900assert(listener->m_active_connections > 0);901--listener->m_active_connections;902if (resume_accept) _Listen<InitInterface>(listener, loop, init);903});904_Listen<InitInterface>(listener, loop, init);905}));906}908//! Given stream file descriptor and an init object, handle requests on the909//! stream by calling methods on the Init object.910template <typename InitInterface, typename InitImpl>911void ServeStream(EventLoop& loop, int fd, InitImpl& init)912{913_Serve<InitInterface>(914loop,915loop.m_io_context.lowLevelProvider->wrapSocketFd(fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP),916init,917[] {});918}920//! Given listening socket file descriptor and an init object, handle incoming921//! connections and requests by calling methods on the Init object.922template <typename InitInterface, typename InitImpl>923void ListenConnections(EventLoop& loop, int fd, InitImpl& init, std::optional<size_t> max_connections = std::nullopt)924{925loop.sync([&]() {926auto listener{std::make_shared<Listener>(927loop.m_io_context.lowLevelProvider->wrapListenSocketFd(fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP),928max_connections)};929_Listen<InitInterface>(listener, loop, init);930});931}933extern thread_local ThreadContext g_thread_context; // NOLINT(bitcoin-nontrivial-threadlocal)934// Silence nonstandard bitcoin tidy error "Variable with non-trivial destructor935// cannot be thread_local" which should not be a problem on modern platforms, and936// could lead to a small memory leak at worst on older ones.938} // namespace mp940#endif // MP_PROXY_IO_H
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.