Skip to content

proxy-io.h: Add Connection disconnect and waitDrained methods - #335

Open
ryanofsky wants to merge 3 commits into
bitcoin-core:masterfrom
ryanofsky:pr/keepconn
Open

proxy-io.h: Add Connection disconnect and waitDrained methods#335
ryanofsky wants to merge 3 commits into
bitcoin-core:masterfrom
ryanofsky:pr/keepconn

Conversation

@ryanofsky

@ryanofsky ryanofsky commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Add Connection class disconnect and waitDrained methods to provide more flexibility when forcibly disconnecting from remote clients or servers.

Without these methods, the only way to forcibly close IPC connections is to delete Connection objects. This works but is not ideal because once a Connection object is gone, it is difficult to track state still associated with the connection, particularly:

  • ProxyServer objects that may still be alive because they are executing asynchronous requests made before the disconnect. Without a way to track these objects, there is no generic way to wait for requests to finish existing after disconnecting. So individual IPC interfaces like the Bitcoin mining interface would need to implement custom synchronization to avoid race conditions during shutdown. Followup PR ipc: make ipc::disconnectIncoming wait for in-progress calls to complete bitcoin/bitcoin#35932 builds on this PR, calling the new waitDrained method introduced here to avoid IPC mining crashes on Bitcoin core shutdown without needing to change the mining code. A unit test is added here simulating these mining crashes.

  • ProxyClient objects that contain pointers to Connection objects. Currently ProxyClient object need to register cleanup handlers with Connection objects to deal with Connections being deleted, which consumes memory and complicates ProxyClient shutdown logic. After this change, a followup PR will drop the cleanup handlers so Connection objects no longer need to track lists of ProxyClient objects associated with them. This is implemented in proxy-io: Reference-count Connection objects #336.

ryanofsky and others added 3 commits August 3, 2026 18:29
Split connection teardown out of ~Connection into an idempotent disconnect()
method, with the destructor delegating to it. This is a behavior-neutral
refactor: the same steps run in the same order on destruction.

Having a separate disconnect() method allows severing a connection while
keeping the Connection object alive, which the next commits use to let
shutdown code wait for in-flight server call bodies to finish after a
disconnect (bitcoin/bitcoin#35845). Two details are new:

- disconnect() cancels the m_on_disconnect handlers before severing the
  connection. Previously they were implicitly canceled when the TaskSet
  member was destroyed. When disconnect() is called separately from
  destruction, this is required for correctness: severing the stream
  completes m_network.onDisconnect(), and the registered handlers (_Serve,
  ConnectStream) destroy the Connection object out from under the caller.

- disconnect() explicitly releases m_thread_pool and m_thread_map so worker
  thread teardown happens at disconnect time whether or not the object is
  destroyed right away. Previously this happened implicitly during member
  destruction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a per-connection ServerObjectTracker counting live ProxyServer objects,
incremented in the ProxyServerBase constructor and decremented in its
destructor, with Connection::waitDrained() blocking until the count reaches
zero and Connection::pendingServerObjects() exposing it for logging.

Disconnecting a connection cancels the KJ promise of an in-flight call, but a
C++ server method body already dispatched to a worker thread runs to
completion. Counting live server objects turns Cap'n Proto's object lifetime
rules into a usable quiescence signal: a ProxyServer object is not destroyed
until its outstanding calls finish (the target capability is kept alive for
the duration of a call and pinned by post()/PassField via thisCap()), so
after disconnect() the count drains to zero exactly when no server call body
is still executing. Waiting for that lets shutdown code avoid freeing
application state that a still-running call body dereferences
(bitcoin/bitcoin#35845).

The tracker is held via shared_ptr by the Connection and by every
ProxyServer object because objects kept alive by in-flight calls can outlive
the Connection on some teardown paths (see ~ProxyServerBase), and their
destructors must decrement state that is still valid. It must be declared
before m_rpc_system, whose construction creates the bootstrap server object
that registers itself with the tracker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a deterministic mptest regression test for bitcoin/bitcoin#35845: hold a
server method body in flight on a worker thread, call
Connection::disconnect(), and assert that Connection::waitDrained() blocks
until the body finishes and its server object is destroyed. Also covers
destroying an already-disconnected connection (~Connection noticing
disconnect() has run).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@DrahtBot

DrahtBot commented Aug 7, 2026

Copy link
Copy Markdown

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline and AI policy for information on the review process.

Type Reviewers
Concept ACK xyzconstant

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

@xyzconstant

Copy link
Copy Markdown
Contributor

Concept ACK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants